feat: add website wide force refresh button
Build and Push Image / build-backend (push) Successful in 4m11s
Build and Push Image / build-frontend (push) Successful in 5m20s
Build and Push Image / push (push) Successful in 10m31s

This commit is contained in:
2026-07-19 02:15:57 +07:00 Unverified
parent 745a9152d9
commit 9b338dc850
4 changed files with 55 additions and 37 deletions
+10
View File
@@ -7,13 +7,17 @@ import { revalidatePath } from "next/cache";
import { actionLog } from "@/lib/api";
import { db } from "@/lib/db";
import { settings } from "@/lib/db/schema";
import { adminCheck } from "@/lib/auth";
import { sse } from "@/lib/db/sse-endpoints";
export async function getEnka() {
if (!(await adminCheck())) throw "Unauthorized";
const [{ enka } = { enka: true }] = await db.select().from(settings).limit(1);
return enka;
}
export async function toggleEnka(state: boolean) {
if (!(await adminCheck())) throw "Unauthorized";
const last = await getEnka();
if (last === state) return;
await db
@@ -27,9 +31,15 @@ export async function toggleEnka(state: boolean) {
}
export async function syncAmber() {
if (!(await adminCheck())) throw "Unauthorized";
const res = await promisify(exec)("bun util/sync 2>&1", {
env: { ...env, NO_AUTH_CHECK: "1" },
});
await actionLog("Triggered an Amber sync", { result: res.stdout });
return res.stdout;
}
export async function forceRefresh() {
if (!(await adminCheck())) throw "Unauthorized";
sse.active.pub("refresh", null);
}
+14 -13
View File
@@ -1,21 +1,19 @@
"use client";
import { FolderSync } from "lucide-react";
import { Bomb, FolderSync } from "lucide-react";
import { useEffect, useState } from "react";
import { CdnChooser } from "@/components/chooser";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { Switch } from "@/components/ui/switch";
import { shared } from "@/lib/comms";
import { syncAmber, toggleEnka } from "./api";
import { forceRefresh, syncAmber, toggleEnka } from "./api";
import { Section } from "./page";
import { ActionButton } from "@/components/action-button";
export function SettingsServicesSection({
enka: enkaInitial,
}: {
enka: boolean;
}) {
const [syncing, setSyncing] = useState(false);
const [syncResult, setSyncResult] = useState("");
const [enka, setEnka] = useState<boolean>();
const [debug] = shared.state("debug");
@@ -26,10 +24,7 @@ export function SettingsServicesSection({
}, [enka, enkaInitial]);
async function sync() {
setSyncing(true);
syncAmber()
.then(setSyncResult)
.finally(() => setSyncing(false));
return syncAmber().then(setSyncResult);
}
return (
@@ -39,10 +34,16 @@ export function SettingsServicesSection({
<Switch checked={enka} onCheckedChange={setEnka} />
เปิดใช้การดึงตัวละครใน Artifact
</div>
<Button disabled={syncing} onClick={sync} className="w-min">
{syncing ? <Spinner /> : <FolderSync />}
Sync Amber
</Button>
<div className="flex items-center gap-2">
<ActionButton action={sync}>
<FolderSync />
Sync Amber
</ActionButton>
<ActionButton action={forceRefresh} variant="destructive">
<Bomb />
FORCE RELOAD
</ActionButton>
</div>
{debug && (
<div className="grid w-fit gap-1 rounded-sm border border-dashed p-1">
<span className="text-sm text-muted-foreground">
+30 -24
View File
@@ -42,36 +42,42 @@ export function VersionCheck({ headless = false }: { headless?: boolean }) {
useEffect(() => {
if (!readyForUpdate && window.location.hash === "#update") {
setUpdated(true);
setTimeout(() => toast.success("คุณอยู่ในเวอร์ชั่นล่าสุดแล้ว"), 1000);
if (!headless)
setTimeout(() => toast.success("คุณอยู่ในเวอร์ชั่นล่าสุดแล้ว"), 1000);
const url = new URL(window.location.href);
url.hash = "";
window.history.replaceState("", "", url);
}
const { clean, es } = sse.active.sub(
"version",
(newVersion) => {
setConnected(true);
emit("sync");
const version = ver.current;
if (!version) console.log("Client version:", newVersion);
console.log("Server version:", newVersion);
if (version && newVersion !== version) {
setReadyForUpdate(true);
const { clean, es } = sse.active.subMany(
{
version(newVersion) {
setConnected(true);
emit("sync");
const version = ver.current;
if (!version) console.log("Client version:", newVersion);
console.log("Server version:", newVersion);
if (version && newVersion !== version) {
setReadyForUpdate(true);
window.location.hash = "#update";
if (headless) window.location.reload();
else
toast("มีอัปเดตใหม่พร้อมใช้งาน", {
description: "รีโหลดเพื่ออัปเดตเป็นเวอร์ชันล่าสุด",
action: {
label: "รีโหลด",
// Do a full reload
onClick: () => window.location.reload(),
},
duration: Infinity,
});
}
if (newVersion !== version) ver.current = newVersion;
},
refresh() {
window.location.hash = "#update";
if (headless) window.location.reload();
else
toast("มีอัปเดตใหม่พร้อมใช้งาน", {
description: "รีโหลดเพื่ออัปเดตเป็นเวอร์ชันล่าสุด",
action: {
label: "รีโหลด",
// Do a full reload
onClick: () => window.location.reload(),
},
duration: Infinity,
});
}
if (newVersion !== version) ver.current = newVersion;
queueMicrotask(() => window.location.reload());
},
},
{
endpoint: "/api/active",
+1
View File
@@ -48,6 +48,7 @@ export const sse = sseEndpointMap({
active: {
version: z.string(),
live: z.custom<YoutubeLiveInfo>(),
refresh: z.null(),
},
// Admin Live Log
log: {