feat: create scheduler to run cron jobs and setup periodic repo update
This commit is contained in:
parent
90115b149f
commit
747fee006d
5 changed files with 52 additions and 28 deletions
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"watch": ["server.ts"],
|
||||
"watch": ["server.ts", "src/server"],
|
||||
"exec": "ts-node --project tsconfig.server.json server.ts",
|
||||
"ext": "js ts"
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"prisma:pull": "prisma db pull",
|
||||
"test": "dotenv -e .env.test -- ts-node ./run-migration.ts && jest --colors",
|
||||
"test": "dotenv -e .env.test -- jest --colors",
|
||||
"test:client": "jest --colors --selectProjects client --",
|
||||
"test:server": "jest --colors --selectProjects server --",
|
||||
"postinstall": "prisma generate",
|
||||
|
@ -45,7 +45,6 @@
|
|||
"redis": "^4.3.1",
|
||||
"remark-breaks": "^3.0.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-mdx": "^2.1.1",
|
||||
"sass": "^1.55.0",
|
||||
"semver": "^7.3.7",
|
||||
"sharp": "0.30.7",
|
||||
|
@ -86,6 +85,7 @@
|
|||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-config-next": "13.1.1",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"eslint-plugin-jsdoc": "^39.6.9",
|
||||
|
|
|
@ -2,20 +2,21 @@ import path from 'path';
|
|||
import pg from 'pg';
|
||||
import { migrate } from '@runtipi/postgres-migrations';
|
||||
import { Logger } from './src/server/core/Logger';
|
||||
import { getConfig } from './src/server/core/TipiConfig';
|
||||
|
||||
export const runPostgresMigrations = async (dbName?: string) => {
|
||||
Logger.info('Starting database migration');
|
||||
|
||||
const { POSTGRES_HOST, POSTGRES_DBNAME, POSTGRES_USERNAME, POSTGRES_PASSWORD, POSTGRES_PORT = 5432 } = process.env;
|
||||
const { postgresHost, postgresDatabase, postgresUsername, postgresPassword, postgresPort } = getConfig();
|
||||
|
||||
Logger.info('Connecting to database', POSTGRES_DBNAME, 'on', POSTGRES_HOST, 'as', POSTGRES_USERNAME, 'on port', POSTGRES_PORT);
|
||||
Logger.info(`Connecting to database ${postgresDatabase} on ${postgresHost} as ${postgresUsername} on port ${postgresPort}`);
|
||||
|
||||
const client = new pg.Client({
|
||||
user: POSTGRES_USERNAME,
|
||||
host: POSTGRES_HOST,
|
||||
database: dbName || POSTGRES_DBNAME,
|
||||
password: POSTGRES_PASSWORD,
|
||||
port: Number(POSTGRES_PORT),
|
||||
user: postgresUsername,
|
||||
host: postgresHost,
|
||||
database: dbName || postgresDatabase,
|
||||
password: postgresPassword,
|
||||
port: Number(postgresPort),
|
||||
});
|
||||
await client.connect();
|
||||
|
||||
|
@ -44,5 +45,3 @@ export const runPostgresMigrations = async (dbName?: string) => {
|
|||
Logger.info('Migration complete');
|
||||
await client.end();
|
||||
};
|
||||
|
||||
runPostgresMigrations();
|
||||
|
|
|
@ -32,13 +32,16 @@ nextApp.prepare().then(async () => {
|
|||
|
||||
// Run database migrations
|
||||
await runPostgresMigrations();
|
||||
|
||||
// startJobs();
|
||||
setConfig('status', 'RUNNING');
|
||||
|
||||
// Clone and update apps repo
|
||||
await EventDispatcher.dispatchEventAsync('clone_repo', [getConfig().appsRepoUrl]);
|
||||
await EventDispatcher.dispatchEventAsync('update_repo', [getConfig().appsRepoUrl]);
|
||||
|
||||
// Scheduled events
|
||||
EventDispatcher.scheduleEvent({ type: 'update_repo', args: [getConfig().appsRepoUrl], cronExpression: '*/30 * * * *' });
|
||||
EventDispatcher.scheduleEvent({ type: 'system_info', args: [], cronExpression: '* * * * *' });
|
||||
|
||||
appService.startAllApps();
|
||||
|
||||
Logger.info(`> Server listening at http://localhost:${port} as ${dev ? 'development' : process.env.NODE_ENV}`);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* eslint-disable vars-on-top */
|
||||
import cron from 'node-cron';
|
||||
import fs from 'fs-extra';
|
||||
import { Logger } from '../Logger';
|
||||
import { getConfig } from '../TipiConfig';
|
||||
|
@ -27,7 +28,14 @@ type SystemEvent = {
|
|||
creationDate: Date;
|
||||
};
|
||||
|
||||
type EventStatusTypes = 'running' | 'success' | 'error' | 'waiting';
|
||||
const EVENT_STATUS = {
|
||||
RUNNING: 'running',
|
||||
SUCCESS: 'success',
|
||||
ERROR: 'error',
|
||||
WAITING: 'waiting',
|
||||
} as const;
|
||||
|
||||
type EventStatus = typeof EVENT_STATUS[keyof typeof EVENT_STATUS]; // 'running' | 'success' | 'error' | 'waiting';
|
||||
|
||||
const WATCH_FILE = '/runtipi/state/events';
|
||||
|
||||
|
@ -60,7 +68,8 @@ class EventDispatcher {
|
|||
|
||||
/**
|
||||
* Generate a random task id
|
||||
* @returns - Random id
|
||||
*
|
||||
* @returns {string} id - Randomly generated id
|
||||
*/
|
||||
static generateId() {
|
||||
return Math.random().toString(36).substring(2, 9);
|
||||
|
@ -125,10 +134,11 @@ class EventDispatcher {
|
|||
|
||||
/**
|
||||
* Check event status
|
||||
* @param id - Event id
|
||||
* @returns - Event status
|
||||
*
|
||||
* @param {string} id - Event id
|
||||
* @returns {EventStatus} - Event status
|
||||
*/
|
||||
private getEventStatus(id: string): EventStatusTypes {
|
||||
private getEventStatus(id: string): EventStatus {
|
||||
const event = this.queue.find((e) => e.id === id);
|
||||
|
||||
if (!event) {
|
||||
|
@ -148,16 +158,17 @@ class EventDispatcher {
|
|||
return 'waiting';
|
||||
}
|
||||
|
||||
const status = line.split(' ')[2] as EventStatusTypes;
|
||||
const status = line.split(' ')[2] as EventStatus;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an event to the queue
|
||||
* @param type - Event type
|
||||
* @param args - Event arguments
|
||||
* @returns - Event object
|
||||
*
|
||||
* @param {EventType} type - Event type
|
||||
* @param {[string]} args - Event arguments
|
||||
* @returns {SystemEvent} event - Event object
|
||||
*/
|
||||
public dispatchEvent(type: EventType, args?: string[]): SystemEvent {
|
||||
const event: SystemEvent = {
|
||||
|
@ -173,10 +184,12 @@ class EventDispatcher {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clear event from queue
|
||||
* @param id - Event id
|
||||
* Clears an event from the queue
|
||||
*
|
||||
* @param {SystemEvent} event - The event to clear
|
||||
* @param {EventStatus} status - The status to consider the event to
|
||||
*/
|
||||
private clearEvent(event: SystemEvent, status: EventStatusTypes = 'success') {
|
||||
private clearEvent(event: SystemEvent, status: EventStatus = 'success') {
|
||||
this.queue = this.queue.filter((e) => e.id !== event.id);
|
||||
if (fs.existsSync(`/app/logs/${event.id}.log`)) {
|
||||
const log = fs.readFileSync(`/app/logs/${event.id}.log`, 'utf8');
|
||||
|
@ -192,8 +205,9 @@ class EventDispatcher {
|
|||
|
||||
/**
|
||||
* Dispatch an event to the queue and wait for it to finish
|
||||
* @param type - Event type
|
||||
* @param args - Event arguments
|
||||
*
|
||||
* @param {EventType} type - Event type
|
||||
* @param {[string[]]} args - Event arguments
|
||||
* @returns - Promise that resolves when the event is done
|
||||
*/
|
||||
public async dispatchEventAsync(type: EventType, args?: string[]): Promise<{ success: boolean; stdout?: string }> {
|
||||
|
@ -231,6 +245,14 @@ class EventDispatcher {
|
|||
EventDispatcher.instance = null;
|
||||
fs.writeFileSync(WATCH_FILE, '');
|
||||
}
|
||||
|
||||
public scheduleEvent(params: { type: EventType; args?: string[]; cronExpression: string }) {
|
||||
const { type, args, cronExpression } = params;
|
||||
|
||||
cron.schedule(cronExpression, async () => {
|
||||
this.dispatchEvent(type, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const EventDispatcherInstance = global.EventDispatcher || EventDispatcher.getInstance();
|
||||
|
|
Loading…
Add table
Reference in a new issue