Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions src/app/[password]/admin/[register]/public/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getMenus, getOrdersToServe, Menu, Order } from "@/data";
import { delay } from "@/utils";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";
import Image from "next/image";
import backgroundImage from "@public/background.webp";

export default function Public() {
Expand All @@ -30,15 +31,19 @@ export default function Public() {
}, []);

return (
<div
className="public"
style={{
backgroundImage: `url(${backgroundImage.src})`,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
}}
>
<div className="public">
<Image
src={backgroundImage}
alt="Background"
fill
priority
placeholder="blur"
style={{
objectFit: "cover",
objectPosition: "center",
zIndex: -1,
}}
/>
<div className="menu-section">
<h1>Croques</h1>
<div className="menu">
Expand Down
101 changes: 101 additions & 0 deletions src/app/[password]/admin/danger/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"use client";

import { downloadDatabase, resetDatabase } from "@/data";
import { ReactNode, useCallback } from "react";
import { Slide, toast, ToastContainer } from "react-toastify";

export default function Danger() {
const handleDownload = useCallback(async () => {
try {
const result = await downloadDatabase();

if (!result.success || !result.data) {
toast.error(`Download failed: ${result.error || "Unknown error"}`);
return;
}

const byteCharacters = atob(result.data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: result.mimeType });

const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = result.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
toast.error(`Download failed: ${message}`);
console.error("Download error:", error);
}
}, []);

const handleReset = useCallback(() => {
toast.warning(
"WARNING: This will permanently delete ALL database content. " +
"Are you ABSOLUTELY sure?",
{
autoClose: false,
closeButton: ToastButtons({
onOk: async () => {
const result = await resetDatabase();
if (!result.success) {
toast.error(result.message);
console.error("Reset error:", result);
return;
}
toast.success(result.message, { closeButton: false });
},
}),
}
);
}, []);

return (
<div className="danger-page">
<h1>
Danger Zone
<br />
IT Admin Only
</h1>
<button className="validate" onClick={handleDownload}>
Download Database
</button>
<button className="danger" onClick={handleReset}>
Reset Database
</button>
<ToastContainer transition={Slide} position="top-center" />
</div>
);
}

function ToastButtons({
onOk,
}: {
onOk: () => void;
}): ({ closeToast }: { closeToast: () => void }) => ReactNode {
const ToastButtonsResult = ({ closeToast }: { closeToast: () => void }) => (
<>
<button
className="danger"
onClick={() => {
onOk();
closeToast();
}}
>
Yes
</button>
<button className="validate" onClick={closeToast}>
No
</button>
</>
);
return ToastButtonsResult;
}
56 changes: 55 additions & 1 deletion src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ export interface Menu {
stocks: number;
}

const DB_PATH = process.env["OVERCLICKED_DB_DIR"] + "/db.sqlite";
const DB_DOWNLOAD_FILENAME = "overclicked_db_backup.sqlite";

function runOnDb<T>(func: (db: Database.Database) => T) {
const db = Database(process.env["OVERCLICKED_DB_DIR"] + "/db.sqlite");
const db = Database(DB_PATH);
db.exec(fs.readFileSync("migration.sql").toString());
const res = db.transaction(func)(db);
db.close();
Expand Down Expand Up @@ -128,3 +131,54 @@ export const addMenuStocks = async (menu: number, stocks: number) =>
)
.run([stocks, stocks, menu])
);

export const downloadDatabase = async () => {
try {
if (!fs.existsSync(DB_PATH)) {
throw new Error("Database file not found on server.");
}
const fileBuffer = fs.readFileSync(DB_PATH);
const base64Data = fileBuffer.toString("base64");

return {
success: true,
data: base64Data,
fileName: DB_DOWNLOAD_FILENAME,
mimeType: "application/x-sqlite3",
error: null,
};
} catch (e) {
console.error("Failed to download database file:", e);
return {
success: false,
data: null,
fileName: DB_DOWNLOAD_FILENAME,
mimeType: "application/x-sqlite3",
error: `Failed to download database: ${
e instanceof Error ? e.message : String(e)
}`,
};
}
};

export const resetDatabase = async () => {
try {
if (fs.existsSync(DB_PATH)) {
// Synchronously delete the file
fs.unlinkSync(DB_PATH);
return {
success: true,
message: "Database deleted! It will be recreated on next access",
};
}
return {
success: true,
message: "Database was already missing",
};
} catch (e) {
console.error("Failed to delete database file:", e);
throw new Error(
`Failed to reset database: ${e instanceof Error ? e.message : String(e)}`
);
}
};
28 changes: 28 additions & 0 deletions src/styles/pages/_danger.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.danger-page {
overflow: hidden;
display: flex;
flex-direction: column;
max-width: 60ch;
margin: 4em auto;
gap: 1em;

width: 100%;

display: flex;
justify-content: center;
align-items: center;

h1 {
display: flex;
justify-content: center;
align-items: center;
}

button {
width: 100%;
}

.Toastify__toast--warning {
flex-direction: column;
}
}
3 changes: 2 additions & 1 deletion src/styles/pages/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
@forward "preparation";
@forward "admin";
@forward "public";
@forward "finished";
@forward "finished";
@forward "danger";
1 change: 0 additions & 1 deletion src/styles/pages/_public.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
width: 100%;
height: 100vh;

background-color: #000;
color: #f8f7c8;
font-size: 2rem;

Expand Down
Loading