Compare commits

..
Author SHA1 Message Date
Meier Lukas 30a336f951 fix: missing updated imports 2025-12-20 22:50:50 +01:00
Meier Lukas 978121b6ed fix(db): remove bulk drizzle-orm export 2025-12-20 22:37:09 +01:00
Manuel fbb5298cb0 feat: enable dns caching by default (#4706) 2025-12-20 21:07:57 +01:00
homarr-renovate[bot] 2dc8feae7e fix(deps): update dependency typescript-eslint to ^8.50.0 (#4720)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 17:25:20 +00:00
homarr-renovate[bot] 6e39524f9d fix(deps): update dependency zod to ^4.2.0 (#4712)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 13:37:00 +00:00
homarr-renovate[bot] b699f28177 chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=7.3.0 (#4713)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 13:35:32 +00:00
homarr-renovate[bot] de39c7e85b chore(deps): update pnpm to v10.26.0 (#4715)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 12:48:32 +00:00
homarr-renovate[bot] e9c33cdd5a chore(deps): update dependency rollup@&gt;&#x3d;4.0.0 &lt;4.22.4 to >=4.53.4 (#4714)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 12:47:56 +00:00
homarr-renovate[bot] 648f7e2c77 fix(deps): update dependency ldapts to v8.0.24 (#4711)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 07:28:01 +00:00
homarr-renovate[bot] 41f40149b3 chore(deps): update dependency hono@&lt;4.6.5 to >=4.11.1 (#4708)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 07:27:41 +00:00
homarr-renovate[bot] 689523a194 fix(deps): update dependency @t3-oss/env-nextjs to ^0.13.10 (#4710)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2025-12-20 05:28:31 +00:00
Releases Homarr 2ea0437a97 chore(release): 1.47.0 [skip ci] 2025-12-19 19:35:16 +00:00
homarr-releases[bot] df3a3f94c8 chore(release): automatic release v1.47.0 2025-12-19 19:34:14 +00:00
Meier Lukas 47d7458143 fix: reference to prebuilt-debian action does not work 2025-12-19 20:27:34 +01:00
112 changed files with 12549 additions and 2525 deletions

No files matched your search

@@ -20,7 +20,7 @@ runs:
using: "composite"
steps:
- name: Prebuilt debian dependencies
uses: ./.github/actions/prebuilt-debian@dev
uses: homarr-labs/homarr/.github/actions/prebuilt-debian@dev
id: prebuilt-debian
with:
architecture: ${{ inputs.architecture }}
+41
View File
@@ -1,3 +1,44 @@
## [1.47.0](https://github.com/homarr-labs/homarr/compare/v1.46.0...v1.47.0) (2025-12-19)
### Features
* [#4452](https://github.com/homarr-labs/homarr/issues/4452) improve calendar design ([#4461](https://github.com/homarr-labs/homarr/issues/4461)) ([08e9f7d](https://github.com/homarr-labs/homarr/commit/08e9f7d8d85475788534b46e6cceb40a7231c09d))
### Bug Fixes
* **boards:** selection menu not shown to users without change access ([#4689](https://github.com/homarr-labs/homarr/issues/4689)) ([a0a11e3](https://github.com/homarr-labs/homarr/commit/a0a11e35707513fee45551e09c26ff633f636c32))
* **deps:** update dependency dns-caching to ^0.2.9 ([#4648](https://github.com/homarr-labs/homarr/issues/4648)) ([6deab2e](https://github.com/homarr-labs/homarr/commit/6deab2ecec2dc4f5192b53213cb755a9853eb7c0))
* **deps:** update dependency drizzle-orm to ^0.45.1 ([#4663](https://github.com/homarr-labs/homarr/issues/4663)) ([f635009](https://github.com/homarr-labs/homarr/commit/f63500961f11589a972a41dde9e4117b1f25141c))
* **deps:** update dependency fast-xml-parser to ^5.3.3 ([#4683](https://github.com/homarr-labs/homarr/issues/4683)) ([611b65e](https://github.com/homarr-labs/homarr/commit/611b65e8bd420aaad580bf35071b651093623c95))
* **deps:** update dependency isomorphic-dompurify to ^2.34.0 ([#4650](https://github.com/homarr-labs/homarr/issues/4650)) ([5d5c18b](https://github.com/homarr-labs/homarr/commit/5d5c18b3649d5fa1b2b933d443ccd5abcbb84e3a))
* **deps:** update dependency jotai to ^2.16.0 ([#4655](https://github.com/homarr-labs/homarr/issues/4655)) ([4aef85b](https://github.com/homarr-labs/homarr/commit/4aef85ba60f4978da5a0b13c08e1799949fe4497))
* **deps:** update dependency ldapts to v8.0.16 ([#4640](https://github.com/homarr-labs/homarr/issues/4640)) ([ada5597](https://github.com/homarr-labs/homarr/commit/ada5597cf3ab0bafb04e9176b431d257b36d1dc8))
* **deps:** update dependency ldapts to v8.0.17 ([#4642](https://github.com/homarr-labs/homarr/issues/4642)) ([c09da85](https://github.com/homarr-labs/homarr/commit/c09da85f3a8450a70991ff0656228a9aa1364f89))
* **deps:** update dependency ldapts to v8.0.18 ([#4653](https://github.com/homarr-labs/homarr/issues/4653)) ([4bd142e](https://github.com/homarr-labs/homarr/commit/4bd142e09c666cc84c67c93e2202e571565b766c))
* **deps:** update dependency ldapts to v8.0.19 ([#4669](https://github.com/homarr-labs/homarr/issues/4669)) ([8ed771d](https://github.com/homarr-labs/homarr/commit/8ed771d30073809b08a6ace2c29b3de2675e37d1))
* **deps:** update dependency ldapts to v8.0.20 ([#4684](https://github.com/homarr-labs/homarr/issues/4684)) ([703c8cc](https://github.com/homarr-labs/homarr/commit/703c8ccc060bb1775a72f405da1e169926b1ce5d))
* **deps:** update dependency ldapts to v8.0.21 ([#4695](https://github.com/homarr-labs/homarr/issues/4695)) ([7f6eaa0](https://github.com/homarr-labs/homarr/commit/7f6eaa0b1f397a37b6acd15e0ccfea75ee2e0161))
* **deps:** update dependency ldapts to v8.0.22 ([#4700](https://github.com/homarr-labs/homarr/issues/4700)) ([391d725](https://github.com/homarr-labs/homarr/commit/391d725191563ba5048593c14baf077a924cea44))
* **deps:** update dependency ldapts to v8.0.23 ([#4701](https://github.com/homarr-labs/homarr/issues/4701)) ([db337b2](https://github.com/homarr-labs/homarr/commit/db337b2525762c96acd815bd4fdacfaabff5f0cf))
* **deps:** update dependency next-intl to v4.6.0 ([#4691](https://github.com/homarr-labs/homarr/issues/4691)) ([1ff6fac](https://github.com/homarr-labs/homarr/commit/1ff6fac71bb9187d0eb703a7b6543daf8861efe8))
* **deps:** update dependency sass to ^1.94.3 ([#4646](https://github.com/homarr-labs/homarr/issues/4646)) ([7e91d12](https://github.com/homarr-labs/homarr/commit/7e91d12439826eee76f118448c583294c3c46b42))
* **deps:** update dependency sass to ^1.95.0 ([#4647](https://github.com/homarr-labs/homarr/issues/4647)) ([eb760da](https://github.com/homarr-labs/homarr/commit/eb760da8b30583575fb638995fc2f2acbd5b2961))
* **deps:** update dependency sass to ^1.95.1 ([#4661](https://github.com/homarr-labs/homarr/issues/4661)) ([e327c66](https://github.com/homarr-labs/homarr/commit/e327c660b239840c4929f536eff3096342cc3433))
* **deps:** update dependency sass to ^1.96.0 ([#4668](https://github.com/homarr-labs/homarr/issues/4668)) ([bdfc150](https://github.com/homarr-labs/homarr/commit/bdfc1501152450a1546962331037a6fc1c782e4e))
* **deps:** update dependency swagger-ui-react to ^5.31.0 ([#4672](https://github.com/homarr-labs/homarr/issues/4672)) ([4d593f5](https://github.com/homarr-labs/homarr/commit/4d593f5b9d1f47ae2368f42131e8ef8d32f73fc9))
* **deps:** update dependency typescript-eslint to ^8.49.0 ([#4645](https://github.com/homarr-labs/homarr/issues/4645)) ([220cb66](https://github.com/homarr-labs/homarr/commit/220cb669a13fead953ff42040b39e06d1dcc1fe2))
* **deps:** update mantine monorepo to ^8.3.10 ([#4651](https://github.com/homarr-labs/homarr/issues/4651)) ([d0652f7](https://github.com/homarr-labs/homarr/commit/d0652f7cf5ac0e63a61adcfe43e53c4ed130e8fc))
* **deps:** update react monorepo to v19.2.2 ([#4676](https://github.com/homarr-labs/homarr/issues/4676)) ([82e79f2](https://github.com/homarr-labs/homarr/commit/82e79f26274a289e1ce4f9331b6ba16f00f5a86c))
* **deps:** update react monorepo to v19.2.3 ([#4680](https://github.com/homarr-labs/homarr/issues/4680)) ([4c71856](https://github.com/homarr-labs/homarr/commit/4c7185646d9d061d1aebf9194f9ef2f810fc3384))
* **deps:** update testcontainers-node monorepo to ^11.10.0 ([#4659](https://github.com/homarr-labs/homarr/issues/4659)) ([c880ce9](https://github.com/homarr-labs/homarr/commit/c880ce9bd99a7056f20d450275fcbc37f9372f63))
* **deps:** update trpc monorepo to ^11.8.0 ([#4697](https://github.com/homarr-labs/homarr/issues/4697)) ([1f44845](https://github.com/homarr-labs/homarr/commit/1f44845df489bb33bcfe943c6a05c0f157037ed6))
* **downloads:** improve progress to only show 100 percent if fully completed ([#4621](https://github.com/homarr-labs/homarr/issues/4621)) ([1062745](https://github.com/homarr-labs/homarr/commit/10627453d5b5a17991bbe9b8d4bc23981b366c39))
* **iframe-widget:** permission selection not applying to any origin ([#4616](https://github.com/homarr-labs/homarr/issues/4616)) ([479f083](https://github.com/homarr-labs/homarr/commit/479f083ab8ec405d1b8296b32a20262f6192a1f9))
* **invites:** creation with custom expiration not working ([#4677](https://github.com/homarr-labs/homarr/issues/4677)) ([423da9e](https://github.com/homarr-labs/homarr/commit/423da9eeb88510a123bdd2447dc97b54c5f1dec4))
* reference to prebuilt-debian action does not work ([47d7458](https://github.com/homarr-labs/homarr/commit/47d7458143d8801947cae1985745fa827b247dd1))
* **stock-price:** use previous-close for change calculation ([#4674](https://github.com/homarr-labs/homarr/issues/4674)) ([17fc7a0](https://github.com/homarr-labs/homarr/commit/17fc7a043b4adbed9f14408e23c95ec5fba0c8b8))
* **tdarr:** array parse error ([#4678](https://github.com/homarr-labs/homarr/issues/4678)) ([d86af07](https://github.com/homarr-labs/homarr/commit/d86af072bf136d76b02f3efa2c15520a4f63974e))
## [1.46.0](https://github.com/homarr-labs/homarr/compare/v1.45.3...v1.46.0) (2025-12-12)
### Features
+4 -2
View File
@@ -55,7 +55,6 @@
"@mantine/modals": "^8.3.10",
"@mantine/tiptap": "^8.3.10",
"@million/lint": "1.0.14",
"@scalar/api-reference-react": "^0.8.11",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
@@ -72,6 +71,7 @@
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"flag-icons": "^7.5.0",
"glob": "^13.0.0",
"isomorphic-dompurify": "^2.34.0",
@@ -86,8 +86,9 @@
"react-simple-code-editor": "^0.14.1",
"sass": "^1.96.0",
"superjson": "2.2.6",
"swagger-ui-react": "^5.31.0",
"use-deep-compare-effect": "^1.8.1",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -98,6 +99,7 @@
"@types/prismjs": "^1.26.5",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.2.1",
"eslint": "^9.39.2",
"node-loader": "^2.1.0",
@@ -1,9 +1,10 @@
import { notFound } from "next/navigation";
import { Card, Center, Stack, Text, Title } from "@mantine/core";
import { and, eq } from "drizzle-orm";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { and, db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { invites } from "@homarr/db/schema";
import { getScopedI18n } from "@homarr/translation/server";
@@ -0,0 +1,23 @@
"use client";
import type { OpenAPIObject } from "openapi3-ts/oas31";
import SwaggerUI from "swagger-ui-react";
// workaround for CSS that cannot be processed by next.js, https://github.com/swagger-api/swagger-ui/issues/10045
import "../swagger-ui-dark.css";
import "../swagger-ui-overrides.css";
import "../swagger-ui.css";
interface SwaggerUIClientProps {
document: OpenAPIObject;
}
export const SwaggerUIClient = ({ document }: SwaggerUIClientProps) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const requestInterceptor = (req: Record<string, any>) => {
req.credentials = "omit";
return req;
};
return <SwaggerUI requestInterceptor={requestInterceptor} spec={document} />;
};
@@ -1,7 +1,6 @@
import { headers } from "next/headers";
import { notFound } from "next/navigation";
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from "@mantine/core";
import { ApiReferenceReact } from "@scalar/api-reference-react";
import { openApiDocument } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -9,14 +8,11 @@ import { auth } from "@homarr/auth/next";
import { extractBaseUrlFromHeaders } from "@homarr/common";
import { getScopedI18n } from "@homarr/translation/server";
import { SwaggerUIClient } from "~/app/[locale]/manage/tools/api/components/swagger-ui";
import { createMetaTitle } from "~/metadata";
import { ApiKeysManagement } from "./components/api-keys";
import "@scalar/api-reference-react/style.css";
import { Metadata } from "next";
export async function generateMetadata(): Promise<Metadata> {
export async function generateMetadata() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
return {};
@@ -49,21 +45,7 @@ export default async function ApiPage() {
<ApiKeysManagement apiKeys={apiKeys} />
</TabsPanel>
<TabsPanel value={"documentation"}>
<ApiReferenceReact
configuration={{
content: document,
layout: "classic",
showSidebar: false,
hideDarkModeToggle: true,
showDeveloperTools: "never",
hiddenClients: true,
darkMode: session.user.colorScheme === "dark",
authentication: {
preferredSecurityScheme: "apikey",
},
hideSearch: true,
}}
/>
<SwaggerUIClient document={document} />
</TabsPanel>
</Tabs>
</Stack>
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,4 @@
.swagger-ui .info {
margin: 0 !important;
margin-bottom: 20px !important;
}
File diff suppressed because it is too large. Load diff
@@ -1,8 +1,9 @@
import { notFound } from "next/navigation";
import { inArray } from "drizzle-orm";
import { auth } from "@homarr/auth/next";
import { isProviderEnabled } from "@homarr/auth/server";
import { db, inArray } from "@homarr/db";
import { db } from "@homarr/db";
import { groups } from "@homarr/db/schema";
import { everyoneGroup } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
+2 -1
View File
@@ -1,5 +1,6 @@
import type { NextRequest } from "next/server";
import { userAgent } from "next/server";
import { eq } from "drizzle-orm";
import { createOpenApiFetchHandler } from "trpc-to-openapi";
import { appRouter, createTRPCContext } from "@homarr/api";
@@ -8,7 +9,7 @@ import { hashPasswordAsync } from "@homarr/auth";
import { createSessionAsync } from "@homarr/auth/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema";
const logger = createLogger({ module: "trpcOpenApiRoute" });
@@ -1,9 +1,10 @@
import { notFound } from "next/navigation";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { eq } from "drizzle-orm";
import DOMPurify from "isomorphic-dompurify";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { medias } from "@homarr/db/schema";
export async function GET(_req: NextRequest, props: { params: Promise<{ id: string }> }) {
+1
View File
@@ -39,6 +39,7 @@
"@homarr/widgets": "workspace:^0.1.0",
"dayjs": "^1.11.19",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"fastify": "^5.6.2",
"superjson": "2.2.6",
"undici": "7.16.0"
+3 -2
View File
@@ -1,10 +1,11 @@
import type { InferInsertModel } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { schedule, validate as validateCron } from "node-cron";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { IJobManager } from "@homarr/cron-job-api";
import type { jobGroup as cronJobGroup, JobGroupKeys } from "@homarr/cron-jobs";
import type { Database, InferInsertModel } from "@homarr/db";
import { eq } from "@homarr/db";
import type { Database } from "@homarr/db";
import { cronJobConfigurations } from "@homarr/db/schema";
const logger = createLogger({ module: "jobManager" });
+3 -1
View File
@@ -1,6 +1,8 @@
import { eq, inArray } from "drizzle-orm";
import { env } from "@homarr/auth/env";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { db, eq, inArray } from "@homarr/db";
import { db } from "@homarr/db";
import { sessions, users } from "@homarr/db/schema";
import { supportedAuthProviders } from "@homarr/definitions";
+2 -2
View File
@@ -4,10 +4,10 @@
"dependencies": {
"better-sqlite3": "^12.5.0"
},
"packageManager": "pnpm@10.25.0",
"packageManager": "pnpm@10.26.0",
"engines": {
"node": ">=24.12.0",
"pnpm": ">=10.25.0"
"pnpm": ">=10.26.0"
},
"pnpm": {
"onlyBuiltDependencies": [
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.46.0",
"version": "1.47.0",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -59,10 +59,10 @@
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.0.15"
},
"packageManager": "pnpm@10.25.0",
"packageManager": "pnpm@10.26.0",
"engines": {
"node": ">=24.12.0",
"pnpm": ">=10.25.0"
"pnpm": ">=10.26.0"
},
"pnpm": {
"onlyBuiltDependencies": [
@@ -84,18 +84,18 @@
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
"esbuild@<=0.24.2": ">=0.27.1",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.11.0",
"hono@<4.6.5": ">=4.11.1",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.16.0",
"react-is": "^19.2.3",
"rollup@>=4.0.0 <4.22.4": ">=4.53.3",
"rollup@>=4.0.0 <4.22.4": ">=4.53.4",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
"tmp@<=0.2.3": ">=0.2.5",
"vite@>=5.0.0 <=5.4.18": ">=7.2.7"
"vite@>=5.0.0 <=5.4.18": ">=7.3.0"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
+1
View File
@@ -26,6 +26,7 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@umami/node": "^0.4.0",
"drizzle-orm": "^0.45.1",
"superjson": "2.2.6"
},
"devDependencies": {
@@ -1,8 +1,9 @@
import type { UmamiEventData } from "@umami/node";
import { Umami } from "@umami/node";
import { count } from "drizzle-orm";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { count, db } from "@homarr/db";
import { db } from "@homarr/db";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import { integrations, items, users } from "@homarr/db/schema";
import type { defaultServerSettings } from "@homarr/server-settings";
+2 -1
View File
@@ -46,13 +46,14 @@
"@trpc/react-query": "^11.8.0",
"@trpc/server": "^11.8.0",
"@trpc/tanstack-react-query": "^11.8.0",
"drizzle-orm": "^0.45.1",
"lodash.clonedeep": "^4.5.0",
"next": "16.0.10",
"react": "19.2.3",
"react-dom": "19.2.3",
"superjson": "2.2.6",
"trpc-to-openapi": "^3.1.0",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -1,4 +1,5 @@
import { TRPCError } from "@trpc/server";
import { and, eq, inArray } from "drizzle-orm";
import { z } from "zod/v4";
import type { Session } from "@homarr/auth";
@@ -7,7 +8,6 @@ import { constructIntegrationPermissions } from "@homarr/auth/shared";
import { decryptSecret } from "@homarr/common/server";
import type { AtLeastOneOf } from "@homarr/common/types";
import type { Database } from "@homarr/db";
import { and, eq, inArray } from "@homarr/db";
import { integrations } from "@homarr/db/schema";
import type { IntegrationKind } from "@homarr/definitions";
+1 -1
View File
@@ -1,7 +1,7 @@
import { TRPCError } from "@trpc/server";
import { and, eq } from "drizzle-orm";
import { z } from "zod/v4";
import { and, eq } from "@homarr/db";
import { items } from "@homarr/db/schema";
import type { WidgetKind } from "@homarr/definitions";
-2
View File
@@ -11,7 +11,6 @@ import { importRouter } from "./router/import/import-router";
import { infoRouter } from "./router/info";
import { integrationRouter } from "./router/integration/integration-router";
import { inviteRouter } from "./router/invite";
import { itemsRouter } from "./router/item/item-router";
import { kubernetesRouter } from "./router/kubernetes/router/kubernetes-router";
import { locationRouter } from "./router/location";
import { logRouter } from "./router/log";
@@ -50,7 +49,6 @@ export const appRouter = createTRPCRouter({
updateChecker: updateCheckerRouter,
certificates: certificateRouter,
info: infoRouter,
item: itemsRouter,
});
// export type definition of API
+2 -1
View File
@@ -1,9 +1,10 @@
import { eq } from "drizzle-orm";
import { z } from "zod/v4";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { generateSecureRandomToken } from "@homarr/common/server";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema";
import { createTRPCRouter, permissionRequiredProcedure } from "../trpc";
+1 -1
View File
@@ -1,8 +1,8 @@
import { TRPCError } from "@trpc/server";
import { asc, eq, inArray, like } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import { asc, eq, inArray, like } from "@homarr/db";
import { apps } from "@homarr/db/schema";
import { selectAppSchema } from "@homarr/db/validationSchemas";
import { getIconForName } from "@homarr/icons";
@@ -1,7 +1,8 @@
import { eq, or } from "drizzle-orm";
import SuperJSON from "superjson";
import type { Session } from "@homarr/auth";
import { db, eq, or } from "@homarr/db";
import { db } from "@homarr/db";
import { items } from "@homarr/db/schema";
import type { WidgetComponentProps } from "../../../../widgets/src";
+4 -2
View File
@@ -1,12 +1,14 @@
import { TRPCError } from "@trpc/server";
import type { InferInsertModel, InferSelectModel, SQL } from "drizzle-orm";
import { and, asc, eq, inArray, isNull, like, not, or, sql } from "drizzle-orm";
import superjson from "superjson";
import { z } from "zod/v4";
import { constructBoardPermissions } from "@homarr/auth/shared";
import { createId } from "@homarr/common";
import type { DeviceType } from "@homarr/common/server";
import type { Database, InferInsertModel, InferSelectModel, SQL } from "@homarr/db";
import { and, asc, eq, handleTransactionsAsync, inArray, isNull, like, not, or, sql } from "@homarr/db";
import type { Database } from "@homarr/db";
import { handleTransactionsAsync } from "@homarr/db";
import { createDbInsertCollectionWithoutTransaction } from "@homarr/db/collection";
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
import {
+6 -10
View File
@@ -1,9 +1,10 @@
import { TRPCError } from "@trpc/server";
import type { SQL } from "drizzle-orm";
import { eq, inArray } from "drizzle-orm";
import type { Session } from "@homarr/auth";
import { constructBoardPermissions } from "@homarr/auth/shared";
import type { Database, SQL } from "@homarr/db";
import { eq, inArray } from "@homarr/db";
import type { Database } from "@homarr/db";
import { boardGroupPermissions, boardUserPermissions, groupMembers } from "@homarr/db/schema";
import type { BoardPermission } from "@homarr/definitions";
@@ -17,7 +18,6 @@ export const throwIfActionForbiddenAsync = async (
ctx: { db: Database; session: Session | null },
boardWhere: SQL<unknown>,
permission: BoardPermission,
error?: TRPCError,
) => {
const { db, session } = ctx;
const groupsOfCurrentUser = await db.query.groupMembers.findMany({
@@ -41,7 +41,7 @@ export const throwIfActionForbiddenAsync = async (
});
if (!board) {
notAllowed(error);
notAllowed();
}
const { hasViewAccess, hasChangeAccess, hasFullAccess } = constructBoardPermissions(board, session);
@@ -58,18 +58,14 @@ export const throwIfActionForbiddenAsync = async (
return; // As view access is required and user has view access, allow
}
notAllowed(error);
notAllowed();
};
/**
* This method returns NOT_FOUND to prevent snooping on board existence
* A function is used to use the method without return statement
*/
function notAllowed(error?: TRPCError): never {
if (error) {
throw error;
}
function notAllowed(): never {
throw new TRPCError({
code: "NOT_FOUND",
message: "Board not found",
@@ -1,5 +1,6 @@
import { X509Certificate } from "node:crypto";
import { TRPCError } from "@trpc/server";
import { and, eq } from "drizzle-orm";
import { zfd } from "zod-form-data";
import { z } from "zod/v4";
@@ -8,7 +9,6 @@ import {
removeCustomRootCertificateAsync,
} from "@homarr/core/infrastructure/certificates";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { and, eq } from "@homarr/db";
import { trustedCertificateHostnames } from "@homarr/db/schema";
import { certificateValidFileNameSchema, checkCertificateFile } from "@homarr/validation/certificates";
+21 -105
View File
@@ -1,13 +1,13 @@
import { TRPCError } from "@trpc/server";
import { and, eq, like, not } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import type { Database } from "@homarr/db";
import { and, eq, handleTransactionsAsync, like, not } from "@homarr/db";
import { handleTransactionsAsync } from "@homarr/db";
import { getMaxGroupPositionAsync } from "@homarr/db/queries";
import { groupMembers, groupPermissions, groups } from "@homarr/db/schema";
import { selectGroupSchema, selectUserSchema } from "@homarr/db/validationSchemas";
import { everyoneGroup, groupPermissionKeys } from "@homarr/definitions";
import { everyoneGroup } from "@homarr/definitions";
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
import {
groupCreateSchema,
@@ -23,65 +23,33 @@ import { throwIfCredentialsDisabled } from "./invite/checks";
import { nextOnboardingStepAsync } from "./onboard/onboard-queries";
export const groupRouter = createTRPCRouter({
getAll: permissionRequiredProcedure
.requiresPermission("admin")
.input(z.void())
.output(
z.array(
selectGroupSchema.and(
z.object({ members: z.array(selectUserSchema.pick({ id: true, name: true, email: true, image: true })) }),
),
),
)
.meta({
openapi: { method: "GET", path: "/api/groups", tags: ["groups"], protect: true, summary: "Retrieve all groups" },
})
.query(async ({ ctx }) => {
const dbGroups = await ctx.db.query.groups.findMany({
with: {
members: {
with: {
user: {
columns: {
id: true,
name: true,
email: true,
image: true,
},
getAll: permissionRequiredProcedure.requiresPermission("admin").query(async ({ ctx }) => {
const dbGroups = await ctx.db.query.groups.findMany({
with: {
members: {
with: {
user: {
columns: {
id: true,
name: true,
email: true,
image: true,
},
},
},
},
});
},
});
return dbGroups.map((group) => ({
...group,
members: group.members.map((member) => member.user),
}));
}),
return dbGroups.map((group) => ({
...group,
members: group.members.map((member) => member.user),
}));
}),
getPaginated: permissionRequiredProcedure
.requiresPermission("admin")
.input(paginatedSchema)
.output(
z.object({
items: z.array(
selectGroupSchema.and(
z.object({ members: z.array(selectUserSchema.pick({ id: true, name: true, email: true, image: true })) }),
),
),
totalCount: z.number(),
}),
)
.meta({
openapi: {
method: "GET",
path: "/api/groups/paginated",
tags: ["groups"],
protect: true,
summary: "Retrieve groups with pagination",
},
})
.query(async ({ input, ctx }) => {
const whereQuery = input.search ? like(groups.name, `%${input.search.trim()}%`) : undefined;
const groupCount = await ctx.db.$count(groups, whereQuery);
@@ -117,24 +85,6 @@ export const groupRouter = createTRPCRouter({
getById: permissionRequiredProcedure
.requiresPermission("admin")
.input(byIdSchema)
.output(
selectGroupSchema.and(
z.object({
owner: selectUserSchema.pick({ id: true, name: true, image: true, email: true }).nullable(),
members: z.array(selectUserSchema.pick({ id: true, name: true, email: true, image: true, provider: true })),
permissions: z.array(z.enum(groupPermissionKeys)),
}),
),
)
.meta({
openapi: {
method: "GET",
path: "/api/groups/{id}",
tags: ["groups"],
protect: true,
summary: "Retrieve group details",
},
})
.query(async ({ input, ctx }) => {
const group = await ctx.db.query.groups.findFirst({
where: eq(groups.id, input.id),
@@ -252,10 +202,6 @@ export const groupRouter = createTRPCRouter({
createGroup: permissionRequiredProcedure
.requiresPermission("admin")
.input(groupCreateSchema)
.output(z.string())
.meta({
openapi: { method: "POST", path: "/api/groups", tags: ["groups"], protect: true, summary: "Create group" },
})
.mutation(async ({ input, ctx }) => {
await checkSimilarNameAndThrowAsync(ctx.db, input.name);
@@ -274,10 +220,6 @@ export const groupRouter = createTRPCRouter({
updateGroup: permissionRequiredProcedure
.requiresPermission("admin")
.input(groupUpdateSchema)
.output(z.void())
.meta({
openapi: { method: "PUT", path: "/api/groups/{id}", tags: ["groups"], protect: true, summary: "Update group" },
})
.mutation(async ({ input, ctx }) => {
await throwIfGroupNotFoundAsync(ctx.db, input.id);
await throwIfGroupNameIsReservedAsync(ctx.db, input.id);
@@ -362,10 +304,6 @@ export const groupRouter = createTRPCRouter({
deleteGroup: permissionRequiredProcedure
.requiresPermission("admin")
.input(byIdSchema)
.output(z.void())
.meta({
openapi: { method: "DELETE", path: "/api/groups/{id}", tags: ["groups"], protect: true, summary: "Delete group" },
})
.mutation(async ({ input, ctx }) => {
await throwIfGroupNotFoundAsync(ctx.db, input.id);
await throwIfGroupNameIsReservedAsync(ctx.db, input.id);
@@ -375,16 +313,6 @@ export const groupRouter = createTRPCRouter({
addMember: permissionRequiredProcedure
.requiresPermission("admin")
.input(groupUserSchema)
.output(z.void())
.meta({
openapi: {
method: "POST",
path: "/api/groups/{groupId}/members/{userId}",
tags: ["groups"],
protect: true,
summary: "Add member to group",
},
})
.mutation(async ({ input, ctx }) => {
await throwIfGroupNotFoundAsync(ctx.db, input.groupId);
await throwIfGroupNameIsReservedAsync(ctx.db, input.groupId);
@@ -401,8 +329,6 @@ export const groupRouter = createTRPCRouter({
});
}
// TODO: Create another pr to only allow adding users with provider=credentials
await ctx.db.insert(groupMembers).values({
groupId: input.groupId,
userId: input.userId,
@@ -411,16 +337,6 @@ export const groupRouter = createTRPCRouter({
removeMember: permissionRequiredProcedure
.requiresPermission("admin")
.input(groupUserSchema)
.output(z.void())
.meta({
openapi: {
method: "DELETE",
path: "/api/groups/{groupId}/members/{userId}",
tags: ["groups"],
protect: true,
summary: "Remove member from group",
},
})
.mutation(async ({ input, ctx }) => {
await throwIfGroupNotFoundAsync(ctx.db, input.groupId);
await throwIfGroupNameIsReservedAsync(ctx.db, input.groupId);
+3 -1
View File
@@ -1,5 +1,7 @@
import { eq, inArray, or } from "drizzle-orm";
import { isProviderEnabled } from "@homarr/auth/server";
import { db, eq, inArray, or } from "@homarr/db";
import { db } from "@homarr/db";
import {
apps,
boards,
+2 -1
View File
@@ -1,4 +1,5 @@
import { and, like } from "@homarr/db";
import { and, like } from "drizzle-orm";
import { icons } from "@homarr/db/schema";
import { iconsFindSchema } from "@homarr/validation/icons";
@@ -1,9 +1,10 @@
import { TRPCError } from "@trpc/server";
import type { SQL } from "drizzle-orm";
import { eq, inArray } from "drizzle-orm";
import type { Session } from "@homarr/auth";
import { constructIntegrationPermissions } from "@homarr/auth/shared";
import type { Database, SQL } from "@homarr/db";
import { eq, inArray } from "@homarr/db";
import type { Database } from "@homarr/db";
import { groupMembers, integrationGroupPermissions, integrationUserPermissions } from "@homarr/db/schema";
import type { IntegrationPermission } from "@homarr/definitions";
@@ -1,11 +1,12 @@
import { TRPCError } from "@trpc/server";
import { and, asc, eq, inArray, like, or } from "drizzle-orm";
import { z } from "zod/v4";
import { createId, objectEntries } from "@homarr/common";
import { decryptSecret, encryptSecret } from "@homarr/common/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { handleTransactionsAsync } from "@homarr/db";
import type { Database } from "@homarr/db";
import { and, asc, eq, handleTransactionsAsync, inArray, like, or } from "@homarr/db";
import {
apps,
groupMembers,
+1 -1
View File
@@ -1,9 +1,9 @@
import { randomBytes } from "crypto";
import { TRPCError } from "@trpc/server";
import { asc, eq } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import { asc, eq } from "@homarr/db";
import { invites } from "@homarr/db/schema";
import { selectInviteSchema } from "@homarr/db/validationSchemas";
-366
View File
@@ -1,366 +0,0 @@
import { TRPCError } from "@trpc/server";
import SuperJSON from "superjson";
import z from "zod";
import { createId, objectEntries } from "@homarr/common";
import type { InferInsertModel } from "@homarr/db";
import { and, eq, handleTransactionsAsync } from "@homarr/db";
import { createDbInsertCollectionWithoutTransaction } from "@homarr/db/collection";
import { boards, itemLayouts, items } from "@homarr/db/schema";
import { selectItemLayoutSchema, selectitemSchema } from "@homarr/db/validationSchemas";
import type { WidgetKind } from "@homarr/definitions";
import { widgetKinds } from "@homarr/definitions";
import { byIdSchema } from "@homarr/validation/common";
import { zodEnumFromArray } from "@homarr/validation/enums";
import { itemAdvancedOptionsSchema } from "@homarr/validation/shared";
import { reduceWidgetOptionsWithDefaultValues, widgetImports } from "../../../../widgets/src";
import type { WidgetOptionDefinition } from "../../../../widgets/src/options";
import { convertIntersectionToZodObject } from "../../schema-merger";
import { createTRPCRouter, protectedProcedure } from "../../trpc";
import { throwIfActionForbiddenAsync } from "../board/board-access";
const createItemOptionsSchema = (kind: WidgetKind): z.ZodObject =>
z.object(
objectEntries(
widgetImports[kind].definition.createOptions({
enableStatusByDefault: true,
firstDayOfWeek: 1,
forceDisableStatus: false,
}),
).reduce(
(previous, [key, value]: [string, WidgetOptionDefinition]) => {
previous[key] = value.validate;
return previous;
},
{} as Record<string, z.ZodType>,
),
);
const outputItemSchema = z.union([
...widgetKinds.map((kind) =>
z
.object({
kind: z.literal(kind),
options: createItemOptionsSchema(kind),
advancedOptions: itemAdvancedOptionsSchema,
layouts: z.array(selectItemLayoutSchema.omit({ itemId: true })),
})
.and(
selectitemSchema.pick({
id: true,
boardId: true,
}),
),
),
]);
const manageItemSchema = z.object({
boardId: z.string(),
kind: zodEnumFromArray(widgetKinds),
options: z.record(z.string(), z.unknown().optional()),
advancedOptions: itemAdvancedOptionsSchema,
layouts: z.array(selectItemLayoutSchema.omit({ itemId: true })),
});
export const itemsRouter = createTRPCRouter({
getAll: protectedProcedure
.input(
z.object({
boardId: z.string(),
}),
)
.output(z.array(outputItemSchema))
.meta({
openapi: {
method: "GET",
path: "/api/boards/{boardId}/items",
tags: ["items"],
protect: true,
summary: "Retrieve all items for board",
},
})
.query(async ({ ctx, input }) => {
const board = await ctx.db.query.boards.findFirst({
where: (fields, { eq }) => eq(fields.id, input.boardId),
with: {
items: {
with: {
layouts: true,
},
},
},
});
const error = new TRPCError({
code: "NOT_FOUND",
message: "Board not found",
});
if (!board) throw error;
await throwIfActionForbiddenAsync(ctx, eq(boards.id, input.boardId), "view", error);
return board.items.map((item) => ({
...item,
options: reduceWidgetOptionsWithDefaultValues(
item.kind,
{
enableStatusByDefault: true,
firstDayOfWeek: 1,
forceDisableStatus: false,
},
SuperJSON.parse(item.options),
),
advancedOptions: itemAdvancedOptionsSchema.parse(SuperJSON.parse(item.advancedOptions)),
layouts: item.layouts.map(({ itemId: _, ...layout }) => layout),
}));
}),
createItem: protectedProcedure
.input(manageItemSchema)
.output(outputItemSchema)
.meta({
openapi: {
method: "POST",
path: "/api/items",
tags: ["items"],
protect: true,
summary: "Create item on board",
description: "Options available can be viewed in response options object. Input options are optional",
},
})
.mutation(async ({ ctx, input }) => {
const board = await ctx.db.query.boards.findFirst({
where: (fields, { eq }) => eq(fields.id, input.boardId),
});
const error = new TRPCError({
code: "NOT_FOUND",
message: "Board not found",
});
if (!board) throw error;
await throwIfActionForbiddenAsync(ctx, eq(boards.id, board.id), "modify", error);
const itemOptionsSchema = createItemOptionsSchema(input.kind);
const fullOptions = reduceWidgetOptionsWithDefaultValues(
input.kind,
{ enableStatusByDefault: true, firstDayOfWeek: 1, forceDisableStatus: false },
input.options,
);
const result = await itemOptionsSchema.safeParseAsync(fullOptions);
if (!result.success) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Failed to parse item options",
cause: result.error,
});
}
const item = {
id: createId(),
boardId: input.boardId,
kind: input.kind,
advancedOptions: input.advancedOptions,
options: result.data,
};
const itemInsert: InferInsertModel<typeof items> = {
...item,
advancedOptions: SuperJSON.stringify(item.advancedOptions),
options: SuperJSON.stringify(item.options),
};
const layoutInserts: InferInsertModel<typeof itemLayouts>[] = input.layouts.map((layout) => ({
...layout,
itemId: item.id,
}));
const insertCollection = createDbInsertCollectionWithoutTransaction(["items", "itemLayouts"]);
insertCollection.items.push(itemInsert);
insertCollection.itemLayouts.push(...layoutInserts);
await insertCollection.insertAllAsync(ctx.db);
return {
...item,
layouts: layoutInserts,
};
}),
getItem: protectedProcedure
.input(byIdSchema)
.output(outputItemSchema)
.meta({
openapi: {
method: "GET",
path: "/api/items/{id}",
tags: ["items"],
protect: true,
summary: "Retrieve item from a board",
},
})
.query(async ({ ctx, input }) => {
const item = await ctx.db.query.items.findFirst({
where: (fields, { eq }) => eq(fields.id, input.id),
with: {
layouts: true,
},
});
const error = new TRPCError({
code: "NOT_FOUND",
message: "Item not found",
});
if (!item) throw error;
await throwIfActionForbiddenAsync(ctx, eq(boards.id, item.boardId), "view", error);
return {
...item,
options: reduceWidgetOptionsWithDefaultValues(
item.kind,
{
enableStatusByDefault: true,
firstDayOfWeek: 1,
forceDisableStatus: false,
},
SuperJSON.parse(item.options),
),
advancedOptions: itemAdvancedOptionsSchema.parse(SuperJSON.parse(item.advancedOptions)),
layouts: item.layouts.map(({ itemId: _, ...layout }) => layout),
};
}),
updateItem: protectedProcedure
.input(convertIntersectionToZodObject(manageItemSchema.omit({ boardId: true, kind: true }).and(byIdSchema)))
.output(outputItemSchema)
.meta({
openapi: {
method: "PUT",
path: "/api/items/{id}",
tags: ["items"],
protect: true,
summary: "Update an item on the board",
},
})
.mutation(async ({ ctx, input }) => {
const item = await ctx.db.query.items.findFirst({
where: (fields, { eq }) => eq(fields.id, input.id),
with: {
layouts: true,
},
});
const error = new TRPCError({
code: "NOT_FOUND",
message: "Item not found",
});
if (!item) throw error;
await throwIfActionForbiddenAsync(ctx, eq(boards.id, item.boardId), "modify", error);
const previousOptions = SuperJSON.parse<Record<string, unknown>>(item.options);
const combinedOptions = {
...previousOptions,
...input.options,
};
const itemOptionsSchema = createItemOptionsSchema(item.kind);
const fullOptions = reduceWidgetOptionsWithDefaultValues(
item.kind,
{ enableStatusByDefault: true, firstDayOfWeek: 1, forceDisableStatus: false },
combinedOptions,
);
const optionsResult = await itemOptionsSchema.safeParseAsync(fullOptions);
if (!optionsResult.success) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Failed to parse item options",
cause: optionsResult.error,
});
}
await handleTransactionsAsync(ctx.db, {
async handleAsync(db, schema) {
await db.transaction(async (transaction) => {
await transaction
.update(schema.items)
.set({
advancedOptions: SuperJSON.stringify(input.advancedOptions),
options: SuperJSON.stringify(optionsResult),
})
.where(eq(schema.items.id, input.id));
for (const itemLayout of input.layouts) {
await transaction
.update(schema.itemLayouts)
.set({
height: itemLayout.height,
width: itemLayout.width,
sectionId: itemLayout.sectionId,
xOffset: itemLayout.xOffset,
yOffset: itemLayout.yOffset,
})
.where(
and(eq(schema.itemLayouts.itemId, input.id), eq(schema.itemLayouts.layoutId, itemLayout.layoutId)),
);
}
});
},
handleSync(db) {
db.transaction((transaction) => {
transaction
.update(items)
.set({
advancedOptions: SuperJSON.stringify(input.advancedOptions),
options: SuperJSON.stringify(optionsResult),
})
.where(eq(items.id, input.id));
for (const itemLayout of input.layouts) {
transaction
.update(itemLayouts)
.set({
height: itemLayout.height,
width: itemLayout.width,
sectionId: itemLayout.sectionId,
xOffset: itemLayout.xOffset,
yOffset: itemLayout.yOffset,
})
.where(and(eq(itemLayouts.itemId, input.id), eq(itemLayouts.layoutId, itemLayout.layoutId)));
}
});
},
});
return {
id: input.id,
kind: item.kind,
boardId: item.boardId,
advancedOptions: input.advancedOptions,
options: optionsResult,
layouts: input.layouts,
};
}),
deleteItem: protectedProcedure
.input(byIdSchema)
.output(z.void())
.meta({
openapi: {
method: "DELETE",
path: "/api/items/{id}",
tags: ["items"],
protect: true,
summary: "Delete an item from board",
},
})
.mutation(async ({ ctx, input }) => {
const item = await ctx.db.query.items.findFirst({
where: (fields, { eq }) => eq(fields.id, input.id),
});
const error = new TRPCError({
code: "NOT_FOUND",
message: "Item not found",
});
if (!item) throw error;
await throwIfActionForbiddenAsync(ctx, eq(boards.id, item.boardId), "modify", error);
await ctx.db.delete(items).where(eq(items.id, input.id));
}),
});
@@ -1,9 +1,9 @@
import { TRPCError } from "@trpc/server";
import type { InferInsertModel } from "drizzle-orm";
import { and, desc, eq, like } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import type { InferInsertModel } from "@homarr/db";
import { and, desc, eq, like } from "@homarr/db";
import { iconRepositories, icons, medias } from "@homarr/db/schema";
import { createLocalImageUrl, LOCAL_ICON_REPOSITORY_SLUG, mapMediaToIcon } from "@homarr/icons/local";
import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
@@ -1,8 +1,9 @@
import { eq } from "drizzle-orm";
import { isProviderEnabled } from "@homarr/auth/server";
import { objectEntries } from "@homarr/common";
import type { MaybePromise } from "@homarr/common/types";
import type { Database } from "@homarr/db";
import { eq } from "@homarr/db";
import { groups, onboarding } from "@homarr/db/schema";
import type { OnboardingStep } from "@homarr/definitions";
import { credentialsAdminGroup } from "@homarr/definitions";
@@ -1,9 +1,9 @@
import { TRPCError } from "@trpc/server";
import { asc, eq, like } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { asc, eq, like } from "@homarr/db";
import { getServerSettingByKeyAsync, updateServerSettingByKeyAsync } from "@homarr/db/queries";
import { searchEngines, users } from "@homarr/db/schema";
import { createIntegrationAsync } from "@homarr/integrations";
@@ -1,7 +1,7 @@
import { TRPCError } from "@trpc/server";
import { and, eq } from "drizzle-orm";
import { z } from "zod/v4";
import { and, eq } from "@homarr/db";
import { sectionCollapseStates, sections } from "@homarr/db/schema";
import { createTRPCRouter, protectedProcedure } from "../../trpc";
+3 -2
View File
@@ -1,10 +1,11 @@
import type { InferInsertModel } from "drizzle-orm";
import { and, eq, not } from "drizzle-orm";
import SuperJSON from "superjson";
import { describe, expect, it, test, vi } from "vitest";
import type { Session } from "@homarr/auth";
import { createId } from "@homarr/common";
import type { Database, InferInsertModel } from "@homarr/db";
import { and, eq, not } from "@homarr/db";
import type { Database } from "@homarr/db";
import {
boardGroupPermissions,
boards,
@@ -1,8 +1,8 @@
import { eq } from "drizzle-orm";
import { describe, expect, test, vi } from "vitest";
import * as authShared from "@homarr/auth/shared";
import { createId } from "@homarr/common";
import { eq } from "@homarr/db";
import { boards, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
+1 -1
View File
@@ -1,9 +1,9 @@
import { eq } from "drizzle-orm";
import { describe, expect, test, vi } from "vitest";
import type { Session } from "@homarr/auth";
import * as env from "@homarr/auth/env";
import { createId } from "@homarr/common";
import { eq } from "@homarr/db";
import { groupMembers, groupPermissions, groups, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
import type { GroupPermissionKey } from "@homarr/definitions";
@@ -1,8 +1,8 @@
import { eq } from "drizzle-orm";
import { describe, expect, test, vi } from "vitest";
import * as authShared from "@homarr/auth/shared";
import { createId } from "@homarr/common";
import { eq } from "@homarr/db";
import { integrations, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
+1 -1
View File
@@ -1,9 +1,9 @@
import { eq } from "drizzle-orm";
import { describe, expect, it, test, vi } from "vitest";
import type { Session } from "@homarr/auth";
import { createId } from "@homarr/common";
import type { Database } from "@homarr/db";
import { eq } from "@homarr/db";
import { invites, onboarding, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
import type { GroupPermissionKey, OnboardingStep } from "@homarr/definitions";
+1 -1
View File
@@ -1,11 +1,11 @@
import { TRPCError } from "@trpc/server";
import { and, eq, like } from "drizzle-orm";
import { z } from "zod/v4";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Database } from "@homarr/db";
import { and, eq, like } from "@homarr/db";
import { getMaxGroupPositionAsync } from "@homarr/db/queries";
import { boards, groupMembers, groupPermissions, groups, invites, users } from "@homarr/db/schema";
import { selectUserSchema } from "@homarr/db/validationSchemas";
@@ -1,9 +1,9 @@
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { z } from "zod/v4";
import type { Session } from "@homarr/auth";
import type { Modify } from "@homarr/common/types";
import { eq } from "@homarr/db";
import type { Database } from "@homarr/db";
import { users } from "@homarr/db/schema";
import { userChangeSearchPreferencesSchema } from "@homarr/validation/user";
+1 -1
View File
@@ -1,8 +1,8 @@
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { z } from "zod/v4";
import { eq } from "@homarr/db";
import { boards, items } from "@homarr/db/schema";
import { createTRPCRouter, protectedProcedure } from "../../trpc";
+1 -1
View File
@@ -1,8 +1,8 @@
import type { Adapter } from "@auth/core/adapters";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { and, eq } from "drizzle-orm";
import type { Database } from "@homarr/db";
import { and, eq } from "@homarr/db";
import { accounts, sessions, users } from "@homarr/db/schema";
import type { SupportedAuthProvider } from "@homarr/definitions";
+1 -1
View File
@@ -1,9 +1,9 @@
import dayjs from "dayjs";
import { eq, inArray } from "drizzle-orm";
import type { NextAuthConfig } from "next-auth";
import type { Session } from "@homarr/auth";
import type { Database } from "@homarr/db";
import { eq, inArray } from "@homarr/db";
import { groupMembers, groupPermissions, users } from "@homarr/db/schema";
import { getPermissionsWithChildren } from "@homarr/definitions";
+1 -1
View File
@@ -1,9 +1,9 @@
import { cookies } from "next/headers";
import dayjs from "dayjs";
import { and, eq, inArray } from "drizzle-orm";
import type { NextAuthConfig } from "next-auth";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { and, eq, inArray } from "@homarr/db";
import type { Database } from "@homarr/db";
import { groupMembers, groups, users } from "@homarr/db/schema";
import { colorSchemeCookieKey, everyoneGroup } from "@homarr/definitions";
+3 -2
View File
@@ -32,12 +32,13 @@
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.0.23",
"drizzle-orm": "^0.45.1",
"ldapts": "8.0.24",
"next": "16.0.10",
"next-auth": "5.0.0-beta.30",
"react": "19.2.3",
"react-dom": "19.2.3",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -1,7 +1,7 @@
import { and, eq, inArray, or } from "drizzle-orm";
import type { Session } from "next-auth";
import type { Database } from "@homarr/db";
import { and, eq, inArray, or } from "@homarr/db";
import { boards, boardUserPermissions, groupMembers } from "@homarr/db/schema";
import type { IntegrationPermission } from "@homarr/definitions";
@@ -1,6 +1,7 @@
import { eq, inArray } from "drizzle-orm";
import type { Session } from "next-auth";
import { db, eq, inArray } from "@homarr/db";
import { db } from "@homarr/db";
import { groupMembers, integrationGroupPermissions, integrationUserPermissions } from "@homarr/db/schema";
import { constructIntegrationPermissions } from "./integration-permissions";
@@ -1,8 +1,8 @@
import type { InferInsertModel } from "drizzle-orm";
import type { Session } from "next-auth";
import { describe, expect, test, vi } from "vitest";
import { createId } from "@homarr/common";
import type { InferInsertModel } from "@homarr/db";
import { boardGroupPermissions, boards, boardUserPermissions, groupMembers, groups, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
@@ -1,9 +1,9 @@
import bcrypt from "bcrypt";
import { and, eq } from "drizzle-orm";
import type { z } from "zod/v4";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Database } from "@homarr/db";
import { and, eq } from "@homarr/db";
import { users } from "@homarr/db/schema";
import type { userSignInSchema } from "@homarr/validation/user";
@@ -1,10 +1,11 @@
import { CredentialsSignin } from "@auth/core/errors";
import { and, eq } from "drizzle-orm";
import type { InferInsertModel } from "drizzle-orm";
import { z } from "zod/v4";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Database, InferInsertModel } from "@homarr/db";
import { and, eq } from "@homarr/db";
import type { Database } from "@homarr/db";
import { users } from "@homarr/db/schema";
import type { ldapSignInSchema } from "@homarr/validation/user";
@@ -1,9 +1,9 @@
import { CredentialsSignin } from "@auth/core/errors";
import { and, eq } from "drizzle-orm";
import { describe, expect, test, vi } from "vitest";
import { createId } from "@homarr/common";
import type { Database } from "@homarr/db";
import { and, eq } from "@homarr/db";
import { groups, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
+1 -1
View File
@@ -1,9 +1,9 @@
import type { ResponseCookie } from "next/dist/compiled/@edge-runtime/cookies";
import type { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
import { cookies } from "next/headers";
import { eq } from "drizzle-orm";
import { describe, expect, test, vi } from "vitest";
import { eq } from "@homarr/db";
import type { Database } from "@homarr/db";
import { groupMembers, groups, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
+2 -1
View File
@@ -28,7 +28,8 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"dotenv": "^17.2.3"
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -1,6 +1,7 @@
import { command } from "@drizzle-team/brocli";
import { eq } from "drizzle-orm";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { users } from "@homarr/db/schema";
export const fixUsernames = command({
+2 -1
View File
@@ -1,9 +1,10 @@
import { command, string } from "@drizzle-team/brocli";
import { and, count, eq } from "drizzle-orm";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { generateSecureRandomToken } from "@homarr/common/server";
import { and, count, db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { getMaxGroupPositionAsync } from "@homarr/db/queries";
import { groupMembers, groupPermissions, groups, users } from "@homarr/db/schema";
import { usernameSchema } from "@homarr/validation/user";
+2 -1
View File
@@ -1,8 +1,9 @@
import { command, string } from "@drizzle-team/brocli";
import { and, eq } from "drizzle-orm";
import { hashPasswordAsync } from "@homarr/auth";
import { generateSecureRandomToken } from "@homarr/common/server";
import { and, db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { sessions, users } from "@homarr/db/schema";
export const resetPassword = command({
+1 -1
View File
@@ -34,7 +34,7 @@
"react": "19.2.3",
"react-dom": "19.2.3",
"undici": "7.16.0",
"zod": "^4.1.13",
"zod": "^4.2.0",
"zod-validation-error": "^5.0.0"
},
"devDependencies": {
+2 -2
View File
@@ -37,7 +37,7 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@t3-oss/env-nextjs": "^0.13.8",
"@t3-oss/env-nextjs": "^0.13.10",
"better-sqlite3": "^12.5.0",
"dns-caching": "^0.2.9",
"drizzle-orm": "^0.45.1",
@@ -46,7 +46,7 @@
"pg": "^8.16.3",
"superjson": "2.2.6",
"winston": "3.19.0",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -2,7 +2,7 @@ import { createBooleanSchema, createEnv } from "../env";
export const dnsEnv = createEnv({
server: {
ENABLE_DNS_CACHING: createBooleanSchema(false),
ENABLE_DNS_CACHING: createBooleanSchema(true),
},
experimental__runtimeEnv: process.env,
});
+1 -1
View File
@@ -34,7 +34,7 @@
"@trpc/tanstack-react-query": "^11.8.0",
"node-cron": "^4.2.1",
"react": "19.2.3",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1
View File
@@ -38,6 +38,7 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"drizzle-orm": "^0.45.1",
"semver-parser": "^4.1.8"
},
"devDependencies": {
+2 -1
View File
@@ -1,9 +1,10 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { items } from "@homarr/db/schema";
import { dockerContainersRequestHandler } from "@homarr/request-handler/docker";
+4 -2
View File
@@ -1,9 +1,11 @@
import type { InferInsertModel } from "drizzle-orm";
import { inArray, sql } from "drizzle-orm";
import { createId, splitToNChunks, Stopwatch } from "@homarr/common";
import { env } from "@homarr/common/env";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions";
import type { InferInsertModel } from "@homarr/db";
import { db, handleTransactionsAsync, inArray, sql } from "@homarr/db";
import { db, handleTransactionsAsync } from "@homarr/db";
import { iconRepositories, icons } from "@homarr/db/schema";
import { fetchIconsAsync } from "@homarr/icons";
@@ -1,7 +1,8 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { EVERY_5_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { items } from "@homarr/db/schema";
import { minecraftServerStatusRequestHandler } from "@homarr/request-handler/minecraft-server-status";
+2 -1
View File
@@ -1,9 +1,10 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { EVERY_10_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { items } from "@homarr/db/schema";
// This import is done that way to avoid circular dependencies.
import { rssFeedsRequestHandler } from "@homarr/request-handler/rss-feeds";
+2 -1
View File
@@ -1,9 +1,10 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { EVERY_10_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { db } from "@homarr/db";
import { items } from "@homarr/db/schema";
import { weatherRequestHandler } from "@homarr/request-handler/weather";
-1
View File
@@ -2,7 +2,6 @@ import { createDb } from "@homarr/core/infrastructure/db";
import { schema } from "./schema";
export * from "drizzle-orm";
export type { HomarrDatabaseMysql, HomarrDatabasePostgresql } from "./driver";
export const db = createDb(schema);
@@ -1,10 +1,10 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { createId } from "@homarr/common";
import type { IntegrationKind } from "@homarr/definitions";
import { getIntegrationKindsByCategory } from "@homarr/definitions";
import { eq } from "../..";
import type { Database } from "../..";
import { items } from "../../schema";
@@ -1,5 +1,6 @@
import { and, eq } from "drizzle-orm";
import type { Database } from "../..";
import { and, eq } from "../..";
import { integrationSecrets } from "../../schema";
/**
@@ -1,6 +1,6 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import { eq } from "../..";
import type { Database } from "../..";
import { items } from "../../schema";
+2 -1
View File
@@ -1,3 +1,5 @@
import { eq } from "drizzle-orm";
import { createId, objectKeys } from "@homarr/common";
import {
createDocumentationLink,
@@ -9,7 +11,6 @@ import {
import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
import type { Database } from "..";
import { eq } from "..";
import {
getServerSettingByKeyAsync,
insertServerSettingByKeyAsync,
+2 -1
View File
@@ -1,7 +1,8 @@
import { inArray } from "drizzle-orm";
import type { WidgetKind } from "@homarr/definitions";
import type { Database } from "..";
import { inArray } from "..";
import type { inferSupportedIntegrations } from "../../widgets/src";
import { items } from "../schema";
+1 -1
View File
@@ -1,10 +1,10 @@
import { eq } from "drizzle-orm";
import SuperJSON from "superjson";
import type { ServerSettings } from "@homarr/server-settings";
import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
import type { Database } from "..";
import { eq } from "..";
import { serverSettings } from "../schema";
export const getServerSettingsAsync = async (db: Database) => {
+2 -5
View File
@@ -1,12 +1,9 @@
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { createSelectSchema } from "drizzle-zod";
import { apps, boards, groups, invites, itemLayouts, items, searchEngines, serverSettings, users } from "./schema";
import { apps, boards, groups, invites, searchEngines, serverSettings, users } from "./schema";
export const selectAppSchema = createSelectSchema(apps);
export const selectBoardSchema = createSelectSchema(boards);
export const selectitemSchema = createSelectSchema(items);
export const insertItemSchema = createInsertSchema(items);
export const selectItemLayoutSchema = createSelectSchema(itemLayouts);
export const selectGroupSchema = createSelectSchema(groups);
export const selectInviteSchema = createSelectSchema(invites);
export const selectSearchEnginesSchema = createSelectSchema(searchEngines);
+1 -1
View File
@@ -25,7 +25,7 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"fast-xml-parser": "^5.3.3",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -28,7 +28,7 @@
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.3.10",
"mantine-form-zod-resolver": "^1.3.0",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"react": "19.2.3",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -25,7 +25,8 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0"
"@homarr/db": "workspace:^0.1.0",
"drizzle-orm": "^0.45.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -1,5 +1,6 @@
import { like } from "drizzle-orm";
import type { Database } from "@homarr/db";
import { like } from "@homarr/db";
import { icons } from "@homarr/db/schema";
export const getIconForName = (db: Database, name: string) => {
@@ -1,6 +1,6 @@
import { createHash } from "crypto";
import type { InferSelectModel } from "drizzle-orm";
import type { InferSelectModel } from "@homarr/db";
import { db } from "@homarr/db";
import type { medias } from "@homarr/db/schema";
+1 -1
View File
@@ -48,7 +48,7 @@
"tsdav": "^2.1.6",
"undici": "7.16.0",
"xml2js": "^0.6.2",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -39,7 +39,7 @@
"next": "16.0.10",
"react": "19.2.3",
"react-dom": "19.2.3",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -40,11 +40,12 @@
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"adm-zip": "0.5.16",
"drizzle-orm": "^0.45.1",
"next": "16.0.10",
"react": "19.2.3",
"react-dom": "19.2.3",
"superjson": "2.2.6",
"zod": "^4.1.13",
"zod": "^4.2.0",
"zod-form-data": "^3.0.1"
},
"devDependencies": {
@@ -1,4 +1,6 @@
import { handleTransactionsAsync, inArray } from "@homarr/db";
import { inArray } from "drizzle-orm";
import { handleTransactionsAsync } from "@homarr/db";
import type { Database } from "@homarr/db";
import { apps } from "@homarr/db/schema";
import type { OldmarrConfig } from "@homarr/old-schema";
+2 -1
View File
@@ -1,4 +1,5 @@
import type { InferSelectModel } from "@homarr/db";
import type { InferSelectModel } from "drizzle-orm";
import type { apps } from "@homarr/db/schema";
import type { OldmarrApp } from "@homarr/old-schema";
+2 -1
View File
@@ -1,5 +1,6 @@
import type { InferInsertModel } from "drizzle-orm";
import { createId } from "@homarr/common";
import type { InferInsertModel } from "@homarr/db";
import type { boards } from "@homarr/db/schema";
import type { prepareMultipleImports } from "../prepare/prepare-multiple";
+1 -1
View File
@@ -1,8 +1,8 @@
import type { InferInsertModel } from "drizzle-orm";
import SuperJSON from "superjson";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { InferInsertModel } from "@homarr/db";
import type { itemLayouts, items } from "@homarr/db/schema";
import type { BoardSize, OldmarrApp, OldmarrWidget } from "@homarr/old-schema";
import { boardSizes } from "@homarr/old-schema";
@@ -1,5 +1,6 @@
import type { InferInsertModel } from "drizzle-orm";
import { createId } from "@homarr/common";
import type { InferInsertModel } from "@homarr/db";
import type { sections } from "@homarr/db/schema";
import type { OldmarrCategorySection, OldmarrEmptySection } from "@homarr/old-schema";
+2 -1
View File
@@ -1,6 +1,7 @@
import type { InferInsertModel } from "drizzle-orm";
import { createId } from "@homarr/common";
import { decryptSecretWithKey } from "@homarr/common/server";
import type { InferInsertModel } from "@homarr/db";
import type { users } from "@homarr/db/schema";
import type { OldmarrImportUser } from "../user-schema";
@@ -1,4 +1,5 @@
import type { InferSelectModel } from "@homarr/db";
import type { InferSelectModel } from "drizzle-orm";
import type { apps } from "@homarr/db/schema";
import type { OldmarrConfig } from "@homarr/old-schema";
+1 -1
View File
@@ -23,7 +23,7 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -31,10 +31,11 @@
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"dayjs": "^1.11.19",
"drizzle-orm": "^0.45.1",
"octokit": "^5.0.5",
"superjson": "2.2.6",
"undici": "7.16.0",
"zod": "^4.1.13"
"zod": "^4.2.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -1,7 +1,8 @@
import dayjs from "dayjs";
import type { ContainerInfo, ContainerStats } from "dockerode";
import { like, or } from "drizzle-orm";
import { db, like, or } from "@homarr/db";
import { db } from "@homarr/db";
import { icons } from "@homarr/db/schema";
import type { ContainerState } from "@homarr/docker";
import { dockerLabels, DockerSingleton } from "@homarr/docker";
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff. Show more