Big commit again...
Some checks failed
continuous-integration/drone/push Build encountered an error

This commit is contained in:
2023-05-02 01:26:01 -04:00
parent 52782efc2e
commit 2eadb66246
19 changed files with 1638 additions and 1854 deletions

23
.drone.yml Normal file
View File

@@ -0,0 +1,23 @@
kind: pipeline
type: docker
name: default
workspace:
path: /drone/src
kind: pipeline
name: default
steps:
- name: test
image: node:latest
commands:
- npm install -g yarn
- yarn install
- yarn test
trigger:
branch:
- main
event:
- push

View File

@@ -1,5 +0,0 @@
{
"watch": ["src"],
"exec": "yarn run serve",
"ext": "ts"
}

View File

@@ -1,7 +1,6 @@
{
"name": "@mifi/grow-api",
"name": "@mifi/auth",
"version": "0.0.1",
"main": "server.js",
"author": "mifi (Mike Fitzpatrick)",
"license": "MIT",
"scripts": {
@@ -13,6 +12,7 @@
},
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
"@types/bcrypt": "^5.0.0",
"@types/crypto-js": "^4.1.1",
"@types/dotenv": "^8.2.0",
"@types/http-status-codes": "^1.2.0",
@@ -44,7 +44,8 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@mifi/grow-db": "0.0.1",
"@simplewebauthn/server": "^7.2.0",
"bcrypt": "^5.1.0",
"crypto": "^1.0.1",
"dotenv": "^16.0.3",
"http-status-codes": "^2.2.0",
@@ -67,6 +68,6 @@
"description": "",
"repository": {
"type": "git",
"url": "https://git.mifi.dev/mifi/grow-api.git"
"url": "https://git.mifi.dev/mifi/auth.git"
}
}

View File

@@ -1,27 +0,0 @@
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import cookie from 'koa-cookie';
import passport from 'koa-passport';
import session from 'koa-session';
import { performanceLogger, perfromanceTimer } from '../middleware/performance';
import { errorHandler } from '../middleware/errorHandler';
const app: Koa = new Koa();
app.use(errorHandler);
app.use(perfromanceTimer);
app.use(performanceLogger);
app.use(bodyParser());
app.use(cookie());
app.keys = [process.env.SESSION_KEYS as string];
app.use(session({}, app));
app.use(passport.initialize())
app.use(passport.session())
// Application error logging.
app.on('error', console.error);
export default app;

View File

@@ -1,16 +0,0 @@
import passport from 'koa-passport';
import Users from 'grow-db/lib/models/users';
import { User } from 'grow-db/lib/schemas/user';
passport.serializeUser((user: User, done) => { done(null, user._id); });
passport.deserializeUser(async (id, done) => {
const user = await Users.findById(id);
if (user) {
done(null, user);
}
done('user not found', null);
});

View File

@@ -1,231 +0,0 @@
// const errors = require('restify-errors');
// const config = require('../config');
// const handlePassportResponse = (req, res, next) => (err, user, info) => {
// if (err) {
// return next(err);
// }
// const isVerifiedUser = user &&
// user.isRegistrationVerified();
// if (user && isVerifiedUser) {
// return res.send({ ...user.toAuthJSON() });
// } else if (user && !isVerifiedUser){
// return res.send({
// registrationSuccess: true,
// nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.'
// });
// }
// return res.send(400, info);
// };
// module.exports = function (server, auth) {
// const { passport } = auth;
// /* Local Auth */
// server.post('/auth', (req, res, next) => {
// const { body: { username = null, password = null } = {} } = req;
// if (!username || !password) {
// let errors = {};
// if (!username) {
// errors.username = 'is required';
// }
// if (!password) {
// errors.password = 'is required';
// }
// return res.send(422, { errors });
// }
// const callback = handlePassportResponse(req, res, next);
// return passport.authenticate('local', { session: false }.then(callback)(req, res, next);
// });
// /**
// * SERVICES
// */
// /* Google */
// server.get(
// '/auth/google',
// passport.authenticate('google', { scope: 'profile email', session: false }),
// );
// server.get(
// '/auth/google/callback',
// (req, res, next) => {
// const callback = handlePassportResponse(req, res, next);
// return passport.authenticate(
// 'google',
// { failureRedirect: '/login' },
// callback,
// )(req, res, next);
// },
// );
// /* Facebook */
// server.get(
// '/auth/facebook/login',
// passport.authenticate('facebook', {
// scope: ['email', 'public_profile'],
// session: false,
// }),
// );
// server.get(
// '/auth/facebook/loggedin',
// (req, res, next) => {
// const callback = handlePassportResponse(req, res, next);
// return passport.authenticate(
// 'facebook',
// { failureRedirect: '/login' },
// callback,
// )(req, res, next);
// }
// );
// server.get(
// '/auth/facebook/link',
// auth.secure,
// (req, res, next) => {
// req.user.record.setLinkCheckBit((err, linkCheckBit) => {
// passport.authenticate('facebookLink', {
// scope: ['email', 'public_profile'],
// session: false,
// state: linkCheckbit,
// })(req, res, next);
// });
// },
// );
//
// server.get(
// '/auth/facebook/linked',
// (req, res, next) => {
// const linkCheckBit = req.query.state;
//
// return passport.authenticate(
// 'facebook',
// { failureRedirect: '/profile' },
// (err, profile) => {
// if (err) {
// return next(err);
// }
//
// User.linkFacebookProfile(linkCheckBit, profile, (err, user) => {
// if (err) {
// return next(err);
// }
//
// if (!user) {
// return next(err, false, 'Linking the account to Facebook was unsuccessful, please try again.');
// }
//
// res.send({
// success: true,
// info: 'Facerbook account successfully linked',
// });
// });
// },
// )(req, res, next);
// }
// );
};
import Koa from 'koa';
import Router from 'koa-router';
import { StatusCodes } from 'http-status-codes';
import Users from 'grow-db/lib/models/users';
const handlePassportResponse = (ctx: Koa.Context) => (err, user, info) => {
if (err) {
return next(err);
}
const isVerifiedUser = user &&
user.isRegistrationVerified();
if (user && isVerifiedUser) {
return res.send({ ...user.toAuthJSON() });
} else if (user && !isVerifiedUser){
return res.send({
registrationSuccess: true,
nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.'
});
}
return res.send(400, info);
};
const routerOpts: Router.IRouterOptions = {
prefix: '/auth',
};
const router: Router = new Router(routerOpts);
router.get('/', async (ctx: Koa.Context) => {
const data = await Customers.find({}).exec();
ctx.body = { data };
});
router.get('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findById(ctx.params.customer_id).populate('person').exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { data };
});
router.delete('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findByIdAndDelete(ctx.params.customer_id).exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});
router.post('/', async (ctx: Koa.Context) => {
const data = await Customers.create(ctx.body);
data.save();
ctx.body = { success: true, data };
});
router.post('/', async (ctx: Koa.Context) => {
const { body: { username = null, password = null } = {} } = ctx;
if (!username || !password) {
let errors = {};
if (!username) {
errors.username = 'is required';
}
if (!password) {
errors.password = 'is required';
}
ctx.status = StatusCodes.UNPROCESSABLE_ENTITY;
ctx.throw(422, { errors });
}
const callback = handlePassportResponse(req, res, next);
return passport.authenticate('local', { session: false }, callback)(req, res, next);
});
router.patch('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findByIdAndUpdate(ctx.params.customer_id);
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});

View File

@@ -1,47 +0,0 @@
import Koa from 'koa';
import Router from 'koa-router';
import { StatusCodes } from 'http-status-codes';
import Customers from 'grow-db/lib/models/customers';
const routerOpts: Router.IRouterOptions = {
prefix: '/customers',
};
const router: Router = new Router(routerOpts);
router.get('/', async (ctx: Koa.Context) => {
const data = await Customers.find({}).exec();
ctx.body = { data };
});
router.get('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findById(ctx.params.customer_id).populate('person').exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { data };
});
router.delete('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findByIdAndDelete(ctx.params.customer_id).exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});
router.post('/', async (ctx: Koa.Context) => {
const data = await Customers.create(ctx.body);
data.save();
ctx.body = { success: true, data };
});
router.patch('/:customer_id', async (ctx: Koa.Context) => {
const data = await Customers.findByIdAndUpdate(ctx.params.customer_id);
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});

View File

@@ -1,17 +0,0 @@
import Koa from 'koa';
import Router from 'koa-router';
import { StatusCodes } from 'http-status-codes';
import Users from 'grow-db/lib/models/users';
const routerOpts: Router.IRouterOptions = {
prefix: '/login',
};
const router: Router = new Router(routerOpts);
router.post('/', async (ctx: Koa.Context) => {
const data = await Users.create(ctx.body);
data.save();
ctx.body = { success: true, data };
});

View File

@@ -1,46 +0,0 @@
import Koa from 'koa';
import Router from 'koa-router';
import { StatusCodes } from 'http-status-codes';
import People from 'grow-db/lib/models/people';
const routerOpts: Router.IRouterOptions = {
prefix: '/people',
};
const router: Router = new Router(routerOpts);
router.get('/', async (ctx: Koa.Context) => {
const data = await People.find({}).exec();
ctx.body = { data };
});
router.get('/:person_id', async (ctx: Koa.Context) => {
const data = await People.findById(ctx.params.person_id).populate('person').exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { data };
});
router.delete('/:person_id', async (ctx: Koa.Context) => {
const data = await People.findByIdAndDelete(ctx.params.person_id).exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});
router.post('/', async (ctx: Koa.Context) => {
const data = await People.create(ctx.body);
data.save();
ctx.body = { success: true, data };
});
router.patch('/:person_id', async (ctx: Koa.Context) => {
const data = await People.findByIdAndUpdate(ctx.params.person_id);
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});

View File

@@ -1,68 +0,0 @@
const errors = require('restify-errors');
const jwt = require('jsonwebtoken');
const config = require('../config');
const User = require('../models/user');
const {
api: { url },
security: { reset: { route, tokenPlaceholder } },
} = config;
const routes = {
resetWithToken: `${route}/${tokenPlaceholder}([A-Za-z0-9_]+\.{3})`,
getTestToken: `${route}/generate`,
};
module.exports = function (server, auth) {
server.get(routes.getTestToken, auth.secure, function (req, res, next) {
const { record: user } = req.user;
const resetToken = user.generateResetToken();
const resetUrl = `${url}${route}/${resetToken}`;
res.send({ resetToken, resetUrl });
next();
});
server.post(routes.resetWithToken, auth.bypass, function (req, res, next) {
const { reset_token } = req.params;
const { password } = req.body;
if (!reset_token) {
return next(
new errors.InvalidContentError('A reset token was not provided.'),
);
}
if (!password) {
return next(
new errors.InvalidContentError('Password cannot be empty.'),
);
}
User.verifyTokenAndResetPassword(reset_token, password, (err, user, info) => {
if (err) {
console.error(err);
return next(
new errors.InvalidContentError(err),
);
}
if (!user) {
console.error(err);
res.send({
success: false,
info: 'Password reset failed. ' + info,
});
return next();
}
res.send({
success: true,
info: 'Password reset successful.',
...user.toAuthJSON()
});
next();
});
});
};

View File

@@ -1,106 +0,0 @@
const errors = require('restify-errors');
const User = require('../models/user');
module.exports = function (server, auth) {
const { passport } = auth;
server.post('/signup', auth.basic, (req, res, next) => {
const { body: { user = null } = {} } = req;
let errors = {};
let errorCount = 0;
if (!user) {
errors.user = 'is required - can\'t make something from nothing...';
errorCount++;
}
if (errorCount) {
return res.send(422, { errors });
}
User.register(user, (err, user, info) => {
if (err) {
next(err);
}
if (info) {
res.send(200, {
success: false,
nextSteps: 'Please fix the problems indicated and try again.',
...info
});
return next();
}
res.send(200, {
success: true,
nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.',
});
next();
});
});
server.get('/signup/validate/email/:email', auth.basic, (req, res, next) => {
const email = decodeURI(req.params.email);
User.findOne({ email }, (err, user) => {
if (err) {
return next(err);
}
res.send(200, { available: !!!user });
next();
});
});
server.get('/signup/validate/nom/:nom_de_bid', auth.basic, (req, res, next) => {
const nomDeBid = decodeURI(req.params.nom_de_bid);
User.findOne({ nomDeBid }, (err, user) => {
if (err) {
return next(err);
}
res.send(200, { available: !!!user });
next();
});
});
server.post('/signup/verify/resend', auth.basic, (req, res, next) => {
const { body: { email = null } = {} } = req;
User.resendVerificationEmail(email, (err, user, info) => {
if (err) {
next(err);
}
if (!user) {
res.send(200, {
success: false,
nextSteps: 'There was no user located with the email address provided. Please try again.',
});
return next();
}
if (user && info.success) {
res.send(200, {
success: true,
nextSteps: 'Check your email for our confirmation email, you will not be able to login without confirming.',
});
return next();
}
res.send(200, {
success: false,
nextSteps: 'There was a problem resending the verification email. Please try again later.',
});
next();
});
});
};

View File

@@ -1,49 +0,0 @@
import { Context } from 'koa';
import Router from 'koa-router';
import { } from 'koa-bodyparser';
import { StatusCodes } from 'http-status-codes';
import Strains from 'grow-db/lib/models/strains';
import { Strain } from 'grow-db/lib/schemas/strain';
const routerOpts: Router.IRouterOptions = {
prefix: '/strains',
};
const router: Router = new Router(routerOpts);
router.get('/', async (ctx: Context) => {
const data = await Strains.find({}).exec();
ctx.body = { data };
});
router.get('/:strain_id', async (ctx) => {
const data = await Strains.findById(ctx.params.strain_id).populate('person').exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { data };
});
router.delete('/:strain_id', async (ctx: Context) => {
const data = await Strains.findByIdAndDelete(ctx.params.strain_id).exec();
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});
router.post('/', async (ctx: Context) => {
const data = await Strains.create(ctx.request.body);
data.save();
ctx.body = { success: true, data };
});
router.patch('/:strain_id', async (ctx) => {
const data = await Strains.findByIdAndUpdate(ctx.params.strain_id, <Strain>ctx.request.body, { lean: true, returnDocument: 'after' });
if (!data) {
ctx.throw(StatusCodes.NOT_FOUND);
}
ctx.body = { success: true, data };
});

View File

@@ -1,5 +0,0 @@
import mongoose from 'mongoose';
export const connection = mongoose.connect(
`${process.env.DB_USER}:${process.env.DB_PASSWORD}@mongodb:27017/${process.env.DB_NAME}`
);

View File

@@ -1,13 +0,0 @@
import { StatusCodes } from "http-status-codes";
import { Context, Next } from "koa";
export const errorHandler = async (ctx: Context, next: Next) => {
try {
await next();
} catch (error: any) {
ctx.status = error.statusCode || error.status || StatusCodes.INTERNAL_SERVER_ERROR;
error.status = ctx.status;
ctx.body = { error };
ctx.app.emit('error', error, ctx);
}
};

View File

@@ -1,15 +0,0 @@
import { Next } from 'koa';
import { KoaContext } from '../types/KoaContext';
export const performanceLogger = async (ctx: KoaContext, next: Next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
};
export const perfromanceTimer = async (ctx: KoaContext, next: Next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
};

View File

@@ -1,12 +0,0 @@
import dotenv from 'dotenv';
dotenv.config();
import app from './app/app';
import { connection } from './database/database.connection';
const PORT: number = Number(process.env.PORT) || 9000;
connection.then(
() => app.listen(PORT),
(err) => console.error('ERROR!', err),
);

View File

@@ -1,12 +0,0 @@
import { Context, Request } from 'koa';
interface KoaRequest<RequestBody = any> extends Request {
body?: RequestBody;
}
export interface KoaContext<RequestBody = any, ResponseBody = any> extends Context {
request: KoaRequest<RequestBody>;
body: ResponseBody;
}
export interface KoaResponseContext<ResponseBody> extends KoaContext<any, ResponseBody> {};

2791
yarn.lock

File diff suppressed because it is too large Load Diff