diff --git a/.env.example b/.env.example index dc82ea23a..b05d685b1 100644 --- a/.env.example +++ b/.env.example @@ -20,8 +20,6 @@ DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE' # DB_PASSWORD='password' # DB_NAME='name-of-database' -# @see https://next-auth.js.org/configuration/options#nextauth_url -AUTH_URL='http://localhost:3000' # You can generate the secret via 'openssl rand -base64 32' on Unix # @see https://next-auth.js.org/configuration/options#secret diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 5f410d270..c251b874f 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -37,17 +37,17 @@ "@homarr/ui": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", "@homarr/widgets": "workspace:^0.1.0", - "@mantine/colors-generator": "^7.13.2", - "@mantine/core": "^7.13.2", - "@mantine/hooks": "^7.13.2", - "@mantine/modals": "^7.13.2", - "@mantine/tiptap": "^7.13.2", - "@million/lint": "1.0.9", + "@mantine/colors-generator": "^7.13.3", + "@mantine/core": "^7.13.3", + "@mantine/hooks": "^7.13.3", + "@mantine/modals": "^7.13.3", + "@mantine/tiptap": "^7.13.3", + "@million/lint": "1.0.11", "@t3-oss/env-nextjs": "^0.11.1", "@tabler/icons-react": "^3.19.0", - "@tanstack/react-query": "^5.59.9", - "@tanstack/react-query-devtools": "^5.59.9", - "@tanstack/react-query-next-experimental": "5.59.9", + "@tanstack/react-query": "^5.59.15", + "@tanstack/react-query-devtools": "^5.59.15", + "@tanstack/react-query-next-experimental": "5.59.15", "@trpc/client": "next", "@trpc/next": "next", "@trpc/react-query": "next", @@ -55,22 +55,22 @@ "@xterm/addon-canvas": "^0.7.0", "@xterm/addon-fit": "0.10.0", "@xterm/xterm": "^5.5.0", - "chroma-js": "^3.1.1", + "chroma-js": "^3.1.2", "clsx": "^2.1.1", "dayjs": "^1.11.13", "dotenv": "^16.4.5", "flag-icons": "^7.2.3", "glob": "^11.0.0", - "jotai": "^2.10.0", - "mantine-react-table": "2.0.0-beta.6", + "jotai": "^2.10.1", + "mantine-react-table": "2.0.0-beta.7", "next": "^14.2.15", "postcss-preset-mantine": "^1.17.0", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-error-boundary": "^4.0.13", + "react-error-boundary": "^4.1.1", "react-simple-code-editor": "^0.14.1", - "sass": "^1.79.5", + "sass": "^1.80.2", "superjson": "2.2.1", "swagger-ui-react": "^5.17.14", "use-deep-compare-effect": "^1.8.1" @@ -80,7 +80,7 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", "@types/chroma-js": "2.4.4", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@types/prismjs": "^1.26.4", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", diff --git a/apps/nextjs/public/images/pwa/192.maskable.png b/apps/nextjs/public/images/pwa/192.maskable.png new file mode 100644 index 000000000..0783e1c6e Binary files /dev/null and b/apps/nextjs/public/images/pwa/192.maskable.png differ diff --git a/apps/nextjs/public/images/pwa/512.maskable.png b/apps/nextjs/public/images/pwa/512.maskable.png new file mode 100644 index 000000000..d6366700a Binary files /dev/null and b/apps/nextjs/public/images/pwa/512.maskable.png differ diff --git a/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx index e5e867815..b5b4a5b34 100644 --- a/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx +++ b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx @@ -16,7 +16,7 @@ export const CustomMantineProvider = ({ children }: PropsWithChildren) => { return ( { + if (value === "auto") return; try { if (session) { mutateColorScheme({ colorScheme: value }); diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx index 346baff75..808759af1 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx @@ -49,6 +49,7 @@ export const BoardProvider = ({ useEffect(() => { setReadySections((previous) => previous.filter((id) => data.sections.some((section) => section.id === id))); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [data.sections.length, setReadySections]); const markAsReady = useCallback((id: string) => { diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx index 6a6be9b42..4827a3b1d 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx @@ -28,7 +28,6 @@ export const createBoardContentPage = >( layout: createBoardLayout({ headerActions: , getInitialBoardAsync: getInitialBoard, - isBoardContentPage: true, }), // eslint-disable-next-line no-restricted-syntax page: async () => { @@ -50,6 +49,10 @@ export const createBoardContentPage = >( title: board.metaTitle ?? createMetaTitle(t("board.content.metaTitle", { boardName: board.name })), icons: { icon: board.faviconImageUrl ? board.faviconImageUrl : undefined, + apple: board.faviconImageUrl ? board.faviconImageUrl : undefined, + }, + appleWebApp: { + startupImage: { url: board.faviconImageUrl ? board.faviconImageUrl : "/logo/logo.png" }, }, }; } catch (error) { diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/layout.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/layout.tsx index 42107bf2d..1f129e4c1 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/layout.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/layout.tsx @@ -8,5 +8,4 @@ export default createBoardLayout<{ locale: string; name: string }>({ async getInitialBoardAsync({ name }) { return await api.board.getBoardByName({ name }); }, - isBoardContentPage: false, }); diff --git a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx index 56dabdd88..4e8edd149 100644 --- a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx +++ b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx @@ -4,7 +4,6 @@ import { AppShellMain } from "@mantine/core"; import { TRPCError } from "@trpc/server"; import { logger } from "@homarr/log"; -import { GlobalItemServerDataRunner } from "@homarr/widgets"; import { MainHeader } from "~/components/layout/header"; import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo"; @@ -18,13 +17,11 @@ import { BoardMantineProvider } from "./(content)/_theme"; interface CreateBoardLayoutProps { headerActions: JSX.Element; getInitialBoardAsync: (params: TParams) => Promise; - isBoardContentPage: boolean; } export const createBoardLayout = ({ headerActions, getInitialBoardAsync: getInitialBoard, - isBoardContentPage, }: CreateBoardLayoutProps) => { const Layout = async ({ params, @@ -42,21 +39,19 @@ export const createBoardLayout = ({ }); return ( - - - - - - } - actions={headerActions} - hasNavigation={false} - /> - {children} - - - - + + + + + } + actions={headerActions} + hasNavigation={false} + /> + {children} + + + ); }; diff --git a/apps/nextjs/src/app/[locale]/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx index 6adc5aaef..22888ea0b 100644 --- a/apps/nextjs/src/app/[locale]/layout.tsx +++ b/apps/nextjs/src/app/[locale]/layout.tsx @@ -28,8 +28,7 @@ const fontSans = Inter({ variable: "--font-sans", }); -export const metadata: Metadata = { - metadataBase: new URL("http://localhost:3000"), +export const generateMetadata = (): Metadata => ({ title: "Homarr", description: "Simplify the management of your server with Homarr - a sleek, modern dashboard that puts all of your apps and services at your fingertips.", @@ -40,12 +39,17 @@ export const metadata: Metadata = { url: "https://homarr.dev", siteName: "Homarr Documentation", }, - twitter: { - card: "summary_large_image", - site: "@jullerino", - creator: "@jullerino", + icons: { + icon: "/logo/logo.png", + apple: "/logo/logo.png", }, -}; + appleWebApp: { + title: "Homarr", + capable: true, + startupImage: { url: "/logo/logo.png" }, + statusBarStyle: getColorScheme() === "dark" ? "black-translucent" : "default", + }, +}); export const viewport: Viewport = { themeColor: [ @@ -56,7 +60,7 @@ export const viewport: Viewport = { export default async function Layout(props: { children: React.ReactNode; params: { locale: string } }) { const session = await auth(); - const colorScheme = cookies().get("homarr-color-scheme")?.value ?? "light"; + const colorScheme = getColorScheme(); const tCommon = await getScopedI18n("common"); const direction = tCommon("direction"); @@ -73,7 +77,15 @@ export default async function Layout(props: { children: React.ReactNode; params: return ( // Instead of ColorSchemScript we use data-mantine-color-scheme to prevent flickering - + @@ -87,3 +99,7 @@ export default async function Layout(props: { children: React.ReactNode; params: ); } + +const getColorScheme = () => { + return cookies().get("homarr-color-scheme")?.value ?? "dark"; +}; diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx index 25eacf9b9..791be2938 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx @@ -9,7 +9,7 @@ import { IconInfoCircle } from "@tabler/icons-react"; import { clientApi } from "@homarr/api/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions"; -import { getAllSecretKindOptions } from "@homarr/definitions"; +import { getAllSecretKindOptions, getIntegrationName } from "@homarr/definitions"; import type { UseFormReturnType } from "@homarr/form"; import { useZodForm } from "@homarr/form"; import { convertIntegrationTestConnectionError } from "@homarr/integrations/client"; @@ -32,7 +32,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) => const router = useRouter(); const form = useZodForm(validation.integration.create.omit({ kind: true }), { initialValues: { - name: searchParams.name ?? "", + name: searchParams.name ?? getIntegrationName(searchParams.kind), url: searchParams.url ?? "", secrets: secretKinds[0].map((kind) => ({ kind, @@ -81,7 +81,7 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) => return (
void handleSubmitAsync(value))}> - + diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx index ad03dc244..fafc49fc8 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx @@ -161,21 +161,20 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps - {hasFullAccess || - (integration.permissions.hasFullAccess && ( - - - - - - - ))} + {(hasFullAccess || integration.permissions.hasFullAccess) && ( + + + + + + + )} diff --git a/apps/nextjs/src/app/[locale]/manage/tools/api/components/api-keys.tsx b/apps/nextjs/src/app/[locale]/manage/tools/api/components/api-keys.tsx index 86503427c..71a0efb1b 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/api/components/api-keys.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/api/components/api-keys.tsx @@ -47,7 +47,7 @@ export const ApiKeysManagement = ({ apiKeys }: ApiKeysManagementProps) => { ), }, ], - [], + [t], ); const table = useMantineReactTable({ diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_ping-icons-enabled.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_ping-icons-enabled.tsx new file mode 100644 index 000000000..1631189dc --- /dev/null +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_ping-icons-enabled.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { Button, Group, Stack, Switch } from "@mantine/core"; + +import type { RouterOutputs } from "@homarr/api"; +import { clientApi } from "@homarr/api/client"; +import { revalidatePathActionAsync } from "@homarr/common/client"; +import { useZodForm } from "@homarr/form"; +import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; +import { useI18n } from "@homarr/translation/client"; +import type { z } from "@homarr/validation"; +import { validation } from "@homarr/validation"; + +interface PingIconsEnabledProps { + user: RouterOutputs["user"]["getById"]; +} + +export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => { + const t = useI18n(); + const { mutate, isPending } = clientApi.user.changePingIconsEnabled.useMutation({ + async onSettled() { + await revalidatePathActionAsync(`/manage/users/${user.id}`); + }, + onSuccess(_, variables) { + form.setInitialValues({ + pingIconsEnabled: variables.pingIconsEnabled, + }); + showSuccessNotification({ + message: t("user.action.changePingIconsEnabled.notification.success.message"), + }); + }, + onError() { + showErrorNotification({ + message: t("user.action.changePingIconsEnabled.notification.error.message"), + }); + }, + }); + const form = useZodForm(validation.user.pingIconsEnabled, { + initialValues: { + pingIconsEnabled: user.pingIconsEnabled, + }, + }); + + const handleSubmit = (values: FormType) => { + mutate({ + id: user.id, + ...values, + }); + }; + + return ( + + + + + + + + + + ); +}; + +type FormType = z.infer; diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-form.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-form.tsx index 0099dcbd8..b2d5ab87e 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-form.tsx @@ -61,7 +61,7 @@ export const UserProfileForm = ({ user }: UserProfileFormProps) => { id: user.id, }); }, - [user.id, mutate], + [isProviderCredentials, mutate, user.id], ); return ( diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx index e29a140f3..fa26d7bd1 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx @@ -14,6 +14,7 @@ import { canAccessUserEditPage } from "../access"; import { ChangeHomeBoardForm } from "./_components/_change-home-board"; import { DeleteUserButton } from "./_components/_delete-user-button"; import { FirstDayOfWeek } from "./_components/_first-day-of-week"; +import { PingIconsEnabled } from "./_components/_ping-icons-enabled"; import { UserProfileAvatarForm } from "./_components/_profile-avatar-form"; import { UserProfileForm } from "./_components/_profile-form"; @@ -99,6 +100,11 @@ export default async function EditUserPage({ params }: Props) {
+ + {tGeneral("item.accessibility")} + + + {isCredentialsUser && ( { const board = useRequiredBoard(); const [isEditMode] = useEditMode(); - const serverData = useServerDataFor(item.id); const Comp = loadWidgetDynamic(item.kind); const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options); const newItem = { ...item, options }; @@ -61,8 +60,6 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => { const updateOptions = ({ newOptions }: { newOptions: Record }) => updateItemOptions({ itemId: item.id, newOptions }); - if (!serverData?.isReady) return null; - return ( {({ reset }) => ( @@ -79,8 +76,6 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => { (({ actions, innerProps }) = const t = useI18n(); // Keep track of the maximum width based on the x offset const maxWidthRef = useRef(innerProps.columnCount - innerProps.item.xOffset); - const { moveAndResizeItem } = useItemActions(); const form = useZodForm( z.object({ xOffset: z @@ -62,7 +60,7 @@ export const ItemMoveModal = createModal(({ actions, innerProps }) = }); actions.closeModal(); }, - [moveAndResizeItem], + [actions, innerProps.gridStack, innerProps.item.id], ); return ( diff --git a/apps/nextjs/src/components/board/sections/gridstack/gridstack-item.tsx b/apps/nextjs/src/components/board/sections/gridstack/gridstack-item.tsx index 554a376fa..beff12f66 100644 --- a/apps/nextjs/src/components/board/sections/gridstack/gridstack-item.tsx +++ b/apps/nextjs/src/components/board/sections/gridstack/gridstack-item.tsx @@ -39,7 +39,7 @@ export const GridStackItem = ({ if (type !== "section") return; innerRef.current.gridstackNode.minW = minWidth; innerRef.current.gridstackNode.minH = minHeight; - }, [minWidth, minHeight, innerRef]); + }, [minWidth, minHeight, innerRef, type]); return ( , itemIds: string[]) } // Only run this effect when the section items change + // eslint-disable-next-line react-hooks/exhaustive-deps }, [itemIds.length, columnCount]); /** diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index 9a03b4200..1dafdce25 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -58,7 +58,7 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { router.refresh(); }, }); - }, [openModal, router]); + }, [logoutUrl, openModal, router]); return ( diff --git a/apps/tasks/package.json b/apps/tasks/package.json index ac21b2feb..708987d2c 100644 --- a/apps/tasks/package.json +++ b/apps/tasks/package.json @@ -38,13 +38,13 @@ "dayjs": "^1.11.13", "dotenv": "^16.4.5", "superjson": "2.2.1", - "undici": "6.20.0" + "undici": "6.20.1" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "dotenv-cli": "^7.4.2", "eslint": "^9.12.0", "prettier": "^3.3.3", diff --git a/package.json b/package.json index 0827b1d81..35e5bc009 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@turbo/gen": "^2.1.3", "@vitejs/plugin-react": "^4.3.2", - "@vitest/coverage-v8": "^2.1.2", - "@vitest/ui": "^2.1.2", + "@vitest/coverage-v8": "^2.1.3", + "@vitest/ui": "^2.1.3", "cross-env": "^7.0.3", "jsdom": "^25.0.1", "prettier": "^3.3.3", @@ -38,9 +38,9 @@ "turbo": "^2.1.3", "typescript": "^5.6.3", "vite-tsconfig-paths": "^5.0.1", - "vitest": "^2.1.2" + "vitest": "^2.1.3" }, - "packageManager": "pnpm@9.12.1", + "packageManager": "pnpm@9.12.2", "engines": { "node": ">=20.18.0" }, diff --git a/packages/api/src/router/test/user.spec.ts b/packages/api/src/router/test/user.spec.ts index f19502bd5..1d51afe9f 100644 --- a/packages/api/src/router/test/user.spec.ts +++ b/packages/api/src/router/test/user.spec.ts @@ -248,18 +248,11 @@ describe("editProfile shoud update user", () => { const user = await db.select().from(schema.users).where(eq(schema.users.id, defaultOwnerId)); expect(user).toHaveLength(1); - expect(user[0]).toStrictEqual({ + expect(user[0]).containSubset({ id: defaultOwnerId, name: "ABC", email: "abc@gmail.com", emailVerified, - salt: null, - password: null, - image: null, - homeBoardId: null, - provider: "credentials", - colorScheme: "auto", - firstDayOfWeek: 1, }); }); @@ -289,18 +282,11 @@ describe("editProfile shoud update user", () => { const user = await db.select().from(schema.users).where(eq(schema.users.id, defaultOwnerId)); expect(user).toHaveLength(1); - expect(user[0]).toStrictEqual({ + expect(user[0]).containSubset({ id: defaultOwnerId, name: "ABC", email: "myNewEmail@gmail.com", emailVerified: null, - salt: null, - password: null, - image: null, - homeBoardId: null, - provider: "credentials", - colorScheme: "auto", - firstDayOfWeek: 1, }); }); }); @@ -317,40 +303,14 @@ describe("delete should delete user", () => { { id: createId(), name: "User 1", - email: null, - emailVerified: null, - image: null, - password: null, - salt: null, - homeBoardId: null, - provider: "ldap" as const, - colorScheme: "auto" as const, - firstDayOfWeek: 1 as const, }, { id: defaultOwnerId, name: "User 2", - email: null, - emailVerified: null, - image: null, - password: null, - salt: null, - homeBoardId: null, - colorScheme: "auto" as const, - firstDayOfWeek: 1 as const, }, { id: createId(), name: "User 3", - email: null, - emailVerified: null, - image: null, - password: null, - salt: null, - homeBoardId: null, - provider: "oidc" as const, - colorScheme: "auto" as const, - firstDayOfWeek: 1 as const, }, ]; @@ -359,6 +319,8 @@ describe("delete should delete user", () => { await caller.delete(defaultOwnerId); const usersInDb = await db.select().from(schema.users); - expect(usersInDb).toStrictEqual([initialUsers[0], initialUsers[2]]); + expect(usersInDb).toHaveLength(2); + expect(usersInDb[0]).containSubset(initialUsers[0]); + expect(usersInDb[1]).containSubset(initialUsers[2]); }); }); diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index d1a9719f3..805140540 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -209,6 +209,7 @@ export const userRouter = createTRPCRouter({ provider: true, homeBoardId: true, firstDayOfWeek: true, + pingIconsEnabled: true, }, where: eq(users.id, input.userId), }); @@ -376,6 +377,39 @@ export const userRouter = createTRPCRouter({ }) .where(eq(users.id, ctx.session.user.id)); }), + getPingIconsEnabledOrDefault: publicProcedure.query(async ({ ctx }) => { + if (!ctx.session?.user) { + return false; + } + + const user = await ctx.db.query.users.findFirst({ + columns: { + id: true, + pingIconsEnabled: true, + }, + where: eq(users.id, ctx.session.user.id), + }); + + return user?.pingIconsEnabled ?? false; + }), + changePingIconsEnabled: protectedProcedure + .input(validation.user.pingIconsEnabled.and(validation.common.byId)) + .mutation(async ({ input, ctx }) => { + // Only admins can change other users ping icons enabled + if (!ctx.session.user.permissions.includes("admin") && ctx.session.user.id !== input.id) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } + + await ctx.db + .update(users) + .set({ + pingIconsEnabled: input.pingIconsEnabled, + }) + .where(eq(users.id, ctx.session.user.id)); + }), getFirstDayOfWeekForUserOrDefault: publicProcedure.query(async ({ ctx }) => { if (!ctx.session?.user) { return 1 as const; @@ -394,7 +428,7 @@ export const userRouter = createTRPCRouter({ changeFirstDayOfWeek: protectedProcedure .input(validation.user.firstDayOfWeek.and(validation.common.byId)) .mutation(async ({ input, ctx }) => { - // Only admins can change other users' passwords + // Only admins can change other users first day of week if (!ctx.session.user.permissions.includes("admin") && ctx.session.user.id !== input.id) { throw new TRPCError({ code: "NOT_FOUND", diff --git a/packages/api/src/router/widgets/calendar.ts b/packages/api/src/router/widgets/calendar.ts index 606ec04b7..7713db4f7 100644 --- a/packages/api/src/router/widgets/calendar.ts +++ b/packages/api/src/router/widgets/calendar.ts @@ -9,11 +9,12 @@ export const calendarRouter = createTRPCRouter({ findAllEvents: publicProcedure .unstable_concat(createManyIntegrationOfOneItemMiddleware("query", ...getIntegrationKindsByCategory("calendar"))) .query(async ({ ctx }) => { - return await Promise.all( + const result = await Promise.all( ctx.integrations.flatMap(async (integration) => { const cache = createItemAndIntegrationChannel("calendar", integration.id); return await cache.getAsync(); }), ); + return result.filter((item) => item !== null).flatMap((item) => item.data); }), }); diff --git a/packages/api/src/router/widgets/health-monitoring.ts b/packages/api/src/router/widgets/health-monitoring.ts index bd3d3bf27..2003a5bf6 100644 --- a/packages/api/src/router/widgets/health-monitoring.ts +++ b/packages/api/src/router/widgets/health-monitoring.ts @@ -13,15 +13,13 @@ export const healthMonitoringRouter = createTRPCRouter({ return await Promise.all( ctx.integrations.map(async (integration) => { const channel = createItemAndIntegrationChannel("healthMonitoring", integration.id); - const data = await channel.getAsync(); - if (!data) { - return null; - } + const { data: healthInfo, timestamp } = (await channel.getAsync()) ?? { data: null, timestamp: new Date(0) }; return { integrationId: integration.id, integrationName: integration.name, - healthInfo: data.data, + healthInfo, + timestamp, }; }), ); @@ -30,7 +28,7 @@ export const healthMonitoringRouter = createTRPCRouter({ subscribeHealthStatus: publicProcedure .unstable_concat(createManyIntegrationMiddleware("query", "openmediavault")) .subscription(({ ctx }) => { - return observable<{ integrationId: string; healthInfo: HealthMonitoring }>((emit) => { + return observable<{ integrationId: string; healthInfo: HealthMonitoring; timestamp: Date }>((emit) => { const unsubscribes: (() => void)[] = []; for (const integration of ctx.integrations) { const channel = createItemAndIntegrationChannel("healthMonitoring", integration.id); @@ -38,6 +36,7 @@ export const healthMonitoringRouter = createTRPCRouter({ emit.next({ integrationId: integration.id, healthInfo, + timestamp: new Date(0), }); }); unsubscribes.push(unsubscribe); diff --git a/packages/auth/callbacks.ts b/packages/auth/callbacks.ts index 6a381f57a..d5fac0b7a 100644 --- a/packages/auth/callbacks.ts +++ b/packages/auth/callbacks.ts @@ -36,7 +36,7 @@ export const createSessionAsync = async ( ...user, email: user.email ?? "", permissions: await getCurrentUserPermissionsAsync(db, user.id), - colorScheme: "auto", + colorScheme: "dark", }, } as Session; }; diff --git a/packages/auth/configuration.ts b/packages/auth/configuration.ts index 3b855c2f9..65d67d4b4 100644 --- a/packages/auth/configuration.ts +++ b/packages/auth/configuration.ts @@ -34,6 +34,17 @@ export const createConfiguration = (provider: SupportedAuthProvider | "unknown", }, }, trustHost: true, + cookies: { + sessionToken: { + name: sessionTokenCookieName, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: true, + }, + }, + }, adapter, providers: filterProviders([ Credentials(createCredentialsConfiguration(db)), diff --git a/packages/auth/package.json b/packages/auth/package.json index 56c14c80f..a336fe327 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -23,8 +23,8 @@ }, "prettier": "@homarr/prettier-config", "dependencies": { - "@auth/core": "^0.37.0", - "@auth/drizzle-adapter": "^1.7.0", + "@auth/core": "^0.37.1", + "@auth/drizzle-adapter": "^1.7.1", "@homarr/common": "workspace:^0.1.0", "@homarr/db": "workspace:^0.1.0", "@homarr/definitions": "workspace:^0.1.0", @@ -35,7 +35,7 @@ "cookies": "^0.9.1", "ldapts": "7.2.1", "next": "^14.2.15", - "next-auth": "5.0.0-beta.22", + "next-auth": "5.0.0-beta.23", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/packages/common/package.json b/packages/common/package.json index a7c915305..42a57a561 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -29,7 +29,7 @@ "dayjs": "^1.11.13", "next": "^14.2.15", "react": "^18.3.1", - "tldts": "^6.1.50" + "tldts": "^6.1.52" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/packages/db/migrations/mysql/0011_freezing_banshee.sql b/packages/db/migrations/mysql/0011_freezing_banshee.sql new file mode 100644 index 000000000..f088e6b9d --- /dev/null +++ b/packages/db/migrations/mysql/0011_freezing_banshee.sql @@ -0,0 +1 @@ +ALTER TABLE `user` ADD `pingIconsEnabled` boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/packages/db/migrations/mysql/meta/0011_snapshot.json b/packages/db/migrations/mysql/meta/0011_snapshot.json new file mode 100644 index 000000000..afa00d67d --- /dev/null +++ b/packages/db/migrations/mysql/meta/0011_snapshot.json @@ -0,0 +1,1497 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "c2595909-0d92-4b2f-b934-6792271f52c0", + "prevId": "ea24896b-e311-49b3-855b-e50b224e7719", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "userId_idx": { + "name": "userId_idx", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": ["provider", "providerAccountId"] + } + }, + "uniqueConstraints": {} + }, + "apiKey": { + "name": "apiKey", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "salt": { + "name": "salt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "apiKey_userId_user_id_fk": { + "name": "apiKey_userId_user_id_fk", + "tableFrom": "apiKey", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "apiKey_id": { + "name": "apiKey_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "app": { + "name": "app", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "href": { + "name": "href", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "app_id": { + "name": "app_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "boardGroupPermission": { + "name": "boardGroupPermission", + "columns": { + "board_id": { + "name": "board_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "boardGroupPermission_board_id_board_id_fk": { + "name": "boardGroupPermission_board_id_board_id_fk", + "tableFrom": "boardGroupPermission", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "boardGroupPermission_group_id_group_id_fk": { + "name": "boardGroupPermission_group_id_group_id_fk", + "tableFrom": "boardGroupPermission", + "tableTo": "group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boardGroupPermission_board_id_group_id_permission_pk": { + "name": "boardGroupPermission_board_id_group_id_permission_pk", + "columns": ["board_id", "group_id", "permission"] + } + }, + "uniqueConstraints": {} + }, + "boardUserPermission": { + "name": "boardUserPermission", + "columns": { + "board_id": { + "name": "board_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "boardUserPermission_board_id_board_id_fk": { + "name": "boardUserPermission_board_id_board_id_fk", + "tableFrom": "boardUserPermission", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "boardUserPermission_user_id_user_id_fk": { + "name": "boardUserPermission_user_id_user_id_fk", + "tableFrom": "boardUserPermission", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boardUserPermission_board_id_user_id_permission_pk": { + "name": "boardUserPermission_board_id_user_id_permission_pk", + "columns": ["board_id", "user_id", "permission"] + } + }, + "uniqueConstraints": {} + }, + "board": { + "name": "board", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "creator_id": { + "name": "creator_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_title": { + "name": "page_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "meta_title": { + "name": "meta_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "logo_image_url": { + "name": "logo_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "favicon_image_url": { + "name": "favicon_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "background_image_url": { + "name": "background_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "background_image_attachment": { + "name": "background_image_attachment", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('fixed')" + }, + "background_image_repeat": { + "name": "background_image_repeat", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('no-repeat')" + }, + "background_image_size": { + "name": "background_image_size", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('cover')" + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('#fa5252')" + }, + "secondary_color": { + "name": "secondary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('#fd7e14')" + }, + "opacity": { + "name": "opacity", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "custom_css": { + "name": "custom_css", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "column_count": { + "name": "column_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + } + }, + "indexes": {}, + "foreignKeys": { + "board_creator_id_user_id_fk": { + "name": "board_creator_id_user_id_fk", + "tableFrom": "board", + "tableTo": "user", + "columnsFrom": ["creator_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "board_id": { + "name": "board_id", + "columns": ["id"] + } + }, + "uniqueConstraints": { + "board_name_unique": { + "name": "board_name_unique", + "columns": ["name"] + } + } + }, + "groupMember": { + "name": "groupMember", + "columns": { + "groupId": { + "name": "groupId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "groupMember_groupId_group_id_fk": { + "name": "groupMember_groupId_group_id_fk", + "tableFrom": "groupMember", + "tableTo": "group", + "columnsFrom": ["groupId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "groupMember_userId_user_id_fk": { + "name": "groupMember_userId_user_id_fk", + "tableFrom": "groupMember", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "groupMember_groupId_userId_pk": { + "name": "groupMember_groupId_userId_pk", + "columns": ["groupId", "userId"] + } + }, + "uniqueConstraints": {} + }, + "groupPermission": { + "name": "groupPermission", + "columns": { + "groupId": { + "name": "groupId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "groupPermission_groupId_group_id_fk": { + "name": "groupPermission_groupId_group_id_fk", + "tableFrom": "groupPermission", + "tableTo": "group", + "columnsFrom": ["groupId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "group": { + "name": "group", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "group_owner_id_user_id_fk": { + "name": "group_owner_id_user_id_fk", + "tableFrom": "group", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "group_id": { + "name": "group_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "iconRepository": { + "name": "iconRepository", + "columns": { + "iconRepository_id": { + "name": "iconRepository_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "iconRepository_slug": { + "name": "iconRepository_slug", + "type": "varchar(150)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "iconRepository_iconRepository_id": { + "name": "iconRepository_iconRepository_id", + "columns": ["iconRepository_id"] + } + }, + "uniqueConstraints": {} + }, + "icon": { + "name": "icon", + "columns": { + "icon_id": { + "name": "icon_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_name": { + "name": "icon_name", + "type": "varchar(250)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_checksum": { + "name": "icon_checksum", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "iconRepository_id": { + "name": "iconRepository_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "icon_iconRepository_id_iconRepository_iconRepository_id_fk": { + "name": "icon_iconRepository_id_iconRepository_iconRepository_id_fk", + "tableFrom": "icon", + "tableTo": "iconRepository", + "columnsFrom": ["iconRepository_id"], + "columnsTo": ["iconRepository_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "icon_icon_id": { + "name": "icon_icon_id", + "columns": ["icon_id"] + } + }, + "uniqueConstraints": {} + }, + "integrationGroupPermissions": { + "name": "integrationGroupPermissions", + "columns": { + "integration_id": { + "name": "integration_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integrationGroupPermissions_integration_id_integration_id_fk": { + "name": "integrationGroupPermissions_integration_id_integration_id_fk", + "tableFrom": "integrationGroupPermissions", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integrationGroupPermissions_group_id_group_id_fk": { + "name": "integrationGroupPermissions_group_id_group_id_fk", + "tableFrom": "integrationGroupPermissions", + "tableTo": "group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integration_group_permission__pk": { + "name": "integration_group_permission__pk", + "columns": ["integration_id", "group_id", "permission"] + } + }, + "uniqueConstraints": {} + }, + "integration_item": { + "name": "integration_item", + "columns": { + "item_id": { + "name": "item_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "integration_id": { + "name": "integration_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integration_item_item_id_item_id_fk": { + "name": "integration_item_item_id_item_id_fk", + "tableFrom": "integration_item", + "tableTo": "item", + "columnsFrom": ["item_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integration_item_integration_id_integration_id_fk": { + "name": "integration_item_integration_id_integration_id_fk", + "tableFrom": "integration_item", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integration_item_item_id_integration_id_pk": { + "name": "integration_item_item_id_integration_id_pk", + "columns": ["item_id", "integration_id"] + } + }, + "uniqueConstraints": {} + }, + "integrationSecret": { + "name": "integrationSecret", + "columns": { + "kind": { + "name": "kind", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "integration_id": { + "name": "integration_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "integration_secret__kind_idx": { + "name": "integration_secret__kind_idx", + "columns": ["kind"], + "isUnique": false + }, + "integration_secret__updated_at_idx": { + "name": "integration_secret__updated_at_idx", + "columns": ["updated_at"], + "isUnique": false + } + }, + "foreignKeys": { + "integrationSecret_integration_id_integration_id_fk": { + "name": "integrationSecret_integration_id_integration_id_fk", + "tableFrom": "integrationSecret", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrationSecret_integration_id_kind_pk": { + "name": "integrationSecret_integration_id_kind_pk", + "columns": ["integration_id", "kind"] + } + }, + "uniqueConstraints": {} + }, + "integrationUserPermission": { + "name": "integrationUserPermission", + "columns": { + "integration_id": { + "name": "integration_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integrationUserPermission_integration_id_integration_id_fk": { + "name": "integrationUserPermission_integration_id_integration_id_fk", + "tableFrom": "integrationUserPermission", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integrationUserPermission_user_id_user_id_fk": { + "name": "integrationUserPermission_user_id_user_id_fk", + "tableFrom": "integrationUserPermission", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrationUserPermission_integration_id_user_id_permission_pk": { + "name": "integrationUserPermission_integration_id_user_id_permission_pk", + "columns": ["integration_id", "user_id", "permission"] + } + }, + "uniqueConstraints": {} + }, + "integration": { + "name": "integration", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "integration__kind_idx": { + "name": "integration__kind_idx", + "columns": ["kind"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "integration_id": { + "name": "integration_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "invite": { + "name": "invite", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expiration_date": { + "name": "expiration_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "creator_id": { + "name": "creator_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "invite_creator_id_user_id_fk": { + "name": "invite_creator_id_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": ["creator_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "invite_id": { + "name": "invite_id", + "columns": ["id"] + } + }, + "uniqueConstraints": { + "invite_token_unique": { + "name": "invite_token_unique", + "columns": ["token"] + } + } + }, + "item": { + "name": "item", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section_id": { + "name": "section_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "x_offset": { + "name": "x_offset", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "y_offset": { + "name": "y_offset", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "options": { + "name": "options", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('{\"json\": {}}')" + }, + "advanced_options": { + "name": "advanced_options", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('{\"json\": {}}')" + } + }, + "indexes": {}, + "foreignKeys": { + "item_section_id_section_id_fk": { + "name": "item_section_id_section_id_fk", + "tableFrom": "item", + "tableTo": "section", + "columnsFrom": ["section_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "item_id": { + "name": "item_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "search_engine": { + "name": "search_engine", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "short": { + "name": "short", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url_template": { + "name": "url_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "search_engine_id": { + "name": "search_engine_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "section": { + "name": "section", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "board_id": { + "name": "board_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "x_offset": { + "name": "x_offset", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "y_offset": { + "name": "y_offset", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parent_section_id": { + "name": "parent_section_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "section_board_id_board_id_fk": { + "name": "section_board_id_board_id_fk", + "tableFrom": "section", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "section_parent_section_id_section_id_fk": { + "name": "section_parent_section_id_section_id_fk", + "tableFrom": "section", + "tableTo": "section", + "columnsFrom": ["parent_section_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "section_id": { + "name": "section_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "serverSetting": { + "name": "serverSetting", + "columns": { + "key": { + "name": "key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "('{\"json\": {}}')" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "serverSetting_key": { + "name": "serverSetting_key", + "columns": ["key"] + } + }, + "uniqueConstraints": { + "serverSetting_key_unique": { + "name": "serverSetting_key_unique", + "columns": ["key"] + } + } + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "user_id_idx": { + "name": "user_id_idx", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "session_sessionToken": { + "name": "session_sessionToken", + "columns": ["sessionToken"] + } + }, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "salt": { + "name": "salt", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'credentials'" + }, + "homeBoardId": { + "name": "homeBoardId", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "colorScheme": { + "name": "colorScheme", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'auto'" + }, + "firstDayOfWeek": { + "name": "firstDayOfWeek", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "pingIconsEnabled": { + "name": "pingIconsEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_homeBoardId_board_id_fk": { + "name": "user_homeBoardId_board_id_fk", + "tableFrom": "user", + "tableTo": "board", + "columnsFrom": ["homeBoardId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_id": { + "name": "user_id", + "columns": ["id"] + } + }, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": ["identifier", "token"] + } + }, + "uniqueConstraints": {} + } + }, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} diff --git a/packages/db/migrations/mysql/meta/_journal.json b/packages/db/migrations/mysql/meta/_journal.json index dacc6be5a..0c9a41d33 100644 --- a/packages/db/migrations/mysql/meta/_journal.json +++ b/packages/db/migrations/mysql/meta/_journal.json @@ -78,6 +78,13 @@ "when": 1728142597094, "tag": "0010_melted_pestilence", "breakpoints": true + }, + { + "idx": 11, + "version": "5", + "when": 1728490046896, + "tag": "0011_freezing_banshee", + "breakpoints": true } ] } diff --git a/packages/db/migrations/sqlite/0011_classy_angel.sql b/packages/db/migrations/sqlite/0011_classy_angel.sql new file mode 100644 index 000000000..10f3d6bf5 --- /dev/null +++ b/packages/db/migrations/sqlite/0011_classy_angel.sql @@ -0,0 +1 @@ +ALTER TABLE `user` ADD `pingIconsEnabled` integer DEFAULT false NOT NULL; \ No newline at end of file diff --git a/packages/db/migrations/sqlite/meta/0011_snapshot.json b/packages/db/migrations/sqlite/meta/0011_snapshot.json new file mode 100644 index 000000000..b3bf026e0 --- /dev/null +++ b/packages/db/migrations/sqlite/meta/0011_snapshot.json @@ -0,0 +1,1430 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7c2e4a9e-350a-4551-90d9-bdfa90db58ee", + "prevId": "52be1539-1b06-4581-8347-ce2102e782c7", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "userId_idx": { + "name": "userId_idx", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "columns": ["provider", "providerAccountId"], + "name": "account_provider_providerAccountId_pk" + } + }, + "uniqueConstraints": {} + }, + "apiKey": { + "name": "apiKey", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "apiKey": { + "name": "apiKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "salt": { + "name": "salt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "apiKey_userId_user_id_fk": { + "name": "apiKey_userId_user_id_fk", + "tableFrom": "apiKey", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "app": { + "name": "app", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "href": { + "name": "href", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "boardGroupPermission": { + "name": "boardGroupPermission", + "columns": { + "board_id": { + "name": "board_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "boardGroupPermission_board_id_board_id_fk": { + "name": "boardGroupPermission_board_id_board_id_fk", + "tableFrom": "boardGroupPermission", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "boardGroupPermission_group_id_group_id_fk": { + "name": "boardGroupPermission_group_id_group_id_fk", + "tableFrom": "boardGroupPermission", + "tableTo": "group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boardGroupPermission_board_id_group_id_permission_pk": { + "columns": ["board_id", "group_id", "permission"], + "name": "boardGroupPermission_board_id_group_id_permission_pk" + } + }, + "uniqueConstraints": {} + }, + "boardUserPermission": { + "name": "boardUserPermission", + "columns": { + "board_id": { + "name": "board_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "boardUserPermission_board_id_board_id_fk": { + "name": "boardUserPermission_board_id_board_id_fk", + "tableFrom": "boardUserPermission", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "boardUserPermission_user_id_user_id_fk": { + "name": "boardUserPermission_user_id_user_id_fk", + "tableFrom": "boardUserPermission", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boardUserPermission_board_id_user_id_permission_pk": { + "columns": ["board_id", "user_id", "permission"], + "name": "boardUserPermission_board_id_user_id_permission_pk" + } + }, + "uniqueConstraints": {} + }, + "board": { + "name": "board", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_public": { + "name": "is_public", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_title": { + "name": "page_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "meta_title": { + "name": "meta_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "logo_image_url": { + "name": "logo_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "favicon_image_url": { + "name": "favicon_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "background_image_url": { + "name": "background_image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "background_image_attachment": { + "name": "background_image_attachment", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'fixed'" + }, + "background_image_repeat": { + "name": "background_image_repeat", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'no-repeat'" + }, + "background_image_size": { + "name": "background_image_size", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'cover'" + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#fa5252'" + }, + "secondary_color": { + "name": "secondary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#fd7e14'" + }, + "opacity": { + "name": "opacity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "custom_css": { + "name": "custom_css", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "column_count": { + "name": "column_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + } + }, + "indexes": { + "board_name_unique": { + "name": "board_name_unique", + "columns": ["name"], + "isUnique": true + } + }, + "foreignKeys": { + "board_creator_id_user_id_fk": { + "name": "board_creator_id_user_id_fk", + "tableFrom": "board", + "tableTo": "user", + "columnsFrom": ["creator_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "groupMember": { + "name": "groupMember", + "columns": { + "groupId": { + "name": "groupId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "groupMember_groupId_group_id_fk": { + "name": "groupMember_groupId_group_id_fk", + "tableFrom": "groupMember", + "tableTo": "group", + "columnsFrom": ["groupId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "groupMember_userId_user_id_fk": { + "name": "groupMember_userId_user_id_fk", + "tableFrom": "groupMember", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "groupMember_groupId_userId_pk": { + "columns": ["groupId", "userId"], + "name": "groupMember_groupId_userId_pk" + } + }, + "uniqueConstraints": {} + }, + "groupPermission": { + "name": "groupPermission", + "columns": { + "groupId": { + "name": "groupId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "groupPermission_groupId_group_id_fk": { + "name": "groupPermission_groupId_group_id_fk", + "tableFrom": "groupPermission", + "tableTo": "group", + "columnsFrom": ["groupId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "group": { + "name": "group", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "group_owner_id_user_id_fk": { + "name": "group_owner_id_user_id_fk", + "tableFrom": "group", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "iconRepository": { + "name": "iconRepository", + "columns": { + "iconRepository_id": { + "name": "iconRepository_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "iconRepository_slug": { + "name": "iconRepository_slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "icon": { + "name": "icon", + "columns": { + "icon_id": { + "name": "icon_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "icon_name": { + "name": "icon_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_checksum": { + "name": "icon_checksum", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "iconRepository_id": { + "name": "iconRepository_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "icon_iconRepository_id_iconRepository_iconRepository_id_fk": { + "name": "icon_iconRepository_id_iconRepository_iconRepository_id_fk", + "tableFrom": "icon", + "tableTo": "iconRepository", + "columnsFrom": ["iconRepository_id"], + "columnsTo": ["iconRepository_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "integrationGroupPermissions": { + "name": "integrationGroupPermissions", + "columns": { + "integration_id": { + "name": "integration_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integrationGroupPermissions_integration_id_integration_id_fk": { + "name": "integrationGroupPermissions_integration_id_integration_id_fk", + "tableFrom": "integrationGroupPermissions", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integrationGroupPermissions_group_id_group_id_fk": { + "name": "integrationGroupPermissions_group_id_group_id_fk", + "tableFrom": "integrationGroupPermissions", + "tableTo": "group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrationGroupPermissions_integration_id_group_id_permission_pk": { + "columns": ["integration_id", "group_id", "permission"], + "name": "integrationGroupPermissions_integration_id_group_id_permission_pk" + } + }, + "uniqueConstraints": {} + }, + "integration_item": { + "name": "integration_item", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "integration_id": { + "name": "integration_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integration_item_item_id_item_id_fk": { + "name": "integration_item_item_id_item_id_fk", + "tableFrom": "integration_item", + "tableTo": "item", + "columnsFrom": ["item_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integration_item_integration_id_integration_id_fk": { + "name": "integration_item_integration_id_integration_id_fk", + "tableFrom": "integration_item", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integration_item_item_id_integration_id_pk": { + "columns": ["item_id", "integration_id"], + "name": "integration_item_item_id_integration_id_pk" + } + }, + "uniqueConstraints": {} + }, + "integrationSecret": { + "name": "integrationSecret", + "columns": { + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "integration_id": { + "name": "integration_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "integration_secret__kind_idx": { + "name": "integration_secret__kind_idx", + "columns": ["kind"], + "isUnique": false + }, + "integration_secret__updated_at_idx": { + "name": "integration_secret__updated_at_idx", + "columns": ["updated_at"], + "isUnique": false + } + }, + "foreignKeys": { + "integrationSecret_integration_id_integration_id_fk": { + "name": "integrationSecret_integration_id_integration_id_fk", + "tableFrom": "integrationSecret", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrationSecret_integration_id_kind_pk": { + "columns": ["integration_id", "kind"], + "name": "integrationSecret_integration_id_kind_pk" + } + }, + "uniqueConstraints": {} + }, + "integrationUserPermission": { + "name": "integrationUserPermission", + "columns": { + "integration_id": { + "name": "integration_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integrationUserPermission_integration_id_integration_id_fk": { + "name": "integrationUserPermission_integration_id_integration_id_fk", + "tableFrom": "integrationUserPermission", + "tableTo": "integration", + "columnsFrom": ["integration_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "integrationUserPermission_user_id_user_id_fk": { + "name": "integrationUserPermission_user_id_user_id_fk", + "tableFrom": "integrationUserPermission", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrationUserPermission_integration_id_user_id_permission_pk": { + "columns": ["integration_id", "user_id", "permission"], + "name": "integrationUserPermission_integration_id_user_id_permission_pk" + } + }, + "uniqueConstraints": {} + }, + "integration": { + "name": "integration", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "integration__kind_idx": { + "name": "integration__kind_idx", + "columns": ["kind"], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "invite": { + "name": "invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expiration_date": { + "name": "expiration_date", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invite_token_unique": { + "name": "invite_token_unique", + "columns": ["token"], + "isUnique": true + } + }, + "foreignKeys": { + "invite_creator_id_user_id_fk": { + "name": "invite_creator_id_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": ["creator_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "item": { + "name": "item", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "x_offset": { + "name": "x_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "y_offset": { + "name": "y_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "options": { + "name": "options", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{\"json\": {}}'" + }, + "advanced_options": { + "name": "advanced_options", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{\"json\": {}}'" + } + }, + "indexes": {}, + "foreignKeys": { + "item_section_id_section_id_fk": { + "name": "item_section_id_section_id_fk", + "tableFrom": "item", + "tableTo": "section", + "columnsFrom": ["section_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "search_engine": { + "name": "search_engine", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url_template": { + "name": "url_template", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "section": { + "name": "section", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "board_id": { + "name": "board_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "x_offset": { + "name": "x_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "y_offset": { + "name": "y_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parent_section_id": { + "name": "parent_section_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "section_board_id_board_id_fk": { + "name": "section_board_id_board_id_fk", + "tableFrom": "section", + "tableTo": "board", + "columnsFrom": ["board_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "section_parent_section_id_section_id_fk": { + "name": "section_parent_section_id_section_id_fk", + "tableFrom": "section", + "tableTo": "section", + "columnsFrom": ["parent_section_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "serverSetting": { + "name": "serverSetting", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{\"json\": {}}'" + } + }, + "indexes": { + "serverSetting_key_unique": { + "name": "serverSetting_key_unique", + "columns": ["key"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "user_id_idx": { + "name": "user_id_idx", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "salt": { + "name": "salt", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'credentials'" + }, + "homeBoardId": { + "name": "homeBoardId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "colorScheme": { + "name": "colorScheme", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'auto'" + }, + "firstDayOfWeek": { + "name": "firstDayOfWeek", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "pingIconsEnabled": { + "name": "pingIconsEnabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_homeBoardId_board_id_fk": { + "name": "user_homeBoardId_board_id_fk", + "tableFrom": "user", + "tableTo": "board", + "columnsFrom": ["homeBoardId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "columns": ["identifier", "token"], + "name": "verificationToken_identifier_token_pk" + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/packages/db/migrations/sqlite/meta/_journal.json b/packages/db/migrations/sqlite/meta/_journal.json index 40d9bf4d0..99b2038fe 100644 --- a/packages/db/migrations/sqlite/meta/_journal.json +++ b/packages/db/migrations/sqlite/meta/_journal.json @@ -78,6 +78,13 @@ "when": 1728142590232, "tag": "0010_gorgeous_stingray", "breakpoints": true + }, + { + "idx": 11, + "version": "6", + "when": 1728490026154, + "tag": "0011_classy_angel", + "breakpoints": true } ] } diff --git a/packages/db/package.json b/packages/db/package.json index 67ffa31ba..f855029ea 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -22,8 +22,10 @@ "lint": "eslint", "migration:mysql:generate": "drizzle-kit generate --config ./configs/mysql.config.ts", "migration:mysql:run": "drizzle-kit migrate --config ./configs/mysql.config.ts", + "migration:mysql:drop": "drizzle-kit drop --config ./configs/mysql.config.ts", "migration:sqlite:generate": "drizzle-kit generate --config ./configs/sqlite.config.ts", "migration:sqlite:run": "drizzle-kit migrate --config ./configs/sqlite.config.ts", + "migration:sqlite:drop": "drizzle-kit drop --config ./configs/sqlite.config.ts", "push:mysql": "drizzle-kit push --config ./configs/mysql.config.ts", "push:sqlite": "drizzle-kit push --config ./configs/sqlite.config.ts", "studio": "drizzle-kit studio --config ./configs/sqlite.config.ts", @@ -31,16 +33,16 @@ }, "prettier": "@homarr/prettier-config", "dependencies": { - "@auth/core": "^0.37.0", + "@auth/core": "^0.37.1", "@homarr/common": "workspace:^0.1.0", "@homarr/definitions": "workspace:^0.1.0", "@homarr/log": "workspace:^0.1.0", "@paralleldrive/cuid2": "^2.2.2", "@testcontainers/mysql": "^10.13.2", - "better-sqlite3": "^11.3.0", + "better-sqlite3": "^11.4.0", "dotenv": "^16.4.5", - "drizzle-kit": "^0.25.0", - "drizzle-orm": "^0.34.1", + "drizzle-kit": "^0.26.2", + "drizzle-orm": "^0.35.2", "mysql2": "3.11.3" }, "devDependencies": { diff --git a/packages/db/schema/mysql.ts b/packages/db/schema/mysql.ts index 383ac2e5a..510ddaf89 100644 --- a/packages/db/schema/mysql.ts +++ b/packages/db/schema/mysql.ts @@ -43,8 +43,9 @@ export const users = mysqlTable("user", { homeBoardId: varchar("homeBoardId", { length: 64 }).references((): AnyMySqlColumn => boards.id, { onDelete: "set null", }), - colorScheme: varchar("colorScheme", { length: 5 }).$type().default("auto").notNull(), + colorScheme: varchar("colorScheme", { length: 5 }).$type().default("dark").notNull(), firstDayOfWeek: tinyint("firstDayOfWeek").$type().default(1).notNull(), // Defaults to Monday + pingIconsEnabled: boolean("pingIconsEnabled").default(false).notNull(), }); export const accounts = mysqlTable( diff --git a/packages/db/schema/sqlite.ts b/packages/db/schema/sqlite.ts index cec5844c1..f0d1a52e4 100644 --- a/packages/db/schema/sqlite.ts +++ b/packages/db/schema/sqlite.ts @@ -44,8 +44,9 @@ export const users = sqliteTable("user", { homeBoardId: text("homeBoardId").references((): AnySQLiteColumn => boards.id, { onDelete: "set null", }), - colorScheme: text("colorScheme").$type().default("auto").notNull(), + colorScheme: text("colorScheme").$type().default("dark").notNull(), firstDayOfWeek: int("firstDayOfWeek").$type().default(1).notNull(), // Defaults to Monday + pingIconsEnabled: int("pingIconsEnabled", { mode: "boolean" }).default(false).notNull(), }); export const accounts = sqliteTable( diff --git a/packages/definitions/src/user.ts b/packages/definitions/src/user.ts index 28f24ae7b..77d8d4138 100644 --- a/packages/definitions/src/user.ts +++ b/packages/definitions/src/user.ts @@ -1,2 +1,2 @@ -export const colorSchemes = ["light", "dark", "auto"] as const; +export const colorSchemes = ["light", "dark"] as const; export type ColorScheme = (typeof colorSchemes)[number]; diff --git a/packages/form/package.json b/packages/form/package.json index 1871c1cfe..f666059fc 100644 --- a/packages/form/package.json +++ b/packages/form/package.json @@ -24,7 +24,7 @@ "dependencies": { "@homarr/translation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", - "@mantine/form": "^7.13.2" + "@mantine/form": "^7.13.3" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/packages/integrations/src/calendar-types.ts b/packages/integrations/src/calendar-types.ts index f9b97b431..95399c4b1 100644 --- a/packages/integrations/src/calendar-types.ts +++ b/packages/integrations/src/calendar-types.ts @@ -1,7 +1,11 @@ +export const radarrReleaseTypes = ["inCinemas", "digitalRelease", "physicalRelease"] as const; +type ReleaseType = (typeof radarrReleaseTypes)[number]; + export interface CalendarEvent { name: string; subName: string; date: Date; + dates?: { type: ReleaseType; date: Date }[]; description?: string; thumbnail?: string; mediaInformation?: { diff --git a/packages/integrations/src/media-organizer/radarr/radarr-integration.ts b/packages/integrations/src/media-organizer/radarr/radarr-integration.ts index 1ef79bf78..a562ce2d0 100644 --- a/packages/integrations/src/media-organizer/radarr/radarr-integration.ts +++ b/packages/integrations/src/media-organizer/radarr/radarr-integration.ts @@ -1,8 +1,10 @@ +import type { AtLeastOneOf } from "@homarr/common/types"; import { logger } from "@homarr/log"; import { z } from "@homarr/validation"; import { Integration } from "../../base/integration"; import type { CalendarEvent } from "../../calendar-types"; +import { radarrReleaseTypes } from "../../calendar-types"; export class RadarrIntegration extends Integration { /** @@ -37,19 +39,23 @@ export class RadarrIntegration extends Integration { }); const radarrCalendarEvents = await z.array(radarrCalendarEventSchema).parseAsync(await response.json()); - return radarrCalendarEvents.map( - (radarrCalendarEvent): CalendarEvent => ({ + return radarrCalendarEvents.map((radarrCalendarEvent): CalendarEvent => { + const dates = radarrReleaseTypes + .map((type) => (radarrCalendarEvent[type] ? { type, date: radarrCalendarEvent[type] } : undefined)) + .filter((date) => date) as AtLeastOneOf[number]>; + return { name: radarrCalendarEvent.title, subName: radarrCalendarEvent.originalTitle, description: radarrCalendarEvent.overview, thumbnail: this.chooseBestImageAsURL(radarrCalendarEvent), - date: radarrCalendarEvent.inCinemas, + date: dates[0].date, + dates, mediaInformation: { type: "movie", }, links: this.getLinksForRadarrCalendarEvent(radarrCalendarEvent), - }), - ); + }; + }); } private getLinksForRadarrCalendarEvent = (event: z.infer) => { @@ -118,7 +124,18 @@ const radarrCalendarEventImageSchema = z.array( const radarrCalendarEventSchema = z.object({ title: z.string(), originalTitle: z.string(), - inCinemas: z.string().transform((value) => new Date(value)), + inCinemas: z + .string() + .transform((value) => new Date(value)) + .optional(), + physicalRelease: z + .string() + .transform((value) => new Date(value)) + .optional(), + digitalRelease: z + .string() + .transform((value) => new Date(value)) + .optional(), overview: z.string().optional(), titleSlug: z.string(), images: radarrCalendarEventImageSchema, diff --git a/packages/modals-collection/package.json b/packages/modals-collection/package.json index 72d2def67..4a3f000df 100644 --- a/packages/modals-collection/package.json +++ b/packages/modals-collection/package.json @@ -30,7 +30,7 @@ "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", - "@mantine/core": "^7.13.2", + "@mantine/core": "^7.13.3", "@tabler/icons-react": "^3.19.0", "dayjs": "^1.11.13", "next": "^14.2.15", diff --git a/packages/modals/package.json b/packages/modals/package.json index 2e714ecc3..4e86befe3 100644 --- a/packages/modals/package.json +++ b/packages/modals/package.json @@ -24,8 +24,8 @@ "dependencies": { "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", - "@mantine/core": "^7.13.2", - "@mantine/hooks": "^7.13.2", + "@mantine/core": "^7.13.3", + "@mantine/hooks": "^7.13.3", "react": "^18.3.1" }, "devDependencies": { diff --git a/packages/modals/src/confirm-modal.tsx b/packages/modals/src/confirm-modal.tsx index 752506cb8..9f3f2b4ac 100644 --- a/packages/modals/src/confirm-modal.tsx +++ b/packages/modals/src/confirm-modal.tsx @@ -53,7 +53,7 @@ export const ConfirmModal = createModal>(({ act actions.closeModal(); } }, - [cancelProps?.onClick, onCancel, actions.closeModal], + [cancelProps, onCancel, closeOnCancel, actions], ); const handleConfirm = useCallback( @@ -73,7 +73,7 @@ export const ConfirmModal = createModal>(({ act } setLoading(false); }, - [confirmProps?.onClick, onConfirm, actions.closeModal], + [confirmProps, onConfirm, closeOnConfirm, actions], ); return ( diff --git a/packages/modals/src/index.tsx b/packages/modals/src/index.tsx index f3c9e1386..02bc4cff4 100644 --- a/packages/modals/src/index.tsx +++ b/packages/modals/src/index.tsx @@ -38,7 +38,7 @@ export const ModalProvider = ({ children }: PropsWithChildren) => { (id: string, canceled?: boolean) => { dispatch({ type: "CLOSE", modalId: id, canceled }); }, - [stateRef, dispatch], + [dispatch], ); const openModalInner: ModalContextProps["openModalInner"] = useCallback( @@ -63,10 +63,7 @@ export const ModalProvider = ({ children }: PropsWithChildren) => { [dispatch], ); - const handleCloseModal = useCallback( - () => state.current && closeModal(state.current.id), - [closeModal, state.current?.id], - ); + const handleCloseModal = useCallback(() => state.current && closeModal(state.current.id), [closeModal, state]); const activeModals = state.modals.filter((modal) => modal.id === state.current?.id || modal.props.keepMounted); diff --git a/packages/notifications/package.json b/packages/notifications/package.json index 4384d745b..b4e9802a3 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -24,7 +24,7 @@ "prettier": "@homarr/prettier-config", "dependencies": { "@homarr/ui": "workspace:^0.1.0", - "@mantine/notifications": "^7.13.2", + "@mantine/notifications": "^7.13.3", "@tabler/icons-react": "^3.19.0" }, "devDependencies": { diff --git a/packages/old-import/src/widgets/options.ts b/packages/old-import/src/widgets/options.ts index cb95a7629..dde760af2 100644 --- a/packages/old-import/src/widgets/options.ts +++ b/packages/old-import/src/widgets/options.ts @@ -23,6 +23,7 @@ const optionMapping: OptionMapping = { }, "mediaRequests-requestStats": {}, calendar: { + releaseType: (oldOptions) => [oldOptions.radarrReleaseType], filterFutureMonths: () => undefined, filterPastMonths: () => undefined, }, diff --git a/packages/spotlight/package.json b/packages/spotlight/package.json index b23509a1b..edcfd2fde 100644 --- a/packages/spotlight/package.json +++ b/packages/spotlight/package.json @@ -30,11 +30,11 @@ "@homarr/modals-collection": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", - "@mantine/core": "^7.13.2", - "@mantine/hooks": "^7.13.2", - "@mantine/spotlight": "^7.13.2", + "@mantine/core": "^7.13.3", + "@mantine/hooks": "^7.13.3", + "@mantine/spotlight": "^7.13.3", "@tabler/icons-react": "^3.19.0", - "jotai": "^2.10.0", + "jotai": "^2.10.1", "next": "^14.2.15", "react": "^18.3.1", "use-deep-compare-effect": "^1.8.1" diff --git a/packages/spotlight/src/components/actions/items/children-action-item.tsx b/packages/spotlight/src/components/actions/items/children-action-item.tsx index c36fb3c65..0b070b147 100644 --- a/packages/spotlight/src/components/actions/items/children-action-item.tsx +++ b/packages/spotlight/src/components/actions/items/children-action-item.tsx @@ -24,7 +24,7 @@ export const ChildrenActionItem = ({ childrenOptions, action, query }: ChildrenA return ( - + ); }; diff --git a/packages/spotlight/src/components/actions/items/group-action-item.tsx b/packages/spotlight/src/components/actions/items/group-action-item.tsx index 1b2ebe1c5..6dd70c5ff 100644 --- a/packages/spotlight/src/components/actions/items/group-action-item.tsx +++ b/packages/spotlight/src/components/actions/items/group-action-item.tsx @@ -48,7 +48,7 @@ export const SpotlightGroupActionItem = closeSpotlightOnTrigger={interaction.type !== "mode" && interaction.type !== "children"} className={classes.spotlightAction} > - + ); }; diff --git a/packages/spotlight/src/components/spotlight.tsx b/packages/spotlight/src/components/spotlight.tsx index f84af4add..2de76db8c 100644 --- a/packages/spotlight/src/components/spotlight.tsx +++ b/packages/spotlight/src/components/spotlight.tsx @@ -92,7 +92,7 @@ export const Spotlight = () => { {childrenOptions ? ( - + ) : null} diff --git a/packages/spotlight/src/lib/children.ts b/packages/spotlight/src/lib/children.ts index 1a792a2b6..69b3c00b1 100644 --- a/packages/spotlight/src/lib/children.ts +++ b/packages/spotlight/src/lib/children.ts @@ -3,13 +3,13 @@ import type { ReactNode } from "react"; import type { inferSearchInteractionDefinition } from "./interaction"; export interface CreateChildrenOptionsProps> { - detailComponent: ({ options }: { options: TParentOptions }) => ReactNode; + DetailComponent: ({ options }: { options: TParentOptions }) => ReactNode; useActions: (options: TParentOptions, query: string) => ChildrenAction[]; } export interface ChildrenAction> { key: string; - component: (option: TParentOptions) => JSX.Element; + Component: (option: TParentOptions) => JSX.Element; useInteraction: (option: TParentOptions, query: string) => inferSearchInteractionDefinition<"link" | "javaScript">; hide?: boolean | ((option: TParentOptions) => boolean); } diff --git a/packages/spotlight/src/lib/group.ts b/packages/spotlight/src/lib/group.ts index dda1fa7a8..900990e4a 100644 --- a/packages/spotlight/src/lib/group.ts +++ b/packages/spotlight/src/lib/group.ts @@ -8,7 +8,7 @@ type CommonSearchGroup, TOptionProps ext // key path is used to define the path to a unique key in the option object keyPath: keyof TOption; title: stringOrTranslation; - component: (option: TOption) => JSX.Element; + Component: (option: TOption) => JSX.Element; useInteraction: (option: TOption, query: string) => inferSearchInteractionDefinition; onKeyDown?: ( event: KeyboardEvent, diff --git a/packages/spotlight/src/lib/interaction.ts b/packages/spotlight/src/lib/interaction.ts index 1528a39ca..b917b3bcd 100644 --- a/packages/spotlight/src/lib/interaction.ts +++ b/packages/spotlight/src/lib/interaction.ts @@ -16,7 +16,7 @@ const searchInteractions = [ // eslint-disable-next-line @typescript-eslint/no-explicit-any useActions: CreateChildrenOptionsProps["useActions"]; // eslint-disable-next-line @typescript-eslint/no-explicit-any - detailComponent: CreateChildrenOptionsProps["detailComponent"]; + DetailComponent: CreateChildrenOptionsProps["DetailComponent"]; // eslint-disable-next-line @typescript-eslint/no-explicit-any option: any; }>(), diff --git a/packages/spotlight/src/modes/app-integration-board/apps-search-group.tsx b/packages/spotlight/src/modes/app-integration-board/apps-search-group.tsx index 5d2dc98a6..477842ac2 100644 --- a/packages/spotlight/src/modes/app-integration-board/apps-search-group.tsx +++ b/packages/spotlight/src/modes/app-integration-board/apps-search-group.tsx @@ -16,7 +16,7 @@ const appChildrenOptions = createChildrenOptions({ useActions: () => [ { key: "open", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -34,7 +34,7 @@ const appChildrenOptions = createChildrenOptions({ }, { key: "edit", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -47,7 +47,7 @@ const appChildrenOptions = createChildrenOptions({ useInteraction: interaction.link(({ id }) => ({ href: `/manage/apps/edit/${id}` })), }, ], - detailComponent: ({ options }) => { + DetailComponent: ({ options }) => { const t = useI18n(); return ( @@ -75,7 +75,7 @@ const appChildrenOptions = createChildrenOptions({ export const appsSearchGroup = createGroup({ keyPath: "id", title: (t) => t("search.mode.appIntegrationBoard.group.app.title"), - component: (app) => ( + Component: (app) => ( ({ const actions: (ChildrenAction & { hidden?: boolean })[] = [ { key: "open", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -37,7 +37,7 @@ const boardChildrenOptions = createChildrenOptions({ }, { key: "homeBoard", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -61,7 +61,7 @@ const boardChildrenOptions = createChildrenOptions({ }, { key: "settings", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -78,7 +78,7 @@ const boardChildrenOptions = createChildrenOptions({ return actions; }, - detailComponent: ({ options: board }) => { + DetailComponent: ({ options: board }) => { const t = useI18n(); return ( @@ -102,7 +102,7 @@ const boardChildrenOptions = createChildrenOptions({ export const boardsSearchGroup = createGroup({ keyPath: "id", title: "Boards", - component: (board) => ( + Component: (board) => ( {board.logoImageUrl ? ( {board.name} diff --git a/packages/spotlight/src/modes/app-integration-board/integrations-search-group.tsx b/packages/spotlight/src/modes/app-integration-board/integrations-search-group.tsx index 55fd14eca..28926a165 100644 --- a/packages/spotlight/src/modes/app-integration-board/integrations-search-group.tsx +++ b/packages/spotlight/src/modes/app-integration-board/integrations-search-group.tsx @@ -10,7 +10,7 @@ import { interaction } from "../../lib/interaction"; export const integrationsSearchGroup = createGroup<{ id: string; kind: IntegrationKind; name: string }>({ keyPath: "id", title: (t) => t("search.mode.appIntegrationBoard.group.integration.title"), - component: (integration) => ( + Component: (integration) => ( diff --git a/packages/spotlight/src/modes/command/children/language.tsx b/packages/spotlight/src/modes/command/children/language.tsx index 7a248275a..848139b68 100644 --- a/packages/spotlight/src/modes/command/children/language.tsx +++ b/packages/spotlight/src/modes/command/children/language.tsx @@ -30,7 +30,7 @@ export const languageChildrenOptions = createChildrenOptions ({ key: localeKey, - component() { + Component() { return ( @@ -53,7 +53,7 @@ export const languageChildrenOptions = createChildrenOptions { + DetailComponent: () => { const t = useI18n(); return ( diff --git a/packages/spotlight/src/modes/command/children/new-integration.tsx b/packages/spotlight/src/modes/command/children/new-integration.tsx index d70a719f3..aebb6ede1 100644 --- a/packages/spotlight/src/modes/command/children/new-integration.tsx +++ b/packages/spotlight/src/modes/command/children/new-integration.tsx @@ -20,7 +20,7 @@ export const newIntegrationChildrenOptions = createChildrenOptions ({ key: kind, - component() { + Component() { return ( @@ -31,7 +31,7 @@ export const newIntegrationChildrenOptions = createChildrenOptions ({ href: `/manage/integrations/new?kind=${kind}` })), })); }, - detailComponent() { + DetailComponent() { const t = useI18n(); return ( diff --git a/packages/spotlight/src/modes/command/index.tsx b/packages/spotlight/src/modes/command/index.tsx index 240fd13b5..88f208104 100644 --- a/packages/spotlight/src/modes/command/index.tsx +++ b/packages/spotlight/src/modes/command/index.tsx @@ -44,7 +44,7 @@ export const commandMode = { keyPath: "commandKey", title: "Global commands", useInteraction: (option, query) => option.useInteraction(option, query), - component: ({ icon: Icon, name }) => ( + Component: ({ icon: Icon, name }) => ( {name} diff --git a/packages/spotlight/src/modes/external/search-engines-search-group.tsx b/packages/spotlight/src/modes/external/search-engines-search-group.tsx index 4c76532e9..fd7563207 100644 --- a/packages/spotlight/src/modes/external/search-engines-search-group.tsx +++ b/packages/spotlight/src/modes/external/search-engines-search-group.tsx @@ -15,7 +15,7 @@ export const searchEnginesChildrenOptions = createChildrenOptions( useActions: () => [ { key: "search", - component: ({ name }) => { + Component: ({ name }) => { const tChildren = useScopedI18n("search.mode.external.group.searchEngine.children"); return ( @@ -30,7 +30,7 @@ export const searchEnginesChildrenOptions = createChildrenOptions( })), }, ], - detailComponent({ options }) { + DetailComponent({ options }) { const tChildren = useScopedI18n("search.mode.external.group.searchEngine.children"); return ( @@ -47,7 +47,7 @@ export const searchEnginesChildrenOptions = createChildrenOptions( export const searchEnginesSearchGroups = createGroup({ keyPath: "short", title: (t) => t("search.mode.external.group.searchEngine.title"), - component: ({ iconUrl, name, short, description }) => { + Component: ({ iconUrl, name, short, description }) => { return ( diff --git a/packages/spotlight/src/modes/index.tsx b/packages/spotlight/src/modes/index.tsx index 077e8fa11..afb613e74 100644 --- a/packages/spotlight/src/modes/index.tsx +++ b/packages/spotlight/src/modes/index.tsx @@ -22,7 +22,7 @@ const helpMode = { keyPath: "character", title: (t) => t("search.mode.help.group.mode.title"), options: searchModesWithoutHelp.map(({ character, modeKey }) => ({ character, modeKey })), - component: ({ modeKey, character }) => { + Component: ({ modeKey, character }) => { const t = useScopedI18n(`search.mode.${modeKey}`); return ( @@ -59,7 +59,7 @@ const helpMode = { }, ]; }, - component: (props) => ( + Component: (props) => ( {props.label} diff --git a/packages/spotlight/src/modes/page/pages-search-group.tsx b/packages/spotlight/src/modes/page/pages-search-group.tsx index e0b268a12..698f6166c 100644 --- a/packages/spotlight/src/modes/page/pages-search-group.tsx +++ b/packages/spotlight/src/modes/page/pages-search-group.tsx @@ -29,7 +29,7 @@ export const pagesSearchGroup = createGroup<{ }>({ keyPath: "path", title: (t) => t("search.mode.page.group.page.title"), - component: ({ name, icon: Icon }) => ( + Component: ({ name, icon: Icon }) => ( {name} diff --git a/packages/spotlight/src/modes/user-group/groups-search-group.tsx b/packages/spotlight/src/modes/user-group/groups-search-group.tsx index 507c7cb80..8ffb2a6e2 100644 --- a/packages/spotlight/src/modes/user-group/groups-search-group.tsx +++ b/packages/spotlight/src/modes/user-group/groups-search-group.tsx @@ -16,7 +16,7 @@ const groupChildrenOptions = createChildrenOptions({ useActions: () => [ { key: "detail", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -29,7 +29,7 @@ const groupChildrenOptions = createChildrenOptions({ }, { key: "manageMember", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -42,7 +42,7 @@ const groupChildrenOptions = createChildrenOptions({ }, { key: "managePermission", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -54,7 +54,7 @@ const groupChildrenOptions = createChildrenOptions({ useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/groups/${id}/permissions` })), }, ], - detailComponent: ({ options }) => { + DetailComponent: ({ options }) => { const t = useI18n(); return ( @@ -71,7 +71,7 @@ const groupChildrenOptions = createChildrenOptions({ export const groupsSearchGroup = createGroup({ keyPath: "id", title: "Groups", - component: ({ name }) => ( + Component: ({ name }) => ( {name} diff --git a/packages/spotlight/src/modes/user-group/users-search-group.tsx b/packages/spotlight/src/modes/user-group/users-search-group.tsx index ec750f182..b27dfbc37 100644 --- a/packages/spotlight/src/modes/user-group/users-search-group.tsx +++ b/packages/spotlight/src/modes/user-group/users-search-group.tsx @@ -17,7 +17,7 @@ const userChildrenOptions = createChildrenOptions({ useActions: () => [ { key: "detail", - component: () => { + Component: () => { const t = useI18n(); return ( @@ -30,7 +30,7 @@ const userChildrenOptions = createChildrenOptions({ useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/${id}/general` })), }, ], - detailComponent: ({ options }) => { + DetailComponent: ({ options }) => { const t = useI18n(); return ( @@ -49,7 +49,7 @@ const userChildrenOptions = createChildrenOptions({ export const usersSearchGroup = createGroup({ keyPath: "id", title: (t) => t("search.mode.userGroup.group.user.title"), - component: (user) => ( + Component: (user) => ( {user.name} diff --git a/packages/translation/package.json b/packages/translation/package.json index aeec42149..5697f33be 100644 --- a/packages/translation/package.json +++ b/packages/translation/package.json @@ -26,7 +26,7 @@ "prettier": "@homarr/prettier-config", "dependencies": { "dayjs": "^1.11.13", - "mantine-react-table": "2.0.0-beta.6", + "mantine-react-table": "2.0.0-beta.7", "next-international": "^1.2.4" }, "devDependencies": { diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts index f61c5da64..69ed66a7a 100644 --- a/packages/translation/src/lang/en.ts +++ b/packages/translation/src/lang/en.ts @@ -48,6 +48,9 @@ export default { homeBoard: { label: "Home board", }, + pingIconsEnabled: { + label: "Use icons for pings", + }, }, error: { usernameTaken: "Username already taken", @@ -116,6 +119,16 @@ export default { }, }, }, + changePingIconsEnabled: { + notification: { + success: { + message: "Ping icons toggled successfully", + }, + error: { + message: "Unable to toggle ping icons", + }, + }, + }, manageAvatar: { changeImage: { label: "Change image", @@ -563,6 +576,7 @@ export default { tryAgain: "Try again", loading: "Loading", }, + here: "here", iconPicker: { label: "Icon URL", header: "Type name or objects to filter for icons... Homarr will search through {countIcons} icons for you.", @@ -570,6 +584,9 @@ export default { information: { min: "Min", max: "Max", + days: "Days", + hours: "Hours", + minutes: "Minutes", }, notification: { create: { @@ -1018,6 +1035,14 @@ export default { name: "Calendar", description: "Display events from your integrations in a calendar view within a certain relative time period", option: { + releaseType: { + label: "Radarr release type", + options: { + inCinemas: "In cinemas", + digitalRelease: "Digital release", + physicalRelease: "Physical release", + }, + }, filterPastMonths: { label: "Start from", }, @@ -1097,16 +1122,17 @@ export default { }, popover: { information: "Information", - processor: "Processor:", - memory: "Memory:", - version: "Version:", - uptime: "Uptime: {days} days, {hours} hours", + processor: "Processor: {cpuModelName}", + memory: "Memory: {memory}GiB", + memoryAvailable: "Available: {memoryAvailable}GiB ({percent}%)", + version: "Version: {version}", + uptime: "Uptime: {days} Days, {hours} Hours, {minutes} Minutes", loadAverage: "Load average:", - minute: "1 minute:", - minutes: "{count} minutes:", + minute: "1 minute", + minutes: "{count} minutes", used: "Used", - diskAvailable: "Available", - memAvailable: "Available:", + available: "Available", + lastSeen: "Last status update: {lastSeen}", }, memory: {}, error: { @@ -1136,6 +1162,14 @@ export default { }, }, }, + integration: { + noData: "No integration found", + description: "Click {here} to create a new integration", + }, + app: { + noData: "No app found", + description: "Click {here} to create a new app", + }, error: { action: { logs: "Check logs for more details", @@ -1703,6 +1737,7 @@ export default { language: "Language & Region", board: "Home board", firstDayOfWeek: "First day of the week", + accessibility: "Accessibility", }, }, security: { diff --git a/packages/ui/package.json b/packages/ui/package.json index 2b8e9d069..7aad5a35f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -28,11 +28,11 @@ "@homarr/log": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", - "@mantine/core": "^7.13.2", - "@mantine/dates": "^7.13.2", - "@mantine/hooks": "^7.13.2", + "@mantine/core": "^7.13.3", + "@mantine/dates": "^7.13.3", + "@mantine/hooks": "^7.13.3", "@tabler/icons-react": "^3.19.0", - "mantine-react-table": "2.0.0-beta.6", + "mantine-react-table": "2.0.0-beta.7", "next": "^14.2.15", "react": "^18.3.1" }, diff --git a/packages/ui/src/components/table-pagination.tsx b/packages/ui/src/components/table-pagination.tsx index 83420ba54..e2380f821 100644 --- a/packages/ui/src/components/table-pagination.tsx +++ b/packages/ui/src/components/table-pagination.tsx @@ -34,7 +34,7 @@ export const TablePagination = ({ total }: TablePaginationProps) => { (control: ControlType) => { return getItemProps(calculatePageFor(control, current, total)); }, - [current], + [current, getItemProps, total], ); const handleChange = useCallback( @@ -43,7 +43,7 @@ export const TablePagination = ({ total }: TablePaginationProps) => { params.set("page", page.toString()); replace(`${pathName}?${params.toString()}`); }, - [pathName, searchParams], + [pathName, replace, searchParams], ); return ( diff --git a/packages/validation/src/user.ts b/packages/validation/src/user.ts index e44395e67..566e43363 100644 --- a/packages/validation/src/user.ts +++ b/packages/validation/src/user.ts @@ -108,6 +108,10 @@ const firstDayOfWeekSchema = z.object({ firstDayOfWeek: z.custom((value) => z.number().min(0).max(6).safeParse(value).success), }); +const pingIconsEnabledSchema = z.object({ + pingIconsEnabled: z.boolean(), +}); + export const userSchemas = { signIn: signInSchema, registration: registrationSchema, @@ -121,4 +125,5 @@ export const userSchemas = { changePasswordApi: changePasswordApiSchema, changeColorScheme: changeColorSchemeSchema, firstDayOfWeek: firstDayOfWeekSchema, + pingIconsEnabled: pingIconsEnabledSchema, }; diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 74ee3276a..5b4df05aa 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -38,8 +38,8 @@ "@homarr/translation": "workspace:^0.1.0", "@homarr/ui": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", - "@mantine/core": "^7.13.2", - "@mantine/hooks": "^7.13.2", + "@mantine/core": "^7.13.3", + "@mantine/hooks": "^7.13.3", "@tabler/icons-react": "^3.19.0", "@tiptap/extension-color": "2.8.0", "@tiptap/extension-highlight": "2.8.0", @@ -58,7 +58,7 @@ "@tiptap/starter-kit": "^2.8.0", "clsx": "^2.1.1", "dayjs": "^1.11.13", - "mantine-react-table": "2.0.0-beta.6", + "mantine-react-table": "2.0.0-beta.7", "next": "^14.2.15", "react": "^18.3.1", "video.js": "^8.18.1" diff --git a/packages/widgets/src/_inputs/widget-app-input.tsx b/packages/widgets/src/_inputs/widget-app-input.tsx index 04a85d468..ee22b08da 100644 --- a/packages/widgets/src/_inputs/widget-app-input.tsx +++ b/packages/widgets/src/_inputs/widget-app-input.tsx @@ -1,19 +1,22 @@ "use client"; import { memo, useMemo } from "react"; +import Link from "next/link"; import type { SelectProps } from "@mantine/core"; -import { Group, Loader, Select } from "@mantine/core"; +import { Anchor, Group, Loader, Select, Text } from "@mantine/core"; import { IconCheck } from "@tabler/icons-react"; import type { RouterOutputs } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; +import { useI18n } from "@homarr/translation/client"; import type { CommonWidgetInputProps } from "./common"; import { useWidgetInputTranslation } from "./common"; import { useFormContext } from "./form"; -export const WidgetAppInput = ({ property, kind, options }: CommonWidgetInputProps<"app">) => { - const t = useWidgetInputTranslation(kind, property); +export const WidgetAppInput = ({ property, kind }: CommonWidgetInputProps<"app">) => { + const t = useI18n(); + const tInput = useWidgetInputTranslation(kind, property); const form = useFormContext(); const { data: apps, isPending } = clientApi.app.selectable.useQuery(); @@ -24,10 +27,11 @@ export const WidgetAppInput = ({ property, kind, options }: CommonWidgetInputPro return (