117 lines
3.0 KiB
TypeScript
117 lines
3.0 KiB
TypeScript
import "server-only";
|
||
|
||
import { eq } from "drizzle-orm";
|
||
import { z } from "zod";
|
||
import type { ColorMode } from "@/lib/color-mode";
|
||
import { getSqliteConnection } from "@/server/db/client";
|
||
import { instanceLocales, userPreferences } from "@/server/db/schema";
|
||
import { assertEnabledActor, type DomainActor } from "@/server/domain/actor";
|
||
import { DomainError } from "@/server/domain/errors";
|
||
|
||
const colorModeInputSchema = z.object({
|
||
colorMode: z.enum(["light", "dark", "system"]),
|
||
});
|
||
const languageInputSchema = z.object({
|
||
language: z.string().trim().regex(/^[a-z]{2}(?:-[A-Z]{2}[0-9]?)?$/),
|
||
});
|
||
|
||
export type PublicUserPreferences = {
|
||
colorMode: ColorMode;
|
||
language: string;
|
||
};
|
||
|
||
export function getUserPreferences(actor: DomainActor): PublicUserPreferences {
|
||
assertEnabledActor(actor);
|
||
|
||
const row = getSqliteConnection().db
|
||
.select({
|
||
colorMode: userPreferences.colorMode,
|
||
language: userPreferences.language,
|
||
})
|
||
.from(userPreferences)
|
||
.where(eq(userPreferences.ownerUserId, actor.authUserId))
|
||
.get();
|
||
|
||
return {
|
||
colorMode: (row?.colorMode as ColorMode | undefined) ?? "system",
|
||
language: row?.language ?? "tr",
|
||
};
|
||
}
|
||
|
||
export function updateColorModePreference(
|
||
actor: DomainActor,
|
||
input: unknown,
|
||
): PublicUserPreferences {
|
||
assertEnabledActor(actor);
|
||
|
||
const parsed = colorModeInputSchema.safeParse(input);
|
||
if (!parsed.success) {
|
||
throw new DomainError("VALIDATION_ERROR", "Renk modu tercihi geçersiz.");
|
||
}
|
||
|
||
const { db } = getSqliteConnection();
|
||
db.insert(userPreferences)
|
||
.values({
|
||
ownerUserId: actor.authUserId,
|
||
colorMode: parsed.data.colorMode,
|
||
})
|
||
.onConflictDoUpdate({
|
||
target: userPreferences.ownerUserId,
|
||
set: {
|
||
colorMode: parsed.data.colorMode,
|
||
updatedAt: new Date().toISOString(),
|
||
},
|
||
})
|
||
.run();
|
||
|
||
return {
|
||
colorMode: parsed.data.colorMode,
|
||
language: getUserPreferences(actor).language,
|
||
};
|
||
}
|
||
|
||
export function updateLanguagePreference(
|
||
actor: DomainActor,
|
||
input: unknown,
|
||
): PublicUserPreferences {
|
||
assertEnabledActor(actor);
|
||
|
||
const parsed = languageInputSchema.safeParse(input);
|
||
if (!parsed.success) {
|
||
throw new DomainError("VALIDATION_ERROR", "Dil tercihi geçersiz.");
|
||
}
|
||
|
||
const { db } = getSqliteConnection();
|
||
const locale = db
|
||
.select({
|
||
code: instanceLocales.code,
|
||
status: instanceLocales.status,
|
||
})
|
||
.from(instanceLocales)
|
||
.where(eq(instanceLocales.code, parsed.data.language))
|
||
.get();
|
||
if (!locale || locale.status !== "active") {
|
||
throw new DomainError("VALIDATION_ERROR", "Dil tercihi aktif bir dil olmalıdır.");
|
||
}
|
||
|
||
db.insert(userPreferences)
|
||
.values({
|
||
ownerUserId: actor.authUserId,
|
||
language: parsed.data.language,
|
||
})
|
||
.onConflictDoUpdate({
|
||
target: userPreferences.ownerUserId,
|
||
set: {
|
||
language: parsed.data.language,
|
||
updatedAt: new Date().toISOString(),
|
||
},
|
||
})
|
||
.run();
|
||
|
||
const preferences = getUserPreferences(actor);
|
||
return {
|
||
...preferences,
|
||
language: parsed.data.language,
|
||
};
|
||
}
|