First Commit. Duplicate bunext-sqlite preset

This commit is contained in:
2026-10-18 16:47:11 +01:00
commit 81e4a8aecc
718 changed files with 41695 additions and 0 deletions
@@ -0,0 +1,45 @@
import Span from "@moduletrace/twui/layout/Span";
import formatBytes from "@/src/utils/format-bytes";
import type { DbBackup } from "@/src/types";
import dayjs from "dayjs";
import type { BackupsTableMode } from "./backups-table";
import DownloadBackupButton from "./download-backup-button";
import RestoreBackupButton from "./restore-backup-button";
type Props = {
backup: DbBackup;
mode: BackupsTableMode;
};
export default function BackupTableRow({ backup, mode }: Props) {
return (
<tr className="border-t border-slate-200 dark:border-white/10">
<td className="px-4 py-2.5 max-w-0 w-full">
<Span
size="small"
className="block truncate font-mono"
title={backup.name}
>
{backup.name}
</Span>
</td>
<td className="px-4 py-2.5 whitespace-nowrap">
<Span size="small">
{dayjs(backup.created_at).format("MMM D, YYYY HH:mm:ss")}
</Span>
</td>
<td className="hidden md:table-cell px-4 py-2.5 whitespace-nowrap">
<Span size="small" variant="faded">
{formatBytes({ bytes: backup.size })}
</Span>
</td>
<td className="px-4 py-2 text-right whitespace-nowrap">
{mode == "restore" ? (
<RestoreBackupButton backup={backup} />
) : (
<DownloadBackupButton backup={backup} />
)}
</td>
</tr>
);
}
@@ -0,0 +1,21 @@
import { twMerge } from "tailwind-merge";
import Span from "@moduletrace/twui/layout/Span";
import type { BackupsTableColumn } from "../(data)/backups-table-columns";
export default function BackupsTableHeadCell({
title,
className,
hide_title,
}: BackupsTableColumn) {
return (
<th className={twMerge("px-4 py-2.5 text-left font-medium", className)}>
<Span
size="smaller"
variant="faded"
className={twMerge(hide_title && "sr-only")}
>
{title}
</Span>
</th>
);
}
@@ -0,0 +1,37 @@
import Paper from "@moduletrace/twui/elements/Paper";
import type { DbBackup } from "@/src/types";
import { BackupsTableColumns } from "../(data)/backups-table-columns";
import BackupTableRow from "./backup-table-row";
import BackupsTableHeadCell from "./backups-table-head-cell";
export type BackupsTableMode = "view" | "restore";
type Props = {
backups: DbBackup[];
mode: BackupsTableMode;
};
export default function BackupsTable({ backups, mode }: Props) {
return (
<Paper className="p-0 gap-0 overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-slate-50 dark:bg-white/5">
<tr>
{BackupsTableColumns.map((column) => (
<BackupsTableHeadCell {...column} key={column.title} />
))}
</tr>
</thead>
<tbody>
{backups.map((backup) => (
<BackupTableRow
backup={backup}
mode={mode}
key={backup.name}
/>
))}
</tbody>
</table>
</Paper>
);
}
@@ -0,0 +1,43 @@
import Button from "@moduletrace/twui/layout/Button";
import Tag from "@moduletrace/twui/elements/Tag";
import Row from "@moduletrace/twui/layout/Row";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import createBackup from "../(functions)/create-backup";
export default function CreateBackupButton() {
const { loading, setLoading, status, setStatus } = useStatus();
const create_backup = async () => {
setStatus(undefined);
setLoading(true);
const res = await createBackup();
if (res.success) {
window.location.reload();
return;
}
setLoading(false);
if (res.msg) {
setStatus({ error: true, msg: res.msg });
}
};
return (
<Row className="gap-2! flex-nowrap">
{status?.error && <Tag color="error">{status.msg}</Tag>}
<Button
title="Create Backup"
beforeIcon="DatabaseBackup"
loading={loading}
onClick={() => {
create_backup();
}}
>
Create backup
</Button>
</Row>
);
}
@@ -0,0 +1,30 @@
import Button from "@moduletrace/twui/layout/Button";
import type { DbBackup } from "@/src/types";
import { Download } from "lucide-react";
type Props = {
backup: DbBackup;
};
export default function DownloadBackupButton({ backup }: Props) {
return (
<Button
title="Download Backup"
variant="outlined"
color="gray"
size="small"
beforeIcon={<Download size={20} />}
href={`/api/admin/backups/download?backup_name=${encodeURIComponent(backup.name)}`}
linkProps={{
download: backup.name,
onClick: (e) => {
if (!window.confirm(`Download backup ${backup.name}?`)) {
e.preventDefault();
}
},
}}
>
Download
</Button>
);
}
@@ -0,0 +1,52 @@
import Button from "@moduletrace/twui/layout/Button";
import Tag from "@moduletrace/twui/elements/Tag";
import Row from "@moduletrace/twui/layout/Row";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import type { DbBackup } from "@/src/types";
import restoreBackup from "../(functions)/restore-backup";
import { ArchiveRestore } from "lucide-react";
type Props = {
backup: DbBackup;
};
export default function RestoreBackupButton({ backup }: Props) {
const { loading, setLoading, status, setStatus } = useStatus();
const restore_backup = async () => {
setStatus(undefined);
setLoading(true);
const res = await restoreBackup({ backup });
if (res.success) {
window.location.reload();
return;
}
setLoading(false);
if (res.msg) {
setStatus({ error: true, msg: res.msg });
}
};
return (
<Row className="gap-2! flex-nowrap justify-end">
{status?.error && <Tag color="error">{status.msg}</Tag>}
<Button
title="Restore Backup"
variant="outlined"
color="warning"
size="small"
beforeIcon={<ArchiveRestore size={20} />}
loading={loading}
onClick={() => {
restore_backup();
}}
>
Restore
</Button>
</Row>
);
}