feat(cli): run migrations and app in start command (#284)
* feat: perform pending migrations in start command * feat: log error when DB connection is refused * feat: log error when Redis connection is refused * refactor: fix type errors * fix: correct server and copy graphql schema * fix: differentiate migrations by env * chore: remove dev executable * chore: fix typo in default postgresUsername * fix: copy json files into dist folder * chore(cli): add dev script * chore: pull non-dev logs to info level * feat(cli): run app in start command * fix(backend): remove default count in Connection * fix(cli): remove .eslintrc usage in lint script * refactor: remove disableMigrationsListValidation * refactor: make Step optional in ExecutionStep * refactor: make Flow optional in Step
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import User from '../../src/models/user';
|
||||
import '../../src/config/database';
|
||||
import '../../src/config/orm';
|
||||
import logger from '../../src/helpers/logger';
|
||||
|
||||
const userParams = {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import appConfig from './src/config/app';
|
||||
|
||||
const fileExtension = appConfig.isDev ? 'ts' : 'js';
|
||||
|
||||
const knexConfig = {
|
||||
client: 'pg',
|
||||
connection: {
|
||||
@@ -13,6 +15,8 @@ const knexConfig = {
|
||||
pool: { min: 0, max: 20 },
|
||||
migrations: {
|
||||
directory: __dirname + '/src/db/migrations',
|
||||
extension: fileExtension,
|
||||
loadExtensions: [`.${fileExtension}`],
|
||||
},
|
||||
seeds: {
|
||||
directory: __dirname + '/src/db/seeds',
|
||||
|
@@ -3,10 +3,10 @@
|
||||
"version": "0.1.0",
|
||||
"description": "> TODO: description",
|
||||
"scripts": {
|
||||
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/app.ts",
|
||||
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/server.ts",
|
||||
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/src/app.js",
|
||||
"build": "tsc && yarn copy-statics",
|
||||
"start": "node dist/src/server.js",
|
||||
"test": "ava",
|
||||
"lint": "eslint . --ignore-path ../../.eslintignore",
|
||||
"db:create": "ts-node ./bin/database/create.ts",
|
||||
@@ -14,7 +14,8 @@
|
||||
"db:drop": "ts-node ./bin/database/drop.ts",
|
||||
"db:migration:create": "knex migrate:make",
|
||||
"db:rollback": "knex migrate:rollback",
|
||||
"db:migrate": "knex migrate:latest"
|
||||
"db:migrate": "knex migrate:latest",
|
||||
"copy-statics": "copyfiles src/**/*.{graphql,json} dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@automatisch/web": "0.1.0",
|
||||
@@ -27,6 +28,7 @@
|
||||
"axios": "0.24.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"bullmq": "^1.76.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
"debug": "~2.6.9",
|
||||
@@ -60,7 +62,7 @@
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/automatisch/automatisch#readme",
|
||||
"main": "src/app.ts",
|
||||
"main": "dist/src/app",
|
||||
"directories": {
|
||||
"src": "src",
|
||||
"test": "__tests__"
|
||||
@@ -78,6 +80,7 @@
|
||||
"devDependencies": {
|
||||
"@automatisch/types": "0.1.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/bull": "^3.15.8",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
"@types/express": "^4.17.13",
|
||||
|
@@ -4,12 +4,11 @@ import express, { Request, Response, NextFunction } from 'express';
|
||||
import cors from 'cors';
|
||||
import corsOptions from './config/cors-options';
|
||||
import graphQLInstance from './helpers/graphql-instance';
|
||||
import logger from './helpers/logger';
|
||||
import morgan from './helpers/morgan';
|
||||
import appAssetsHandler from './helpers/app-assets-handler';
|
||||
import webUIHandler from './helpers/web-ui-handler';
|
||||
import errorHandler from './helpers/error-handler';
|
||||
import './config/database';
|
||||
import './config/orm';
|
||||
import {
|
||||
createBullBoardHandler,
|
||||
serverAdapter,
|
||||
@@ -21,7 +20,6 @@ if (appConfig.appEnv === 'development') {
|
||||
}
|
||||
|
||||
const app = express();
|
||||
const port = appConfig.port;
|
||||
|
||||
if (appConfig.appEnv === 'development') {
|
||||
injectBullBoardHandler(app, serverAdapter);
|
||||
@@ -44,6 +42,4 @@ app.use(function (req: Request, res: Response, next: NextFunction) {
|
||||
|
||||
app.use(errorHandler);
|
||||
|
||||
app.listen(port, () => {
|
||||
logger.info(`Server is listening on ${port}`);
|
||||
});
|
||||
export default app;
|
||||
|
@@ -5,15 +5,16 @@ type AppConfig = {
|
||||
host: string;
|
||||
protocol: string;
|
||||
port: string;
|
||||
webAppUrl?: string;
|
||||
webAppUrl: string;
|
||||
appEnv: string;
|
||||
isDev: boolean;
|
||||
postgresDatabase: string;
|
||||
postgresPort: number;
|
||||
postgresHost: string;
|
||||
postgresUsername: string;
|
||||
postgresPassword: string;
|
||||
postgresPassword?: string;
|
||||
postgresEnableSsl: boolean;
|
||||
baseUrl?: string;
|
||||
baseUrl: string;
|
||||
encryptionKey: string;
|
||||
appSecretKey: string;
|
||||
serveWebAppSeparately: boolean;
|
||||
@@ -21,37 +22,44 @@ type AppConfig = {
|
||||
redisPort: number;
|
||||
};
|
||||
|
||||
const host = process.env.HOST || 'localhost';
|
||||
const protocol = process.env.PROTOCOL || 'http';
|
||||
const port = process.env.PORT || '3000';
|
||||
const serveWebAppSeparately = process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false;
|
||||
|
||||
let webAppUrl = `${protocol}://${host}:${port}`;
|
||||
if (serveWebAppSeparately) {
|
||||
webAppUrl = process.env.WEB_APP_URL || 'http://localhost:3001';
|
||||
}
|
||||
|
||||
const baseUrl = `${protocol}://${host}:${port}`;
|
||||
|
||||
const appEnv = process.env.APP_ENV || 'development';
|
||||
|
||||
const appConfig: AppConfig = {
|
||||
host: process.env.HOST || 'localhost',
|
||||
protocol: process.env.PROTOCOL || 'http',
|
||||
port: process.env.PORT || '3000',
|
||||
appEnv: process.env.APP_ENV || 'development',
|
||||
host,
|
||||
protocol,
|
||||
port,
|
||||
appEnv: appEnv,
|
||||
isDev: appEnv === 'development',
|
||||
postgresDatabase: process.env.POSTGRES_DATABASE || 'automatisch_development',
|
||||
postgresPort: parseInt(process.env.POSTGRES_PORT) || 5432,
|
||||
postgresPort: parseInt(process.env.POSTGRES_PORT|| '5432'),
|
||||
postgresHost: process.env.POSTGRES_HOST || 'localhost',
|
||||
postgresUsername:
|
||||
process.env.POSTGRES_USERNAME || 'automatish_development_user',
|
||||
process.env.POSTGRES_USERNAME || 'automatisch_development_user',
|
||||
postgresPassword: process.env.POSTGRES_PASSWORD,
|
||||
postgresEnableSsl: process.env.POSTGRES_ENABLE_SSL === 'true' ? true : false,
|
||||
encryptionKey: process.env.ENCRYPTION_KEY,
|
||||
appSecretKey: process.env.APP_SECRET_KEY,
|
||||
serveWebAppSeparately:
|
||||
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false,
|
||||
encryptionKey: process.env.ENCRYPTION_KEY || '',
|
||||
appSecretKey: process.env.APP_SECRET_KEY || '',
|
||||
serveWebAppSeparately,
|
||||
redisHost: process.env.REDIS_HOST || '127.0.0.1',
|
||||
redisPort: parseInt(process.env.REDIS_PORT) || 6379,
|
||||
redisPort: parseInt(process.env.REDIS_PORT || '6379'),
|
||||
baseUrl,
|
||||
webAppUrl,
|
||||
};
|
||||
|
||||
if (appConfig.serveWebAppSeparately) {
|
||||
appConfig.webAppUrl = process.env.WEB_APP_URL || 'http://localhost:3001';
|
||||
} else {
|
||||
appConfig.webAppUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.port}`;
|
||||
}
|
||||
|
||||
if (!appConfig.encryptionKey) {
|
||||
throw new Error('ENCRYPTION_KEY environment variable needs to be set!');
|
||||
}
|
||||
|
||||
const baseUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.port}`;
|
||||
appConfig.baseUrl = baseUrl;
|
||||
|
||||
export default appConfig;
|
||||
|
@@ -1,6 +1,19 @@
|
||||
import { Model } from 'objection';
|
||||
import knexInstance from 'knex';
|
||||
import process from 'process';
|
||||
import knex from 'knex';
|
||||
import type { Knex } from 'knex';
|
||||
import knexConfig from '../../knexfile';
|
||||
import logger from '../helpers/logger';
|
||||
|
||||
const knex = knexInstance(knexConfig)
|
||||
Model.knex(knex)
|
||||
const knexInstance: Knex = knex(knexConfig);
|
||||
|
||||
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||
|
||||
knexInstance.raw('SELECT 1')
|
||||
.catch((err) => {
|
||||
if (err.code === CONNECTION_REFUSED) {
|
||||
logger.error('Make sure you have installed PostgreSQL and it is running.', err);
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
|
||||
export default knexInstance;
|
||||
|
4
packages/backend/src/config/orm.ts
Normal file
4
packages/backend/src/config/orm.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Model } from 'objection';
|
||||
import database from './database';
|
||||
|
||||
Model.knex(database)
|
@@ -3,6 +3,7 @@ import appConfig from './app';
|
||||
const redisConfig = {
|
||||
host: appConfig.redisHost,
|
||||
port: appConfig.redisPort,
|
||||
enableOfflineQueue: false,
|
||||
};
|
||||
|
||||
export default redisConfig;
|
||||
|
@@ -22,6 +22,8 @@ const createAuthData = async (
|
||||
const appClass = (await import(`../../apps/${connection.key}`)).default;
|
||||
const appData = App.findOneByKey(connection.key);
|
||||
|
||||
if (!connection.formattedData) { return null; }
|
||||
|
||||
const appInstance = new appClass(appData, {
|
||||
consumerKey: connection.formattedData.consumerKey,
|
||||
consumerSecret: connection.formattedData.consumerSecret,
|
||||
|
@@ -19,6 +19,8 @@ const deleteStep = async (
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!step) return;
|
||||
|
||||
await step.$query().delete();
|
||||
|
||||
const nextSteps = await step.flow
|
||||
|
@@ -18,6 +18,8 @@ const resetConnection = async (
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!connection.formattedData) { return null; }
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: { screenName: connection.formattedData.screenName },
|
||||
});
|
||||
|
@@ -26,10 +26,13 @@ const getConnectedApps = async (
|
||||
.filter((app: IApp) => connectionKeys.includes(app.key))
|
||||
.map((app: IApp) => {
|
||||
const connection = connections.find(
|
||||
(connection: IConnection) => connection.key === app.key
|
||||
(connection) => (connection as IConnection).key === app.key
|
||||
);
|
||||
|
||||
app.connectionCount = connection.count;
|
||||
if (connection) {
|
||||
app.connectionCount = connection.count;
|
||||
}
|
||||
|
||||
return app;
|
||||
});
|
||||
|
||||
|
@@ -12,8 +12,12 @@ const getData = async (_parent: unknown, params: Params, context: Context) => {
|
||||
.withGraphFetched('connection')
|
||||
.findById(params.stepId);
|
||||
|
||||
if (!step) return null;
|
||||
|
||||
const connection = step.connection;
|
||||
|
||||
if (!connection || !step.appKey) return null;
|
||||
|
||||
const appData = App.findOneByKey(step.appKey);
|
||||
const AppClass = (await import(`../../apps/${step.appKey}`)).default;
|
||||
|
||||
|
@@ -10,7 +10,7 @@ const levels = {
|
||||
}
|
||||
|
||||
const level = () => {
|
||||
return appConfig.appEnv === 'development' ? 'debug' : 'warn'
|
||||
return appConfig.appEnv === 'development' ? 'debug' : 'info'
|
||||
}
|
||||
|
||||
const colors = {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import { dirname, join } from 'path';
|
||||
import { IApp } from '@automatisch/types';
|
||||
import appInfoConverter from '../helpers/app-info-converter';
|
||||
|
||||
class App {
|
||||
@@ -7,7 +8,7 @@ class App {
|
||||
static folderPath = join(dirname(this.backendPath), 'apps');
|
||||
static list = fs.readdirSync(this.folderPath);
|
||||
|
||||
static findAll(name?: string): object[] {
|
||||
static findAll(name?: string): IApp[] {
|
||||
if (!name) return this.list.map((name) => this.findOneByName(name));
|
||||
|
||||
return this.list
|
||||
@@ -15,7 +16,7 @@ class App {
|
||||
.map((name) => this.findOneByName(name));
|
||||
}
|
||||
|
||||
static findOneByName(name: string): object {
|
||||
static findOneByName(name: string): IApp {
|
||||
const rawAppData = fs.readFileSync(
|
||||
this.folderPath + `/${name}/info.json`,
|
||||
'utf-8'
|
||||
@@ -23,7 +24,7 @@ class App {
|
||||
return appInfoConverter(rawAppData);
|
||||
}
|
||||
|
||||
static findOneByKey(key: string): object {
|
||||
static findOneByKey(key: string): IApp {
|
||||
const rawAppData = fs.readFileSync(
|
||||
this.folderPath + `/${key}/info.json`,
|
||||
'utf-8'
|
||||
|
@@ -9,10 +9,10 @@ import { IJSONObject } from '@automatisch/types';
|
||||
class Connection extends Base {
|
||||
id!: string;
|
||||
key!: string;
|
||||
data: string;
|
||||
formattedData!: IJSONObject;
|
||||
data = '';
|
||||
formattedData?: IJSONObject;
|
||||
userId!: string;
|
||||
verified: boolean;
|
||||
verified = false;
|
||||
count: number;
|
||||
|
||||
static tableName = 'connections';
|
||||
@@ -50,7 +50,7 @@ class Connection extends Base {
|
||||
appConfig.encryptionKey
|
||||
).toString();
|
||||
|
||||
delete this['formattedData'];
|
||||
delete this.formattedData;
|
||||
}
|
||||
|
||||
decryptData(): void {
|
||||
|
@@ -8,7 +8,7 @@ class ExecutionStep extends Base {
|
||||
stepId!: string;
|
||||
dataIn!: Record<string, unknown>;
|
||||
dataOut!: Record<string, unknown>;
|
||||
status: string;
|
||||
status = 'failure';
|
||||
step: Step;
|
||||
|
||||
static tableName = 'execution_steps';
|
||||
|
@@ -5,8 +5,8 @@ import ExecutionStep from './execution-step';
|
||||
class Execution extends Base {
|
||||
id!: string;
|
||||
flowId!: string;
|
||||
testRun: boolean;
|
||||
executionSteps: ExecutionStep[];
|
||||
testRun = false;
|
||||
executionSteps: ExecutionStep[] = [];
|
||||
|
||||
static tableName = 'executions';
|
||||
|
||||
|
@@ -6,9 +6,9 @@ import Execution from './execution';
|
||||
|
||||
class Flow extends Base {
|
||||
id!: string;
|
||||
name: string;
|
||||
name!: string;
|
||||
userId!: string;
|
||||
active: boolean;
|
||||
active = false;
|
||||
steps?: [Step];
|
||||
|
||||
static tableName = 'flows';
|
||||
|
@@ -7,15 +7,15 @@ import type { IStep } from '@automatisch/types';
|
||||
class Step extends Base {
|
||||
id!: string;
|
||||
flowId!: string;
|
||||
key: string;
|
||||
appKey: string;
|
||||
key?: string;
|
||||
appKey?: string;
|
||||
type!: IStep["type"];
|
||||
connectionId?: string;
|
||||
status: string;
|
||||
position: number;
|
||||
parameters: Record<string, unknown>;
|
||||
status = 'incomplete';
|
||||
position!: number;
|
||||
parameters: Record<string, unknown> = {};
|
||||
connection?: Connection;
|
||||
flow?: Flow;
|
||||
flow: Flow;
|
||||
executionSteps?: [ExecutionStep];
|
||||
|
||||
static tableName = 'steps';
|
||||
|
@@ -1,11 +1,22 @@
|
||||
import process from 'process';
|
||||
import { Queue, QueueScheduler } from 'bullmq';
|
||||
import redisConfig from '../config/redis';
|
||||
import logger from '../helpers/logger';
|
||||
|
||||
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||
|
||||
const redisConnection = {
|
||||
connection: redisConfig,
|
||||
};
|
||||
|
||||
new QueueScheduler('processor', redisConnection);
|
||||
const processorQueue = new Queue('processor', redisConnection);
|
||||
new QueueScheduler('processor', redisConnection);
|
||||
|
||||
processorQueue.on('error', (err) => {
|
||||
if ((err as any).code === CONNECTION_REFUSED) {
|
||||
logger.error('Make sure you have installed Redis and it is running.', err);
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
|
||||
export default processorQueue;
|
||||
|
9
packages/backend/src/server.ts
Normal file
9
packages/backend/src/server.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import app from './app';
|
||||
import appConfig from './config/app';
|
||||
import logger from './helpers/logger';
|
||||
|
||||
const port = appConfig.port;
|
||||
|
||||
app.listen(port, () => {
|
||||
logger.info(`Server is listening on ${port}`);
|
||||
});
|
@@ -14,8 +14,8 @@ type ProcessorOptions = {
|
||||
|
||||
class Processor {
|
||||
flow: Flow;
|
||||
untilStep: Step;
|
||||
testRun: boolean;
|
||||
untilStep?: Step;
|
||||
testRun?: boolean;
|
||||
|
||||
static variableRegExp = /({{step\..+\..+}})/g;
|
||||
|
||||
@@ -32,7 +32,8 @@ class Processor {
|
||||
.orderBy('position', 'asc');
|
||||
|
||||
const triggerStep = steps.find((step) => step.type === 'trigger');
|
||||
let initialTriggerData = await this.getInitialTriggerData(triggerStep);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
let initialTriggerData = await this.getInitialTriggerData(triggerStep!);
|
||||
|
||||
if (this.testRun) {
|
||||
initialTriggerData = [initialTriggerData[0]];
|
||||
@@ -53,6 +54,8 @@ class Processor {
|
||||
let fetchedActionData = {};
|
||||
|
||||
for await (const step of steps) {
|
||||
if (!step.appKey) continue;
|
||||
|
||||
const appData = App.findOneByKey(step.appKey);
|
||||
|
||||
const {
|
||||
@@ -74,11 +77,11 @@ class Processor {
|
||||
|
||||
const appInstance = new AppClass(
|
||||
appData,
|
||||
connection.formattedData,
|
||||
connection?.formattedData,
|
||||
computedParameters
|
||||
);
|
||||
|
||||
if (!isTrigger) {
|
||||
if (!isTrigger && key) {
|
||||
const command = appInstance.actions[key];
|
||||
fetchedActionData = await command.run();
|
||||
}
|
||||
@@ -107,17 +110,21 @@ class Processor {
|
||||
.orderBy('created_at', 'desc')
|
||||
.first();
|
||||
|
||||
return lastExecutionStepFromFirstExecution.dataOut;
|
||||
return lastExecutionStepFromFirstExecution?.dataOut;
|
||||
}
|
||||
|
||||
async getInitialTriggerData(step: Step) {
|
||||
if (!step.appKey) return null;
|
||||
|
||||
const appData = App.findOneByKey(step.appKey);
|
||||
const { appKey, connection, key, parameters: rawParameters = {} } = step;
|
||||
|
||||
if (!key) return null;
|
||||
|
||||
const AppClass = (await import(`../apps/${appKey}`)).default;
|
||||
const appInstance = new AppClass(
|
||||
appData,
|
||||
connection.formattedData,
|
||||
connection?.formattedData,
|
||||
rawParameters
|
||||
);
|
||||
|
||||
@@ -149,30 +156,34 @@ class Processor {
|
||||
executionSteps: ExecutionSteps
|
||||
): Step['parameters'] {
|
||||
const entries = Object.entries(parameters);
|
||||
return entries.reduce((result, [key, value]: [string, string]) => {
|
||||
const parts = value.split(Processor.variableRegExp);
|
||||
return entries.reduce((result, [key, value]: [string, unknown]) => {
|
||||
if (typeof value === 'string') {
|
||||
const parts = value.split(Processor.variableRegExp);
|
||||
|
||||
const computedValue = parts
|
||||
.map((part: string) => {
|
||||
const isVariable = part.match(Processor.variableRegExp);
|
||||
if (isVariable) {
|
||||
const stepIdAndKeyPath = part.replace(/{{step.|}}/g, '') as string;
|
||||
const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.');
|
||||
const keyPath = keyPaths.join('.');
|
||||
const executionStep = executionSteps[stepId.toString() as string];
|
||||
const data = executionStep?.dataOut;
|
||||
const dataValue = get(data, keyPath);
|
||||
return dataValue;
|
||||
}
|
||||
const computedValue = parts
|
||||
.map((part: string) => {
|
||||
const isVariable = part.match(Processor.variableRegExp);
|
||||
if (isVariable) {
|
||||
const stepIdAndKeyPath = part.replace(/{{step.|}}/g, '') as string;
|
||||
const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.');
|
||||
const keyPath = keyPaths.join('.');
|
||||
const executionStep = executionSteps[stepId.toString() as string];
|
||||
const data = executionStep?.dataOut;
|
||||
const dataValue = get(data, keyPath);
|
||||
return dataValue;
|
||||
}
|
||||
|
||||
return part;
|
||||
})
|
||||
.join('');
|
||||
return part;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return {
|
||||
...result,
|
||||
[key]: computedValue,
|
||||
};
|
||||
return {
|
||||
...result,
|
||||
[key]: computedValue,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import type { IApp } from '@automatisch/types';
|
||||
import JSONObject from './json-object';
|
||||
import type { IApp, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default interface AuthenticationInterface {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: unknown;
|
||||
verifyCredentials(): Promise<JSONObject>;
|
||||
verifyCredentials(): Promise<IJSONObject>;
|
||||
isStillVerified(): Promise<boolean>;
|
||||
}
|
||||
|
@@ -1,2 +1,2 @@
|
||||
import './config/database';
|
||||
import './config/orm';
|
||||
import './workers/processor';
|
||||
|
@@ -1,19 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"target": "es6",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2021"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "dist",
|
||||
"paths": {
|
||||
"*": [
|
||||
"node_modules/*",
|
||||
"src/types/*"
|
||||
]
|
||||
},
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"target": "es2021",
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"./src/types",
|
||||
@@ -21,6 +24,12 @@
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
"src/**/*",
|
||||
"bin/**/*"
|
||||
],
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const oclif = require('@oclif/core')
|
||||
|
||||
const path = require('path')
|
||||
const project = path.join(__dirname, '..', 'tsconfig.json')
|
||||
|
||||
// In dev mode -> use ts-node and dev plugins
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
require('ts-node').register({project})
|
||||
|
||||
// In dev mode, always show stack traces
|
||||
oclif.settings.debug = true;
|
||||
|
||||
// Start the CLI
|
||||
oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
|
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
|
||||
node "%~dp0\dev" %*
|
@@ -24,16 +24,19 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "shx rm -rf dist && tsc -b",
|
||||
"lint": "eslint . --ext .ts --config .eslintrc",
|
||||
"dev": "nodemon --watch 'src/**/*.ts' --exec 'shx rm -rf dist && tsc -b' --ext 'ts'",
|
||||
"lint": "eslint . --ext .ts --ignore-path ../../.eslintignore",
|
||||
"postpack": "shx rm -f oclif.manifest.json",
|
||||
"posttest": "yarn lint",
|
||||
"prepack": "yarn build && oclif manifest && oclif readme",
|
||||
"version": "oclif readme && git add README.md"
|
||||
},
|
||||
"dependencies": {
|
||||
"@automatisch/backend": "^0.1.0",
|
||||
"@oclif/core": "^1",
|
||||
"@oclif/plugin-help": "^5",
|
||||
"@oclif/plugin-plugins": "^2.0.1"
|
||||
"@oclif/plugin-plugins": "^2.0.1",
|
||||
"dotenv": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@oclif/test": "^2",
|
||||
@@ -46,7 +49,7 @@
|
||||
"shx": "^0.3.3",
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.3"
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"oclif": {
|
||||
"bin": "automatisch",
|
||||
|
@@ -1,9 +1,56 @@
|
||||
import { Command } from '@oclif/core';
|
||||
import { Command, Flags } from '@oclif/core';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
export default class Start extends Command {
|
||||
static description = 'Say hello world';
|
||||
static description = 'Run automatisch';
|
||||
|
||||
static flags = {
|
||||
env: Flags.string({
|
||||
multiple: true,
|
||||
char: 'e',
|
||||
}),
|
||||
'env-file': Flags.string(),
|
||||
}
|
||||
|
||||
async prepareEnvVars(): Promise<void> {
|
||||
const { flags } = await this.parse(Start);
|
||||
|
||||
if (flags['env-file']) {
|
||||
dotenv.config({ path: flags['env-file'] });
|
||||
}
|
||||
|
||||
if (flags.env) {
|
||||
for (const env of flags.env) {
|
||||
const [key, value] = env.split('=');
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
delete process.env.SERVE_WEB_APP_SEPARATELY;
|
||||
}
|
||||
|
||||
async runMigrationsIfNeeded(): Promise<void> {
|
||||
const database = (await import('@automatisch/backend/dist/src/config/database')).default;
|
||||
const migrator = database.migrate;
|
||||
|
||||
const [, pendingMigrations] = await migrator.list();
|
||||
const pendingMigrationsCount = pendingMigrations.length;
|
||||
const needsToMigrate = pendingMigrationsCount > 0;
|
||||
|
||||
if (needsToMigrate) {
|
||||
await migrator.latest();
|
||||
}
|
||||
}
|
||||
|
||||
async runApp(): Promise<void> {
|
||||
await import('@automatisch/backend/dist/src/server');
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.log('hello world from start script');
|
||||
await this.prepareEnvVars();
|
||||
|
||||
await this.runMigrationsIfNeeded();
|
||||
|
||||
await this.runApp();
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"lib": ["es2021"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "es2019",
|
||||
"target": "es2021",
|
||||
"traceResolution": false,
|
||||
"typeRoots": ["node_modules/@types", "./src/types"]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
|
@@ -35,7 +35,7 @@
|
||||
"slate": "^0.72.8",
|
||||
"slate-history": "^0.66.0",
|
||||
"slate-react": "^0.72.9",
|
||||
"typescript": "^4.1.2",
|
||||
"typescript": "^4.6.3",
|
||||
"web-vitals": "^1.0.1",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
|
86
yarn.lock
86
yarn.lock
@@ -4198,6 +4198,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bull@^3.15.8":
|
||||
version "3.15.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.8.tgz#ae2139f94490d740b37c8da5d828ce75dd82ce7c"
|
||||
integrity sha512-8DbSPMSsZH5PWPnGEkAZLYgJEH4ghHJNKF7LB6Wr5R0/v6g+Vs+JoaA7kcvLtHE936xg2WpFPkaoaJgExOmKDw==
|
||||
dependencies:
|
||||
"@types/ioredis" "*"
|
||||
"@types/redis" "^2.8.0"
|
||||
|
||||
"@types/chai@*":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
|
||||
@@ -4338,6 +4346,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ioredis@*":
|
||||
version "4.28.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff"
|
||||
integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/is-hotkey@^0.1.1":
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/is-hotkey/-/is-hotkey-0.1.7.tgz#30ec6d4234895230b576728ef77e70a52962f3b3"
|
||||
@@ -4593,6 +4608,13 @@
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/redis@^2.8.0":
|
||||
version "2.8.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11"
|
||||
integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/resolve@1.17.1":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
||||
@@ -7120,6 +7142,19 @@ copy-webpack-plugin@^9.0.1:
|
||||
schema-utils "^3.1.1"
|
||||
serialize-javascript "^6.0.0"
|
||||
|
||||
copyfiles@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
|
||||
integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
minimatch "^3.0.3"
|
||||
mkdirp "^1.0.4"
|
||||
noms "0.0.0"
|
||||
through2 "^2.0.1"
|
||||
untildify "^4.0.0"
|
||||
yargs "^16.1.0"
|
||||
|
||||
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
||||
version "3.20.3"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6"
|
||||
@@ -9710,7 +9745,7 @@ glob-to-regexp@^0.4.1:
|
||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||
glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
@@ -10529,7 +10564,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -12786,6 +12821,13 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^3.0.3:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
|
||||
@@ -13205,6 +13247,14 @@ nodemon@^2.0.13:
|
||||
undefsafe "^2.0.5"
|
||||
update-notifier "^5.1.0"
|
||||
|
||||
noms@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
||||
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "~1.0.31"
|
||||
|
||||
nopt@^4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
|
||||
@@ -15803,6 +15853,16 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@~1.0.31:
|
||||
version "1.0.34"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.1"
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
|
||||
@@ -17223,6 +17283,11 @@ string_decoder@^1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
string_decoder@~0.10.x:
|
||||
version "0.10.31"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
||||
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
@@ -17694,7 +17759,7 @@ throat@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
|
||||
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
||||
|
||||
through2@^2.0.0:
|
||||
through2@^2.0.0, through2@^2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||
@@ -18108,15 +18173,10 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@^4.1.2:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8"
|
||||
integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==
|
||||
|
||||
typescript@^4.4.3:
|
||||
version "4.5.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
typescript@^4.6.3:
|
||||
version "4.6.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
|
||||
integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
|
||||
|
||||
ua-parser-js@^0.7.30:
|
||||
version "0.7.31"
|
||||
@@ -19351,7 +19411,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3:
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs@^16.2.0:
|
||||
yargs@^16.1.0, yargs@^16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||
|
Reference in New Issue
Block a user