diff --git a/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx
new file mode 100644
index 000000000..9a2f3aecb
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import { useState } from "react";
+import type { PropsWithChildren } from "react";
+import type { MantineColorScheme, MantineColorSchemeManager } from "@mantine/core";
+import { createTheme, isMantineColorScheme, MantineProvider } from "@mantine/core";
+
+import { clientApi } from "@homarr/api/client";
+import { useSession } from "@homarr/auth/client";
+
+export const CustomMantineProvider = ({ children }: PropsWithChildren) => {
+ const manager = useColorSchemeManager();
+
+ return (
+
+ {children}
+
+ );
+};
+
+function useColorSchemeManager(): MantineColorSchemeManager {
+ const key = "homarr-color-scheme";
+ const { data: session } = useSession();
+ const [sessionColorScheme, setSessionColorScheme] = useState(
+ session?.user.colorScheme,
+ );
+ const { mutate: mutateColorScheme } = clientApi.user.changeColorScheme.useMutation({
+ onSuccess: (_, variables) => {
+ setSessionColorScheme(variables.colorScheme);
+ },
+ });
+
+ let handleStorageEvent: (event: StorageEvent) => void;
+
+ return {
+ get: (defaultValue) => {
+ if (typeof window === "undefined") {
+ return defaultValue;
+ }
+
+ if (sessionColorScheme) {
+ return sessionColorScheme;
+ }
+
+ try {
+ return (window.localStorage.getItem(key) as MantineColorScheme | undefined) ?? defaultValue;
+ } catch {
+ return defaultValue;
+ }
+ },
+
+ set: (value) => {
+ try {
+ if (session) {
+ mutateColorScheme({ colorScheme: value });
+ }
+ window.localStorage.setItem(key, value);
+ } catch (error) {
+ console.warn("[@mantine/core] Local storage color scheme manager was unable to save color scheme.", error);
+ }
+ },
+
+ subscribe: (onUpdate) => {
+ handleStorageEvent = (event) => {
+ if (session) return; // Ignore updates when session is available as we are using session color scheme
+ if (event.storageArea === window.localStorage && event.key === key && isMantineColorScheme(event.newValue)) {
+ onUpdate(event.newValue);
+ }
+ };
+
+ window.addEventListener("storage", handleStorageEvent);
+ },
+
+ unsubscribe: () => {
+ window.removeEventListener("storage", handleStorageEvent);
+ },
+
+ clear: () => {
+ window.localStorage.removeItem(key);
+ },
+ };
+}
diff --git a/apps/nextjs/src/app/[locale]/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx
index 223ebf9be..ae7c4600c 100644
--- a/apps/nextjs/src/app/[locale]/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/layout.tsx
@@ -1,13 +1,11 @@
import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google";
-import "@homarr/ui/styles.css";
import "@homarr/notifications/styles.css";
import "@homarr/spotlight/styles.css";
+import "@homarr/ui/styles.css";
import "~/styles/scroll-area.scss";
-import { ColorSchemeScript, createTheme, MantineProvider } from "@mantine/core";
-
import { env } from "@homarr/auth/env.mjs";
import { auth } from "@homarr/auth/next";
import { ModalProvider } from "@homarr/modals";
@@ -15,6 +13,7 @@ import { Notifications } from "@homarr/notifications";
import { Analytics } from "~/components/layout/analytics";
import { JotaiProvider } from "./_client-providers/jotai";
+import { CustomMantineProvider } from "./_client-providers/mantine";
import { NextInternationalProvider } from "./_client-providers/next-international";
import { AuthProvider } from "./_client-providers/session";
import { TRPCReactProvider } from "./_client-providers/trpc";
@@ -51,34 +50,25 @@ export const viewport: Viewport = {
],
};
-export default function Layout(props: { children: React.ReactNode; params: { locale: string } }) {
- const colorScheme = "dark";
+export default async function Layout(props: { children: React.ReactNode; params: { locale: string } }) {
+ const session = await auth();
+ const colorScheme = session?.user.colorScheme;
const StackedProvider = composeWrappers([
- async (innerProps) => {
- const session = await auth();
+ (innerProps) => {
return ;
},
(innerProps) => ,
(innerProps) => ,
(innerProps) => ,
- (innerProps) => (
-
- ),
+ (innerProps) => ,
(innerProps) => ,
]);
return (
-
+ // Instead of ColorSchemScript we use data-mantine-color-scheme to prevent flickering
+
-
diff --git a/packages/api/src/router/test/board.spec.ts b/packages/api/src/router/test/board.spec.ts
index c1d0f45bb..b95f6279c 100644
--- a/packages/api/src/router/test/board.spec.ts
+++ b/packages/api/src/router/test/board.spec.ts
@@ -30,6 +30,7 @@ const defaultSession = {
user: {
id: defaultCreatorId,
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -87,6 +88,7 @@ describe("getAllBoards should return all boards accessable to the current user",
user: {
id: defaultCreatorId,
permissions: ["board-view-all"],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
},
diff --git a/packages/api/src/router/test/docker/docker-router.spec.ts b/packages/api/src/router/test/docker/docker-router.spec.ts
index c92a0bbfc..f81313fe2 100644
--- a/packages/api/src/router/test/docker/docker-router.spec.ts
+++ b/packages/api/src/router/test/docker/docker-router.spec.ts
@@ -29,6 +29,7 @@ const createSessionWithPermissions = (...permissions: GroupPermissionKey[]) =>
user: {
id: "1",
permissions,
+ colorScheme: "light",
},
expires: new Date().toISOString(),
}) satisfies Session;
diff --git a/packages/api/src/router/test/group.spec.ts b/packages/api/src/router/test/group.spec.ts
index 51c11e70e..5e3ca35a4 100644
--- a/packages/api/src/router/test/group.spec.ts
+++ b/packages/api/src/router/test/group.spec.ts
@@ -12,6 +12,7 @@ const defaultSession = {
user: {
id: defaultOwnerId,
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
diff --git a/packages/api/src/router/test/integration/integration-router.spec.ts b/packages/api/src/router/test/integration/integration-router.spec.ts
index 2f21e666b..dde896352 100644
--- a/packages/api/src/router/test/integration/integration-router.spec.ts
+++ b/packages/api/src/router/test/integration/integration-router.spec.ts
@@ -17,6 +17,7 @@ const defaultSessionWithPermissions = (permissions: GroupPermissionKey[] = []) =
user: {
id: defaultUserId,
permissions,
+ colorScheme: "light",
},
expires: new Date().toISOString(),
}) satisfies Session;
diff --git a/packages/api/src/router/test/invite.spec.ts b/packages/api/src/router/test/invite.spec.ts
index 9c39d5bb2..6be30f7a9 100644
--- a/packages/api/src/router/test/invite.spec.ts
+++ b/packages/api/src/router/test/invite.spec.ts
@@ -12,6 +12,7 @@ const defaultSession = {
user: {
id: createId(),
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
diff --git a/packages/api/src/router/test/serverSettings.spec.ts b/packages/api/src/router/test/serverSettings.spec.ts
index 331631229..dffa42e35 100644
--- a/packages/api/src/router/test/serverSettings.spec.ts
+++ b/packages/api/src/router/test/serverSettings.spec.ts
@@ -16,6 +16,7 @@ const defaultSession = {
user: {
id: createId(),
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
diff --git a/packages/api/src/router/test/user.spec.ts b/packages/api/src/router/test/user.spec.ts
index 9a4696969..e27074426 100644
--- a/packages/api/src/router/test/user.spec.ts
+++ b/packages/api/src/router/test/user.spec.ts
@@ -246,6 +246,7 @@ describe("editProfile shoud update user", () => {
image: null,
homeBoardId: null,
provider: "credentials",
+ colorScheme: "auto",
});
});
@@ -287,6 +288,7 @@ describe("editProfile shoud update user", () => {
image: null,
homeBoardId: null,
provider: "credentials",
+ colorScheme: "auto",
});
});
});
@@ -312,6 +314,7 @@ describe("delete should delete user", () => {
salt: null,
homeBoardId: null,
provider: "ldap" as const,
+ colorScheme: "auto" as const,
},
{
id: userToDelete,
@@ -322,6 +325,7 @@ describe("delete should delete user", () => {
password: null,
salt: null,
homeBoardId: null,
+ colorScheme: "auto" as const,
},
{
id: createId(),
@@ -333,6 +337,7 @@ describe("delete should delete user", () => {
salt: null,
homeBoardId: null,
provider: "oidc" as const,
+ colorScheme: "auto" as const,
},
];
diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts
index 4ede622cf..362b3b7d7 100644
--- a/packages/api/src/router/user.ts
+++ b/packages/api/src/router/user.ts
@@ -317,6 +317,14 @@ export const userRouter = createTRPCRouter({
})
.where(eq(users.id, input.userId));
}),
+ changeColorScheme: protectedProcedure.input(validation.user.changeColorScheme).mutation(async ({ input, ctx }) => {
+ await ctx.db
+ .update(users)
+ .set({
+ colorScheme: input.colorScheme,
+ })
+ .where(eq(users.id, ctx.session.user.id));
+ }),
});
const createUserAsync = async (db: Database, input: z.infer) => {
diff --git a/packages/auth/callbacks.ts b/packages/auth/callbacks.ts
index 90242b836..3665a674f 100644
--- a/packages/auth/callbacks.ts
+++ b/packages/auth/callbacks.ts
@@ -4,7 +4,7 @@ import type { NextAuthConfig } from "next-auth";
import type { Database } from "@homarr/db";
import { eq, inArray } from "@homarr/db";
-import { groupMembers, groupPermissions } from "@homarr/db/schema/sqlite";
+import { groupMembers, groupPermissions, users } from "@homarr/db/schema/sqlite";
import { getPermissionsWithChildren } from "@homarr/definitions";
import { env } from "./env.mjs";
@@ -31,10 +31,18 @@ export const getCurrentUserPermissionsAsync = async (db: Database, userId: strin
export const createSessionCallback = (db: Database): NextAuthCallbackOf<"session"> => {
return async ({ session, user }) => {
+ const additionalProperties = await db.query.users.findFirst({
+ where: eq(users.id, user.id),
+ columns: {
+ colorScheme: true,
+ },
+ });
+
return {
...session,
user: {
...session.user,
+ ...additionalProperties,
id: user.id,
name: user.name,
permissions: await getCurrentUserPermissionsAsync(db, user.id),
diff --git a/packages/auth/index.ts b/packages/auth/index.ts
index 78c3e7e88..1cb226fa2 100644
--- a/packages/auth/index.ts
+++ b/packages/auth/index.ts
@@ -1,7 +1,7 @@
import { headers } from "next/headers";
import type { DefaultSession } from "@auth/core/types";
-import type { GroupPermissionKey } from "@homarr/definitions";
+import type { ColorScheme, GroupPermissionKey } from "@homarr/definitions";
import { createConfiguration } from "./configuration";
@@ -12,6 +12,7 @@ declare module "next-auth" {
user: {
id: string;
permissions: GroupPermissionKey[];
+ colorScheme: ColorScheme;
} & DefaultSession["user"];
}
}
diff --git a/packages/auth/permissions/test/board-permissions.spec.ts b/packages/auth/permissions/test/board-permissions.spec.ts
index 3e02e6be6..f897491ae 100644
--- a/packages/auth/permissions/test/board-permissions.spec.ts
+++ b/packages/auth/permissions/test/board-permissions.spec.ts
@@ -20,6 +20,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "1",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -47,6 +48,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["board-full-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -74,6 +76,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["board-modify-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -102,6 +105,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -129,6 +133,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -156,6 +161,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["board-view-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -183,6 +189,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -210,6 +217,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -237,6 +245,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -264,6 +273,7 @@ describe("constructBoardPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
diff --git a/packages/auth/permissions/test/integration-permissions.spec.ts b/packages/auth/permissions/test/integration-permissions.spec.ts
index 01c9ba818..b84041ae6 100644
--- a/packages/auth/permissions/test/integration-permissions.spec.ts
+++ b/packages/auth/permissions/test/integration-permissions.spec.ts
@@ -16,6 +16,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["integration-full-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -39,6 +40,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["integration-interact-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -62,6 +64,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -85,6 +88,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -108,6 +112,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: getPermissionsWithChildren(["integration-use-all"]),
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -131,6 +136,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -154,6 +160,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -177,6 +184,7 @@ describe("constructIntegrationPermissions", () => {
user: {
id: "2",
permissions: [],
+ colorScheme: "light",
},
expires: new Date().toISOString(),
} satisfies Session;
@@ -190,40 +198,3 @@ describe("constructIntegrationPermissions", () => {
expect(result.hasUseAccess).toBe(false);
});
});
-/*
-
-
-
-
-
-
-
-
- test("should return hasViewAccess as true when board is public", () => {
- // Arrange
- const board = {
- creator: {
- id: "1",
- },
- userPermissions: [],
- groupPermissions: [],
- isPublic: true,
- };
- const session = {
- user: {
- id: "2",
- permissions: [],
- },
- expires: new Date().toISOString(),
- } satisfies Session;
-
- // Act
- const result = constructBoardPermissions(board, session);
-
- // Assert
- expect(result.hasFullAccess).toBe(false);
- expect(result.hasChangeAccess).toBe(false);
- expect(result.hasViewAccess).toBe(true);
- });
-});
-*/
diff --git a/packages/auth/permissions/test/integration-query-permissions.spec.ts b/packages/auth/permissions/test/integration-query-permissions.spec.ts
index c0185c177..faf527d92 100644
--- a/packages/auth/permissions/test/integration-query-permissions.spec.ts
+++ b/packages/auth/permissions/test/integration-query-permissions.spec.ts
@@ -20,6 +20,7 @@ const createSession = (user: Partial): Session => ({
user: {
id: "1",
permissions: [],
+ colorScheme: "light",
...user,
},
expires: new Date().toISOString(),
diff --git a/packages/auth/session.ts b/packages/auth/session.ts
index 48701ef0b..5510ca89b 100644
--- a/packages/auth/session.ts
+++ b/packages/auth/session.ts
@@ -32,6 +32,7 @@ export const getSessionFromTokenAsync = async (db: Database, token: string | und
name: true,
email: true,
image: true,
+ colorScheme: true,
},
},
},
diff --git a/packages/auth/test/callbacks.spec.ts b/packages/auth/test/callbacks.spec.ts
index 755b56fe8..e793c6749 100644
--- a/packages/auth/test/callbacks.spec.ts
+++ b/packages/auth/test/callbacks.spec.ts
@@ -101,6 +101,7 @@ describe("session callback", () => {
email: "no-email",
emailVerified: new Date("2023-01-13"),
permissions: [],
+ colorScheme: "dark",
},
expires: "2023-01-13" as Date & string,
sessionToken: "token",
diff --git a/packages/db/migrations/mysql/0007_boring_nocturne.sql b/packages/db/migrations/mysql/0007_boring_nocturne.sql
new file mode 100644
index 000000000..b041b6918
--- /dev/null
+++ b/packages/db/migrations/mysql/0007_boring_nocturne.sql
@@ -0,0 +1 @@
+ALTER TABLE `user` ADD `colorScheme` varchar(5) DEFAULT 'auto' NOT NULL;
\ No newline at end of file
diff --git a/packages/db/migrations/mysql/meta/0007_snapshot.json b/packages/db/migrations/mysql/meta/0007_snapshot.json
new file mode 100644
index 000000000..b1b162698
--- /dev/null
+++ b/packages/db/migrations/mysql/meta/0007_snapshot.json
@@ -0,0 +1,1373 @@
+{
+ "version": "5",
+ "dialect": "mysql",
+ "id": "b7264307-fc31-48c0-973f-0e0dd91f5f90",
+ "prevId": "67352107-06b7-4f5d-a4e0-4ba27327f588",
+ "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": {}
+ },
+ "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": "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": {
+ "name": "integrationGroupPermissions_integration_id_group_id_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": "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": {
+ "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": {}
+ },
+ "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": "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": {
+ "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'"
+ }
+ },
+ "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 1a45a04c0..8917665c6 100644
--- a/packages/db/migrations/mysql/meta/_journal.json
+++ b/packages/db/migrations/mysql/meta/_journal.json
@@ -50,6 +50,13 @@
"when": 1722517058725,
"tag": "0006_young_micromax",
"breakpoints": true
+ },
+ {
+ "idx": 7,
+ "version": "5",
+ "when": 1723749320706,
+ "tag": "0007_boring_nocturne",
+ "breakpoints": true
}
]
}
diff --git a/packages/db/migrations/sqlite/0007_known_ultragirl.sql b/packages/db/migrations/sqlite/0007_known_ultragirl.sql
new file mode 100644
index 000000000..7a4040a01
--- /dev/null
+++ b/packages/db/migrations/sqlite/0007_known_ultragirl.sql
@@ -0,0 +1 @@
+ALTER TABLE `user` ADD `colorScheme` text DEFAULT 'auto' NOT NULL;
diff --git a/packages/db/migrations/sqlite/meta/0007_snapshot.json b/packages/db/migrations/sqlite/meta/0007_snapshot.json
new file mode 100644
index 000000000..85bccfaea
--- /dev/null
+++ b/packages/db/migrations/sqlite/meta/0007_snapshot.json
@@ -0,0 +1,1316 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "11d3d936-3e3a-43b7-84b8-f0a1020b3b82",
+ "prevId": "589ffaa4-4b95-4c6e-84b0-5b0d7959750b",
+ "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": {}
+ },
+ "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": {}
+ },
+ "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'"
+ }
+ },
+ "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 99839fc25..e2e4864d9 100644
--- a/packages/db/migrations/sqlite/meta/_journal.json
+++ b/packages/db/migrations/sqlite/meta/_journal.json
@@ -50,6 +50,13 @@
"when": 1722517033483,
"tag": "0006_windy_doctor_faustus",
"breakpoints": true
+ },
+ {
+ "idx": 7,
+ "version": "6",
+ "when": 1723746828385,
+ "tag": "0007_known_ultragirl",
+ "breakpoints": true
}
]
}
diff --git a/packages/db/schema/mysql.ts b/packages/db/schema/mysql.ts
index e45186ac0..32cabdbe5 100644
--- a/packages/db/schema/mysql.ts
+++ b/packages/db/schema/mysql.ts
@@ -8,6 +8,7 @@ import type {
BackgroundImageRepeat,
BackgroundImageSize,
BoardPermission,
+ ColorScheme,
GroupPermissionKey,
IntegrationKind,
IntegrationPermission,
@@ -30,6 +31,7 @@ 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(),
});
export const accounts = mysqlTable(
diff --git a/packages/db/schema/sqlite.ts b/packages/db/schema/sqlite.ts
index 197dd9c8e..268c19c75 100644
--- a/packages/db/schema/sqlite.ts
+++ b/packages/db/schema/sqlite.ts
@@ -10,6 +10,7 @@ import type {
BackgroundImageRepeat,
BackgroundImageSize,
BoardPermission,
+ ColorScheme,
GroupPermissionKey,
IntegrationKind,
IntegrationPermission,
@@ -31,6 +32,7 @@ export const users = sqliteTable("user", {
homeBoardId: text("homeBoardId").references((): AnySQLiteColumn => boards.id, {
onDelete: "set null",
}),
+ colorScheme: text("colorScheme").$type().default("auto").notNull(),
});
export const accounts = sqliteTable(
diff --git a/packages/definitions/src/index.ts b/packages/definitions/src/index.ts
index 140b2d924..d2dcd8e0a 100644
--- a/packages/definitions/src/index.ts
+++ b/packages/definitions/src/index.ts
@@ -5,3 +5,4 @@ export * from "./widget";
export * from "./permissions";
export * from "./docker";
export * from "./auth";
+export * from "./user";
diff --git a/packages/definitions/src/user.ts b/packages/definitions/src/user.ts
new file mode 100644
index 000000000..28f24ae7b
--- /dev/null
+++ b/packages/definitions/src/user.ts
@@ -0,0 +1,2 @@
+export const colorSchemes = ["light", "dark", "auto"] as const;
+export type ColorScheme = (typeof colorSchemes)[number];
diff --git a/packages/validation/src/user.ts b/packages/validation/src/user.ts
index 5d26be3d3..1057265cd 100644
--- a/packages/validation/src/user.ts
+++ b/packages/validation/src/user.ts
@@ -1,7 +1,9 @@
import { z } from "zod";
+import { colorSchemes } from "@homarr/definitions";
import type { TranslationObject } from "@homarr/translation";
+import { zodEnumFromArray } from "./enums";
import { createCustomErrorParams } from "./form/i18n";
const usernameSchema = z.string().min(3).max(255);
@@ -98,6 +100,10 @@ const changeHomeBoardSchema = z.object({
homeBoardId: z.string().min(1),
});
+const changeColorSchemeSchema = z.object({
+ colorScheme: zodEnumFromArray(colorSchemes),
+});
+
export const userSchemas = {
signIn: signInSchema,
registration: registrationSchema,
@@ -109,4 +115,5 @@ export const userSchemas = {
changePassword: changePasswordSchema,
changeHomeBoard: changeHomeBoardSchema,
changePasswordApi: changePasswordApiSchema,
+ changeColorScheme: changeColorSchemeSchema,
};