diff --git a/.env.example b/.env.example index d643c1194..ff3c38f3d 100644 --- a/.env.example +++ b/.env.example @@ -12,10 +12,6 @@ AUTH_SECRET="supersecret" # or starting the project without any (which will show a randomly generated one). SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000 -# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api -# export CRON_JOB_API_KEY=$(openssl rand -base64 32) -# CRON_JOB_API_KEY="your-generated-api-key" - LOG_LEVEL='info' # This is how you can use the sqlite driver: diff --git a/Dockerfile b/Dockerfile index eab3c4419..51b562f7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,8 +43,6 @@ RUN mkdir -p /var/cache/nginx && \ COPY --from=builder /app/apps/nextjs/next.config.ts . COPY --from=builder /app/apps/nextjs/package.json . -COPY --from=builder /app/apps/tasks/tasks.cjs ./apps/tasks/tasks.cjs -COPY --from=builder /app/apps/websocket/wssServer.cjs ./apps/websocket/wssServer.cjs COPY --from=builder /app/node_modules/better-sqlite3/build/Release/better_sqlite3.node /app/build/better_sqlite3.node COPY --from=builder /app/packages/db/migrations ./db/migrations diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 92538095f..4a7323606 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -44,9 +44,11 @@ "@homarr/server-settings": "workspace:^0.1.0", "@homarr/settings": "workspace:^0.1.0", "@homarr/spotlight": "workspace:^0.1.0", + "@homarr/tasks": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", + "@homarr/websocket": "workspace:^0.1.0", "@homarr/widgets": "workspace:^0.1.0", "@mantine/colors-generator": "catalog:", "@mantine/core": "catalog:", diff --git a/apps/nextjs/src/instrumentation.ts b/apps/nextjs/src/instrumentation.ts new file mode 100644 index 000000000..19a2865b7 --- /dev/null +++ b/apps/nextjs/src/instrumentation.ts @@ -0,0 +1,6 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === "nodejs") { + await import("@homarr/tasks"); + await import("@homarr/websocket"); + } +} diff --git a/apps/tasks/package.json b/apps/tasks/package.json index 3cb918df5..552beb0f1 100644 --- a/apps/tasks/package.json +++ b/apps/tasks/package.json @@ -5,12 +5,11 @@ "license": "Apache-2.0", "type": "module", "exports": { - ".": "./src/index.ts" + ".": "./src/main.ts" }, "main": "./src/main.ts", "types": "./src/main.ts", "scripts": { - "build": "esbuild src/main.ts --bundle --platform=node --loader:.scss=text --external:*.node --external:@opentelemetry/api --external:deasync --external:bcrypt --outfile=tasks.cjs", "clean": "rm -rf .turbo node_modules", "dev": "pnpm with-env tsx ./src/main.ts", "format": "prettier --check . --ignore-path ../../.gitignore", @@ -24,7 +23,6 @@ "@homarr/auth": "workspace:^0.1.0", "@homarr/common": "workspace:^0.1.0", "@homarr/core": "workspace:^", - "@homarr/cron-job-api": "workspace:^0.1.0", "@homarr/cron-jobs": "workspace:^0.1.0", "@homarr/cron-jobs-core": "workspace:^0.1.0", "@homarr/db": "workspace:^0.1.0", diff --git a/apps/tasks/src/main.ts b/apps/tasks/src/main.ts index 4c60f1646..55416e689 100644 --- a/apps/tasks/src/main.ts +++ b/apps/tasks/src/main.ts @@ -1,52 +1,12 @@ // This import has to be the first import in the file so that the agent is overridden before any other modules are imported. import "./overrides"; -import type { FastifyTRPCPluginOptions } from "@trpc/server/adapters/fastify"; -import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify"; -import fastify from "fastify"; - -import { createLogger } from "@homarr/core/infrastructure/logs"; -import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error"; -import type { JobRouter } from "@homarr/cron-job-api"; -import { jobRouter } from "@homarr/cron-job-api"; -import { CRON_JOB_API_KEY_HEADER, CRON_JOB_API_PATH, CRON_JOB_API_PORT } from "@homarr/cron-job-api/constants"; import { jobGroup } from "@homarr/cron-jobs"; -import { db } from "@homarr/db"; -import { JobManager } from "./job-manager"; import { onStartAsync } from "./on-start"; -const logger = createLogger({ module: "tasksMain" }); - -const server = fastify({ - maxParamLength: 5000, -}); -server.register(fastifyTRPCPlugin, { - prefix: CRON_JOB_API_PATH, - trpcOptions: { - router: jobRouter, - createContext: ({ req }) => ({ - manager: new JobManager(db, jobGroup), - apiKey: req.headers[CRON_JOB_API_KEY_HEADER] as string | undefined, - }), - onError({ path, error }) { - logger.error(new ErrorWithMetadata("Error in tasks tRPC handler", { path }, { cause: error })); - }, - } satisfies FastifyTRPCPluginOptions["trpcOptions"], -}); - void (async () => { await onStartAsync(); await jobGroup.initializeAsync(); await jobGroup.startAllAsync(); - - try { - await server.listen({ port: CRON_JOB_API_PORT }); - logger.info("Tasks web server started successfully", { port: CRON_JOB_API_PORT }); - } catch (err) { - logger.error( - new ErrorWithMetadata("Failed to start tasks web server", { port: CRON_JOB_API_PORT }, { cause: err }), - ); - process.exit(1); - } })(); diff --git a/apps/websocket/package.json b/apps/websocket/package.json index 8fe731bcb..c4a9cf9d0 100644 --- a/apps/websocket/package.json +++ b/apps/websocket/package.json @@ -4,10 +4,12 @@ "private": true, "license": "Apache-2.0", "type": "module", + "exports": { + ".": "./src/main.ts" + }, "main": "./src/main.ts", "types": "./src/main.ts", "scripts": { - "build": "esbuild src/main.ts --bundle --platform=node --outfile=wssServer.cjs --external:bcrypt --external:@opentelemetry/api --external:deasync --external:cpu-features --loader:.html=text --loader:.scss=text --loader:.node=text", "clean": "rm -rf .turbo node_modules", "dev": "pnpm with-env tsx ./src/main.ts", "format": "prettier --check . --ignore-path ../../.gitignore", diff --git a/package.json b/package.json index cf499a5af..fe347d041 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "db:migration:sqlite:run": "pnpm -F db migration:sqlite:run", "db:push": "pnpm -F db push:sqlite", "db:studio": "pnpm -F db studio", - "dev": "turbo dev --parallel", + "dev": "pnpm -F nextjs dev", "docker:dev": "docker compose -f ./development/development.docker-compose.yml up", "format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache", "format:fix": "turbo format --continue -- --write --cache --cache-location node_modules/.cache/.prettiercache", diff --git a/packages/api/package.json b/packages/api/package.json index 04f7bae84..8aa4b9a10 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -24,7 +24,6 @@ "@homarr/auth": "workspace:^0.1.0", "@homarr/common": "workspace:^0.1.0", "@homarr/core": "workspace:^0.1.0", - "@homarr/cron-job-api": "workspace:^0.1.0", "@homarr/cron-job-status": "workspace:^0.1.0", "@homarr/cron-jobs": "workspace:^0.1.0", "@homarr/db": "workspace:^0.1.0", diff --git a/packages/api/src/router/cron-jobs.ts b/packages/api/src/router/cron-jobs.ts index 5428ac374..4fd3fba5b 100644 --- a/packages/api/src/router/cron-jobs.ts +++ b/packages/api/src/router/cron-jobs.ts @@ -2,33 +2,36 @@ import { observable } from "@trpc/server/observable"; import z from "zod/v4"; import { createLogger } from "@homarr/core/infrastructure/logs"; -import { cronExpressionSchema, jobGroupKeys, jobNameSchema } from "@homarr/cron-job-api"; -import { cronJobApi } from "@homarr/cron-job-api/client"; import type { TaskStatus } from "@homarr/cron-job-status"; import { createCronJobStatusChannel } from "@homarr/cron-job-status"; +import { cronExpressionSchema, jobGroup } from "@homarr/cron-jobs"; +import { JobManager } from "@homarr/cron-jobs/job-manager"; import { createTRPCRouter, permissionRequiredProcedure } from "../trpc"; +const jobGroupKeys = jobGroup.getKeys(); +const jobNameSchema = z.enum(jobGroup.getKeys()); + const logger = createLogger({ module: "cronJobsRouter" }); export const cronJobsRouter = createTRPCRouter({ triggerJob: permissionRequiredProcedure .requiresPermission("admin") .input(jobNameSchema) - .mutation(async ({ input }) => { - await cronJobApi.trigger.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).triggerAsync(input); }), startJob: permissionRequiredProcedure .requiresPermission("admin") .input(jobNameSchema) - .mutation(async ({ input }) => { - await cronJobApi.start.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).startAsync(input); }), stopJob: permissionRequiredProcedure .requiresPermission("admin") .input(jobNameSchema) - .mutation(async ({ input }) => { - await cronJobApi.stop.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).stopAsync(input); }), updateJobInterval: permissionRequiredProcedure .requiresPermission("admin") @@ -38,23 +41,23 @@ export const cronJobsRouter = createTRPCRouter({ cron: cronExpressionSchema, }), ) - .mutation(async ({ input }) => { - await cronJobApi.updateInterval.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).updateIntervalAsync(input.name, input.cron); }), disableJob: permissionRequiredProcedure .requiresPermission("admin") .input(jobNameSchema) - .mutation(async ({ input }) => { - await cronJobApi.disable.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).disableAsync(input); }), enableJob: permissionRequiredProcedure .requiresPermission("admin") .input(jobNameSchema) - .mutation(async ({ input }) => { - await cronJobApi.enable.mutate(input); + .mutation(async ({ input, ctx }) => { + await new JobManager(ctx.db, jobGroup).enableAsync(input); }), - getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async () => { - return await cronJobApi.getAll.query(); + getJobs: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => { + return await new JobManager(ctx.db, jobGroup).getAllAsync(); }), subscribeToStatusUpdates: permissionRequiredProcedure.requiresPermission("admin").subscription(() => { return observable((emit) => { diff --git a/packages/cron-job-api/eslint.config.js b/packages/cron-job-api/eslint.config.js deleted file mode 100644 index 5b19b6f8a..000000000 --- a/packages/cron-job-api/eslint.config.js +++ /dev/null @@ -1,9 +0,0 @@ -import baseConfig from "@homarr/eslint-config/base"; - -/** @type {import('typescript-eslint').Config} */ -export default [ - { - ignores: [], - }, - ...baseConfig, -]; diff --git a/packages/cron-job-api/package.json b/packages/cron-job-api/package.json deleted file mode 100644 index 09bde876b..000000000 --- a/packages/cron-job-api/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@homarr/cron-job-api", - "version": "0.1.0", - "private": true, - "license": "Apache-2.0", - "type": "module", - "exports": { - ".": "./src/index.ts", - "./env": "./src/env.ts", - "./constants": "./src/constants.ts", - "./client": "./src/client.ts" - }, - "typesVersions": { - "*": { - "*": [ - "src/*" - ] - } - }, - "scripts": { - "clean": "rm -rf .turbo node_modules", - "format": "prettier --check . --ignore-path ../../.gitignore", - "lint": "eslint", - "typecheck": "tsc --noEmit" - }, - "prettier": "@homarr/prettier-config", - "dependencies": { - "@homarr/common": "workspace:^0.1.0", - "@homarr/core": "workspace:^0.1.0", - "@homarr/cron-jobs": "workspace:^0.1.0", - "@tanstack/react-query": "catalog:", - "@trpc/client": "catalog:", - "@trpc/server": "catalog:", - "@trpc/tanstack-react-query": "catalog:", - "node-cron": "catalog:", - "react": "catalog:", - "zod": "catalog:" - }, - "devDependencies": { - "@homarr/eslint-config": "workspace:^0.2.0", - "@homarr/prettier-config": "workspace:^0.1.0", - "@homarr/tsconfig": "workspace:^0.1.0", - "@types/node-cron": "catalog:", - "@types/react": "catalog:", - "eslint": "catalog:", - "typescript": "catalog:" - } -} diff --git a/packages/cron-job-api/src/client.ts b/packages/cron-job-api/src/client.ts deleted file mode 100644 index 0d376a328..000000000 --- a/packages/cron-job-api/src/client.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createTRPCClient, httpLink } from "@trpc/client"; - -import type { JobRouter } from "."; -import { CRON_JOB_API_KEY_HEADER, CRON_JOB_API_PATH, CRON_JOB_API_PORT } from "./constants"; -import { env } from "./env"; - -export const cronJobApi = createTRPCClient({ - links: [ - httpLink({ - url: `${getBaseUrl()}${CRON_JOB_API_PATH}`, - headers: { - [CRON_JOB_API_KEY_HEADER]: env.CRON_JOB_API_KEY, - }, - }), - ], -}); - -function getBaseUrl() { - return `http://localhost:${CRON_JOB_API_PORT}`; -} diff --git a/packages/cron-job-api/src/constants.ts b/packages/cron-job-api/src/constants.ts deleted file mode 100644 index 8ce789e72..000000000 --- a/packages/cron-job-api/src/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const CRON_JOB_API_PORT = 3002; -export const CRON_JOB_API_PATH = "/trpc"; -export const CRON_JOB_API_KEY_HEADER = "cron-job-api-key"; diff --git a/packages/cron-job-api/src/env.ts b/packages/cron-job-api/src/env.ts deleted file mode 100644 index 7efb3f6e2..000000000 --- a/packages/cron-job-api/src/env.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { z } from "zod/v4"; - -import { env as commonEnv } from "@homarr/common/env"; -import { createEnv } from "@homarr/core/infrastructure/env"; - -export const env = createEnv({ - server: { - CRON_JOB_API_KEY: commonEnv.NODE_ENV === "development" ? z.string().default("test") : z.string(), - }, - experimental__runtimeEnv: process.env, -}); diff --git a/packages/cron-job-api/src/index.ts b/packages/cron-job-api/src/index.ts deleted file mode 100644 index 270a51242..000000000 --- a/packages/cron-job-api/src/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server"; -import { validate } from "node-cron"; -import { z } from "zod/v4"; - -import type { JobGroupKeys } from "@homarr/cron-jobs"; -import { jobGroup } from "@homarr/cron-jobs"; - -import { env } from "./env"; - -export const jobGroupKeys = jobGroup.getKeys(); -export const jobNameSchema = z.enum(jobGroup.getKeys()); - -export interface IJobManager { - startAsync(name: JobGroupKeys): Promise; - triggerAsync(name: JobGroupKeys): Promise; - stopAsync(name: JobGroupKeys): Promise; - updateIntervalAsync(name: JobGroupKeys, cron: string): Promise; - disableAsync(name: JobGroupKeys): Promise; - enableAsync(name: JobGroupKeys): Promise; - getAllAsync(): Promise< - { - name: JobGroupKeys; - cron: string; - preventManualExecution: boolean; - preventCustomInterval: boolean; - isEnabled: boolean; - }[] - >; -} - -const t = initTRPC - .context<{ - manager: IJobManager; - apiKey?: string; - }>() - .create(); - -const createTrpcRouter = t.router; -const apiKeyProcedure = t.procedure.use(({ ctx, next }) => { - if (ctx.apiKey !== env.CRON_JOB_API_KEY) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Missing or invalid API key", - }); - } - - return next({ - ctx: { - ...ctx, - apiKey: undefined, // Clear the API key after checking - }, - }); -}); - -export const cronExpressionSchema = z.string().refine((expression) => validate(expression), { - error: "Invalid cron expression", -}); - -export const jobRouter = createTrpcRouter({ - start: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => { - await ctx.manager.startAsync(input); - }), - trigger: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => { - await ctx.manager.triggerAsync(input); - }), - stop: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => { - await ctx.manager.stopAsync(input); - }), - updateInterval: apiKeyProcedure - .input( - z.object({ - name: jobNameSchema, - cron: cronExpressionSchema, - }), - ) - .mutation(async ({ input, ctx }) => { - await ctx.manager.updateIntervalAsync(input.name, input.cron); - }), - disable: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => { - await ctx.manager.disableAsync(input); - }), - enable: apiKeyProcedure.input(jobNameSchema).mutation(async ({ input, ctx }) => { - await ctx.manager.enableAsync(input); - }), - getAll: apiKeyProcedure.query(({ ctx }) => { - return ctx.manager.getAllAsync(); - }), -}); - -export type JobRouter = typeof jobRouter; diff --git a/packages/cron-job-api/tsconfig.json b/packages/cron-job-api/tsconfig.json deleted file mode 100644 index cbe8483d9..000000000 --- a/packages/cron-job-api/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "@homarr/tsconfig/base.json", - "compilerOptions": { - "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" - }, - "include": ["*.ts", "src"], - "exclude": ["node_modules"] -} diff --git a/packages/cron-jobs/package.json b/packages/cron-jobs/package.json index d0a55d25a..99e91bd16 100644 --- a/packages/cron-jobs/package.json +++ b/packages/cron-jobs/package.json @@ -5,7 +5,8 @@ "license": "Apache-2.0", "type": "module", "exports": { - ".": "./index.ts" + ".": "./index.ts", + "./job-manager": "./src/job-manager.ts" }, "typesVersions": { "*": { @@ -38,12 +39,15 @@ "@homarr/server-settings": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", - "semver-parser": "catalog:" + "node-cron": "catalog:", + "semver-parser": "catalog:", + "zod": "catalog:" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", + "@types/node-cron": "catalog:", "eslint": "catalog:", "typescript": "catalog:" } diff --git a/packages/cron-jobs/src/index.ts b/packages/cron-jobs/src/index.ts index 7f2b2349f..0abe74a36 100644 --- a/packages/cron-jobs/src/index.ts +++ b/packages/cron-jobs/src/index.ts @@ -27,33 +27,45 @@ import { updateCheckerJob } from "./jobs/update-checker"; import { weatherJob } from "./jobs/weather"; import { createCronJobGroup } from "./lib"; -export const jobGroup = createCronJobGroup({ - analytics: analyticsJob, - iconsUpdater: iconsUpdaterJob, - ping: pingJob, - smartHomeEntityState: smartHomeEntityStateJob, - mediaServer: mediaServerJob, - mediaOrganizer: mediaOrganizerJob, - downloads: downloadsJob, - dnsHole: dnsHoleJob, - mediaRequestStats: mediaRequestStatsJob, - mediaRequestList: mediaRequestListJob, - rssFeeds: rssFeedsJob, - indexerManager: indexerManagerJob, - healthMonitoring: healthMonitoringJob, - updateChecker: updateCheckerJob, - mediaTranscoding: mediaTranscodingJob, - minecraftServerStatus: minecraftServerStatusJob, - dockerContainers: dockerContainersJob, - networkController: networkControllerJob, - firewallCpu: firewallCpuJob, - firewallMemory: firewallMemoryJob, - firewallVersion: firewallVersionJob, - firewallInterfaces: firewallInterfacesJob, - refreshNotifications: refreshNotificationsJob, - weather: weatherJob, - timetable: timetableJob, - tracearr: tracearrJob, -}); +const getJobGroup = () => { + return createCronJobGroup({ + analytics: analyticsJob, + iconsUpdater: iconsUpdaterJob, + ping: pingJob, + smartHomeEntityState: smartHomeEntityStateJob, + mediaServer: mediaServerJob, + mediaOrganizer: mediaOrganizerJob, + downloads: downloadsJob, + dnsHole: dnsHoleJob, + mediaRequestStats: mediaRequestStatsJob, + mediaRequestList: mediaRequestListJob, + rssFeeds: rssFeedsJob, + indexerManager: indexerManagerJob, + healthMonitoring: healthMonitoringJob, + updateChecker: updateCheckerJob, + mediaTranscoding: mediaTranscodingJob, + minecraftServerStatus: minecraftServerStatusJob, + dockerContainers: dockerContainersJob, + networkController: networkControllerJob, + firewallCpu: firewallCpuJob, + firewallMemory: firewallMemoryJob, + firewallVersion: firewallVersionJob, + firewallInterfaces: firewallInterfacesJob, + refreshNotifications: refreshNotificationsJob, + weather: weatherJob, + timetable: timetableJob, + tracearr: tracearrJob, + }); +}; + +// Add global type augmentation for homarr +declare global { + var cronJobs: ReturnType | undefined; +} + +global.cronJobs ??= getJobGroup(); + +export const jobGroup = global.cronJobs; export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number]; +export * from "./schemas"; diff --git a/apps/tasks/src/job-manager.ts b/packages/cron-jobs/src/job-manager.ts similarity index 97% rename from apps/tasks/src/job-manager.ts rename to packages/cron-jobs/src/job-manager.ts index f4ec8bf97..19ef8f6e8 100644 --- a/apps/tasks/src/job-manager.ts +++ b/packages/cron-jobs/src/job-manager.ts @@ -1,7 +1,6 @@ import { schedule, validate as validateCron } from "node-cron"; import { createLogger } from "@homarr/core/infrastructure/logs"; -import type { IJobManager } from "@homarr/cron-job-api"; import type { jobGroup as cronJobGroup, JobGroupKeys } from "@homarr/cron-jobs"; import type { Database, InferInsertModel } from "@homarr/db"; import { eq } from "@homarr/db"; @@ -9,7 +8,7 @@ import { cronJobConfigurations } from "@homarr/db/schema"; const logger = createLogger({ module: "jobManager" }); -export class JobManager implements IJobManager { +export class JobManager { constructor( private db: Database, private jobGroup: typeof cronJobGroup, diff --git a/packages/cron-jobs/src/schemas.ts b/packages/cron-jobs/src/schemas.ts new file mode 100644 index 000000000..af9f8b727 --- /dev/null +++ b/packages/cron-jobs/src/schemas.ts @@ -0,0 +1,6 @@ +import { validate } from "node-cron"; +import z from "zod"; + +export const cronExpressionSchema = z.string().refine((expression) => validate(expression), { + error: "Invalid cron expression", +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index addb9194c..cb2c7d694 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -754,6 +754,9 @@ importers: '@homarr/spotlight': specifier: workspace:^0.1.0 version: link:../../packages/spotlight + '@homarr/tasks': + specifier: workspace:^0.1.0 + version: link:../tasks '@homarr/translation': specifier: workspace:^0.1.0 version: link:../../packages/translation @@ -763,6 +766,9 @@ importers: '@homarr/validation': specifier: workspace:^0.1.0 version: link:../../packages/validation + '@homarr/websocket': + specifier: workspace:^0.1.0 + version: link:../websocket '@homarr/widgets': specifier: workspace:^0.1.0 version: link:../../packages/widgets @@ -947,9 +953,6 @@ importers: '@homarr/core': specifier: workspace:^ version: link:../../packages/core - '@homarr/cron-job-api': - specifier: workspace:^0.1.0 - version: link:../../packages/cron-job-api '@homarr/cron-jobs': specifier: workspace:^0.1.0 version: link:../../packages/cron-jobs @@ -1148,9 +1151,6 @@ importers: '@homarr/core': specifier: workspace:^0.1.0 version: link:../core - '@homarr/cron-job-api': - specifier: workspace:^0.1.0 - version: link:../cron-job-api '@homarr/cron-job-status': specifier: workspace:^0.1.0 version: link:../cron-job-status @@ -1503,61 +1503,6 @@ importers: specifier: 'catalog:' version: 5.9.3 - packages/cron-job-api: - dependencies: - '@homarr/common': - specifier: workspace:^0.1.0 - version: link:../common - '@homarr/core': - specifier: workspace:^0.1.0 - version: link:../core - '@homarr/cron-jobs': - specifier: workspace:^0.1.0 - version: link:../cron-jobs - '@tanstack/react-query': - specifier: 'catalog:' - version: 5.99.2(react@19.2.5) - '@trpc/client': - specifier: 'catalog:' - version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3) - '@trpc/server': - specifier: 'catalog:' - version: 11.16.0(typescript@5.9.3) - '@trpc/tanstack-react-query': - specifier: 'catalog:' - version: 11.16.0(@tanstack/react-query@5.99.2(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3) - node-cron: - specifier: 'catalog:' - version: 4.2.1 - react: - specifier: 'catalog:' - version: 19.2.5 - zod: - specifier: 'catalog:' - version: 4.3.6 - devDependencies: - '@homarr/eslint-config': - specifier: workspace:^0.2.0 - version: link:../../tooling/eslint - '@homarr/prettier-config': - specifier: workspace:^0.1.0 - version: link:../../tooling/prettier - '@homarr/tsconfig': - specifier: workspace:^0.1.0 - version: link:../../tooling/typescript - '@types/node-cron': - specifier: 'catalog:' - version: 3.0.11 - '@types/react': - specifier: 'catalog:' - version: 19.2.14 - eslint: - specifier: 'catalog:' - version: 9.39.4 - typescript: - specifier: 'catalog:' - version: 5.9.3 - packages/cron-job-status: dependencies: '@homarr/redis': @@ -1630,9 +1575,15 @@ importers: '@homarr/validation': specifier: workspace:^0.1.0 version: link:../validation + node-cron: + specifier: 'catalog:' + version: 4.2.1 semver-parser: specifier: 'catalog:' version: 4.1.8 + zod: + specifier: 'catalog:' + version: 4.3.6 devDependencies: '@homarr/eslint-config': specifier: workspace:^0.2.0 @@ -1643,6 +1594,9 @@ importers: '@homarr/tsconfig': specifier: workspace:^0.1.0 version: link:../../tooling/typescript + '@types/node-cron': + specifier: 'catalog:' + version: 3.0.11 eslint: specifier: 'catalog:' version: 9.39.4 @@ -4383,7 +4337,6 @@ packages: '@million/lint@1.0.14': resolution: {integrity: sha512-u6/kglVwZRu5+GMmtkNlGLqJVkgTl0TtM+hLa9rBg7pldx+5NG5bk45NvL37uZmAr2Xfa1C6qHb7GrFwfP372g==} - deprecated: This package is no longer maintained hasBin: true '@ndaidong/bellajs@12.0.1': @@ -6882,9 +6835,6 @@ packages: dom-walk@0.1.2: resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - dompurify@3.3.3: - resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} - dompurify@3.4.0: resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==} @@ -7495,15 +7445,6 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -10783,10 +10724,12 @@ packages: uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true validate-npm-package-license@3.0.4: @@ -14665,7 +14608,7 @@ snapshots: axios@1.9.0: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.16.0 form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -15319,10 +15262,6 @@ snapshots: dom-walk@0.1.2: {} - dompurify@3.3.3: - optionalDependencies: - '@types/trusted-types': 2.0.7 - dompurify@3.4.0: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -16074,8 +16013,6 @@ snapshots: fn.name@1.1.0: {} - follow-redirects@1.15.9: {} - follow-redirects@1.16.0: {} for-each@0.3.5: @@ -19192,7 +19129,7 @@ snapshots: classnames: 2.5.1 css.escape: 1.5.1 deep-extend: 0.6.0 - dompurify: 3.3.3 + dompurify: 3.4.0 ieee754: 1.2.1 immutable: 3.8.2 js-file-download: 0.4.12 diff --git a/scripts/run.sh b/scripts/run.sh index 4d90499d5..fc6e80ba4 100644 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -16,8 +16,6 @@ fi # Auth secret is generated every time the container starts as it is required, but not used because we don't need JWTs or Mail hashing export AUTH_SECRET=$(openssl rand -base64 32) -# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api -export CRON_JOB_API_KEY=$(openssl rand -base64 32) # Support secret file for encryption key (is not empty and file exists) if [ -n "$SECRET_ENCRYPTION_KEY_FILE" ] && [ -f "$SECRET_ENCRYPTION_KEY_FILE" ]; then @@ -43,21 +41,13 @@ else REDIS_PID=$! fi - - -node apps/tasks/tasks.cjs & -TASKS_PID=$! - -node apps/websocket/wssServer.cjs & -WSS_PID=$! - node apps/nextjs/server.js & NEXTJS_PID=$! # Function to handle SIGTERM and shut down services terminate() { echo "Received SIGTERM. Shutting down..." - kill -TERM $NGINX_PID $TASKS_PID $WSS_PID $NEXTJS_PID 2>/dev/null + kill -TERM $NGINX_PID $NEXTJS_PID 2>/dev/null wait # kill redis-server last because of logging of other services and only if $REDIS_PID is set if [ -n "$REDIS_PID" ]; then