chore: merge current beta into dev (#5718)

* ci: support releases on next branch (#5599)

Co-authored-by: Copilot <copilot@github.com>

* perf(memory): combine 3 node processes into one (#5600)

Co-authored-by: Copilot <copilot@github.com>

* ci: add beta release workflow (#5601)

Co-authored-by: Copilot <copilot@github.com>

* chore(release): 1.60.1-beta.1 [skip ci]

* chore: retrigger release v1.60.1-beta.1

* chore(release): 1.60.1-beta.1 [skip ci]

* ci: disable package-manager cache

* chore(release): 1.60.1-beta.1 [skip ci]

* perf: (next branch) reduce idle memory on startup (#5637)

* chore(release): 1.60.1-beta.2 [skip ci]

* chore(release): 1.61.1-beta.1 [skip ci]

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: homarr-releases[bot] <175486441+homarr-releases[bot]@users.noreply.github.com>
Co-authored-by: Thomas <thomas@ajnart.fr>
This commit is contained in:
authored and GitHub committed 2026-05-15 19:35:55 +02:00
commit 75640f21e0
25 files changed
+3979 -4252

No files matched your search

-4
View File
@@ -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:
+10 -8
View File
@@ -5,6 +5,7 @@ on:
branches:
- main
- beta
- next
- dev
workflow_dispatch:
inputs:
@@ -34,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' }}
SKIP_RELEASE: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'dev' || github.ref_name == 'next' }}
outputs:
version: ${{ steps.read-semver.outputs.version || steps.version-fallback.outputs.version }}
git_ref: ${{ steps.read-git-ref.outputs.ref || github.ref }}
@@ -54,7 +55,7 @@ jobs:
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
client-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
permission-contents: write # required to commit package.json & changelog changes, merge them to dev and publish the release
permission-contents: write # required to commit package.json & changelog changes, merge them to dev / next and publish the release
- uses: actions/checkout@v6
if: env.SKIP_RELEASE == 'false'
@@ -66,7 +67,7 @@ jobs:
if: env.SKIP_RELEASE == 'false'
with:
node-version: 24.15.0
cache: "pnpm"
package-manager-cache: false
- run: npm i -g pnpm
if: env.SKIP_RELEASE == 'false'
- name: Install dependencies
@@ -95,20 +96,21 @@ jobs:
id: read-git-ref
run: |
echo "ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Update dev branch
- name: Update dev / next branch
if: env.SKIP_RELEASE == 'false'
continue-on-error: true # Prevent pipeline from failing when merge fails
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
UPSTREAM_BRANCH: ${{ github.ref_name == 'main' && 'dev' || 'next' }}
run: |
git config user.name "Releases Homarr"
git config user.email "175486441+homarr-releases[bot]@users.noreply.github.com"
git remote set-url origin https://x-access-token:${{ steps.obtainToken.outputs.token }}@github.com/${{ github.repository }}.git
git fetch origin dev
git checkout dev
git pull origin dev
git fetch origin ${{ env.UPSTREAM_BRANCH }}
git checkout ${{ env.UPSTREAM_BRANCH }}
git pull origin ${{ env.UPSTREAM_BRANCH }}
git rebase ${{ github.ref_name }}
git push origin dev
git push origin ${{ env.UPSTREAM_BRANCH }}
build-amd64:
name: Build docker image for amd64
needs: release
+3857 -3838
View File
File diff suppressed because it is too large. Load diff
-2
View File
@@ -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
+2
View File
@@ -32,6 +32,8 @@ const nextConfig: NextConfig = {
experimental: {
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
turbopackFileSystemCacheForDev: true,
preloadEntriesOnStart: false,
webpackMemoryOptimizations: true,
},
transpilePackages: ["@homarr/ui", "@homarr/notifications", "@homarr/modals", "@homarr/spotlight", "@homarr/widgets"],
images: {
+2
View File
@@ -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:",
+6
View File
@@ -0,0 +1,6 @@
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("@homarr/tasks");
await import("@homarr/websocket");
}
}
+1 -3
View File
@@ -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",
-40
View File
@@ -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<JobRouter>["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);
}
})();
+3 -1
View File
@@ -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",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.61.0",
"version": "1.61.1-beta.1",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -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",
-1
View File
@@ -25,7 +25,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",
+19 -16
View File
@@ -2,33 +2,36 @@ import { observable } from "@trpc/server/observable";
import z from "zod/v4";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { 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 { 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(({ input, ctx }) => {
new JobManager(ctx.db, jobGroup).stop(input);
}),
updateJobInterval: permissionRequiredProcedure
.requiresPermission("admin")
@@ -38,23 +41,23 @@ export const cronJobsRouter = createTRPCRouter({
cron: z.string(),
}),
)
.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<TaskStatus>((emit) => {
-9
View File
@@ -1,9 +0,0 @@
import baseConfig from "@homarr/eslint-config/base";
/** @type {import('typescript-eslint').Config} */
export default [
{
ignores: [],
},
...baseConfig,
];
-46
View File
@@ -1,46 +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:",
"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/react": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
-20
View File
@@ -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<JobRouter>({
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}`;
}
-3
View File
@@ -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";
-11
View File
@@ -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,
});
-85
View File
@@ -1,85 +0,0 @@
import { initTRPC, TRPCError } from "@trpc/server";
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<void>;
triggerAsync(name: JobGroupKeys): Promise<void>;
stop(name: JobGroupKeys): void;
updateIntervalAsync(name: JobGroupKeys, cron: string): Promise<void>;
disableAsync(name: JobGroupKeys): Promise<void>;
enableAsync(name: JobGroupKeys): Promise<void>;
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 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(({ input, ctx }) => {
ctx.manager.stop(input);
}),
updateInterval: apiKeyProcedure
.input(
z.object({
name: jobNameSchema,
cron: z.string(),
}),
)
.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;
-8
View File
@@ -1,8 +0,0 @@
{
"extends": "@homarr/tsconfig/base.json",
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
},
"include": ["*.ts", "src"],
"exclude": ["node_modules"]
}
+4 -2
View File
@@ -5,7 +5,8 @@
"license": "Apache-2.0",
"type": "module",
"exports": {
".": "./index.ts"
".": "./index.ts",
"./job-manager": "./src/job-manager.ts"
},
"typesVersions": {
"*": {
@@ -38,7 +39,8 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"semver-parser": "catalog:"
"semver-parser": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+40 -29
View File
@@ -28,34 +28,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,
umamiActiveVisitors: umamiActiveVisitorsJob,
});
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,
umamiActiveVisitors: umamiActiveVisitorsJob,
});
};
// Add global type augmentation for homarr
declare global {
var cronJobs: ReturnType<typeof getJobGroup> | undefined;
}
global.cronJobs ??= getJobGroup();
export const jobGroup = global.cronJobs;
export type JobGroupKeys = ReturnType<(typeof jobGroup)["getKeys"]>[number];
@@ -1,7 +1,6 @@
import { Cron } from "croner";
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,
+31 -111
View File
@@ -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
@@ -1151,9 +1154,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
@@ -1506,55 +1506,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.100.9(react@19.2.6)
'@trpc/client':
specifier: 'catalog:'
version: 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3)
'@trpc/server':
specifier: 'catalog:'
version: 11.17.0(typescript@5.9.3)
'@trpc/tanstack-react-query':
specifier: 'catalog:'
version: 11.17.0(@tanstack/react-query@5.100.9(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3)
react:
specifier: 'catalog:'
version: 19.2.6
zod:
specifier: 'catalog:'
version: 4.4.3
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/react':
specifier: 'catalog:'
version: 19.2.14
eslint:
specifier: 'catalog:'
version: 10.3.0
typescript:
specifier: 'catalog:'
version: 5.9.3
packages/cron-job-status:
dependencies:
'@homarr/redis':
@@ -1630,6 +1581,9 @@ importers:
semver-parser:
specifier: 'catalog:'
version: 4.1.8
zod:
specifier: 'catalog:'
version: 4.4.3
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -4385,7 +4339,6 @@ packages:
'@million/lint@1.0.14':
resolution: {integrity: sha512-u6/kglVwZRu5+GMmtkNlGLqJVkgTl0TtM+hLa9rBg7pldx+5NG5bk45NvL37uZmAr2Xfa1C6qHb7GrFwfP372g==}
deprecated: This package is no longer maintained
hasBin: true
'@next/env@16.2.6':
@@ -5717,9 +5670,6 @@ packages:
'@types/node@18.19.50':
resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==}
'@types/node@24.12.2':
resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==}
'@types/node@24.12.3':
resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==}
@@ -6334,10 +6284,6 @@ packages:
resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
engines: {node: '>=10'}
brace-expansion@5.0.5:
resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==}
engines: {node: 18 || 20 || >=22}
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
@@ -7423,16 +7369,9 @@ packages:
fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
fast-xml-builder@1.1.5:
resolution: {integrity: sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==}
fast-xml-builder@1.1.9:
resolution: {integrity: sha512-jcyKVSEX13iseJqg7n/KWw+xnu/7fdrZ333Fac54KjHDIELVCfDDJXYIm6DTJ0Su4gSzrhqiK0DzY/wZbF40mw==}
fast-xml-parser@5.7.2:
resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==}
hasBin: true
fast-xml-parser@5.7.3:
resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==}
hasBin: true
@@ -11852,7 +11791,7 @@ snapshots:
'@extractus/feed-extractor@7.2.1':
dependencies:
'@pwshub/bellajs': 13.0.2
fast-xml-parser: 5.7.2
fast-xml-parser: 5.7.3
html-entities: 2.6.0
'@fastify/ajv-compiler@4.0.5':
@@ -12290,7 +12229,7 @@ snapshots:
'@kubernetes/client-node@1.4.0':
dependencies:
'@types/js-yaml': 4.0.9
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/node-fetch': 2.6.13
'@types/stream-buffers': 3.0.7
form-data: 4.0.5
@@ -13866,7 +13805,7 @@ snapshots:
'@types/adm-zip@0.5.8':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/aws-lambda@8.10.146': {}
@@ -13893,16 +13832,16 @@ snapshots:
'@types/bcrypt@6.0.0':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/better-sqlite3@7.6.13':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/body-parser@1.19.5':
dependencies:
'@types/connect': 3.4.38
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/chai@5.2.2':
dependencies:
@@ -13912,7 +13851,7 @@ snapshots:
'@types/connect@3.4.38':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/cookie@0.4.1': {}
@@ -13921,7 +13860,7 @@ snapshots:
'@types/connect': 3.4.38
'@types/express': 4.17.21
'@types/keygrip': 1.0.6
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/cors@2.8.17':
dependencies:
@@ -13965,13 +13904,13 @@ snapshots:
'@types/docker-modem@3.0.6':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/ssh2': 1.15.1
'@types/dockerode@4.0.1':
dependencies:
'@types/docker-modem': 3.0.6
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/ssh2': 1.15.1
'@types/esrecurse@4.3.1': {}
@@ -13984,7 +13923,7 @@ snapshots:
'@types/express-serve-static-core@4.19.5':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/qs': 6.9.16
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -14020,7 +13959,7 @@ snapshots:
'@types/node-fetch@2.6.13':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
form-data: 4.0.5
'@types/node-unifi@2.5.1(patch_hash=5e6ae51e2a17a7f9729bfa30b0eb3d0842a5810ac6db47603ab4a6efa1ed84c5)':
@@ -14031,10 +13970,6 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@24.12.2':
dependencies:
undici-types: 7.16.0
'@types/node@24.12.3':
dependencies:
undici-types: 7.16.0
@@ -14043,7 +13978,7 @@ snapshots:
'@types/pg@8.20.0':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
pg-protocol: 1.13.0
pg-types: 2.2.0
@@ -14070,21 +14005,21 @@ snapshots:
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/serve-static@1.15.7':
dependencies:
'@types/http-errors': 2.0.4
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/send': 0.17.4
'@types/ssh2-streams@0.1.12':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/ssh2@0.5.52':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/ssh2-streams': 0.1.12
'@types/ssh2@1.15.1':
@@ -14093,7 +14028,7 @@ snapshots:
'@types/stream-buffers@3.0.7':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/swagger-ui-react@5.18.0':
dependencies:
@@ -14114,11 +14049,11 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@types/xml2js@0.4.14':
dependencies:
'@types/node': 24.12.2
'@types/node': 24.12.3
'@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.3.0)(typescript@5.9.3))(eslint@10.3.0)(typescript@5.9.3)':
dependencies:
@@ -14784,10 +14719,6 @@ snapshots:
widest-line: 3.1.0
wrap-ansi: 7.0.0
brace-expansion@5.0.5:
dependencies:
balanced-match: 4.0.2
brace-expansion@5.0.6:
dependencies:
balanced-match: 4.0.2
@@ -15980,21 +15911,10 @@ snapshots:
fast-uri@3.1.0: {}
fast-xml-builder@1.1.5:
dependencies:
path-expression-matcher: 1.5.0
fast-xml-builder@1.1.9:
dependencies:
path-expression-matcher: 1.5.0
fast-xml-parser@5.7.2:
dependencies:
'@nodable/entities': 2.1.0
fast-xml-builder: 1.1.5
path-expression-matcher: 1.5.0
strnum: 2.2.3
fast-xml-parser@5.7.3:
dependencies:
'@nodable/entities': 2.1.0
@@ -17312,7 +17232,7 @@ snapshots:
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.5
brace-expansion: 5.0.6
minimatch@3.1.5:
dependencies:
@@ -18089,7 +18009,7 @@ snapshots:
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/node': 24.12.2
'@types/node': 24.12.3
long: 5.3.2
proxmox-api@1.1.1:
+1 -11
View File
@@ -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)
# Start nginx proxy
# 1. Replace the HOSTNAME in the nginx template file
@@ -38,21 +36,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