diff --git a/app/client/lib/__tests__/datetime.test.ts b/app/client/lib/__tests__/datetime.test.ts
index 544a9aeb..5a573ea5 100644
--- a/app/client/lib/__tests__/datetime.test.ts
+++ b/app/client/lib/__tests__/datetime.test.ts
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, mock, spyOn, test } from "bun:test";
import {
+ DEFAULT_TIME_FORMAT,
formatDate,
formatDateTime,
formatDateWithMonth,
@@ -64,4 +65,38 @@ describe("datetime formatters", () => {
test("formats calendar values with an explicit locale and timezone", () => {
expect(formatShortDateTime(sampleDate, { locale: "en-US", timeZone: "UTC" })).toBe("1/10, 2:30 PM");
});
+
+ test.each([
+ ["MM/DD/YYYY", "1/10/2026"],
+ ["DD/MM/YYYY", "10/1/2026"],
+ ["YYYY/MM/DD", "2026/1/10"],
+ ] as const)("formats numeric dates with %s order", (dateFormat, expected) => {
+ expect(formatDate(sampleDate, { locale: "en-US", timeZone: "UTC", dateFormat })).toBe(expected);
+ });
+
+ test.each([
+ ["MM/DD/YYYY", "Jan 10, 2026"],
+ ["DD/MM/YYYY", "10 Jan 2026"],
+ ["YYYY/MM/DD", "2026 Jan 10"],
+ ] as const)("formats month dates with %s order", (dateFormat, expected) => {
+ expect(formatDateWithMonth(sampleDate, { locale: "en-US", timeZone: "UTC", dateFormat })).toBe(expected);
+ });
+
+ test.each([
+ [DEFAULT_TIME_FORMAT, "2:30 PM"],
+ ["24h", "14:30"],
+ ] as const)("formats times with %s clock", (timeFormat, expected) => {
+ expect(formatTime(sampleDate, { locale: "en-US", timeZone: "UTC", timeFormat })).toBe(expected);
+ });
+
+ test("formats combined values with custom date and time preferences", () => {
+ expect(
+ formatDateTime(sampleDate, {
+ locale: "en-US",
+ timeZone: "UTC",
+ dateFormat: "DD/MM/YYYY",
+ timeFormat: "24h",
+ }),
+ ).toBe("10/1/2026, 14:30");
+ });
});
diff --git a/app/client/lib/datetime.ts b/app/client/lib/datetime.ts
index 55a5575f..81f3c92f 100644
--- a/app/client/lib/datetime.ts
+++ b/app/client/lib/datetime.ts
@@ -1,12 +1,35 @@
import { formatDistanceToNow, isValid } from "date-fns";
import { useEffect, useMemo, useState } from "react";
+import { authClient } from "~/client/lib/auth-client";
import { Route as RootRoute } from "~/routes/__root";
export type DateInput = Date | string | number | null | undefined;
+export const DATE_FORMATS = ["MM/DD/YYYY", "DD/MM/YYYY", "YYYY/MM/DD"] as const;
+export type DateFormatPreference = (typeof DATE_FORMATS)[number];
+export const DEFAULT_DATE_FORMAT: DateFormatPreference = "MM/DD/YYYY";
+
+export const TIME_FORMATS = ["12h", "24h"] as const;
+export type TimeFormatPreference = (typeof TIME_FORMATS)[number];
+export const DEFAULT_TIME_FORMAT: TimeFormatPreference = "12h";
+
+const DATE_PART_ORDERS = {
+ "MM/DD/YYYY": ["month", "day", "year"],
+ "DD/MM/YYYY": ["day", "month", "year"],
+ "YYYY/MM/DD": ["year", "month", "day"],
+} as const;
+
+const SHORT_DATE_PART_ORDERS = {
+ "MM/DD/YYYY": ["month", "day"],
+ "DD/MM/YYYY": ["day", "month"],
+ "YYYY/MM/DD": ["month", "day"],
+} as const;
+
type DateFormatOptions = {
locale?: string | string[];
timeZone?: string;
+ dateFormat?: DateFormatPreference;
+ timeFormat?: TimeFormatPreference;
};
function formatValidDate(date: DateInput, formatter: (date: Date) => string): string {
@@ -29,71 +52,97 @@ function getDateTimeFormat(
});
}
+function getRequiredPart(parts: Intl.DateTimeFormatPart[], type: Intl.DateTimeFormatPartTypes) {
+ const value = parts.find((part) => part.type === type)?.value;
+
+ if (!value) {
+ throw new Error(`Missing ${type} in formatted date`);
+ }
+
+ return value;
+}
+
+function formatConfiguredDate(date: Date, options: DateFormatOptions, includeYear: boolean) {
+ const dateFormat = options.dateFormat ?? DEFAULT_DATE_FORMAT;
+ const parts = getDateTimeFormat(options.locale, options.timeZone, {
+ month: "numeric",
+ day: "numeric",
+ year: "numeric",
+ }).formatToParts(date);
+ const values = {
+ month: getRequiredPart(parts, "month"),
+ day: getRequiredPart(parts, "day"),
+ year: getRequiredPart(parts, "year"),
+ };
+ const order = includeYear ? DATE_PART_ORDERS[dateFormat] : SHORT_DATE_PART_ORDERS[dateFormat];
+
+ return order.map((part) => values[part]).join("/");
+}
+
+function formatConfiguredDateWithMonth(date: Date, options: DateFormatOptions) {
+ const dateFormat = options.dateFormat ?? DEFAULT_DATE_FORMAT;
+ const parts = getDateTimeFormat(options.locale, options.timeZone, {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ }).formatToParts(date);
+ const month = getRequiredPart(parts, "month");
+ const day = getRequiredPart(parts, "day");
+ const year = getRequiredPart(parts, "year");
+
+ if (dateFormat === "DD/MM/YYYY") {
+ return `${day} ${month} ${year}`;
+ }
+
+ if (dateFormat === "YYYY/MM/DD") {
+ return `${year} ${month} ${day}`;
+ }
+
+ return `${month} ${day}, ${year}`;
+}
+
+function formatConfiguredTime(date: Date, options: DateFormatOptions) {
+ return getDateTimeFormat(options.locale, options.timeZone, {
+ hour: "numeric",
+ minute: "numeric",
+ hour12: (options.timeFormat ?? DEFAULT_TIME_FORMAT) === "12h",
+ }).format(date);
+}
+
// 1/10/2026, 2:30 PM
export function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- month: "numeric",
- day: "numeric",
- year: "numeric",
- hour: "numeric",
- minute: "numeric",
- }).format(validDate),
+ return formatValidDate(
+ date,
+ (validDate) => `${formatConfiguredDate(validDate, options, true)}, ${formatConfiguredTime(validDate, options)}`,
);
}
// Jan 10, 2026
export function formatDateWithMonth(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- month: "short",
- day: "numeric",
- year: "numeric",
- }).format(validDate),
- );
+ return formatValidDate(date, (validDate) => formatConfiguredDateWithMonth(validDate, options));
}
// 1/10/2026
export function formatDate(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- month: "numeric",
- day: "numeric",
- year: "numeric",
- }).format(validDate),
- );
+ return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, true));
}
// 1/10
export function formatShortDate(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- month: "numeric",
- day: "numeric",
- }).format(validDate),
- );
+ return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, false));
}
// 1/10, 2:30 PM
export function formatShortDateTime(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- month: "numeric",
- day: "numeric",
- hour: "numeric",
- minute: "numeric",
- }).format(validDate),
+ return formatValidDate(
+ date,
+ (validDate) => `${formatConfiguredDate(validDate, options, false)}, ${formatConfiguredTime(validDate, options)}`,
);
}
// 2:30 PM
export function formatTime(date: DateInput, options: DateFormatOptions = {}): string {
- return formatValidDate(date, (validDate) =>
- getDateTimeFormat(options.locale, options.timeZone, {
- hour: "numeric",
- minute: "numeric",
- }).format(validDate),
- );
+ return formatValidDate(date, (validDate) => formatConfiguredTime(validDate, options));
}
// 5 minutes ago
@@ -113,23 +162,33 @@ export function formatTimeAgo(date: DateInput, now = Date.now()): string {
}
export function useTimeFormat() {
- const { locale, timeZone, now } = RootRoute.useLoaderData();
+ const { locale, timeZone, dateFormat, timeFormat, now } = RootRoute.useLoaderData();
+ const session = authClient.useSession();
const [currentNow, setCurrentNow] = useState(now);
+ const currentDateFormat = (session.data?.user.dateFormat ?? dateFormat) as DateFormatPreference;
+ const currentTimeFormat = (session.data?.user.timeFormat ?? timeFormat) as TimeFormatPreference;
useEffect(() => {
- setCurrentNow(Date.now());
+ const nextNow = Date.now();
+ setCurrentNow(nextNow === now ? now : nextNow);
}, [now]);
return useMemo(
() => ({
- formatDateTime: (date: DateInput) => formatDateTime(date, { locale, timeZone }),
- formatDateWithMonth: (date: DateInput) => formatDateWithMonth(date, { locale, timeZone }),
- formatDate: (date: DateInput) => formatDate(date, { locale, timeZone }),
- formatShortDate: (date: DateInput) => formatShortDate(date, { locale, timeZone }),
- formatShortDateTime: (date: DateInput) => formatShortDateTime(date, { locale, timeZone }),
- formatTime: (date: DateInput) => formatTime(date, { locale, timeZone }),
+ formatDateTime: (date: DateInput) =>
+ formatDateTime(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
+ formatDateWithMonth: (date: DateInput) =>
+ formatDateWithMonth(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
+ formatDate: (date: DateInput) =>
+ formatDate(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
+ formatShortDate: (date: DateInput) =>
+ formatShortDate(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
+ formatShortDateTime: (date: DateInput) =>
+ formatShortDateTime(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
+ formatTime: (date: DateInput) =>
+ formatTime(date, { locale, timeZone, dateFormat: currentDateFormat, timeFormat: currentTimeFormat }),
formatTimeAgo: (date: DateInput) => formatTimeAgo(date, currentNow),
}),
- [locale, timeZone, currentNow],
+ [locale, timeZone, currentDateFormat, currentTimeFormat, currentNow],
);
}
diff --git a/app/client/modules/settings/routes/settings.tsx b/app/client/modules/settings/routes/settings.tsx
index a968cd09..ea7d43d6 100644
--- a/app/client/modules/settings/routes/settings.tsx
+++ b/app/client/modules/settings/routes/settings.tsx
@@ -17,10 +17,19 @@ import {
} from "~/client/components/ui/dialog";
import { Input } from "~/client/components/ui/input";
import { Label } from "~/client/components/ui/label";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
import { authClient } from "~/client/lib/auth-client";
+import {
+ DATE_FORMATS,
+ type DateFormatPreference,
+ formatDateTime,
+ TIME_FORMATS,
+ type TimeFormatPreference,
+} from "~/client/lib/datetime";
import { logger } from "~/client/lib/logger";
import { type AppContext } from "~/context";
+import { Route as RootRoute } from "~/routes/__root";
import { TwoFactorSection } from "../components/two-factor-section";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { SsoSettingsSection } from "~/client/modules/sso/components/sso-settings-section";
@@ -41,6 +50,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false);
const [downloadPassword, setDownloadPassword] = useState("");
const [isChangingPassword, setIsChangingPassword] = useState(false);
+ const { locale, dateFormat, timeFormat } = RootRoute.useLoaderData();
const { tab } = useSearch({ from: "/(dashboard)/settings/" });
const activeTab = tab || "account";
@@ -48,6 +58,12 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
const navigate = useNavigate();
const { activeMember, activeOrganization } = useOrganizationContext();
const isOrgAdmin = activeMember?.role === "owner" || activeMember?.role === "admin";
+ const dateTimePreview = formatDateTime("2026-01-10T14:30:00.000Z", {
+ locale,
+ timeZone: "UTC",
+ dateFormat,
+ timeFormat,
+ });
const handleLogout = async () => {
await authClient.signOut({
@@ -141,6 +157,43 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
});
};
+ const handleDateTimeFormatChange = async (
+ nextDateFormat: DateFormatPreference,
+ nextTimeFormat: TimeFormatPreference,
+ ) => {
+ await authClient.updateUser({
+ dateFormat: nextDateFormat,
+ timeFormat: nextTimeFormat,
+ fetchOptions: {
+ onError: ({ error }) => {
+ toast.error("Failed to update date and time format", {
+ description: error.message,
+ });
+ },
+ onSuccess: () => {
+ window.location.reload();
+ toast.success("Date and time preferences saved");
+ },
+ },
+ });
+ };
+
+ const handleDateFormatChange = async (nextDateFormat: DateFormatPreference) => {
+ if (nextDateFormat === dateFormat) {
+ return;
+ }
+
+ await handleDateTimeFormatChange(nextDateFormat, timeFormat);
+ };
+
+ const handleTimeFormatChange = async (nextTimeFormat: TimeFormatPreference) => {
+ if (nextTimeFormat === timeFormat) {
+ return;
+ }
+
+ await handleDateTimeFormatChange(dateFormat, nextTimeFormat);
+ };
+
const onTabChange = (value: string) => {
void navigate({ to: ".", search: () => ({ tab: value }) });
};
@@ -174,6 +227,59 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
+
+
+
+ Date and Time Format
+
+
+ Choose how dates and times are shown throughout the app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Preview: {dateTimePreview}
+
+
+
diff --git a/app/context.ts b/app/context.ts
index 0ae36afd..ebdc3f23 100644
--- a/app/context.ts
+++ b/app/context.ts
@@ -4,6 +4,8 @@ type User = {
username: string;
name: string;
hasDownloadedResticPassword: boolean;
+ dateFormat: string;
+ timeFormat: string;
twoFactorEnabled?: boolean | null;
role?: string | null | undefined;
};
diff --git a/app/drizzle/20260326180038_slimy_trish_tilby/migration.sql b/app/drizzle/20260326180038_slimy_trish_tilby/migration.sql
new file mode 100644
index 00000000..9dc7ca73
--- /dev/null
+++ b/app/drizzle/20260326180038_slimy_trish_tilby/migration.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `users_table` ADD `date_format` text DEFAULT 'MM/DD/YYYY' NOT NULL;--> statement-breakpoint
+ALTER TABLE `users_table` ADD `time_format` text DEFAULT '12h' NOT NULL;
\ No newline at end of file
diff --git a/app/drizzle/20260326180038_slimy_trish_tilby/snapshot.json b/app/drizzle/20260326180038_slimy_trish_tilby/snapshot.json
new file mode 100644
index 00000000..dc405e0c
--- /dev/null
+++ b/app/drizzle/20260326180038_slimy_trish_tilby/snapshot.json
@@ -0,0 +1,2301 @@
+{
+ "version": "7",
+ "dialect": "sqlite",
+ "id": "fbd52162-d4fb-4c62-b79a-17078cb469b3",
+ "prevIds": ["ab5feb4c-5609-4ba7-be8c-ccde709abccc"],
+ "ddl": [
+ {
+ "name": "account",
+ "entityType": "tables"
+ },
+ {
+ "name": "app_metadata",
+ "entityType": "tables"
+ },
+ {
+ "name": "backup_schedule_mirrors_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "backup_schedule_notifications_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "backup_schedules_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "invitation",
+ "entityType": "tables"
+ },
+ {
+ "name": "member",
+ "entityType": "tables"
+ },
+ {
+ "name": "notification_destinations_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "organization",
+ "entityType": "tables"
+ },
+ {
+ "name": "repositories_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "sessions_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "sso_provider",
+ "entityType": "tables"
+ },
+ {
+ "name": "two_factor",
+ "entityType": "tables"
+ },
+ {
+ "name": "users_table",
+ "entityType": "tables"
+ },
+ {
+ "name": "verification",
+ "entityType": "tables"
+ },
+ {
+ "name": "volumes_table",
+ "entityType": "tables"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "account_id",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "provider_id",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "access_token",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "refresh_token",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id_token",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "access_token_expires_at",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "refresh_token_expires_at",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "scope",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "password",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "account"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "key",
+ "entityType": "columns",
+ "table": "app_metadata"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "value",
+ "entityType": "columns",
+ "table": "app_metadata"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "app_metadata"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "app_metadata"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "schedule_id",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "repository_id",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "enabled",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_copy_at",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_copy_status",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_copy_error",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "schedule_id",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "destination_id",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "notify_on_start",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "notify_on_success",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "notify_on_warning",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "notify_on_failure",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "short_id",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "volume_id",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "repository_id",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "enabled",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "cron_expression",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "retention_policy",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'",
+ "generated": null,
+ "name": "exclude_patterns",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'",
+ "generated": null,
+ "name": "exclude_if_present",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'",
+ "generated": null,
+ "name": "include_paths",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'",
+ "generated": null,
+ "name": "include_patterns",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_backup_at",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_backup_status",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_backup_error",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "next_backup_at",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "one_file_system",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'[]'",
+ "generated": null,
+ "name": "custom_restic_params",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "0",
+ "generated": null,
+ "name": "sort_order",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "backup_schedules_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "email",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "role",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'pending'",
+ "generated": null,
+ "name": "status",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "expires_at",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "inviter_id",
+ "entityType": "columns",
+ "table": "invitation"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "member"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "member"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "table": "member"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'member'",
+ "generated": null,
+ "name": "role",
+ "entityType": "columns",
+ "table": "member"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "member"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "enabled",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "type",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "config",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "notification_destinations_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "slug",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "logo",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "metadata",
+ "entityType": "columns",
+ "table": "organization"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "short_id",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "provisioning_id",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "type",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "config",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'auto'",
+ "generated": null,
+ "name": "compression_mode",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'unknown'",
+ "generated": null,
+ "name": "status",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_checked",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_error",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "doctor_result",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "stats",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "stats_updated_at",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "upload_limit_enabled",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "real",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "1",
+ "generated": null,
+ "name": "upload_limit_value",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'Mbps'",
+ "generated": null,
+ "name": "upload_limit_unit",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "download_limit_enabled",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "real",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "1",
+ "generated": null,
+ "name": "download_limit_value",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'Mbps'",
+ "generated": null,
+ "name": "download_limit_unit",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "repositories_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "token",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "expires_at",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "ip_address",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_agent",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "impersonated_by",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "active_organization_id",
+ "entityType": "columns",
+ "table": "sessions_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "provider_id",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "issuer",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "domain",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "auto_link_matching_emails",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "oidc_config",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "saml_config",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "sso_provider"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "two_factor"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "secret",
+ "entityType": "columns",
+ "table": "two_factor"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "backup_codes",
+ "entityType": "columns",
+ "table": "two_factor"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "table": "two_factor"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "username",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "password_hash",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "has_downloaded_restic_password",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'MM/DD/YYYY'",
+ "generated": null,
+ "name": "date_format",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'12h'",
+ "generated": null,
+ "name": "time_format",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "email",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "email_verified",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "image",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "display_username",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "two_factor_enabled",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'user'",
+ "generated": null,
+ "name": "role",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "false",
+ "generated": null,
+ "name": "banned",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "ban_reason",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "ban_expires",
+ "entityType": "columns",
+ "table": "users_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "identifier",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "value",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "expires_at",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "verification"
+ },
+ {
+ "type": "integer",
+ "notNull": false,
+ "autoincrement": true,
+ "default": null,
+ "generated": null,
+ "name": "id",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "short_id",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "provisioning_id",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "name",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "type",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'unmounted'",
+ "generated": null,
+ "name": "status",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": false,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "last_error",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "last_health_check",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(unixepoch() * 1000)",
+ "generated": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "config",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "integer",
+ "notNull": true,
+ "autoincrement": false,
+ "default": "true",
+ "generated": null,
+ "name": "auto_remount",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "type": "text",
+ "notNull": true,
+ "autoincrement": false,
+ "default": null,
+ "generated": null,
+ "name": "organization_id",
+ "entityType": "columns",
+ "table": "volumes_table"
+ },
+ {
+ "columns": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "account_user_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "account"
+ },
+ {
+ "columns": ["schedule_id"],
+ "tableTo": "backup_schedules_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedule_mirrors_table_schedule_id_backup_schedules_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "columns": ["repository_id"],
+ "tableTo": "repositories_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedule_mirrors_table_repository_id_repositories_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "columns": ["schedule_id"],
+ "tableTo": "backup_schedules_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedule_notifications_table_schedule_id_backup_schedules_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "columns": ["destination_id"],
+ "tableTo": "notification_destinations_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedule_notifications_table_destination_id_notification_destinations_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "columns": ["volume_id"],
+ "tableTo": "volumes_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedules_table_volume_id_volumes_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedules_table"
+ },
+ {
+ "columns": ["repository_id"],
+ "tableTo": "repositories_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedules_table_repository_id_repositories_table_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedules_table"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "backup_schedules_table_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "backup_schedules_table"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "invitation_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "invitation"
+ },
+ {
+ "columns": ["inviter_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "invitation_inviter_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "invitation"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "member_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "member"
+ },
+ {
+ "columns": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "member_user_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "member"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "notification_destinations_table_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "notification_destinations_table"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "repositories_table_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "repositories_table"
+ },
+ {
+ "columns": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "sessions_table_user_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "sessions_table"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "fk_sso_provider_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "sso_provider"
+ },
+ {
+ "columns": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "SET NULL",
+ "nameExplicit": false,
+ "name": "fk_sso_provider_user_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "sso_provider"
+ },
+ {
+ "columns": ["user_id"],
+ "tableTo": "users_table",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "two_factor_user_id_users_table_id_fk",
+ "entityType": "fks",
+ "table": "two_factor"
+ },
+ {
+ "columns": ["organization_id"],
+ "tableTo": "organization",
+ "columnsTo": ["id"],
+ "onUpdate": "NO ACTION",
+ "onDelete": "CASCADE",
+ "nameExplicit": false,
+ "name": "volumes_table_organization_id_organization_id_fk",
+ "entityType": "fks",
+ "table": "volumes_table"
+ },
+ {
+ "columns": ["schedule_id", "destination_id"],
+ "nameExplicit": false,
+ "name": "backup_schedule_notifications_table_schedule_id_destination_id_pk",
+ "entityType": "pks",
+ "table": "backup_schedule_notifications_table"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "account_pk",
+ "table": "account",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["key"],
+ "nameExplicit": false,
+ "name": "app_metadata_pk",
+ "table": "app_metadata",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "backup_schedule_mirrors_table_pk",
+ "table": "backup_schedule_mirrors_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "backup_schedules_table_pk",
+ "table": "backup_schedules_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "invitation_pk",
+ "table": "invitation",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "member_pk",
+ "table": "member",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "notification_destinations_table_pk",
+ "table": "notification_destinations_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "organization_pk",
+ "table": "organization",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "repositories_table_pk",
+ "table": "repositories_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "sessions_table_pk",
+ "table": "sessions_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "sso_provider_pk",
+ "table": "sso_provider",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "two_factor_pk",
+ "table": "two_factor",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "users_table_pk",
+ "table": "users_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "verification_pk",
+ "table": "verification",
+ "entityType": "pks"
+ },
+ {
+ "columns": ["id"],
+ "nameExplicit": false,
+ "name": "volumes_table_pk",
+ "table": "volumes_table",
+ "entityType": "pks"
+ },
+ {
+ "columns": [
+ {
+ "value": "user_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "account_userId_idx",
+ "entityType": "indexes",
+ "table": "account"
+ },
+ {
+ "columns": [
+ {
+ "value": "organization_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "invitation_organizationId_idx",
+ "entityType": "indexes",
+ "table": "invitation"
+ },
+ {
+ "columns": [
+ {
+ "value": "email",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "invitation_email_idx",
+ "entityType": "indexes",
+ "table": "invitation"
+ },
+ {
+ "columns": [
+ {
+ "value": "organization_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "member_organizationId_idx",
+ "entityType": "indexes",
+ "table": "member"
+ },
+ {
+ "columns": [
+ {
+ "value": "user_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "member_userId_idx",
+ "entityType": "indexes",
+ "table": "member"
+ },
+ {
+ "columns": [
+ {
+ "value": "organization_id",
+ "isExpression": false
+ },
+ {
+ "value": "user_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "member_org_user_uidx",
+ "entityType": "indexes",
+ "table": "member"
+ },
+ {
+ "columns": [
+ {
+ "value": "slug",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "organization_slug_uidx",
+ "entityType": "indexes",
+ "table": "organization"
+ },
+ {
+ "columns": [
+ {
+ "value": "organization_id",
+ "isExpression": false
+ },
+ {
+ "value": "provisioning_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "repositories_table_org_provisioning_id_uidx",
+ "entityType": "indexes",
+ "table": "repositories_table"
+ },
+ {
+ "columns": [
+ {
+ "value": "user_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "sessionsTable_userId_idx",
+ "entityType": "indexes",
+ "table": "sessions_table"
+ },
+ {
+ "columns": [
+ {
+ "value": "secret",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "twoFactor_secret_idx",
+ "entityType": "indexes",
+ "table": "two_factor"
+ },
+ {
+ "columns": [
+ {
+ "value": "user_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "twoFactor_userId_idx",
+ "entityType": "indexes",
+ "table": "two_factor"
+ },
+ {
+ "columns": [
+ {
+ "value": "identifier",
+ "isExpression": false
+ }
+ ],
+ "isUnique": false,
+ "where": null,
+ "origin": "manual",
+ "name": "verification_identifier_idx",
+ "entityType": "indexes",
+ "table": "verification"
+ },
+ {
+ "columns": [
+ {
+ "value": "organization_id",
+ "isExpression": false
+ },
+ {
+ "value": "provisioning_id",
+ "isExpression": false
+ }
+ ],
+ "isUnique": true,
+ "where": null,
+ "origin": "manual",
+ "name": "volumes_table_org_provisioning_id_uidx",
+ "entityType": "indexes",
+ "table": "volumes_table"
+ },
+ {
+ "columns": ["schedule_id", "repository_id"],
+ "nameExplicit": false,
+ "name": "backup_schedule_mirrors_table_schedule_id_repository_id_unique",
+ "entityType": "uniques",
+ "table": "backup_schedule_mirrors_table"
+ },
+ {
+ "columns": ["name", "organization_id"],
+ "nameExplicit": false,
+ "name": "volumes_table_name_organization_id_unique",
+ "entityType": "uniques",
+ "table": "volumes_table"
+ },
+ {
+ "columns": ["short_id"],
+ "nameExplicit": false,
+ "name": "backup_schedules_table_short_id_unique",
+ "entityType": "uniques",
+ "table": "backup_schedules_table"
+ },
+ {
+ "columns": ["short_id"],
+ "nameExplicit": false,
+ "name": "repositories_table_short_id_unique",
+ "entityType": "uniques",
+ "table": "repositories_table"
+ },
+ {
+ "columns": ["token"],
+ "nameExplicit": false,
+ "name": "sessions_table_token_unique",
+ "entityType": "uniques",
+ "table": "sessions_table"
+ },
+ {
+ "columns": ["provider_id"],
+ "nameExplicit": false,
+ "name": "sso_provider_provider_id_unique",
+ "entityType": "uniques",
+ "table": "sso_provider"
+ },
+ {
+ "columns": ["username"],
+ "nameExplicit": false,
+ "name": "users_table_username_unique",
+ "entityType": "uniques",
+ "table": "users_table"
+ },
+ {
+ "columns": ["email"],
+ "nameExplicit": false,
+ "name": "users_table_email_unique",
+ "entityType": "uniques",
+ "table": "users_table"
+ },
+ {
+ "columns": ["short_id"],
+ "nameExplicit": false,
+ "name": "volumes_table_short_id_unique",
+ "entityType": "uniques",
+ "table": "volumes_table"
+ }
+ ],
+ "renames": []
+}
diff --git a/app/routes/__root.tsx b/app/routes/__root.tsx
index cfbc514b..bacde6d8 100644
--- a/app/routes/__root.tsx
+++ b/app/routes/__root.tsx
@@ -10,6 +10,8 @@ import { ThemeProvider, THEME_COOKIE_NAME } from "~/client/components/theme-prov
import { createServerFn } from "@tanstack/react-start";
import { getCookie, getRequestHeaders } from "@tanstack/react-start/server";
import { isAuthRoute } from "~/lib/auth-routes";
+import { auth } from "~/server/lib/auth";
+import type { DateFormatPreference, TimeFormatPreference } from "~/client/lib/datetime";
const fetchTheme = createServerFn({ method: "GET" }).handler(async () => {
const themeCookie = getCookie(THEME_COOKIE_NAME);
@@ -17,11 +19,15 @@ const fetchTheme = createServerFn({ method: "GET" }).handler(async () => {
});
const fetchTimeConfig = createServerFn({ method: "GET" }).handler(async () => {
- const acceptLanguage = getRequestHeaders().get("accept-language");
+ const headers = getRequestHeaders();
+ const acceptLanguage = headers.get("accept-language");
+ const session = await auth.api.getSession({ headers });
return {
locale: (acceptLanguage?.split(",")[0] || "en-US") as string,
timeZone: process.env.TZ || Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
+ dateFormat: (session?.user.dateFormat ?? "MM/DD/YYYY") as DateFormatPreference,
+ timeFormat: (session?.user.timeFormat ?? "12h") as TimeFormatPreference,
now: Date.now(),
};
});
diff --git a/app/server/db/schema.ts b/app/server/db/schema.ts
index 70b62ab5..83ab98ca 100644
--- a/app/server/db/schema.ts
+++ b/app/server/db/schema.ts
@@ -21,6 +21,8 @@ export const usersTable = sqliteTable("users_table", {
username: text().notNull().unique(),
passwordHash: text("password_hash"),
hasDownloadedResticPassword: int("has_downloaded_restic_password", { mode: "boolean" }).notNull().default(false),
+ dateFormat: text("date_format").notNull().default("MM/DD/YYYY"),
+ timeFormat: text("time_format").notNull().default("12h"),
createdAt: int("created_at", { mode: "timestamp_ms" })
.notNull()
.default(sql`(unixepoch() * 1000)`),
diff --git a/app/server/lib/auth.ts b/app/server/lib/auth.ts
index 63a8130b..6344f3af 100644
--- a/app/server/lib/auth.ts
+++ b/app/server/lib/auth.ts
@@ -138,6 +138,14 @@ export const auth = betterAuth({
type: "boolean",
returned: true,
},
+ dateFormat: {
+ type: "string",
+ returned: true,
+ },
+ timeFormat: {
+ type: "string",
+ returned: true,
+ },
},
},
session: {