feat!: release v2 (#6545)
Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> Co-authored-by: arturict <arturict@users.noreply.github.com> Co-authored-by: Thomas <thomas@ajnart.fr> Co-authored-by: Afonso Jorge Ramos <afonsojorgeramos@gmail.com> Co-authored-by: Danura30082 <danura30082+github@gmail.com> Co-authored-by: Kushie <122986913+kushiemoon-dev@users.noreply.github.com> Co-authored-by: ajnart <thomas@ajnart.fr> Co-authored-by: ajnart <thomas@ajnart.dev> Co-authored-by: chenjun <chenjun@foxmail.com> Co-authored-by: Wikibear <wikibear@users.noreply.github.com> Co-authored-by: Bergasha <bergasha@gmail.com> Co-authored-by: leafyelin <43306196+leafyelin@users.noreply.github.com> Co-authored-by: Lucas Araujo <lucas.lra@gmail.com> Co-authored-by: GreenMonito <86085570+GreenMonito@users.noreply.github.com> Co-authored-by: Nerdy-Technician <roffo@nerdy-technician.social> Co-authored-by: Jun <34545896+jcstrong@users.noreply.github.com> Co-authored-by: Edmond Abraham <54119952+gitedmond@users.noreply.github.com> Co-authored-by: thrawnx <tight.bulb9384@fastmail.com> Co-authored-by: Chris <58918656+nordada@users.noreply.github.com> Co-authored-by: yi111 <153097222+Yi-111-a@users.noreply.github.com> Co-authored-by: Yanis <naamaneyanis@gmail.com> Co-authored-by: Wikibear <me@sej.de> Co-authored-by: alessiottavo <93441886+alessiottavo@users.noreply.github.com>
This commit is contained in:
3286 files changed
+284386
-139176
No files matched your search
+170
-23
@@ -3,7 +3,7 @@ import type { MantineSize } from "@mantine/core";
|
||||
import type { DayOfWeek } from "@mantine/dates";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import type { AnySQLiteColumn } from "drizzle-orm/sqlite-core";
|
||||
import { blob, index, int, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
import { blob, foreignKey, index, int, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
|
||||
import {
|
||||
backgroundImageAttachments,
|
||||
@@ -21,18 +21,17 @@ import type {
|
||||
IntegrationKind,
|
||||
IntegrationPermission,
|
||||
IntegrationSecretKind,
|
||||
LayoutRole,
|
||||
OnboardingStep,
|
||||
SearchEngineType,
|
||||
SectionKind,
|
||||
SupportedAuthProvider,
|
||||
WidgetKind,
|
||||
} from "@homarr/definitions";
|
||||
import type {
|
||||
CustomWidgetAuthType,
|
||||
CustomWidgetDisplayType,
|
||||
CustomWidgetMethod,
|
||||
CustomWidgetSecretKind,
|
||||
} from "@homarr/validation/custom-widget";
|
||||
import { defaultByteUnitSystem } from "@homarr/common";
|
||||
import type { ByteUnitSystem } from "@homarr/common";
|
||||
import type { CustomWidgetSecretKind } from "@homarr/custom-widgets/core";
|
||||
import { defaultHeaderPreferencesSerialized } from "@homarr/validation/header-preferences";
|
||||
|
||||
export * from "@homarr/core/infrastructure/certificates/hostnames/db/sqlite";
|
||||
|
||||
@@ -66,9 +65,11 @@ export const users = sqliteTable("user", {
|
||||
openSearchInNewTab: int({ mode: "boolean" }).default(true).notNull(),
|
||||
ddgBangs: int({ mode: "boolean" }).default(true).notNull(),
|
||||
colorScheme: text().$type<ColorScheme>().default("dark").notNull(),
|
||||
byteUnitSystem: text().$type<ByteUnitSystem>().default(defaultByteUnitSystem).notNull(),
|
||||
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(),
|
||||
});
|
||||
@@ -197,8 +198,8 @@ export const integrations = sqliteTable(
|
||||
kind: text().$type<IntegrationKind>().notNull(),
|
||||
appId: text().references(() => apps.id, { onDelete: "set null" }),
|
||||
},
|
||||
(integrations) => ({
|
||||
kindIdx: index("integration__kind_idx").on(integrations.kind),
|
||||
(table) => ({
|
||||
kindIdx: index("integration__kind_idx").on(table.kind),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -329,7 +330,10 @@ export const layouts = sqliteTable("layout", {
|
||||
.notNull()
|
||||
.references(() => boards.id, { onDelete: "cascade" }),
|
||||
columnCount: int().notNull(),
|
||||
leftGutterColumnCount: int().notNull().default(0),
|
||||
rightGutterColumnCount: int().notNull().default(0),
|
||||
breakpoint: int().notNull().default(0),
|
||||
role: text().$type<LayoutRole>().notNull().default("custom"),
|
||||
});
|
||||
|
||||
export const itemLayouts = sqliteTable(
|
||||
@@ -466,6 +470,79 @@ export const serverSettings = sqliteTable("serverSetting", {
|
||||
value: text().default(emptySuperJSON).notNull(),
|
||||
});
|
||||
|
||||
export const assistantConfigurations = sqliteTable("assistant_configuration", {
|
||||
id: text().notNull().primaryKey().default("default"),
|
||||
enabled: int({ mode: "boolean" }).notNull().default(false),
|
||||
webSearchEnabled: int({ mode: "boolean" }).notNull().default(false),
|
||||
provider: text()
|
||||
.$type<
|
||||
| "homarr"
|
||||
| "openrouter"
|
||||
| "openai"
|
||||
| "anthropic"
|
||||
| "google-gemini"
|
||||
| "xai"
|
||||
| "groq"
|
||||
| "mistral"
|
||||
| "deepseek"
|
||||
| "together"
|
||||
| "ollama"
|
||||
| "lm-studio"
|
||||
| "llama-cpp"
|
||||
| "custom"
|
||||
>()
|
||||
.notNull()
|
||||
.default("openrouter"),
|
||||
baseUrl: text().notNull().default("https://openrouter.ai/api/v1"),
|
||||
modelDiscoveryPath: text().default("/models"),
|
||||
encryptedApiKey: text().$type<`${string}.${string}`>(),
|
||||
encryptedHeaders: text().$type<`${string}.${string}`>(),
|
||||
modelId: text(),
|
||||
updatedAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
});
|
||||
|
||||
export const assistantThreads = sqliteTable(
|
||||
"assistant_thread",
|
||||
{
|
||||
id: text().notNull().primaryKey(),
|
||||
userId: text()
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
title: text(),
|
||||
modelId: text(),
|
||||
createdAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
updatedAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
},
|
||||
(thread) => ({
|
||||
userUpdatedAtIdx: index("assistant_thread__user_id_updated_at_idx").on(thread.userId, thread.updatedAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export const assistantMessages = sqliteTable(
|
||||
"assistant_message",
|
||||
{
|
||||
id: text().notNull().primaryKey(),
|
||||
threadId: text()
|
||||
.notNull()
|
||||
.references(() => assistantThreads.id, { onDelete: "cascade" }),
|
||||
parentId: text(),
|
||||
format: text().notNull().default("ai-sdk/v6"),
|
||||
content: text().notNull(),
|
||||
createdAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
},
|
||||
(message) => ({
|
||||
threadCreatedAtIdx: index("assistant_message__thread_id_created_at_idx").on(message.threadId, message.createdAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export const apiKeyRelations = relations(apiKeys, ({ one }) => ({
|
||||
user: one(users, {
|
||||
fields: [apiKeys.userId],
|
||||
@@ -490,18 +567,69 @@ export const onboarding = sqliteTable("onboarding", {
|
||||
previousStep: text().$type<OnboardingStep>(),
|
||||
});
|
||||
|
||||
export const customWidgetDefinitions = sqliteTable("custom_widget_definition", {
|
||||
/**
|
||||
* Read-only v1 definitions retained during the Custom JSX v2 upgrade.
|
||||
* Their original physical table names keep the database readable by a v1 binary.
|
||||
*/
|
||||
export const legacyCustomWidgetDefinitions = sqliteTable(
|
||||
"custom_widget_definition",
|
||||
{
|
||||
id: text().notNull().primaryKey(),
|
||||
name: text().notNull(),
|
||||
description: text(),
|
||||
iconUrl: text(),
|
||||
url: text().notNull(),
|
||||
authType: text().notNull().default("none"),
|
||||
headerName: text(),
|
||||
method: text().notNull().default("GET"),
|
||||
requestBody: text(),
|
||||
displayType: text().notNull().default("singleValue"),
|
||||
displayConfig: text().default(emptySuperJSON).notNull(),
|
||||
enabled: int({ mode: "boolean" }).notNull().default(true),
|
||||
createdAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
updatedAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
.default(sql`(unixepoch())`),
|
||||
creatorId: text(),
|
||||
},
|
||||
(table) => ({
|
||||
creatorFk: foreignKey({
|
||||
columns: [table.creatorId],
|
||||
foreignColumns: [users.id],
|
||||
name: "custom_widget_definition_creator_id_user_id_fk",
|
||||
}).onDelete("set null"),
|
||||
}),
|
||||
);
|
||||
|
||||
export const legacyCustomWidgetSecrets = sqliteTable(
|
||||
"custom_widget_secret",
|
||||
{
|
||||
kind: text().$type<CustomWidgetSecretKind>().notNull(),
|
||||
encryptedValue: text("value").$type<`${string}.${string}`>().notNull(),
|
||||
updatedAt: int({ mode: "timestamp" }).notNull(),
|
||||
definitionId: text().notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
compoundKey: primaryKey({ columns: [table.definitionId, table.kind] }),
|
||||
definitionFk: foreignKey({
|
||||
columns: [table.definitionId],
|
||||
foreignColumns: [legacyCustomWidgetDefinitions.id],
|
||||
name: "custom_widget_secret_definition_id_custom_widget_definition_id_fk",
|
||||
}).onDelete("cascade"),
|
||||
}),
|
||||
);
|
||||
|
||||
export const customWidgetDefinitions = sqliteTable("custom_widget_v2_definition", {
|
||||
id: text().notNull().primaryKey(),
|
||||
name: text().notNull(),
|
||||
description: text(),
|
||||
iconUrl: text(),
|
||||
url: text().notNull(),
|
||||
authType: text().$type<CustomWidgetAuthType>().notNull().default("none"),
|
||||
headerName: text(),
|
||||
method: text().$type<CustomWidgetMethod>().notNull().default("GET"),
|
||||
requestBody: text(),
|
||||
displayType: text().$type<CustomWidgetDisplayType>().notNull().default("singleValue"),
|
||||
displayConfig: text().default(emptySuperJSON).notNull(),
|
||||
sources: text().notNull(),
|
||||
requests: text().notNull(),
|
||||
options: text().notNull(),
|
||||
template: text().notNull(),
|
||||
enabled: int({ mode: "boolean" }).notNull().default(true),
|
||||
createdAt: int({ mode: "timestamp" })
|
||||
.notNull()
|
||||
@@ -513,21 +641,25 @@ export const customWidgetDefinitions = sqliteTable("custom_widget_definition", {
|
||||
});
|
||||
|
||||
export const customWidgetSecrets = sqliteTable(
|
||||
"custom_widget_secret",
|
||||
"custom_widget_v2_secret",
|
||||
{
|
||||
sourceId: text().notNull(),
|
||||
kind: text().$type<CustomWidgetSecretKind>().notNull(),
|
||||
value: text().$type<`${string}.${string}`>().notNull(),
|
||||
encryptedValue: text("encrypted_value").$type<`${string}.${string}`>().notNull(),
|
||||
updatedAt: int({ mode: "timestamp" })
|
||||
.$onUpdateFn(() => new Date())
|
||||
.notNull(),
|
||||
definitionId: text()
|
||||
.notNull()
|
||||
.references(() => customWidgetDefinitions.id, { onDelete: "cascade" }),
|
||||
definitionId: text().notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
compoundKey: primaryKey({
|
||||
columns: [table.definitionId, table.kind],
|
||||
columns: [table.definitionId, table.sourceId, table.kind],
|
||||
}),
|
||||
definitionFk: foreignKey({
|
||||
columns: [table.definitionId],
|
||||
foreignColumns: [customWidgetDefinitions.id],
|
||||
name: "custom_widget_v2_secret_definition_id_fk",
|
||||
}).onDelete("cascade"),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -843,3 +975,18 @@ export const customWidgetSecretRelations = relations(customWidgetSecrets, ({ one
|
||||
references: [customWidgetDefinitions.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const legacyCustomWidgetDefinitionRelations = relations(legacyCustomWidgetDefinitions, ({ many, one }) => ({
|
||||
secrets: many(legacyCustomWidgetSecrets),
|
||||
creator: one(users, {
|
||||
fields: [legacyCustomWidgetDefinitions.creatorId],
|
||||
references: [users.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const legacyCustomWidgetSecretRelations = relations(legacyCustomWidgetSecrets, ({ one }) => ({
|
||||
definition: one(legacyCustomWidgetDefinitions, {
|
||||
fields: [legacyCustomWidgetSecrets.definitionId],
|
||||
references: [legacyCustomWidgetDefinitions.id],
|
||||
}),
|
||||
}));
|
||||
Reference in new issue
Block a user