Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8889537ff8 |
No files matched your search
@@ -1,32 +1,3 @@
|
|||||||
## [1.48.0](https://github.com/homarr-labs/homarr/compare/v1.47.0...v1.48.0) (2025-12-26)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* enable dns caching by default ([#4706](https://github.com/homarr-labs/homarr/issues/4706)) ([fbb5298](https://github.com/homarr-labs/homarr/commit/fbb5298cb05b12eb2f19c121a1bc103caeb3e576))
|
|
||||||
* **http:** add proxy support ([#4721](https://github.com/homarr-labs/homarr/issues/4721)) ([5a57115](https://github.com/homarr-labs/homarr/commit/5a57115ca073aea1c75a000fda6a23648c5488e4))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.10 ([#4710](https://github.com/homarr-labs/homarr/issues/4710)) ([689523a](https://github.com/homarr-labs/homarr/commit/689523a194ab48bdc072b1cdf65f07ade6cbc584))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.24 ([#4711](https://github.com/homarr-labs/homarr/issues/4711)) ([648f7e2](https://github.com/homarr-labs/homarr/commit/648f7e2c7743af81f4bced36b7e1e80c21e851e5))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.25 ([#4724](https://github.com/homarr-labs/homarr/issues/4724)) ([ef12b2b](https://github.com/homarr-labs/homarr/commit/ef12b2b5c2e1131138937fb6da61ce4c167fe0eb))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.27 ([#4737](https://github.com/homarr-labs/homarr/issues/4737)) ([8891ac4](https://github.com/homarr-labs/homarr/commit/8891ac42399e47fda6efc657b8ff2fe58aebca9e))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.28 ([#4743](https://github.com/homarr-labs/homarr/issues/4743)) ([75c5453](https://github.com/homarr-labs/homarr/commit/75c545344aae39550917331458c01a894b6a942f))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.29 ([#4747](https://github.com/homarr-labs/homarr/issues/4747)) ([5dd7400](https://github.com/homarr-labs/homarr/commit/5dd74006843ae72f63e631b14f0fd50c911d610b))
|
|
||||||
* **deps:** update dependency ldapts to v8.0.30 ([#4749](https://github.com/homarr-labs/homarr/issues/4749)) ([96c2c0d](https://github.com/homarr-labs/homarr/commit/96c2c0d604737c64722fa21bf79965c806bf4da3))
|
|
||||||
* **deps:** update dependency mysql2 to v3.16.0 ([#4731](https://github.com/homarr-labs/homarr/issues/4731)) ([f34bdce](https://github.com/homarr-labs/homarr/commit/f34bdce6446d4e62b54b96f63e91cfdddd51e901))
|
|
||||||
* **deps:** update dependency next-intl to v4.6.1 ([#4730](https://github.com/homarr-labs/homarr/issues/4730)) ([f4d782a](https://github.com/homarr-labs/homarr/commit/f4d782abefb78e085ca3fc66034c0ca54fb21120))
|
|
||||||
* **deps:** update dependency sass to ^1.97.0 ([#4726](https://github.com/homarr-labs/homarr/issues/4726)) ([88baad8](https://github.com/homarr-labs/homarr/commit/88baad8143f4ca830e50644d6bd1d1980ef1da82))
|
|
||||||
* **deps:** update dependency sass to ^1.97.1 ([#4746](https://github.com/homarr-labs/homarr/issues/4746)) ([6b774f4](https://github.com/homarr-labs/homarr/commit/6b774f47d0a8bf731e7e1837a9d47e0dc53a1ff0))
|
|
||||||
* **deps:** update dependency typescript-eslint to ^8.50.0 ([#4720](https://github.com/homarr-labs/homarr/issues/4720)) ([2dc8fea](https://github.com/homarr-labs/homarr/commit/2dc8feae7ea6174a1d826d8f0151ddc2c04c6aa6))
|
|
||||||
* **deps:** update dependency zod to ^4.2.0 ([#4712](https://github.com/homarr-labs/homarr/issues/4712)) ([6e39524](https://github.com/homarr-labs/homarr/commit/6e39524f9d28d201c13738212d238726e1685832))
|
|
||||||
* **deps:** update dependency zod to ^4.2.1 ([#4722](https://github.com/homarr-labs/homarr/issues/4722)) ([68dc684](https://github.com/homarr-labs/homarr/commit/68dc6844fdee53aca55b44dd7d19bf9d3a35c057))
|
|
||||||
* **deps:** update nextjs monorepo to v16.1.0 ([#4739](https://github.com/homarr-labs/homarr/issues/4739)) ([99f2842](https://github.com/homarr-labs/homarr/commit/99f2842a5a102056c76ddc68cb5083791f324c3c))
|
|
||||||
* **deps:** update tiptap monorepo to v3.14.0 ([#4744](https://github.com/homarr-labs/homarr/issues/4744)) ([adbe707](https://github.com/homarr-labs/homarr/commit/adbe707c73f462771c3d21cbbcc21e8245eea6f3))
|
|
||||||
* **deps:** update trpc monorepo to ^11.8.1 ([#4750](https://github.com/homarr-labs/homarr/issues/4750)) ([93bf9ab](https://github.com/homarr-labs/homarr/commit/93bf9ab84b5387eefb8cc0b03057a4ccf0b13aa9))
|
|
||||||
* **deps:** update turbo monorepo to ^2.7.0 ([#4745](https://github.com/homarr-labs/homarr/issues/4745)) ([5d659a3](https://github.com/homarr-labs/homarr/commit/5d659a3ebbf79266158cb57236de90e7883e7a4e))
|
|
||||||
* **deps:** update turbo monorepo to ^2.7.1 ([#4751](https://github.com/homarr-labs/homarr/issues/4751)) ([d8db016](https://github.com/homarr-labs/homarr/commit/d8db0169a142c2ec302806fe946cb17431ccae6b))
|
|
||||||
|
|
||||||
## [1.47.0](https://github.com/homarr-labs/homarr/compare/v1.46.0...v1.47.0) (2025-12-19)
|
## [1.47.0](https://github.com/homarr-labs/homarr/compare/v1.46.0...v1.47.0) (2025-12-19)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ 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/tasks/tasks.cjs ./apps/tasks/tasks.cjs
|
||||||
COPY --from=builder /app/apps/websocket/wssServer.cjs ./apps/websocket/wssServer.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
|
COPY --from=builder /app/packages/db/migrations ./db/migrations
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,10 @@
|
|||||||
"@tanstack/react-query": "^5.90.12",
|
"@tanstack/react-query": "^5.90.12",
|
||||||
"@tanstack/react-query-devtools": "^5.91.1",
|
"@tanstack/react-query-devtools": "^5.91.1",
|
||||||
"@tanstack/react-query-next-experimental": "^5.91.0",
|
"@tanstack/react-query-next-experimental": "^5.91.0",
|
||||||
"@trpc/client": "^11.8.1",
|
"@trpc/client": "^11.8.0",
|
||||||
"@trpc/next": "^11.8.1",
|
"@trpc/next": "^11.8.0",
|
||||||
"@trpc/react-query": "^11.8.1",
|
"@trpc/react-query": "^11.8.0",
|
||||||
"@trpc/server": "^11.8.1",
|
"@trpc/server": "^11.8.0",
|
||||||
"@xterm/addon-canvas": "^0.7.0",
|
"@xterm/addon-canvas": "^0.7.0",
|
||||||
"@xterm/addon-fit": "0.10.0",
|
"@xterm/addon-fit": "0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
@@ -76,18 +76,18 @@
|
|||||||
"isomorphic-dompurify": "^2.34.0",
|
"isomorphic-dompurify": "^2.34.0",
|
||||||
"jotai": "^2.16.0",
|
"jotai": "^2.16.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"postcss-preset-mantine": "^1.18.0",
|
"postcss-preset-mantine": "^1.18.0",
|
||||||
"prismjs": "^1.30.0",
|
"prismjs": "^1.30.0",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"react-error-boundary": "^6.0.0",
|
"react-error-boundary": "^6.0.0",
|
||||||
"react-simple-code-editor": "^0.14.1",
|
"react-simple-code-editor": "^0.14.1",
|
||||||
"sass": "^1.97.1",
|
"sass": "^1.96.0",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"swagger-ui-react": "^5.31.0",
|
"swagger-ui-react": "^5.31.0",
|
||||||
"use-deep-compare-effect": "^1.8.1",
|
"use-deep-compare-effect": "^1.8.1",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^12.5.0"
|
"better-sqlite3": "^12.5.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.26.1",
|
"packageManager": "pnpm@10.25.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.12.0",
|
"node": ">=24.12.0",
|
||||||
"pnpm": ">=10.26.1"
|
"pnpm": ">=10.25.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|||||||
+11
-12
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homarr",
|
"name": "homarr",
|
||||||
"version": "1.48.0",
|
"version": "1.47.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env CI=true turbo build",
|
"build": "cross-env CI=true turbo build",
|
||||||
@@ -43,10 +43,10 @@
|
|||||||
"@semantic-release/npm": "^13.1.3",
|
"@semantic-release/npm": "^13.1.3",
|
||||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||||
"@testcontainers/redis": "^11.10.0",
|
"@testcontainers/redis": "^11.10.0",
|
||||||
"@turbo/gen": "^2.7.1",
|
"@turbo/gen": "^2.6.3",
|
||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"@vitest/coverage-v8": "^4.0.16",
|
"@vitest/coverage-v8": "^4.0.15",
|
||||||
"@vitest/ui": "^4.0.16",
|
"@vitest/ui": "^4.0.15",
|
||||||
"conventional-changelog-conventionalcommits": "^9.1.0",
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"jsdom": "^27.3.0",
|
"jsdom": "^27.3.0",
|
||||||
@@ -54,21 +54,20 @@
|
|||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"semantic-release": "^25.0.2",
|
"semantic-release": "^25.0.2",
|
||||||
"testcontainers": "^11.10.0",
|
"testcontainers": "^11.10.0",
|
||||||
"turbo": "^2.7.1",
|
"turbo": "^2.6.3",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.15"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.26.1",
|
"packageManager": "pnpm@10.25.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.12.0",
|
"node": ">=24.12.0",
|
||||||
"pnpm": ">=10.26.1"
|
"pnpm": ">=10.25.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
"@tree-sitter-grammars/tree-sitter-yaml",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"better-sqlite3",
|
|
||||||
"cpu-features",
|
"cpu-features",
|
||||||
"esbuild",
|
"esbuild",
|
||||||
"sharp",
|
"sharp",
|
||||||
@@ -84,18 +83,18 @@
|
|||||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
||||||
"esbuild@<=0.24.2": ">=0.27.1",
|
"esbuild@<=0.24.2": ">=0.27.1",
|
||||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
||||||
"hono@<4.6.5": ">=4.11.1",
|
"hono@<4.6.5": ">=4.11.0",
|
||||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
||||||
"prismjs@<1.30.0": ">=1.30.0",
|
"prismjs@<1.30.0": ">=1.30.0",
|
||||||
"proxmox-api>undici": "7.16.0",
|
"proxmox-api>undici": "7.16.0",
|
||||||
"react-is": "^19.2.3",
|
"react-is": "^19.2.3",
|
||||||
"rollup@>=4.0.0 <4.22.4": ">=4.54.0",
|
"rollup@>=4.0.0 <4.22.4": ">=4.53.3",
|
||||||
"sha.js@<=2.4.11": ">=2.4.12",
|
"sha.js@<=2.4.11": ">=2.4.12",
|
||||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
|
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
|
||||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
|
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
|
||||||
"tmp@<=0.2.3": ">=0.2.5",
|
"tmp@<=0.2.3": ">=0.2.5",
|
||||||
"vite@>=5.0.0 <=5.4.18": ">=7.3.0"
|
"vite@>=5.0.0 <=5.4.18": ">=7.2.7"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
||||||
|
|||||||
@@ -42,17 +42,17 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@kubernetes/client-node": "^1.4.0",
|
"@kubernetes/client-node": "^1.4.0",
|
||||||
"@tanstack/react-query": "^5.90.12",
|
"@tanstack/react-query": "^5.90.12",
|
||||||
"@trpc/client": "^11.8.1",
|
"@trpc/client": "^11.8.0",
|
||||||
"@trpc/react-query": "^11.8.1",
|
"@trpc/react-query": "^11.8.0",
|
||||||
"@trpc/server": "^11.8.1",
|
"@trpc/server": "^11.8.0",
|
||||||
"@trpc/tanstack-react-query": "^11.8.1",
|
"@trpc/tanstack-react-query": "^11.8.0",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"trpc-to-openapi": "^3.1.0",
|
"trpc-to-openapi": "^3.1.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { createId } from "@homarr/common";
|
|||||||
import { asc, eq, inArray, like } from "@homarr/db";
|
import { asc, eq, inArray, like } from "@homarr/db";
|
||||||
import { apps } from "@homarr/db/schema";
|
import { apps } from "@homarr/db/schema";
|
||||||
import { selectAppSchema } from "@homarr/db/validationSchemas";
|
import { selectAppSchema } from "@homarr/db/validationSchemas";
|
||||||
import { getIconForName } from "@homarr/icons";
|
import { getIconForNameAsync } from "@homarr/icons";
|
||||||
import { appCreateManySchema, appEditSchema, appManageSchema } from "@homarr/validation/app";
|
import { appCreateManySchema, appEditSchema, appManageSchema } from "@homarr/validation/app";
|
||||||
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
|
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
|
||||||
|
|
||||||
@@ -141,13 +141,15 @@ export const appRouter = createTRPCRouter({
|
|||||||
.output(z.void())
|
.output(z.void())
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
await ctx.db.insert(apps).values(
|
await ctx.db.insert(apps).values(
|
||||||
input.map((app) => ({
|
await Promise.all(
|
||||||
id: createId(),
|
input.map(async (app) => ({
|
||||||
name: app.name,
|
id: createId(),
|
||||||
description: app.description,
|
name: app.name,
|
||||||
iconUrl: app.iconUrl ?? getIconForName(ctx.db, app.name).sync()?.url ?? defaultIcon,
|
description: app.description,
|
||||||
href: app.href,
|
iconUrl: app.iconUrl ?? (await getIconForNameAsync(ctx.db, app.name))?.url ?? defaultIcon,
|
||||||
})),
|
href: app.href,
|
||||||
|
})),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
update: permissionRequiredProcedure
|
update: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -477,47 +477,6 @@ export const boardRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
transaction
|
|
||||||
.insert(boards)
|
|
||||||
.values({
|
|
||||||
...boardProps,
|
|
||||||
id: newBoardId,
|
|
||||||
name: input.name,
|
|
||||||
creatorId: ctx.session.user.id,
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
|
|
||||||
if (layoutsToInsert.length > 0) {
|
|
||||||
transaction.insert(layouts).values(layoutsToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectionsToInsert.length > 0) {
|
|
||||||
transaction.insert(sections).values(sectionsToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectionLayoutsToInsert.length > 0) {
|
|
||||||
transaction.insert(sectionLayouts).values(sectionLayoutsToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectionCollapseStatesToInsert.length > 0) {
|
|
||||||
transaction.insert(sectionCollapseStates).values(sectionCollapseStatesToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemsToInsert.length > 0) {
|
|
||||||
transaction.insert(items).values(itemsToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemLayoutsToInsert.length > 0) {
|
|
||||||
transaction.insert(itemLayouts).values(itemLayoutsToInsert).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemIntegrationsToInsert.length > 0) {
|
|
||||||
transaction.insert(integrationItems).values(itemIntegrationsToInsert).run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
renameBoard: protectedProcedure.input(boardRenameSchema).mutation(async ({ ctx, input }) => {
|
renameBoard: protectedProcedure.input(boardRenameSchema).mutation(async ({ ctx, input }) => {
|
||||||
@@ -954,216 +913,6 @@ export const boardRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
const addedSections = filterAddedItems(input.sections, dbBoard.sections);
|
|
||||||
|
|
||||||
if (addedSections.length > 0) {
|
|
||||||
transaction
|
|
||||||
.insert(sections)
|
|
||||||
.values(
|
|
||||||
addedSections.map((section) => ({
|
|
||||||
id: section.id,
|
|
||||||
kind: section.kind,
|
|
||||||
yOffset: section.kind !== "dynamic" ? section.yOffset : null,
|
|
||||||
xOffset: section.kind === "dynamic" ? null : 0,
|
|
||||||
options: section.kind === "dynamic" ? superjson.stringify(section.options) : emptySuperJSON,
|
|
||||||
name: "name" in section ? section.name : null,
|
|
||||||
boardId: dbBoard.id,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
|
|
||||||
if (addedSections.some((section) => section.kind === "dynamic")) {
|
|
||||||
transaction
|
|
||||||
.insert(sectionLayouts)
|
|
||||||
.values(
|
|
||||||
addedSections
|
|
||||||
.filter((section) => section.kind === "dynamic")
|
|
||||||
.flatMap((section) =>
|
|
||||||
section.layouts.map(
|
|
||||||
(sectionLayout): InferInsertModel<typeof sectionLayouts> => ({
|
|
||||||
layoutId: sectionLayout.layoutId,
|
|
||||||
sectionId: section.id,
|
|
||||||
parentSectionId: sectionLayout.parentSectionId,
|
|
||||||
height: sectionLayout.height,
|
|
||||||
width: sectionLayout.width,
|
|
||||||
xOffset: sectionLayout.xOffset,
|
|
||||||
yOffset: sectionLayout.yOffset,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addedItems = filterAddedItems(input.items, dbBoard.items);
|
|
||||||
|
|
||||||
if (addedItems.length > 0) {
|
|
||||||
transaction
|
|
||||||
.insert(items)
|
|
||||||
.values(
|
|
||||||
addedItems.map((item) => ({
|
|
||||||
id: item.id,
|
|
||||||
kind: item.kind,
|
|
||||||
options: superjson.stringify(item.options),
|
|
||||||
advancedOptions: superjson.stringify(item.advancedOptions),
|
|
||||||
boardId: dbBoard.id,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
transaction
|
|
||||||
.insert(itemLayouts)
|
|
||||||
.values(
|
|
||||||
addedItems.flatMap((item) =>
|
|
||||||
item.layouts.map(
|
|
||||||
(layoutSection): InferInsertModel<typeof itemLayouts> => ({
|
|
||||||
layoutId: layoutSection.layoutId,
|
|
||||||
sectionId: layoutSection.sectionId,
|
|
||||||
itemId: item.id,
|
|
||||||
height: layoutSection.height,
|
|
||||||
width: layoutSection.width,
|
|
||||||
xOffset: layoutSection.xOffset,
|
|
||||||
yOffset: layoutSection.yOffset,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputIntegrationRelations = input.items.flatMap(({ integrationIds, id: itemId }) =>
|
|
||||||
integrationIds.map((integrationId) => ({
|
|
||||||
integrationId,
|
|
||||||
itemId,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
const dbIntegrationRelations = dbBoard.items.flatMap(({ integrationIds, id: itemId }) =>
|
|
||||||
integrationIds.map((integrationId) => ({
|
|
||||||
integrationId,
|
|
||||||
itemId,
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
const addedIntegrationRelations = inputIntegrationRelations.filter(
|
|
||||||
(inputRelation) =>
|
|
||||||
!dbIntegrationRelations.some(
|
|
||||||
(dbRelation) =>
|
|
||||||
dbRelation.itemId === inputRelation.itemId &&
|
|
||||||
dbRelation.integrationId === inputRelation.integrationId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (addedIntegrationRelations.length > 0) {
|
|
||||||
transaction
|
|
||||||
.insert(integrationItems)
|
|
||||||
.values(
|
|
||||||
addedIntegrationRelations.map((relation) => ({
|
|
||||||
itemId: relation.itemId,
|
|
||||||
integrationId: relation.integrationId,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedItems = filterUpdatedItems(input.items, dbBoard.items);
|
|
||||||
|
|
||||||
for (const item of updatedItems) {
|
|
||||||
transaction
|
|
||||||
.update(items)
|
|
||||||
.set({
|
|
||||||
kind: item.kind,
|
|
||||||
options: superjson.stringify(item.options),
|
|
||||||
advancedOptions: superjson.stringify(item.advancedOptions),
|
|
||||||
})
|
|
||||||
.where(eq(items.id, item.id))
|
|
||||||
.run();
|
|
||||||
|
|
||||||
for (const itemSectionLayout of item.layouts) {
|
|
||||||
transaction
|
|
||||||
.update(itemLayouts)
|
|
||||||
.set({
|
|
||||||
height: itemSectionLayout.height,
|
|
||||||
width: itemSectionLayout.width,
|
|
||||||
xOffset: itemSectionLayout.xOffset,
|
|
||||||
yOffset: itemSectionLayout.yOffset,
|
|
||||||
sectionId: itemSectionLayout.sectionId,
|
|
||||||
})
|
|
||||||
.where(and(eq(itemLayouts.itemId, item.id), eq(itemLayouts.layoutId, itemSectionLayout.layoutId)))
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedSections = filterUpdatedItems(input.sections, dbBoard.sections);
|
|
||||||
|
|
||||||
for (const section of updatedSections) {
|
|
||||||
const prev = dbBoard.sections.find((dbSection) => dbSection.id === section.id);
|
|
||||||
transaction
|
|
||||||
.update(sections)
|
|
||||||
.set({
|
|
||||||
yOffset: prev?.kind !== "dynamic" && "yOffset" in section ? section.yOffset : null,
|
|
||||||
xOffset: prev?.kind !== "dynamic" && "yOffset" in section ? 0 : null,
|
|
||||||
options: section.kind === "dynamic" ? superjson.stringify(section.options) : emptySuperJSON,
|
|
||||||
name: prev?.kind === "category" && "name" in section ? section.name : null,
|
|
||||||
})
|
|
||||||
.where(eq(sections.id, section.id))
|
|
||||||
.run();
|
|
||||||
|
|
||||||
if (section.kind !== "dynamic") continue;
|
|
||||||
|
|
||||||
for (const sectionLayout of section.layouts) {
|
|
||||||
transaction
|
|
||||||
.update(sectionLayouts)
|
|
||||||
.set({
|
|
||||||
height: sectionLayout.height,
|
|
||||||
width: sectionLayout.width,
|
|
||||||
xOffset: sectionLayout.xOffset,
|
|
||||||
yOffset: sectionLayout.yOffset,
|
|
||||||
parentSectionId: sectionLayout.parentSectionId,
|
|
||||||
})
|
|
||||||
.where(
|
|
||||||
and(eq(sectionLayouts.sectionId, section.id), eq(sectionLayouts.layoutId, sectionLayout.layoutId)),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const removedIntegrationRelations = dbIntegrationRelations.filter(
|
|
||||||
(dbRelation) =>
|
|
||||||
!inputIntegrationRelations.some(
|
|
||||||
(inputRelation) =>
|
|
||||||
dbRelation.itemId === inputRelation.itemId &&
|
|
||||||
dbRelation.integrationId === inputRelation.integrationId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const relation of removedIntegrationRelations) {
|
|
||||||
transaction
|
|
||||||
.delete(integrationItems)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(integrationItems.itemId, relation.itemId),
|
|
||||||
eq(integrationItems.integrationId, relation.integrationId),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
const removedItems = filterRemovedItems(input.items, dbBoard.items);
|
|
||||||
|
|
||||||
const itemIds = removedItems.map((item) => item.id);
|
|
||||||
if (itemIds.length > 0) {
|
|
||||||
transaction.delete(items).where(inArray(items.id, itemIds)).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
const removedSections = filterRemovedItems(input.sections, dbBoard.sections);
|
|
||||||
const sectionIds = removedSections.map((section) => section.id);
|
|
||||||
|
|
||||||
if (sectionIds.length > 0) {
|
|
||||||
transaction.delete(sections).where(inArray(sections.id, sectionIds)).run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
getBoardPermissions: protectedProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
|
getBoardPermissions: protectedProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
|
||||||
@@ -1256,24 +1005,6 @@ export const boardRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
transaction.delete(boardUserPermissions).where(eq(boardUserPermissions.boardId, input.entityId)).run();
|
|
||||||
if (input.permissions.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
transaction
|
|
||||||
.insert(boardUserPermissions)
|
|
||||||
.values(
|
|
||||||
input.permissions.map((permission) => ({
|
|
||||||
userId: permission.principalId,
|
|
||||||
permission: permission.permission,
|
|
||||||
boardId: input.entityId,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
saveGroupBoardPermissions: protectedProcedure.input(boardSavePermissionsSchema).mutation(async ({ input, ctx }) => {
|
saveGroupBoardPermissions: protectedProcedure.input(boardSavePermissionsSchema).mutation(async ({ input, ctx }) => {
|
||||||
@@ -1297,24 +1028,6 @@ export const boardRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
transaction.delete(boardGroupPermissions).where(eq(boardGroupPermissions.boardId, input.entityId)).run();
|
|
||||||
if (input.permissions.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
transaction
|
|
||||||
.insert(boardGroupPermissions)
|
|
||||||
.values(
|
|
||||||
input.permissions.map((permission) => ({
|
|
||||||
groupId: permission.principalId,
|
|
||||||
permission: permission.permission,
|
|
||||||
boardId: input.entityId,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
importOldmarrConfig: permissionRequiredProcedure
|
importOldmarrConfig: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
hostname: input.hostname,
|
hostname: input.hostname,
|
||||||
thumbprint: input.thumbprint,
|
thumbprint: input.thumbprint,
|
||||||
});
|
});
|
||||||
const dbResult = await ctx.db
|
await ctx.db
|
||||||
.delete(trustedCertificateHostnames)
|
.delete(trustedCertificateHostnames)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
@@ -103,7 +103,6 @@ export const certificateRouter = createTRPCRouter({
|
|||||||
logger.info("Removed trusted hostname", {
|
logger.info("Removed trusted hostname", {
|
||||||
hostname: input.hostname,
|
hostname: input.hostname,
|
||||||
thumbprint: input.thumbprint,
|
thumbprint: input.thumbprint,
|
||||||
count: dbResult.changes,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
removeCertificate: permissionRequiredProcedure
|
removeCertificate: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -260,13 +260,6 @@ export const groupRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync: (db) => {
|
|
||||||
db.transaction((trx) => {
|
|
||||||
for (const { id, position } of positions) {
|
|
||||||
trx.update(groups).set({ position }).where(eq(groups.id, id)).run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
savePermissions: permissionRequiredProcedure
|
savePermissions: permissionRequiredProcedure
|
||||||
|
|||||||
@@ -541,27 +541,6 @@ export const integrationRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
transaction
|
|
||||||
.delete(integrationUserPermissions)
|
|
||||||
.where(eq(integrationUserPermissions.integrationId, input.entityId))
|
|
||||||
.run();
|
|
||||||
if (input.permissions.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
transaction
|
|
||||||
.insert(integrationUserPermissions)
|
|
||||||
.values(
|
|
||||||
input.permissions.map((permission) => ({
|
|
||||||
userId: permission.principalId,
|
|
||||||
permission: permission.permission,
|
|
||||||
integrationId: input.entityId,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
saveGroupIntegrationPermissions: protectedProcedure
|
saveGroupIntegrationPermissions: protectedProcedure
|
||||||
@@ -587,27 +566,6 @@ export const integrationRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
transaction
|
|
||||||
.delete(integrationGroupPermissions)
|
|
||||||
.where(eq(integrationGroupPermissions.integrationId, input.entityId))
|
|
||||||
.run();
|
|
||||||
if (input.permissions.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
transaction
|
|
||||||
.insert(integrationGroupPermissions)
|
|
||||||
.values(
|
|
||||||
input.permissions.map((permission) => ({
|
|
||||||
groupId: permission.principalId,
|
|
||||||
permission: permission.permission,
|
|
||||||
integrationId: input.entityId,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
searchInIntegration: protectedProcedure
|
searchInIntegration: protectedProcedure
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||||
|
|
||||||
@@ -37,12 +36,7 @@ export const locationRouter = createTRPCRouter({
|
|||||||
.input(locationSearchCityInput)
|
.input(locationSearchCityInput)
|
||||||
.output(locationSearchCityOutput)
|
.output(locationSearchCityOutput)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const res = await withTimeoutAsync(async (signal) => {
|
const res = await fetchWithTimeoutAsync(`https://geocoding-api.open-meteo.com/v1/search?name=${input.query}`);
|
||||||
return await fetchWithTrustedCertificatesAsync(
|
|
||||||
`https://geocoding-api.open-meteo.com/v1/search?name=${input.query}`,
|
|
||||||
{ signal },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (await res.json()) as z.infer<typeof locationSearchCityOutput>;
|
return (await res.json()) as z.infer<typeof locationSearchCityOutput>;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -32,12 +32,12 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"cookies": "^0.9.1",
|
"cookies": "^0.9.1",
|
||||||
"ldapts": "8.0.30",
|
"ldapts": "8.0.23",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"next-auth": "5.0.0-beta.30",
|
"next-auth": "5.0.0-beta.30",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -29,12 +29,12 @@
|
|||||||
"@homarr/core": "workspace:^0.1.0",
|
"@homarr/core": "workspace:^0.1.0",
|
||||||
"@paralleldrive/cuid2": "^3.1.0",
|
"@paralleldrive/cuid2": "^3.1.0",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.5",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"undici": "7.16.0",
|
"undici": "7.16.0",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.1.13",
|
||||||
"zod-validation-error": "^5.0.0"
|
"zod-validation-error": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -37,22 +37,21 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@t3-oss/env-nextjs": "^0.13.10",
|
"@libsql/client": "^0.15.15",
|
||||||
"better-sqlite3": "^12.5.0",
|
"@t3-oss/env-nextjs": "^0.13.8",
|
||||||
"dns-caching": "^0.2.9",
|
"dns-caching": "^0.2.9",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"ioredis": "5.8.2",
|
"ioredis": "5.8.2",
|
||||||
"mysql2": "3.16.0",
|
"mysql2": "3.15.3",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"winston": "3.19.0",
|
"winston": "3.19.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/better-sqlite3": "7.6.13",
|
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import Database from "better-sqlite3";
|
import { createClient } from "@libsql/client";
|
||||||
import { drizzle as drizzleSqlite } from "drizzle-orm/better-sqlite3";
|
import { drizzle as drizzleSqlite } from "drizzle-orm/libsql";
|
||||||
|
|
||||||
import { dbEnv } from "../env";
|
import { dbEnv } from "../env";
|
||||||
import type { SharedDrizzleConfig } from "./shared";
|
import type { SharedDrizzleConfig } from "./shared";
|
||||||
|
|
||||||
export const createSqliteDb = <TSchema extends Record<string, unknown>>(config: SharedDrizzleConfig<TSchema>) => {
|
export const createSqliteDb = <TSchema extends Record<string, unknown>>(config: SharedDrizzleConfig<TSchema>) => {
|
||||||
const connection = new Database(dbEnv.URL);
|
const client = createClient({ url: `file:${dbEnv.URL}` });
|
||||||
return drizzleSqlite<TSchema>(connection, config);
|
return drizzleSqlite<TSchema>(client, config);
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,7 @@ import { createBooleanSchema, createEnv } from "../env";
|
|||||||
|
|
||||||
export const dnsEnv = createEnv({
|
export const dnsEnv = createEnv({
|
||||||
server: {
|
server: {
|
||||||
ENABLE_DNS_CACHING: createBooleanSchema(true),
|
ENABLE_DNS_CACHING: createBooleanSchema(false),
|
||||||
},
|
},
|
||||||
experimental__runtimeEnv: process.env,
|
experimental__runtimeEnv: process.env,
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Dispatcher } from "undici";
|
import type { Dispatcher } from "undici";
|
||||||
import { EnvHttpProxyAgent } from "undici";
|
import { Agent } from "undici";
|
||||||
|
|
||||||
import type { ILogger } from "@homarr/core/infrastructure/logs";
|
import type { ILogger } from "@homarr/core/infrastructure/logs";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||||
@@ -7,15 +7,16 @@ import { createLogger } from "@homarr/core/infrastructure/logs";
|
|||||||
// The below import statement initializes dns-caching
|
// The below import statement initializes dns-caching
|
||||||
import "@homarr/core/infrastructure/dns/init";
|
import "@homarr/core/infrastructure/dns/init";
|
||||||
|
|
||||||
interface HttpAgentOptions extends EnvHttpProxyAgent.Options {
|
interface HttpAgentOptions extends Agent.Options {
|
||||||
logger?: ILogger;
|
logger?: ILogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UndiciHttpAgent extends EnvHttpProxyAgent {
|
export class UndiciHttpAgent extends Agent {
|
||||||
private logger: ILogger;
|
private logger: ILogger;
|
||||||
|
|
||||||
constructor(props?: HttpAgentOptions) {
|
constructor(props?: HttpAgentOptions) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.logger = props?.logger ?? createLogger({ module: "httpAgent" });
|
this.logger = props?.logger ?? createLogger({ module: "httpAgent" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,13 +42,12 @@ export const createCertificateAgentAsync = async (override?: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createHttpsAgentAsync = async (override?: Pick<AgentOptions, "ca" | "checkServerIdentity">) => {
|
export const createHttpsAgentAsync = async (override?: Pick<AgentOptions, "ca" | "checkServerIdentity">) => {
|
||||||
return new HttpsAgent({
|
return new HttpsAgent(
|
||||||
ca: await getAllTrustedCertificatesAsync(),
|
override ?? {
|
||||||
checkServerIdentity: createCustomCheckServerIdentity(await getTrustedCertificateHostnamesAsync()),
|
ca: await getAllTrustedCertificatesAsync(),
|
||||||
// Override the ca and checkServerIdentity if provided
|
checkServerIdentity: createCustomCheckServerIdentity(await getTrustedCertificateHostnamesAsync()),
|
||||||
...override,
|
},
|
||||||
proxyEnv: process.env,
|
);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createAxiosCertificateInstanceAsync = async (
|
export const createAxiosCertificateInstanceAsync = async (
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { TestLogger } from "../logs";
|
|||||||
|
|
||||||
vi.mock("undici", () => {
|
vi.mock("undici", () => {
|
||||||
return {
|
return {
|
||||||
EnvHttpProxyAgent: class EnvHttpProxyAgent {
|
Agent: class Agent {
|
||||||
dispatch(_options: Dispatcher.DispatchOptions, _handler: Dispatcher.DispatchHandler): boolean {
|
dispatch(_options: Dispatcher.DispatchOptions, _handler: Dispatcher.DispatchHandler): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,12 @@
|
|||||||
"@homarr/core": "workspace:^0.1.0",
|
"@homarr/core": "workspace:^0.1.0",
|
||||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||||
"@tanstack/react-query": "^5.90.12",
|
"@tanstack/react-query": "^5.90.12",
|
||||||
"@trpc/client": "^11.8.1",
|
"@trpc/client": "^11.8.0",
|
||||||
"@trpc/server": "^11.8.1",
|
"@trpc/server": "^11.8.0",
|
||||||
"@trpc/tanstack-react-query": "^11.8.1",
|
"@trpc/tanstack-react-query": "^11.8.0",
|
||||||
"node-cron": "^4.2.1",
|
"node-cron": "^4.2.1",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -121,46 +121,6 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
countDeleted += deadIcons.length;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSync() {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
if (newIconRepositories.length >= 1) {
|
|
||||||
transaction.insert(iconRepositories).values(newIconRepositories).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newIcons.length >= 1) {
|
|
||||||
// We only insert 5000 icons at a time to avoid SQLite limitations
|
|
||||||
for (const chunck of splitToNChunks(newIcons, Math.ceil(newIcons.length / 5000))) {
|
|
||||||
transaction.insert(icons).values(chunck).run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deadIcons.length >= 1) {
|
|
||||||
transaction
|
|
||||||
.delete(icons)
|
|
||||||
.where(
|
|
||||||
inArray(
|
|
||||||
// Combine iconRepositoryId and checksum to allow same icons on different repositories
|
|
||||||
sql`concat(${icons.iconRepositoryId}, '.', ${icons.checksum})`,
|
|
||||||
deadIcons.map((icon) => `${icon.iconRepositoryId}.${icon.checksum}`),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deadIconRepositories.length >= 1) {
|
|
||||||
transaction
|
|
||||||
.delete(iconRepositories)
|
|
||||||
.where(
|
|
||||||
inArray(
|
|
||||||
iconRepositories.id,
|
|
||||||
deadIconRepositories.map((iconRepository) => iconRepository.id),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
countDeleted += deadIcons.length;
|
countDeleted += deadIcons.length;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,20 +31,8 @@ export const createDbInsertCollectionForTransaction = <TTableKey extends TableKe
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...context,
|
...context,
|
||||||
insertAll: (db: HomarrDatabase) => {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
for (const [key, values] of objectEntries(context)) {
|
|
||||||
if (values.length >= 1) {
|
|
||||||
transaction
|
|
||||||
.insert(schema[key])
|
|
||||||
.values(values as never)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// We allow any database that supports async passed here but then fallback to mysql to prevent typescript errors
|
// We allow any database that supports async passed here but then fallback to mysql to prevent typescript errors
|
||||||
insertAllAsync: async (db: HomarrDatabaseMysql | HomarrDatabasePostgresql) => {
|
insertAllAsync: async (db: HomarrDatabaseMysql | HomarrDatabasePostgresql | HomarrDatabase) => {
|
||||||
const innerDb = db as HomarrDatabaseMysql;
|
const innerDb = db as HomarrDatabaseMysql;
|
||||||
await innerDb.transaction(async (transaction) => {
|
await innerDb.transaction(async (transaction) => {
|
||||||
for (const [key, values] of objectEntries(context)) {
|
for (const [key, values] of objectEntries(context)) {
|
||||||
@@ -61,7 +49,7 @@ export const createDbInsertCollectionForTransaction = <TTableKey extends TableKe
|
|||||||
export const createDbInsertCollectionWithoutTransaction = <TTableKey extends TableKey>(
|
export const createDbInsertCollectionWithoutTransaction = <TTableKey extends TableKey>(
|
||||||
tablesInInsertOrder: TTableKey[],
|
tablesInInsertOrder: TTableKey[],
|
||||||
) => {
|
) => {
|
||||||
const { insertAll, insertAllAsync, ...collection } = createDbInsertCollectionForTransaction(tablesInInsertOrder);
|
const { insertAllAsync, ...collection } = createDbInsertCollectionForTransaction(tablesInInsertOrder);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...collection,
|
...collection,
|
||||||
@@ -75,7 +63,7 @@ export const createDbInsertCollectionWithoutTransaction = <TTableKey extends Tab
|
|||||||
default:
|
default:
|
||||||
// For better-sqlite3, we need to use the synchronous insertAll method
|
// For better-sqlite3, we need to use the synchronous insertAll method
|
||||||
// default assumes better-sqlite3. It's original implementation.
|
// default assumes better-sqlite3. It's original implementation.
|
||||||
insertAll(db);
|
await insertAllAsync(db);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
import type { LibSQLDatabase } from "drizzle-orm/libsql";
|
||||||
import type { MySql2Database } from "drizzle-orm/mysql2";
|
import type { MySql2Database } from "drizzle-orm/mysql2";
|
||||||
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||||
|
|
||||||
@@ -6,6 +6,6 @@ import type * as mysqlSchema from "./schema/mysql";
|
|||||||
import type * as pgSchema from "./schema/postgresql";
|
import type * as pgSchema from "./schema/postgresql";
|
||||||
import type * as sqliteSchema from "./schema/sqlite";
|
import type * as sqliteSchema from "./schema/sqlite";
|
||||||
|
|
||||||
export type HomarrDatabase = BetterSQLite3Database<typeof sqliteSchema>;
|
export type HomarrDatabase = LibSQLDatabase<typeof sqliteSchema>;
|
||||||
export type HomarrDatabaseMysql = MySql2Database<typeof mysqlSchema>;
|
export type HomarrDatabaseMysql = MySql2Database<typeof mysqlSchema>;
|
||||||
export type HomarrDatabasePostgresql = NodePgDatabase<typeof pgSchema>;
|
export type HomarrDatabasePostgresql = NodePgDatabase<typeof pgSchema>;
|
||||||
@@ -51,12 +51,11 @@
|
|||||||
"@paralleldrive/cuid2": "^3.1.0",
|
"@paralleldrive/cuid2": "^3.1.0",
|
||||||
"@testcontainers/mysql": "^11.10.0",
|
"@testcontainers/mysql": "^11.10.0",
|
||||||
"@testcontainers/postgresql": "^11.10.0",
|
"@testcontainers/postgresql": "^11.10.0",
|
||||||
"better-sqlite3": "^12.5.0",
|
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
"mysql2": "3.16.0",
|
"mysql2": "3.15.3",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"superjson": "2.2.6"
|
"superjson": "2.2.6"
|
||||||
},
|
},
|
||||||
@@ -64,7 +63,6 @@
|
|||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||||
"@types/better-sqlite3": "7.6.13",
|
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
"dotenv-cli": "^11.0.0",
|
"dotenv-cli": "^11.0.0",
|
||||||
"esbuild": "^0.27.1",
|
"esbuild": "^0.27.1",
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import Database from "better-sqlite3";
|
import { createClient } from "@libsql/client";
|
||||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
import { drizzle } from "drizzle-orm/libsql";
|
||||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
import { migrate } from "drizzle-orm/libsql/migrator";
|
||||||
|
|
||||||
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
||||||
|
|
||||||
import * as sqliteSchema from "../schema/sqlite";
|
import * as sqliteSchema from "../schema/sqlite";
|
||||||
|
|
||||||
export const createDb = (debug?: boolean) => {
|
export const createDb = (debug?: boolean) => {
|
||||||
const sqlite = new Database(":memory:");
|
const sqlite = createClient({ url: ":memory:" });
|
||||||
const db = drizzle(sqlite, { schema: sqliteSchema, logger: debug, casing: DB_CASING });
|
const db = drizzle(sqlite, { schema: sqliteSchema, logger: debug, casing: DB_CASING });
|
||||||
migrate(db, {
|
// TODO: Make async and awaitable
|
||||||
|
void migrate(db, {
|
||||||
migrationsFolder: "./packages/db/migrations/sqlite",
|
migrationsFolder: "./packages/db/migrations/sqlite",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { isMysql, isPostgresql } from "./collection";
|
|
||||||
import type { HomarrDatabase, HomarrDatabaseMysql } from "./driver";
|
import type { HomarrDatabase, HomarrDatabaseMysql } from "./driver";
|
||||||
import type { MySqlSchema } from "./schema";
|
import type { MySqlSchema } from "./schema";
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
|
|
||||||
interface HandleTransactionInput {
|
interface HandleTransactionInput {
|
||||||
handleAsync: (db: HomarrDatabaseMysql, schema: MySqlSchema) => Promise<void>;
|
handleAsync: (db: HomarrDatabaseMysql, schema: MySqlSchema) => Promise<void>;
|
||||||
handleSync: (db: HomarrDatabase) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,10 +12,5 @@ interface HandleTransactionInput {
|
|||||||
* But it can also generally be used when dealing with different database drivers.
|
* But it can also generally be used when dealing with different database drivers.
|
||||||
*/
|
*/
|
||||||
export const handleDiffrentDbDriverOperationsAsync = async (db: HomarrDatabase, input: HandleTransactionInput) => {
|
export const handleDiffrentDbDriverOperationsAsync = async (db: HomarrDatabase, input: HandleTransactionInput) => {
|
||||||
if (isMysql() || isPostgresql()) {
|
await input.handleAsync(db as unknown as HomarrDatabaseMysql, schema as unknown as MySqlSchema);
|
||||||
// Schema type is always the correct one based on env variables
|
|
||||||
await input.handleAsync(db as unknown as HomarrDatabaseMysql, schema as unknown as MySqlSchema);
|
|
||||||
} else {
|
|
||||||
input.handleSync(db);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"fast-xml-parser": "^5.3.3",
|
"fast-xml-parser": "^5.3.3",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/form": "^8.3.10",
|
"@mantine/form": "^8.3.10",
|
||||||
"mantine-form-zod-resolver": "^1.3.0",
|
"mantine-form-zod-resolver": "^1.3.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.3.10",
|
"@mantine/core": "^8.3.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import type { Database } from "@homarr/db";
|
|||||||
import { like } from "@homarr/db";
|
import { like } from "@homarr/db";
|
||||||
import { icons } from "@homarr/db/schema";
|
import { icons } from "@homarr/db/schema";
|
||||||
|
|
||||||
export const getIconForName = (db: Database, name: string) => {
|
export const getIconForNameAsync = async (db: Database, name: string) => {
|
||||||
return db.query.icons.findFirst({
|
return await db.query.icons.findFirst({
|
||||||
where: like(icons.name, `%${name}%`),
|
where: like(icons.name, `%${name}%`),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { parse } from "path";
|
import { parse } from "path";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
||||||
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
||||||
@@ -20,12 +19,11 @@ export class GitHubIconRepository extends IconRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
||||||
const url = this.repositoryIndexingUrl;
|
if (!this.repositoryIndexingUrl || !this.repositoryBlobUrlTemplate) {
|
||||||
if (!url || !this.repositoryBlobUrlTemplate) {
|
|
||||||
throw new Error("Repository URLs are required for this repository");
|
throw new Error("Repository URLs are required for this repository");
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await withTimeoutAsync(async (signal) => fetchWithTrustedCertificatesAsync(url, { signal }));
|
const response = await fetchWithTimeoutAsync(this.repositoryIndexingUrl);
|
||||||
const listOfFiles = (await response.json()) as GitHubApiResponse;
|
const listOfFiles = (await response.json()) as GitHubApiResponse;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { parse } from "path";
|
import { parse } from "path";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
||||||
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
||||||
@@ -20,9 +19,7 @@ export class JsdelivrIconRepository extends IconRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
||||||
const response = await withTimeoutAsync(async (signal) =>
|
const response = await fetchWithTimeoutAsync(this.repositoryIndexingUrl);
|
||||||
fetchWithTrustedCertificatesAsync(this.repositoryIndexingUrl, { signal }),
|
|
||||||
);
|
|
||||||
const listOfFiles = (await response.json()) as JsdelivrApiResponse;
|
const listOfFiles = (await response.json()) as JsdelivrApiResponse;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"tsdav": "^2.1.6",
|
"tsdav": "^2.1.6",
|
||||||
"undici": "7.16.0",
|
"undici": "7.16.0",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
"@mantine/core": "^8.3.10",
|
"@mantine/core": "^8.3.10",
|
||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -40,11 +40,11 @@
|
|||||||
"@mantine/core": "^8.3.10",
|
"@mantine/core": "^8.3.10",
|
||||||
"@mantine/hooks": "^8.3.10",
|
"@mantine/hooks": "^8.3.10",
|
||||||
"adm-zip": "0.5.16",
|
"adm-zip": "0.5.16",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.1.13",
|
||||||
"zod-form-data": "^3.0.1"
|
"zod-form-data": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -45,13 +45,6 @@ export const importInitialOldmarrAsync = async (
|
|||||||
await integrationInsertCollection.insertAllAsync(transaction);
|
await integrationInsertCollection.insertAllAsync(transaction);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
db.transaction((transaction) => {
|
|
||||||
boardInsertCollection.insertAll(transaction);
|
|
||||||
userInsertCollection.insertAll(transaction);
|
|
||||||
integrationInsertCollection.insertAll(transaction);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info("Import successful", { duration: stopwatch.getElapsedInHumanWords() });
|
logger.info("Import successful", { duration: stopwatch.getElapsedInHumanWords() });
|
||||||
|
|||||||
@@ -35,8 +35,5 @@ export const importSingleOldmarrConfigAsync = async (
|
|||||||
async handleAsync(db) {
|
async handleAsync(db) {
|
||||||
await boardInsertCollection.insertAllAsync(db);
|
await boardInsertCollection.insertAllAsync(db);
|
||||||
},
|
},
|
||||||
handleSync(db) {
|
|
||||||
boardInsertCollection.insertAll(db);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.5",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"undici": "7.16.0",
|
"undici": "7.16.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
||||||
|
|
||||||
@@ -12,9 +11,7 @@ export const minecraftServerStatusRequestHandler = createCachedWidgetRequestHand
|
|||||||
async requestAsync(input: { domain: string; isBedrockServer: boolean }) {
|
async requestAsync(input: { domain: string; isBedrockServer: boolean }) {
|
||||||
const path = `${input.isBedrockServer ? "/bedrock" : ""}/3/${input.domain}`;
|
const path = `${input.isBedrockServer ? "/bedrock" : ""}/3/${input.domain}`;
|
||||||
|
|
||||||
const response = await withTimeoutAsync(async (signal) =>
|
const response = await fetchWithTimeoutAsync(`https://api.mcsrvstat.us${path}`);
|
||||||
fetchWithTrustedCertificatesAsync(`https://api.mcsrvstat.us${path}`, { signal }),
|
|
||||||
);
|
|
||||||
return responseSchema.parse(await response.json());
|
return responseSchema.parse(await response.json());
|
||||||
},
|
},
|
||||||
cacheDuration: dayjs.duration(5, "minutes"),
|
cacheDuration: dayjs.duration(5, "minutes"),
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { z } from "zod/v4";
|
import { z } from "zod/v4";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
||||||
|
|
||||||
@@ -10,12 +9,9 @@ export const fetchStockPriceHandler = createCachedWidgetRequestHandler({
|
|||||||
queryKey: "fetchStockPriceResult",
|
queryKey: "fetchStockPriceResult",
|
||||||
widgetKind: "stockPrice",
|
widgetKind: "stockPrice",
|
||||||
async requestAsync(input: { stock: string; timeRange: string; timeInterval: string }) {
|
async requestAsync(input: { stock: string; timeRange: string; timeInterval: string }) {
|
||||||
const response = await withTimeoutAsync(async (signal) => {
|
const response = await fetchWithTimeoutAsync(
|
||||||
return await fetchWithTrustedCertificatesAsync(
|
`https://query1.finance.yahoo.com/v8/finance/chart/${input.stock}?range=${input.timeRange}&interval=${input.timeInterval}`,
|
||||||
`https://query1.finance.yahoo.com/v8/finance/chart/${input.stock}?range=${input.timeRange}&interval=${input.timeInterval}`,
|
);
|
||||||
{ signal },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const data = dataSchema.parse(await response.json());
|
const data = dataSchema.parse(await response.json());
|
||||||
|
|
||||||
if ("error" in data) {
|
if ("error" in data) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Octokit } from "octokit";
|
|||||||
import { compareSemVer, isValidSemVer } from "semver-parser";
|
import { compareSemVer, isValidSemVer } from "semver-parser";
|
||||||
|
|
||||||
import { env } from "@homarr/common/env";
|
import { env } from "@homarr/common/env";
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||||
import { createChannelWithLatestAndEvents } from "@homarr/redis";
|
import { createChannelWithLatestAndEvents } from "@homarr/redis";
|
||||||
import { createCachedRequestHandler } from "@homarr/request-handler/lib/cached-request-handler";
|
import { createCachedRequestHandler } from "@homarr/request-handler/lib/cached-request-handler";
|
||||||
@@ -23,7 +23,7 @@ export const updateCheckerRequestHandler = createCachedRequestHandler({
|
|||||||
|
|
||||||
const octokit = new Octokit({
|
const octokit = new Octokit({
|
||||||
request: {
|
request: {
|
||||||
fetch: fetchWithTrustedCertificatesAsync,
|
fetch: fetchWithTimeoutAsync,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const releases = await octokit.rest.repos.listReleases({
|
const releases = await octokit.rest.repos.listReleases({
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
import { fetchWithTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
||||||
import { withTimeoutAsync } from "@homarr/core/infrastructure/http/timeout";
|
|
||||||
|
|
||||||
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
||||||
|
|
||||||
@@ -10,12 +9,9 @@ export const weatherRequestHandler = createCachedWidgetRequestHandler({
|
|||||||
queryKey: "weatherAtLocation",
|
queryKey: "weatherAtLocation",
|
||||||
widgetKind: "weather",
|
widgetKind: "weather",
|
||||||
async requestAsync(input: { latitude: number; longitude: number }) {
|
async requestAsync(input: { latitude: number; longitude: number }) {
|
||||||
const res = await withTimeoutAsync(async (signal) => {
|
const res = await fetchWithTimeoutAsync(
|
||||||
return await fetchWithTrustedCertificatesAsync(
|
`https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset,wind_speed_10m_max,wind_gusts_10m_max¤t_weather=true&timezone=auto`,
|
||||||
`https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset,wind_speed_10m_max,wind_gusts_10m_max¤t_weather=true&timezone=auto`,
|
);
|
||||||
{ signal },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const json: unknown = await res.json();
|
const json: unknown = await res.json();
|
||||||
const weather = await atLocationOutput.parseAsync(json);
|
const weather = await atLocationOutput.parseAsync(json);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"@homarr/db": "workspace:^0.1.0",
|
"@homarr/db": "workspace:^0.1.0",
|
||||||
"@homarr/server-settings": "workspace:^0.1.0",
|
"@homarr/server-settings": "workspace:^0.1.0",
|
||||||
"@mantine/dates": "^8.3.10",
|
"@mantine/dates": "^8.3.10",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3"
|
"react-dom": "19.2.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
"@mantine/spotlight": "^8.3.10",
|
"@mantine/spotlight": "^8.3.10",
|
||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"jotai": "^2.16.0",
|
"jotai": "^2.16.0",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"use-deep-compare-effect": "^1.8.1"
|
"use-deep-compare-effect": "^1.8.1"
|
||||||
|
|||||||
@@ -32,8 +32,8 @@
|
|||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"next-intl": "4.6.1",
|
"next-intl": "4.6.0",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3"
|
"react-dom": "19.2.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -312,7 +312,7 @@
|
|||||||
},
|
},
|
||||||
"mobileBoard": {
|
"mobileBoard": {
|
||||||
"label": "Mobiles Board",
|
"label": "Mobiles Board",
|
||||||
"description": "Nur Boards, welche für die Gruppe zugänglich sind, können ausgewählt werden"
|
"description": "Nur Boards welche für die Gruppe zugänglich sind, können ausgewählt werden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
},
|
},
|
||||||
"view-all": {
|
"view-all": {
|
||||||
"label": "Alle Boards anzeigen",
|
"label": "Alle Boards anzeigen",
|
||||||
"description": "Mitgliedern das Ansehen von Boards erlauben"
|
"description": "Mitgliedern das Ansehen von allen Boards erlauben"
|
||||||
},
|
},
|
||||||
"modify-all": {
|
"modify-all": {
|
||||||
"label": "Alle Boards ändern",
|
"label": "Alle Boards ändern",
|
||||||
@@ -743,7 +743,7 @@
|
|||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
"title": "Autorisierungsfehler",
|
"title": "Autorisierungsfehler",
|
||||||
"description": "Die Anfrage wurde nicht autorisiert. Bitte überprüfen Sie, ob die Anmeldedaten korrekt sind und Sie diese mit den entsprechenden Berechtigungen konfiguriert haben."
|
"description": "Die Anfrage wurde nicht autorisiert. Bitte überprüfen Sie, ob die Anmeldedaten korrekt sind und Sie diese mit mit den entsprechenden Berechtigungen konfiguriert haben."
|
||||||
},
|
},
|
||||||
"statusCode": {
|
"statusCode": {
|
||||||
"title": "Antwort-Fehler",
|
"title": "Antwort-Fehler",
|
||||||
@@ -964,7 +964,7 @@
|
|||||||
},
|
},
|
||||||
"personalAccessToken": {
|
"personalAccessToken": {
|
||||||
"label": "Persönlicher Zugangs-Token",
|
"label": "Persönlicher Zugangs-Token",
|
||||||
"newLabel": "Neuer persönlicher Zugangs-Token"
|
"newLabel": "Neuer Persönliches Zugangs-Token"
|
||||||
},
|
},
|
||||||
"topic": {
|
"topic": {
|
||||||
"label": "Thema",
|
"label": "Thema",
|
||||||
@@ -1237,7 +1237,7 @@
|
|||||||
},
|
},
|
||||||
"openAllInNewTabs": {
|
"openAllInNewTabs": {
|
||||||
"title": "Alle in tabs öffnen",
|
"title": "Alle in tabs öffnen",
|
||||||
"text": "Einige Browser können aus Sicherheitsgründen das gleichzeitige Öffnen von mehreren Tabs blockieren. Homarr konnte nicht alle Fenster öffnen, weil Ihr Browser diese Aktion blockiert hat. Bitte erlauben Sie \"Pop-up Fenster öffnen\" und versuchen Sie es erneut."
|
"text": "Einige Browser können aus Sicherheitsgründen das Öffnen von mehreren Tabs aufeinmal blockieren. Homarr konnte nicht alle Fenster öffnen, weil Ihr Browser diese Aktion blockiert hat. Bitte erlauben Sie \"Pop-up Fenster öffnen\" und versuchen Sie es erneut."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1949,7 +1949,7 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"state": "Status",
|
"state": "Status",
|
||||||
"cpuUsage": "CPU-Auslastung",
|
"cpuUsage": "CPU-Auslastung",
|
||||||
"memoryUsage": "Speichernutzung"
|
"memoryUsage": "Speicherverbrauch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"descendingDefaultSort": {
|
"descendingDefaultSort": {
|
||||||
@@ -2639,7 +2639,7 @@
|
|||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"title": "Änderungen erfolgreich angewendet",
|
"title": "Änderungen erfolgreich angewendet",
|
||||||
"message": "Das Board wurde erfolgreich gespeichert"
|
"message": "Das Board wurde erfolgreich dupliziert"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"title": "Änderungen konnten nicht angewendet werden",
|
"title": "Änderungen konnten nicht angewendet werden",
|
||||||
@@ -2676,7 +2676,7 @@
|
|||||||
},
|
},
|
||||||
"onlyImportApps": {
|
"onlyImportApps": {
|
||||||
"label": "Nur Apps importieren",
|
"label": "Nur Apps importieren",
|
||||||
"description": "Fügt nur die Apps hinzu. Das Board selbst muss manuell neu erstellt werden"
|
"description": "Fügt nur die Apps hinzu, das Board muss manuell neu erstellt werden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
@@ -2926,7 +2926,7 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"noBoard": {
|
"noBoard": {
|
||||||
"title": "Willkommen bei Homarr",
|
"title": "Willkommen bei Homarr",
|
||||||
"description": "Ein schlankes, modernes Dashboard, das alle Ihre Apps und Dienste bereitstellt.",
|
"description": "Ein schlankens, modernes Dashboard, das alle Ihre Apps und Dienste bereit stellt.",
|
||||||
"link": "Erstellen Sie Ihr erstes Board",
|
"link": "Erstellen Sie Ihr erstes Board",
|
||||||
"notice": "Um diese Seite verschwinden zu lassen, erstellen Sie ein Board und setzen es als Home Board"
|
"notice": "Um diese Seite verschwinden zu lassen, erstellen Sie ein Board und setzen es als Home Board"
|
||||||
},
|
},
|
||||||
@@ -3366,7 +3366,7 @@
|
|||||||
"label": "Index Manager"
|
"label": "Index Manager"
|
||||||
},
|
},
|
||||||
"healthMonitoring": {
|
"healthMonitoring": {
|
||||||
"label": "Überwachung des Zustandes"
|
"label": "Überwachung des Gesundheit"
|
||||||
},
|
},
|
||||||
"dnsHole": {
|
"dnsHole": {
|
||||||
"label": "DNS Hole Daten"
|
"label": "DNS Hole Daten"
|
||||||
@@ -3512,7 +3512,7 @@
|
|||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"version": "Version {version}",
|
"version": "Version {version}",
|
||||||
"text": "Homarr ist ein gemeinschaftsorientiertes Open-Source-Projekt, das von Freiwilligen betreut wird. Dank dieser Menschen ist Homarr seit 2021 ein wachsendes Projekt. Unser Team, dessen Mitglieder in vielen verschiedenen Ländern ansässig sind, arbeitet in seiner Freizeit und ohne Vergütung.",
|
"text": "Homarr ist ein gemeinschaftsorientiertes Open-Source Projekt, das von Freiwilligen betreut wird. Dank dieser Menschen ist Homarr seit 2021 ein wachsendes Projekt. Unser Team, sitzhaft mit ihren Migliedern in vielen verschiedenen Ländern, arbeitet in ihrer Freizeit ohne Vergütung.",
|
||||||
"accordion": {
|
"accordion": {
|
||||||
"contributors": {
|
"contributors": {
|
||||||
"title": "Mitwirkende",
|
"title": "Mitwirkende",
|
||||||
@@ -4395,7 +4395,7 @@
|
|||||||
"configControl": "Konfiguration",
|
"configControl": "Konfiguration",
|
||||||
"searchEngineType": {
|
"searchEngineType": {
|
||||||
"generic": "Allgemein",
|
"generic": "Allgemein",
|
||||||
"fromIntegration": "Aus der Integration"
|
"fromIntegration": "Aus den Integration"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
@@ -4498,7 +4498,7 @@
|
|||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"title": "Zertifikat entfernt",
|
"title": "Zertifikat entfernt",
|
||||||
"message": "Das Zertifikat wurde erfolgreich entfernt"
|
"message": "Das Zertifikat wurde erfolgreich entfern"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"title": "Zertifikat nicht entfernt",
|
"title": "Zertifikat nicht entfernt",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"@mantine/hooks": "^8.3.10",
|
"@mantine/hooks": "^8.3.10",
|
||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"svgson": "^5.3.1"
|
"svgson": "^5.3.1"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.1.13",
|
||||||
"zod-form-data": "^3.0.1"
|
"zod-form-data": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -52,33 +52,33 @@
|
|||||||
"@mantine/core": "^8.3.10",
|
"@mantine/core": "^8.3.10",
|
||||||
"@mantine/hooks": "^8.3.10",
|
"@mantine/hooks": "^8.3.10",
|
||||||
"@tabler/icons-react": "^3.35.0",
|
"@tabler/icons-react": "^3.35.0",
|
||||||
"@tiptap/extension-color": "3.14.0",
|
"@tiptap/extension-color": "3.13.0",
|
||||||
"@tiptap/extension-highlight": "3.14.0",
|
"@tiptap/extension-highlight": "3.13.0",
|
||||||
"@tiptap/extension-image": "3.14.0",
|
"@tiptap/extension-image": "3.13.0",
|
||||||
"@tiptap/extension-link": "^3.14.0",
|
"@tiptap/extension-link": "^3.13.0",
|
||||||
"@tiptap/extension-placeholder": "^3.14.0",
|
"@tiptap/extension-placeholder": "^3.13.0",
|
||||||
"@tiptap/extension-table": "3.14.0",
|
"@tiptap/extension-table": "3.13.0",
|
||||||
"@tiptap/extension-table-cell": "3.14.0",
|
"@tiptap/extension-table-cell": "3.13.0",
|
||||||
"@tiptap/extension-table-header": "3.14.0",
|
"@tiptap/extension-table-header": "3.13.0",
|
||||||
"@tiptap/extension-table-row": "3.14.0",
|
"@tiptap/extension-table-row": "3.13.0",
|
||||||
"@tiptap/extension-task-item": "3.14.0",
|
"@tiptap/extension-task-item": "3.13.0",
|
||||||
"@tiptap/extension-task-list": "3.14.0",
|
"@tiptap/extension-task-list": "3.13.0",
|
||||||
"@tiptap/extension-text-align": "3.14.0",
|
"@tiptap/extension-text-align": "3.13.0",
|
||||||
"@tiptap/extension-text-style": "3.14.0",
|
"@tiptap/extension-text-style": "3.13.0",
|
||||||
"@tiptap/extension-underline": "3.14.0",
|
"@tiptap/extension-underline": "3.13.0",
|
||||||
"@tiptap/react": "^3.14.0",
|
"@tiptap/react": "^3.13.0",
|
||||||
"@tiptap/starter-kit": "^3.14.0",
|
"@tiptap/starter-kit": "^3.13.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"mantine-form-zod-resolver": "^1.3.0",
|
"mantine-form-zod-resolver": "^1.3.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.1.0",
|
"next": "16.0.10",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"recharts": "^2.15.4",
|
"recharts": "^2.15.4",
|
||||||
"video.js": "^8.23.4",
|
"video.js": "^8.23.4",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
Generated
+978
-795
File diff suppressed because it is too large.
Load diff
@@ -1 +1 @@
|
|||||||
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"httpsLine truncated
|
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"Skick","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12619811/medium/0331f1de413a645b4516bc4e037f31bb.jpg"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowLine truncated
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/eslint-plugin-next": "16.1.0",
|
"@next/eslint-plugin-next": "16.0.10",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-config-turbo": "^2.7.1",
|
"eslint-config-turbo": "^2.6.3",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^6.1.1",
|
"eslint-plugin-react-hooks": "^6.1.1",
|
||||||
"typescript-eslint": "^8.50.0"
|
"typescript-eslint": "^8.49.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||||
|
|||||||
Reference in new issue
Block a user