Skip to content

Commit

Permalink
Merge pull request #626 from VitNode/admin/corn_jobs
Browse files Browse the repository at this point in the history
feat: Add cron jobs page in AdminCP
  • Loading branch information
aXenDeveloper authored Dec 31, 2024
2 parents 8db606b + 76e37a2 commit 51a7cdf
Show file tree
Hide file tree
Showing 22 changed files with 691 additions and 373 deletions.
16 changes: 16 additions & 0 deletions apps/frontend/src/plugins/admin/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,22 @@
"success": "File has been deleted.",
"submit": "Yes, delete file"
}
},
"cron": {
"title": "Cron Jobs",
"desc": "Tasks that run automatically at a specific time.",
"name": "Name",
"schedule": "Schedule",
"next_date": "Next Date",
"running": {
"title": "Status",
"enabled": "Enabled",
"disabled": "Disabled"
},
"last_execution": {
"title": "Last Execution",
"never": "Never"
}
}
}
},
Expand Down
6 changes: 4 additions & 2 deletions apps/frontend/src/plugins/core/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@
"styles_editor": "Editor",
"langs": "Languages",
"advanced": "Advanced",
"advanced_files": "Files"
"advanced_files": "Files",
"advanced_cron": "Cron Jobs"
},
"admin_permissions": {
"dashboard": "Dashboard",
Expand All @@ -383,7 +384,8 @@
"can_manage_styles_editor": "Can manage editor?",
"can_manage_langs": "Can manage languages?",
"advanced": "Advanced",
"can_manage_advanced_files": "Can manage files?"
"can_manage_advanced_files": "Can manage files?",
"can_manage_advanced_cron": "Can manage cron jobs?"
}
},
"admin_members": {
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"@nestjs/schedule": "^4.1.2",
"@react-email/components": "^0.0.31",
"@swc/cli": "^0.5.2",
"@swc/core": "^1.10.3",
"@swc/core": "^1.10.4",
"@types/cookie-parser": "^1.4.8",
"@types/express": "^5.0.0",
"@types/multer": "^1.4.12",
Expand All @@ -99,7 +99,7 @@
"@types/ua-parser-js": "^0.7.39",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"concurrently": "^9.1.1",
"concurrently": "^9.1.2",
"drizzle-kit": "^0.30.1",
"drizzle-orm": "^0.38.3",
"eslint-config-typescript-vitnode": "workspace:*",
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/core/admin/advanced/advanced.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';

import { CronAdvancedAdminModule } from './cron/cron.module';
import { FilesAdvancedAdminModule } from './files/files.module';

@Module({
imports: [FilesAdvancedAdminModule],
imports: [FilesAdvancedAdminModule, CronAdvancedAdminModule],
})
export class AdvancedAdminModule {}
25 changes: 25 additions & 0 deletions packages/backend/src/core/admin/advanced/cron/cron.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Controllers } from '@/helpers/controller.decorator';
import { Get } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { ShowCronAdvancedAdminObj } from 'vitnode-shared/admin/advanced/cron.dto';

import { ShowCronAdvancedAdminService } from './services/show.service';

@Controllers({
plugin_name: 'Core',
plugin_code: 'advanced',
isAdmin: true,
route: 'cron',
})
export class CoreAdvancedAdminController {
constructor(private readonly showService: ShowCronAdvancedAdminService) {}

@ApiOkResponse({
type: ShowCronAdvancedAdminObj,
description: 'Show cron jobs',
})
@Get()
show(): ShowCronAdvancedAdminObj {
return this.showService.show();
}
}
10 changes: 10 additions & 0 deletions packages/backend/src/core/admin/advanced/cron/cron.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';

import { CoreAdvancedAdminController } from './cron.controller';
import { ShowCronAdvancedAdminService } from './services/show.service';

@Module({
providers: [ShowCronAdvancedAdminService],
controllers: [CoreAdvancedAdminController],
})
export class CronAdvancedAdminModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { SchedulerRegistry } from '@nestjs/schedule';
import { ShowCronAdvancedAdminObj } from 'vitnode-shared/admin/advanced/cron.dto';

@Injectable()
export class ShowCronAdvancedAdminService {
constructor(private readonly schedulerRegistry: SchedulerRegistry) {}

show(): ShowCronAdvancedAdminObj {
const jobs = this.schedulerRegistry.getCronJobs();

const edges: ShowCronAdvancedAdminObj['edges'] = Array.from(jobs).map(
([key, value]) => {
return {
name: key,
running: value.running,
next_date: value.nextDate().toJSDate(),
last_execution: value.lastExecution,
schedule: value.cronTime.source.toString(),
};
},
);

// Sort by next_date descending
const sortedEdges = edges.sort(
(a, b) => b.next_date.getTime() - a.next_date.getTime(),
);

return { edges: sortedEdges };
}
}
4 changes: 4 additions & 0 deletions packages/backend/src/core/admin/auth/services/nav/core.nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export const coreNav: ShowAuthAdminObj['nav'] = [
code: 'files',
keywords: ['file'],
},
{
code: 'cron',
keywords: ['crons', 'cronjob', 'cronjobs', 'schedule', 'task'],
},
],
},
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { core_files } from '@/database/schema/files';
import { core_users } from '@/database/schema/users';
import { FilesHelperService } from '@/helpers/files/files-helper.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import {
ForbiddenException,
Expand All @@ -10,7 +12,10 @@ import { User } from 'vitnode-shared/user.dto';

@Injectable()
export class DeleteUsersMembersAdminService {
constructor(private readonly databaseService: InternalDatabaseService) {}
constructor(
private readonly databaseService: InternalDatabaseService,
private readonly filesService: FilesHelperService,
) {}

async delete({
id,
Expand Down Expand Up @@ -45,6 +50,24 @@ export class DeleteUsersMembersAdminService {
throw new ForbiddenException('DELETE_ADMIN');
}

// Delete all files of the user
const files = await this.databaseService.db.query.core_files.findMany({
where: (table, { eq }) => eq(table.user_id, user.id),
});
await Promise.all(
files.map(async file => {
await Promise.all([
this.filesService.delete({
dir_folder: file.dir_folder,
file_name: file.file_name,
}),
this.databaseService.db
.delete(core_files)
.where(eq(core_files.id, file.id)),
]);
}),
);

await this.databaseService.db
.delete(core_users)
.where(eq(core_users.id, id));
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/core/auth/auth.cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AuthCron {
) {}

@Cron(CronExpression.EVERY_MINUTE, {
name: 'ClearExpiredSessions',
name: 'core_clear_expired_sessions',
})
async clearExpiredSessions() {
const expiresIn = new Date();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class ClearTokenConfirmEmailAuthCron {
constructor(private readonly databaseService: InternalDatabaseService) {}

@Cron(CronExpression.EVERY_MINUTE, {
name: 'ClearTokenConfirmEmailCoreSessionsCron',
name: 'core_clear_expired_confirm_emails',
})
async clearTokenEmail() {
await this.databaseService.db
Expand Down
45 changes: 45 additions & 0 deletions packages/backend/src/core/files/clean.core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { core_files, core_files_using } from '@/database/schema/files';
import { FilesHelperService } from '@/helpers/files/files-helper.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { and, eq, isNull, lte } from 'drizzle-orm';

@Injectable()
export class FilesCron {
constructor(
private readonly databaseService: InternalDatabaseService,
private readonly filesService: FilesHelperService,
) {}

@Cron(CronExpression.EVERY_6_HOURS, {
name: 'core_clear_unused_files',
})
async clearUnusedFiles() {
const twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000);
const findFiles = await this.databaseService.db
.select()
.from(core_files)
.leftJoin(core_files_using, eq(core_files.id, core_files_using.file_id))
.where(
and(
isNull(core_files_using.file_id),
lte(core_files.created_at, twelveHoursAgo),
),
);

await Promise.all(
findFiles.map(async file => {
await Promise.all([
this.filesService.delete({
dir_folder: file.core_files.dir_folder,
file_name: file.core_files.file_name,
}),
this.databaseService.db
.delete(core_files)
.where(eq(core_files.id, file.core_files.id)),
]);
}),
);
}
}
3 changes: 2 additions & 1 deletion packages/backend/src/core/files/files.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';

import { FilesCron } from './clean.core';
import { FilesController } from './files.controller';
import { DeleteFilesService } from './services/delete.service';
import { UploadFilesService } from './services/upload.service';

@Module({
providers: [UploadFilesService, DeleteFilesService],
providers: [UploadFilesService, DeleteFilesService, FilesCron],
controllers: [FilesController],
})
export class FilesModule {}
6 changes: 3 additions & 3 deletions packages/backend/src/helpers/controller.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ interface ArgsWithProtect extends Args {
isProtect: true;
}

export function Controllers({
export const Controllers = ({
plugin_name,
plugin_code,
isAdmin,
isProtect,
route,
isDev,
}: ArgsWithAdmin | ArgsWithout | ArgsWithProtect) {
}: ArgsWithAdmin | ArgsWithout | ArgsWithProtect) => {
const decorators = [Controller(`${plugin_code}${route ? `/${route}` : ''}`)];

if (isDev) {
Expand All @@ -56,4 +56,4 @@ export function Controllers({
}

return applyDecorators(...decorators);
}
};
2 changes: 1 addition & 1 deletion packages/create-vitnode-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@types/node": "^22.10.2",
"@types/prompts": "^2.4.9",
"@types/validate-npm-package-name": "^4.0.2",
"commander": "^12.1.0",
"commander": "^13.0.0",
"cross-spawn": "^7.0.6",
"eslint-config-typescript-vitnode": "workspace:*",
"ora": "^8.1.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config-typescript-vitnode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@
"eslint-plugin-react-hooks": "^5.1.0",
"globals": "^15.14.0",
"prettier-plugin-tailwindcss": "^0.6.9",
"typescript-eslint": "^8.18.2"
"typescript-eslint": "^8.19.0"
}
}
28 changes: 14 additions & 14 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,23 @@
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.1",
"recharts": "^2.14.1",
"shiki": "^1.24.4",
"sonner": "^1.7.0",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.2",
"shiki": "^1.24.4",
"zod": "^3.24.1"
},
"devDependencies": {
"@hookform/devtools": "^4.3.3",
"@hookform/resolvers": "^3.9.1",
"@swc/cli": "^0.5.2",
"@swc/core": "^1.10.3",
"@swc/core": "^1.10.4",
"@types/lodash": "^4.17.13",
"@types/node": "^22.10.2",
"@types/nprogress": "^0.2.3",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"concurrently": "^9.1.1",
"concurrently": "^9.1.2",
"eslint-config-typescript-vitnode": "workspace:*",
"lucide-react": "^0.469.0",
"next": "^15.1.3",
Expand Down Expand Up @@ -160,17 +160,17 @@
"@radix-ui/react-visually-hidden": "^1.1.1",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/react-query": "^5.62.11",
"@tiptap/extension-code-block": "^2.10.4",
"@tiptap/extension-color": "^2.10.4",
"@tiptap/extension-heading": "^2.10.4",
"@tiptap/extension-link": "^2.10.4",
"@tiptap/extension-text-align": "^2.10.4",
"@tiptap/extension-text-style": "^2.10.4",
"@tiptap/extension-underline": "^2.10.4",
"@tiptap/html": "^2.10.4",
"@tiptap/pm": "^2.10.4",
"@tiptap/react": "^2.10.4",
"@tiptap/starter-kit": "^2.10.4",
"@tiptap/extension-code-block": "^2.11.0",
"@tiptap/extension-color": "^2.11.0",
"@tiptap/extension-heading": "^2.11.0",
"@tiptap/extension-link": "^2.11.0",
"@tiptap/extension-text-align": "^2.11.0",
"@tiptap/extension-text-style": "^2.11.0",
"@tiptap/extension-underline": "^2.11.0",
"@tiptap/html": "^2.11.0",
"@tiptap/pm": "^2.11.0",
"@tiptap/react": "^2.11.0",
"@tiptap/starter-kit": "^2.11.0",
"babel-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand Down
Loading

0 comments on commit 51a7cdf

Please sign in to comment.