refactor: db

This commit is contained in:
cha0s 2021-01-19 09:56:44 -06:00
parent 9eb3dfe2c7
commit 17153dc50a
21 changed files with 1530 additions and 1606 deletions

View File

@ -10,8 +10,10 @@
], ],
} }
'@latus/db': { '@latus/db': {
}
'@latus/db/server': {
models.decorate: [ models.decorate: [
'@latus/user/local', '@latus/user/local/server',
], ],
docker: 'cached', docker: 'cached',
} }

File diff suppressed because it is too large Load Diff

View File

@ -34,15 +34,16 @@ export const gatherWithLatus = (
) => (latus) => ( ) => (latus) => (
Object.fromEntries(context.keys().map((path) => { Object.fromEntries(context.keys().map((path) => {
const M = context(path).default; const M = context(path).default;
if ('function' !== typeof M) { if ('function' !== typeof M || /^class\s/.test(Function.prototype.toString.call(M))) {
throw new ReferenceError( throw new ReferenceError(
`gatherWithLatus: require(${path}).default is not a function (from: ${context.id})`, `gatherWithLatus: require(${path}).default is not a function (from: ${context.id})`,
); );
} }
return [ const r = [
transformer(basename(path, extname(path))), transformer(basename(path, extname(path))),
M(latus), M(latus),
]; ];
return r;
})) }))
); );
@ -65,12 +66,23 @@ export default (
Object.entries(composed) Object.entries(composed)
.sort(([lname], [rname]) => (lname < rname ? -1 : 1)) .sort(([lname], [rname]) => (lname < rname ? -1 : 1))
.map(([type, Class]) => { .map(([type, Class]) => {
// eslint-disable-next-line no-eval
const Subclass = eval(`(${type}) => class Gathered${type} extends ${type} {}`)(Class);
const thisUid = uid++; const thisUid = uid++;
const source = `
(${type}) => class Gathered${type} extends ${type} {
static get ${idAttribute}() {
return ${thisUid};
}
static get ${typeAttribute}() {
return '${type}';
}
}
`;
// eslint-disable-next-line no-eval
const Subclass = eval(source)(Class);
fromId[thisUid] = Subclass; fromId[thisUid] = Subclass;
Subclass[idAttribute] = thisUid;
Subclass[typeAttribute] = type;
return [ return [
type, type,
Subclass, Subclass,

View File

@ -1,8 +1,5 @@
import D from 'debug';
import Sequelize from 'sequelize'; import Sequelize from 'sequelize';
const debug = D('@latus/db/db');
const { const {
MYSQL_USER, MYSQL_USER,
MYSQL_PASSWORD, MYSQL_PASSWORD,
@ -14,7 +11,7 @@ const {
export async function createDatabaseConnection(latus) { export async function createDatabaseConnection(latus) {
const { const {
config: { config: {
'@latus/db': { '@latus/db/server': {
database, database,
host, host,
password, password,
@ -32,32 +29,26 @@ export async function createDatabaseConnection(latus) {
port: MYSQL_PORT || port, port: MYSQL_PORT || port,
username: MYSQL_USER || user, username: MYSQL_USER || user,
}); });
// eslint-disable-next-line no-param-reassign latus.set('%sequelize', sequelize);
latus.config['%sequelize'] = sequelize; const Models = latus.get('%models.fromName');
// eslint-disable-next-line no-console Object.values(Models)
const Models = latus.invokeReduce('@latus/db/models'); .filter((Model) => Model.attributes)
debug('models: %O', Object.keys(Models)); .forEach((Model) => {
const map = latus.invokeComposed('@latus/db/models.decorate', Models);
const decorated = Object.fromEntries(Object.entries(map).map(([name, Model]) => ([
name,
class FinalModel extends Model {
static name = name;
},
])));
debug('composed: %O', Object.keys(decorated));
Object.entries(decorated)
.filter(([, Model]) => Model.attributes)
.forEach(([, Model]) => {
Model.init(Model.attributes, { Model.init(Model.attributes, {
sequelize, sequelize,
underscored: true, underscored: true,
}); });
}); });
Object.entries(decorated).forEach(([, Model]) => Model.associate(decorated)); Object.values(Models)
// eslint-disable-next-line no-param-reassign .forEach((Model) => Model.associate(Models));
latus.config['%models'] = decorated; latus.set('%models.fromName', Models);
latus.set(
'%models.fromId',
Object.fromEntries(
Object.values(Models)
.map((Model) => [Model.id, Model]),
),
);
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
try { try {
@ -70,7 +61,7 @@ export async function createDatabaseConnection(latus) {
await new Promise((resolve) => setTimeout(resolve, 250)); await new Promise((resolve) => setTimeout(resolve, 250));
} }
} }
Object.entries(decorated).forEach(([, Model]) => Model.sync()); Object.values(Models).forEach((Model) => Model.sync());
await sequelize.sync(); await sequelize.sync();
} }

View File

@ -1,84 +0,0 @@
import D from 'debug';
import Sequelize from 'sequelize';
const debug = D('@latus/db/db');
const {
MYSQL_USER,
MYSQL_PASSWORD,
MYSQL_HOST,
MYSQL_PORT,
MYSQL_DATABASE,
} = process.env;
export const ModelMap = (latus) => latus.config['%models'];
export async function createDatabaseConnection(latus) {
const {
config: {
'@latus/db': {
database,
host,
password,
port,
user,
},
},
} = latus;
const sequelize = new Sequelize({
database: MYSQL_DATABASE || database,
dialect: 'mysql',
host: MYSQL_HOST || host,
logging: false,
password: MYSQL_PASSWORD || password,
port: MYSQL_PORT || port,
username: MYSQL_USER || user,
});
// eslint-disable-next-line no-param-reassign
latus.config['%sequelize'] = sequelize;
// eslint-disable-next-line no-console
const Models = latus.invokeReduce('@latus/db/models');
debug('models: %O', Object.keys(Models));
const map = latus.invokeComposed('@latus/db/models.decorate', Models);
const decorated = Object.fromEntries(Object.entries(map).map(([name, Model]) => ([
name,
class FinalModel extends Model {
static name = name;
},
])));
debug('composed: %O', Object.keys(decorated));
Object.entries(decorated)
.filter(([, Model]) => Model.attributes)
.forEach(([, Model]) => {
Model.init(Model.attributes, {
sequelize,
underscored: true,
});
});
Object.entries(decorated).forEach(([, Model]) => Model.associate(decorated));
// eslint-disable-next-line no-param-reassign
latus.config['%models'] = decorated;
// eslint-disable-next-line no-constant-condition
while (true) {
try {
// eslint-disable-next-line no-await-in-loop
await sequelize.authenticate();
break;
}
catch (error) {
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => setTimeout(resolve, 250));
}
}
Object.entries(decorated).forEach(([, Model]) => Model.sync());
await sequelize.sync();
}
export function destroyDatabaseConnection(databaseConnection) {
if (!databaseConnection) {
return undefined;
}
return databaseConnection.close();
}

View File

@ -5,7 +5,7 @@ import mkdirp from 'mkdirp';
export default function createDockerContainer(latus) { export default function createDockerContainer(latus) {
const { const {
config: { config: {
'@latus/db': { '@latus/db/server': {
database, database,
docker, docker,
password, password,

View File

@ -1,14 +1 @@
export const ModelMap = (latus) => latus.config['%models']; export default {};
export default {
hooks: {
'@latus/core/config': () => ({
database: 'db',
docker: true,
host: '127.0.0.1',
password: 'UNSAFE_DEV_PASSWORD',
port: '32342',
user: 'root',
}),
},
};

View File

@ -1,3 +1,5 @@
import {gather} from '@latus/core';
import {createDatabaseConnection} from './connection'; import {createDatabaseConnection} from './connection';
import createDockerContainer from './docker'; import createDockerContainer from './docker';
@ -11,8 +13,26 @@ export {default as Model} from './model';
export default { export default {
hooks: { hooks: {
'@latus/core/config': () => ({
database: 'db',
docker: true,
host: '127.0.0.1',
password: 'UNSAFE_DEV_PASSWORD',
port: '32342',
user: 'root',
}),
'@latus/core/starting': (latus) => {
// eslint-disable-next-line no-param-reassign
latus.set('%models', gather(
latus,
{
type: '@latus/db/server/models',
typeAttribute: 'name',
},
));
},
'@latus/core/up': (latus) => { '@latus/core/up': (latus) => {
const {config: {'@latus/db': {docker}}} = latus; const {config: {'@latus/db/server': {docker}}} = latus;
if ('production' !== NODE_ENV && docker) { if ('production' !== NODE_ENV && docker) {
createDockerContainer(latus); createDockerContainer(latus);
} }

View File

@ -26,6 +26,7 @@
"test.js.map" "test.js.map"
], ],
"dependencies": { "dependencies": {
"@latus/core": "^2.0.0",
"debug": "4.3.1", "debug": "4.3.1",
"rate-limiter-flexible": "^2.1.13" "rate-limiter-flexible": "^2.1.13"
}, },

View File

@ -1,6 +1,5 @@
import {ModelMap} from '@latus/db'; import {gatherWithLatus} from '@latus/core';
import Ban from './ban';
import LimitedPacket from './limited-packet'; import LimitedPacket from './limited-packet';
import createLimiter from './limiter'; import createLimiter from './limiter';
@ -8,9 +7,9 @@ export {default as createLimiter} from './limiter';
export default { export default {
hooks: { hooks: {
'@latus/db/models': (latus) => ({ '@latus/db/server/models': gatherWithLatus(
Ban: Ban(latus), require.context('./models', false, /\.js$/),
}), ),
'@latus/http/request': (latus) => { '@latus/http/request': (latus) => {
const {config: {'@latus/governor': {http}}} = latus; const {config: {'@latus/governor': {http}}} = latus;
const limiter = createLimiter(latus, { const limiter = createLimiter(latus, {
@ -18,7 +17,7 @@ export default {
...http, ...http,
}); });
return async (req, res, next) => { return async (req, res, next) => {
const {Ban} = ModelMap(latus); const {Ban} = latus.get('%models.fromName');
try { try {
await Ban.check(req); await Ban.check(req);
} }
@ -51,7 +50,7 @@ export default {
}); });
return async (socket, next) => { return async (socket, next) => {
const {handshake: req} = socket; const {handshake: req} = socket;
const {Ban} = ModelMap(latus); const {Ban} = latus.get('%models.fromName');
try { try {
await Ban.check(req); await Ban.check(req);
} }

View File

@ -901,7 +901,7 @@
object-assign "^4.1.1" object-assign "^4.1.1"
scheduler "^0.20.1" scheduler "^0.20.1"
"@latus/core@2.0.0": "@latus/core@2.0.0", "@latus/core@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "http://npm.cha0sdev/@latus%2fcore/-/core-2.0.0.tgz#2ca04903351edc14f29fcec2ad6ab5e19c72e072" resolved "http://npm.cha0sdev/@latus%2fcore/-/core-2.0.0.tgz#2ca04903351edc14f29fcec2ad6ab5e19c72e072"
integrity sha512-Sq4nPpQzRektLgsXRFQqhZWpUWY1bFTrVbm5X0vGTyTTlfQPmMbHyddmtdVTB8NbANvleuw4PvfuNVqKU4le6w== integrity sha512-Sq4nPpQzRektLgsXRFQqhZWpUWY1bFTrVbm5X0vGTyTTlfQPmMbHyddmtdVTB8NbANvleuw4PvfuNVqKU4le6w==

View File

@ -18,6 +18,8 @@
"files": [ "files": [
"index.js", "index.js",
"index.js.map", "index.js.map",
"server.js",
"server.js.map",
"test.js", "test.js",
"test.js.map" "test.js.map"
], ],

View File

@ -33,6 +33,7 @@
"test.js.map" "test.js.map"
], ],
"dependencies": { "dependencies": {
"@latus/core": "^2.0.0",
"bcrypt": "^5.0.0", "bcrypt": "^5.0.0",
"debug": "4.3.1", "debug": "4.3.1",
"express": "^4.17.1", "express": "^4.17.1",

View File

@ -1,11 +1,9 @@
import {randomBytes} from 'crypto'; import {randomBytes} from 'crypto';
import {ModelMap} from '@latus/db'; import {decorateWithLatus} from '@latus/core';
import passport from 'passport'; import passport from 'passport';
import LocalStrategy from 'passport-local'; import LocalStrategy from 'passport-local';
import UserLocal from '../models/user-local';
export default { export default {
hooks: { hooks: {
'@latus/core/config': () => ({ '@latus/core/config': () => ({
@ -16,7 +14,7 @@ export default {
passport.use(new LocalStrategy( passport.use(new LocalStrategy(
{usernameField: 'email'}, {usernameField: 'email'},
async (email, password, fn) => { async (email, password, fn) => {
const {User} = ModelMap(latus); const {User} = latus.get('%models.fromName');
try { try {
const user = await User.findOne({where: {email}}); const user = await User.findOne({where: {email}});
fn(undefined, user && await user.validatePassword(password) && user); fn(undefined, user && await user.validatePassword(password) && user);
@ -27,10 +25,9 @@ export default {
}, },
)); ));
}, },
'@latus/db/models.decorate': (Models) => ({ '@latus/db/server/models.decorate': decorateWithLatus(
...Models, require.context('../models/decorators', false, /\.js$/),
User: UserLocal(Models.User), ),
}),
'@latus/http/routes': ({ '@latus/http/routes': ({
config: { config: {
'@latus/user/local': { '@latus/user/local': {
@ -67,7 +64,7 @@ export default {
}, },
], ],
'@latus/repl/commands': (latus) => { '@latus/repl/commands': (latus) => {
const {User} = ModelMap(latus); const {User} = latus.get('%models.fromName');
return { return {
createUser: (spec) => { createUser: (spec) => {
const [email, maybePassword] = spec.split(' ', 2); const [email, maybePassword] = spec.split(' ', 2);

View File

@ -1,7 +1,7 @@
import {Types} from '@latus/db/server'; import {Types} from '@latus/db/server';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
export default (User) => class UserLocal extends User { export default (latus, User) => class UserLocal extends User {
static saltRounds = 10; static saltRounds = 10;

View File

@ -1,2 +0,0 @@
export {default as Permission} from './permission';
export {default as User} from './user';

View File

@ -1,6 +1,6 @@
import {Model, Types} from '@latus/db/server'; import {Model, Types} from '@latus/db/server';
class Permission extends Model { export default () => class Permission extends Model {
static get attributes() { static get attributes() {
return { return {
@ -13,6 +13,4 @@ class Permission extends Model {
this.belongsToMany(User, {through: 'user_permissions'}); this.belongsToMany(User, {through: 'user_permissions'});
} }
} };
export default Permission;

View File

@ -4,7 +4,7 @@ import {
Types, Types,
} from '@latus/db/server'; } from '@latus/db/server';
class User extends Model { export default () => class User extends Model {
static get attributes() { static get attributes() {
return { return {
@ -19,6 +19,4 @@ class User extends Model {
this.belongsToMany(Permission, {through: 'user_permissions'}); this.belongsToMany(Permission, {through: 'user_permissions'});
} }
} };
export default User;

View File

@ -1,10 +1,8 @@
import {ModelMap} from '@latus/db'; import {gatherWithLatus} from '@latus/core';
import D from 'debug'; import D from 'debug';
import passport from 'passport'; import passport from 'passport';
import LogOps from 'passport/lib/http/request'; import LogOps from 'passport/lib/http/request';
import * as models from './models';
const debug = D('@latus/user/passport'); const debug = D('@latus/user/passport');
export default { export default {
@ -12,7 +10,7 @@ export default {
'@latus/core/up': (latus) => { '@latus/core/up': (latus) => {
passport.serializeUser((user, fn) => fn(null, user.id)); passport.serializeUser((user, fn) => fn(null, user.id));
passport.deserializeUser(async (id, fn) => { passport.deserializeUser(async (id, fn) => {
const {User} = ModelMap(latus); const {User} = latus.get('%models.fromName');
try { try {
fn(undefined, await User.findByPk(id)); fn(undefined, await User.findByPk(id));
} }
@ -21,14 +19,16 @@ export default {
} }
}); });
}, },
'@latus/db/models': () => models, '@latus/db/server/models': gatherWithLatus(
require.context('./models', false, /\.js$/),
),
'@latus/http/request': (latus) => (req, res, next) => { '@latus/http/request': (latus) => (req, res, next) => {
debug('@latus/http/request: passport.initialize()'); debug('@latus/http/request: passport.initialize()');
passport.initialize()(req, res, () => { passport.initialize()(req, res, () => {
debug('@latus/http/request: passport.session()'); debug('@latus/http/request: passport.session()');
passport.session()(req, res, () => { passport.session()(req, res, () => {
if (!req.user) { if (!req.user) {
const {User} = ModelMap(latus); const {User} = latus.get('%models.fromName');
req.user = new User(); req.user = new User();
req.user.id = 0; req.user.id = 0;
} }
@ -67,7 +67,7 @@ export default {
passport.session()(socket.handshake, undefined, async () => { passport.session()(socket.handshake, undefined, async () => {
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
if (!socket.handshake.user) { if (!socket.handshake.user) {
const {User} = ModelMap(latus); const {User} = latus.get('%models.fromName');
socket.handshake.user = new User(); socket.handshake.user = new User();
socket.handshake.user.id = 0; socket.handshake.user.id = 0;
} }

View File

@ -901,7 +901,7 @@
object-assign "^4.1.1" object-assign "^4.1.1"
scheduler "^0.20.1" scheduler "^0.20.1"
"@latus/core@2.0.0": "@latus/core@2.0.0", "@latus/core@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "http://npm.cha0sdev/@latus%2fcore/-/core-2.0.0.tgz#2ca04903351edc14f29fcec2ad6ab5e19c72e072" resolved "http://npm.cha0sdev/@latus%2fcore/-/core-2.0.0.tgz#2ca04903351edc14f29fcec2ad6ab5e19c72e072"
integrity sha512-Sq4nPpQzRektLgsXRFQqhZWpUWY1bFTrVbm5X0vGTyTTlfQPmMbHyddmtdVTB8NbANvleuw4PvfuNVqKU4le6w== integrity sha512-Sq4nPpQzRektLgsXRFQqhZWpUWY1bFTrVbm5X0vGTyTTlfQPmMbHyddmtdVTB8NbANvleuw4PvfuNVqKU4le6w==