feat: add configurable headers and instance branding (#6690)

This commit is contained in:
Thomas authored and GitHub committed 2026-08-25 17:09:47 +02:00
1 parent 97d8390b20
commit 9303dbd284
102 files changed
+12153 -609

No files matched your search

@@ -0,0 +1,4 @@
ALTER TABLE `user` ADD `header_preferences` text DEFAULT ('{"version":3,"visible":true,"searchDisplay":"input","logoDisplay":"logo","zones":{"left":[{"type":"builtin","id":"logo"}],"center":[{"type":"builtin","id":"search"}],"right":[{"type":"builtin","id":"boardEdit"},{"type":"builtin","id":"boardSettings"},{"type":"builtin","id":"user"}]}}') NOT NULL;
--> statement-breakpoint
INSERT IGNORE INTO `serverSetting` (`setting_key`, `value`)
VALUES ('branding', '{"json":{"appName":"Homarr","greeting":"","logoImageUrl":null,"faviconImageUrl":null,"primaryColor":"#fa5252","secondaryColor":"#fd7e14","lockPrimaryColor":false,"signInBackgroundImageUrl":null,"signInBackgroundOverlay":0.55,"authBranding":{"showAppName":true,"showLogo":true,"showGreeting":true},"defaultRadius":"md"}}');
File diff suppressed because it is too large. Load diff
@@ -337,6 +337,13 @@
"when": 1786217088950,
"tag": "0047_add_assistant_tables",
"breakpoints": true
},
{
"idx": 48,
"version": "5",
"when": 1787556966768,
"tag": "0048_add_header_preferences_and_branding",
"breakpoints": true
}
]
}
@@ -0,0 +1,5 @@
ALTER TABLE "user" ADD COLUMN "header_preferences" text DEFAULT '{"version":3,"visible":true,"searchDisplay":"input","logoDisplay":"logo","zones":{"left":[{"type":"builtin","id":"logo"}],"center":[{"type":"builtin","id":"search"}],"right":[{"type":"builtin","id":"boardEdit"},{"type":"builtin","id":"boardSettings"},{"type":"builtin","id":"user"}]}}' NOT NULL;
--> statement-breakpoint
INSERT INTO "serverSetting" ("setting_key", "value")
VALUES ('branding', '{"json":{"appName":"Homarr","greeting":"","logoImageUrl":null,"faviconImageUrl":null,"primaryColor":"#fa5252","secondaryColor":"#fd7e14","lockPrimaryColor":false,"signInBackgroundImageUrl":null,"signInBackgroundOverlay":0.55,"authBranding":{"showAppName":true,"showLogo":true,"showGreeting":true},"defaultRadius":"md"}}')
ON CONFLICT ("setting_key") DO NOTHING;
File diff suppressed because it is too large. Load diff
@@ -99,6 +99,13 @@
"when": 1786217088954,
"tag": "0013_add_assistant_tables",
"breakpoints": true
},
{
"idx": 14,
"version": "7",
"when": 1787556966592,
"tag": "0014_add_header_preferences_and_branding",
"breakpoints": true
}
]
}
@@ -0,0 +1,5 @@
ALTER TABLE `user` ADD `header_preferences` text DEFAULT '{"version":3,"visible":true,"searchDisplay":"input","logoDisplay":"logo","zones":{"left":[{"type":"builtin","id":"logo"}],"center":[{"type":"builtin","id":"search"}],"right":[{"type":"builtin","id":"boardEdit"},{"type":"builtin","id":"boardSettings"},{"type":"builtin","id":"user"}]}}' NOT NULL;
--> statement-breakpoint
INSERT INTO `serverSetting` (`setting_key`, `value`)
VALUES ('branding', '{"json":{"appName":"Homarr","greeting":"","logoImageUrl":null,"faviconImageUrl":null,"primaryColor":"#fa5252","secondaryColor":"#fd7e14","lockPrimaryColor":false,"signInBackgroundImageUrl":null,"signInBackgroundOverlay":0.55,"authBranding":{"showAppName":true,"showLogo":true,"showGreeting":true},"defaultRadius":"md"}}')
ON CONFLICT (`setting_key`) DO NOTHING;
File diff suppressed because it is too large. Load diff
@@ -323,6 +323,13 @@
"when": 1786217088950,
"tag": "0045_add_assistant_tables",
"breakpoints": true
},
{
"idx": 46,
"version": "6",
"when": 1787556956665,
"tag": "0046_add_header_preferences_and_branding",
"breakpoints": true
}
]
}
+16 -4
View File
@@ -1,7 +1,7 @@
import SuperJSON from "superjson";
import type { ServerSettings } from "@homarr/server-settings";
import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
import { defaultServerSettings, defaultServerSettingsKeys, parseBrandingSettings } from "@homarr/server-settings";
import type { Database } from "..";
import { eq } from "..";
@@ -18,10 +18,15 @@ export const getServerSettingsAsync = async (db: Database) => {
return acc;
}
const parsedSetting = SuperJSON.parse<Record<string, unknown>>(setting.value);
if (settingKey === "branding") {
acc[settingKey] = parseBrandingSettings(parsedSetting) as never;
return acc;
}
acc[settingKey] = {
...defaultServerSettings[settingKey],
...SuperJSON.parse(setting.value),
};
...parsedSetting,
} as never;
return acc;
}, {} as ServerSettings);
};
@@ -35,7 +40,14 @@ export const getServerSettingByKeyAsync = async <TKey extends keyof ServerSettin
return defaultServerSettings[key];
}
return SuperJSON.parse<ServerSettings[TKey]>(dbSettings.value);
const parsedSetting = SuperJSON.parse<ServerSettings[TKey]>(dbSettings.value);
if (key === "branding") {
return parseBrandingSettings(parsedSetting) as ServerSettings[TKey];
}
return {
...defaultServerSettings[key],
...parsedSetting,
} as ServerSettings[TKey];
};
export const updateServerSettingByKeyAsync = async <TKey extends keyof ServerSettings>(
+2
View File
@@ -43,6 +43,7 @@ import type {
WidgetKind,
} from "@homarr/definitions";
import type { CustomWidgetSecretKind } from "@homarr/custom-widgets/core";
import { defaultHeaderPreferencesSerialized } from "@homarr/validation/header-preferences";
const customBlob = customType<{ data: Buffer }>({
dataType() {
@@ -85,6 +86,7 @@ export const users = mysqlTable("user", {
firstDayOfWeek: tinyint().$type<DayOfWeek>().default(1).notNull(), // Defaults to Monday
pingIconsEnabled: boolean().default(false).notNull(),
enableRightClickOnWidgets: boolean().default(true).notNull(),
headerPreferences: text().default(defaultHeaderPreferencesSerialized).notNull(),
completedManageTour: boolean().default(false).notNull(),
completedBoardTour: boolean().default(false).notNull(),
});
+2
View File
@@ -41,6 +41,7 @@ import type {
WidgetKind,
} from "@homarr/definitions";
import type { CustomWidgetSecretKind } from "@homarr/custom-widgets/core";
import { defaultHeaderPreferencesSerialized } from "@homarr/validation/header-preferences";
const customBlob = customType<{ data: Buffer }>({
dataType() {
@@ -83,6 +84,7 @@ export const users = pgTable("user", {
firstDayOfWeek: smallint().$type<DayOfWeek>().default(1).notNull(), // Defaults to Monday
pingIconsEnabled: boolean().default(false).notNull(),
enableRightClickOnWidgets: boolean().default(true).notNull(),
headerPreferences: text().default(defaultHeaderPreferencesSerialized).notNull(),
completedManageTour: boolean().default(false).notNull(),
completedBoardTour: boolean().default(false).notNull(),
});
+2
View File
@@ -29,6 +29,7 @@ import type {
WidgetKind,
} from "@homarr/definitions";
import type { CustomWidgetSecretKind } from "@homarr/custom-widgets/core";
import { defaultHeaderPreferencesSerialized } from "@homarr/validation/header-preferences";
export * from "@homarr/core/infrastructure/certificates/hostnames/db/sqlite";
@@ -65,6 +66,7 @@ export const users = sqliteTable("user", {
firstDayOfWeek: int().$type<DayOfWeek>().default(1).notNull(), // Defaults to Monday
pingIconsEnabled: int({ mode: "boolean" }).default(false).notNull(),
enableRightClickOnWidgets: int({ mode: "boolean" }).default(true).notNull(),
headerPreferences: text().default(defaultHeaderPreferencesSerialized).notNull(),
completedManageTour: int({ mode: "boolean" }).default(false).notNull(),
completedBoardTour: int({ mode: "boolean" }).default(false).notNull(),
});