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/server': {
models.decorate: [
'@latus/user/local',
'@latus/user/local/server',
],
docker: 'cached',
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,8 +1,5 @@
import D from 'debug';
import Sequelize from 'sequelize';
const debug = D('@latus/db/db');
const {
MYSQL_USER,
MYSQL_PASSWORD,
@ -14,7 +11,7 @@ const {
export async function createDatabaseConnection(latus) {
const {
config: {
'@latus/db': {
'@latus/db/server': {
database,
host,
password,
@ -32,32 +29,26 @@ export async function createDatabaseConnection(latus) {
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]) => {
latus.set('%sequelize', sequelize);
const Models = latus.get('%models.fromName');
Object.values(Models)
.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;
Object.values(Models)
.forEach((Model) => Model.associate(Models));
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
while (true) {
try {
@ -70,7 +61,7 @@ export async function createDatabaseConnection(latus) {
await new Promise((resolve) => setTimeout(resolve, 250));
}
}
Object.entries(decorated).forEach(([, Model]) => Model.sync());
Object.values(Models).forEach((Model) => Model.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) {
const {
config: {
'@latus/db': {
'@latus/db/server': {
database,
docker,
password,

View File

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

View File

@ -1,3 +1,5 @@
import {gather} from '@latus/core';
import {createDatabaseConnection} from './connection';
import createDockerContainer from './docker';
@ -11,8 +13,26 @@ export {default as Model} from './model';
export default {
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) => {
const {config: {'@latus/db': {docker}}} = latus;
const {config: {'@latus/db/server': {docker}}} = latus;
if ('production' !== NODE_ENV && docker) {
createDockerContainer(latus);
}

View File

@ -26,6 +26,7 @@
"test.js.map"
],
"dependencies": {
"@latus/core": "^2.0.0",
"debug": "4.3.1",
"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 createLimiter from './limiter';
@ -8,9 +7,9 @@ export {default as createLimiter} from './limiter';
export default {
hooks: {
'@latus/db/models': (latus) => ({
Ban: Ban(latus),
}),
'@latus/db/server/models': gatherWithLatus(
require.context('./models', false, /\.js$/),
),
'@latus/http/request': (latus) => {
const {config: {'@latus/governor': {http}}} = latus;
const limiter = createLimiter(latus, {
@ -18,7 +17,7 @@ export default {
...http,
});
return async (req, res, next) => {
const {Ban} = ModelMap(latus);
const {Ban} = latus.get('%models.fromName');
try {
await Ban.check(req);
}
@ -51,7 +50,7 @@ export default {
});
return async (socket, next) => {
const {handshake: req} = socket;
const {Ban} = ModelMap(latus);
const {Ban} = latus.get('%models.fromName');
try {
await Ban.check(req);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import {
Types,
} from '@latus/db/server';
class User extends Model {
export default () => class User extends Model {
static get attributes() {
return {
@ -19,6 +19,4 @@ class User extends Model {
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 passport from 'passport';
import LogOps from 'passport/lib/http/request';
import * as models from './models';
const debug = D('@latus/user/passport');
export default {
@ -12,7 +10,7 @@ export default {
'@latus/core/up': (latus) => {
passport.serializeUser((user, fn) => fn(null, user.id));
passport.deserializeUser(async (id, fn) => {
const {User} = ModelMap(latus);
const {User} = latus.get('%models.fromName');
try {
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) => {
debug('@latus/http/request: passport.initialize()');
passport.initialize()(req, res, () => {
debug('@latus/http/request: passport.session()');
passport.session()(req, res, () => {
if (!req.user) {
const {User} = ModelMap(latus);
const {User} = latus.get('%models.fromName');
req.user = new User();
req.user.id = 0;
}
@ -67,7 +67,7 @@ export default {
passport.session()(socket.handshake, undefined, async () => {
/* eslint-disable no-param-reassign */
if (!socket.handshake.user) {
const {User} = ModelMap(latus);
const {User} = latus.get('%models.fromName');
socket.handshake.user = new User();
socket.handshake.user.id = 0;
}

View File

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