Skip to content

Commit

Permalink
Server: Moved CLI commands to separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent22 committed Oct 27, 2021
1 parent 5c1cef8 commit dca13b3
Show file tree
Hide file tree
Showing 11 changed files with 22,450 additions and 25,783 deletions.
47,846 changes: 22,131 additions & 25,715 deletions packages/server/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"sqlite3": "^4.1.0",
"stripe": "^8.150.0",
"uuid": "^8.3.2",
"yargs": "^14.0.0",
"yargs": "^17.2.1",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
Expand All @@ -65,7 +65,7 @@
"@types/markdown-it": "^12.0.0",
"@types/mustache": "^0.8.32",
"@types/nodemailer": "^6.4.1",
"@types/yargs": "^13.0.2",
"@types/yargs": "^17.0.4",
"@types/zxcvbn": "^4.4.1",
"gulp": "^4.0.2",
"jest": "^26.6.3",
Expand Down
77 changes: 25 additions & 52 deletions packages/server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ require('source-map-support').install();

import * as Koa from 'koa';
import * as fs from 'fs-extra';
import { argv as yargsArgv } from 'yargs';
import Logger, { LoggerWrapper, TargetType } from '@joplin/lib/Logger';
import config, { initConfig, runningInDocker, EnvVariables } from './config';
import { createDb, dropDb } from './tools/dbTools';
import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown } from './db';
import { migrateLatest, waitForConnection, sqliteDefaultDir } from './db';
import { AppContext, Env, KoaNext } from './utils/types';
import FsDriverNode from '@joplin/lib/fs-driver-node';
import routeHandler from './middleware/routeHandler';
Expand All @@ -19,35 +17,21 @@ import startServices from './utils/startServices';
import { credentialFile } from './utils/testing/testUtils';
import apiVersionHandler from './middleware/apiVersionHandler';
import clickJackingHandler from './middleware/clickJackingHandler';
import deleteOldChanges from './commands/deleteOldChanges';
import newModelFactory from './models/factory';
import deleteOldChanges90 from './commands/deleteOldChanges90';
import setupCommands from './utils/setupCommands';

interface Argv {
env?: Env;
migrateLatest?: boolean;
migrateUp?: boolean;
migrateDown?: boolean;
migrateList?: boolean;
dropDb?: boolean;
pidfile?: string;
dropTables?: boolean;
createDb?: boolean;
envFile?: string;
deleteOldChanges?: boolean;
deleteOldChanges90?: boolean;
}

const argv: Argv = yargsArgv as any;

const nodeSqlite = require('sqlite3');
const cors = require('@koa/cors');
const nodeEnvFile = require('node-env-file');
const { shimInit } = require('@joplin/lib/shim-init-node.js');
shimInit({ nodeSqlite });

const env: Env = argv.env as Env || Env.Prod;

const defaultEnvVariables: Record<Env, EnvVariables> = {
dev: {
// To test with the Postgres database, uncomment DB_CLIENT below and
Expand Down Expand Up @@ -99,6 +83,11 @@ async function getEnvFilePath(env: Env, argv: any): Promise<string> {
}

async function main() {
const { selectedCommand, argv: yargsArgv } = await setupCommands();

const argv: Argv = yargsArgv as any;
const env: Env = argv.env as Env || Env.Prod;

const envFilePath = await getEnvFilePath(env, argv);

if (envFilePath) nodeEnvFile(envFilePath);
Expand Down Expand Up @@ -222,42 +211,26 @@ async function main() {

let runCommandAndExitApp = true;

if (argv.migrateLatest) {
const db = await connectDb(config().database);
await migrateLatest(db);
await disconnectDb(db);
} else if (argv.migrateUp) {
const db = await connectDb(config().database);
await migrateUp(db);
await disconnectDb(db);
} else if (argv.migrateDown) {
const db = await connectDb(config().database);
await migrateDown(db);
await disconnectDb(db);
} else if (argv.migrateList) {
const db = await connectDb(config().database);
console.info(await migrateList(db));
} else if (argv.dropDb) {
await dropDb(config().database, { ignoreIfNotExists: true });
} else if (argv.dropTables) {
const db = await connectDb(config().database);
await dropTables(db);
await disconnectDb(db);
} else if (argv.createDb) {
await createDb(config().database);
} else if (argv.deleteOldChanges || argv.deleteOldChanges90) {
// Eventually all commands should be started in a more generic way. All
// should go under /commands, and they will receive a context object
// with an intialized models property.
//
// Also should use yargs command system.
const connectionCheck = await waitForConnection(config().database);
const models = newModelFactory(connectionCheck.connection, config());
if (selectedCommand) {
const commandArgv = {
...argv,
_: (argv as any)._.slice(),
};
commandArgv._.splice(0, 1);

if (argv.deleteOldChanges90) {
await deleteOldChanges90({ models });
if (selectedCommand.commandName() === 'db') {
await selectedCommand.run(commandArgv, {
db: null,
models: null,
});
} else {
await deleteOldChanges({ models });
const connectionCheck = await waitForConnection(config().database);
const models = newModelFactory(connectionCheck.connection, config());

await selectedCommand.run(commandArgv, {
db: connectionCheck.connection,
models,
});
}
} else {
runCommandAndExitApp = false;
Expand Down
36 changes: 36 additions & 0 deletions packages/server/src/commands/BaseCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Options, PositionalOptions } from 'yargs';
import { DbConnection } from '../db';
import { Models } from '../models/factory';

export interface RunContext {
db: DbConnection;
models: Models;
}

export default abstract class BaseCommand {

public commandName(): string {
const splitted = this.command().split(' ');
if (!splitted.length) throw new Error(`Invalid command: ${this.command()}`);
return splitted[0];
}

public command(): string {
throw new Error('Not implemented');
}

public description(): string {
throw new Error('Not implemented');
}

public positionals(): Record<string, PositionalOptions> {
return {};
}

public options(): Record<string, Options> {
return {};
}

public abstract run(argv: any, context: RunContext): Promise<void>;

}
57 changes: 57 additions & 0 deletions packages/server/src/commands/DbCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { PositionalOptions } from 'yargs';
import config from '../config';
import { connectDb, disconnectDb, dropTables } from '../db';
import BaseCommand from './BaseCommand';
import { createDb } from '../tools/dbTools';

enum ArgvCommand {
DropTables = 'dropTables',
Create = 'create',
}

interface Argv {
command: ArgvCommand;
}

export default class DbCommand extends BaseCommand {

public command() {
return 'db <command>';
}

public description() {
return 'execute a database command';
}

public positionals(): Record<string, PositionalOptions> {
return {
'command': {
description: 'command to execute',
choices: [
ArgvCommand.Create,
ArgvCommand.DropTables,
],
},
};
}

public async run(argv: Argv): Promise<void> {


const commands: Record<ArgvCommand, Function> = {
create: async () => {
await createDb(config().database);
},
dropTables: async () => {
const db = await connectDb(config().database);
await dropTables(db);
await disconnectDb(db);
},
};

if (!commands[argv.command]) throw new Error(`Invalid command: ${argv.command}`);

await commands[argv.command]();
}

}
32 changes: 32 additions & 0 deletions packages/server/src/commands/DeleteOldChangesCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Options } from 'yargs';
import { Day } from '../utils/time';
import BaseCommand, { RunContext } from './BaseCommand';

interface Argv {
ttl: number;
}

export default class DeleteOldChangesCommand extends BaseCommand {

public command() {
return 'deleteOldChanges';
}

public description() {
return 'deletes old changes';
}

public options(): Record<string, Options> {
return {
'ttl': {
type: 'number',
description: 'TTL in days',
},
};
}

public async run(argv: Argv, runContext: RunContext): Promise<void> {
await runContext.models.change().deleteOldChanges(argv.ttl ? argv.ttl * Day : null);
}

}
80 changes: 80 additions & 0 deletions packages/server/src/commands/MigrateCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { PositionalOptions, Options } from 'yargs';
import Logger from '@joplin/lib/Logger';
import { disconnectDb, migrateDown, migrateLatest, migrateList, migrateUnlock, migrateUp } from '../db';
import BaseCommand, { RunContext } from './BaseCommand';

const logger = Logger.create('MigrateCommand');

enum ArgvCommand {
Up = 'up',
Down = 'down',
Latest = 'latest',
List = 'list',
Unlock = 'unlock',
}

interface Argv {
command: ArgvCommand;
}

export default class MigrateCommand extends BaseCommand {

public command() {
return 'migrate <command>';
}

public description() {
return 'execute a database migration';
}

public positionals(): Record<string, PositionalOptions> {
return {
'command': {
description: 'command to execute',
choices: [
ArgvCommand.Up,
ArgvCommand.Down,
ArgvCommand.Latest,
ArgvCommand.List,
ArgvCommand.Unlock,
],
},
};
}

public options(): Record<string, Options> {
return {
'disable-transactions': {
type: 'boolean',
},
};
}

public async run(argv: Argv, runContext: RunContext): Promise<void> {
const commands: Record<ArgvCommand, Function> = {
up: async () => {
await migrateUp(runContext.db);
},
down: async () => {
await migrateDown(runContext.db);
},
latest: async () => {
await migrateLatest(runContext.db);
},
list: async () => {
const s = (await migrateList(runContext.db)) as string;
s.split('\n').forEach(l => logger.info(l));
},
unlock: async () => {
await migrateUnlock(runContext.db);
},
};

if (!commands[argv.command]) throw new Error(`Invalid command: ${argv.command}`);

await commands[argv.command]();

await disconnectDb(runContext.db);
}

}
5 changes: 0 additions & 5 deletions packages/server/src/commands/deleteOldChanges.ts

This file was deleted.

6 changes: 0 additions & 6 deletions packages/server/src/commands/deleteOldChanges90.ts

This file was deleted.

13 changes: 10 additions & 3 deletions packages/server/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,31 @@ export async function disconnectDb(db: DbConnection) {
await db.destroy();
}

export async function migrateLatest(db: DbConnection) {
export async function migrateLatest(db: DbConnection, disableTransactions = false) {
await db.migrate.latest({
directory: migrationDir,
disableTransactions,
});
}

export async function migrateUp(db: DbConnection) {
export async function migrateUp(db: DbConnection, disableTransactions = false) {
await db.migrate.up({
directory: migrationDir,
disableTransactions,
});
}

export async function migrateDown(db: DbConnection) {
export async function migrateDown(db: DbConnection, disableTransactions = false) {
await db.migrate.down({
directory: migrationDir,
disableTransactions,
});
}

export async function migrateUnlock(db: DbConnection) {
await db.migrate.forceFreeMigrationsLock();
}

export async function migrateList(db: DbConnection, asString: boolean = true) {
const migrations: any = await db.migrate.list({
directory: migrationDir,
Expand Down
Loading

0 comments on commit dca13b3

Please sign in to comment.