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,60 @@
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type { ApiReqParams, ClientAdminCrudParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function adminCrudHandler<
T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS,
>(params: ClientAdminCrudParams<T>) {
const {
action,
table,
count,
insert_data,
sql_insert_params,
sql_query,
sql_select_params,
update_data,
update_on_duplicate,
id,
} = params;
let url = `/api/admin/crud/${table}`;
if (id) {
url += `/${id}`;
}
let query: ApiReqParams<T> = {
count,
insert_data,
sql_insert_params,
sql_query,
sql_select_params,
update_data,
update_on_duplicate,
...params.request_query,
...params.request_body,
};
const res = await fetchApi<ApiReqParams<T>, APIResponseObject<T>>(url, {
method:
action == "update"
? "PUT"
: action == "delete"
? "DELETE"
: action == "insert"
? "POST"
: "GET",
body: action == "get" ? undefined : query,
query: action == "get" ? query : undefined,
});
if (!res.debug) {
res.debug = {};
}
res.debug.url = url;
res.debug.query = query;
return res;
}
@@ -0,0 +1,26 @@
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type { AdminFetchParams, ApiReqParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function adminFetchHandler<
T extends {} = BUN_SQLITE_BSQLITE_ALL_TYPEDEFS,
>({ url: passed_url, method = "GET", query, body }: AdminFetchParams<T>) {
let url = passed_url;
const res = await fetchApi<ApiReqParams<T>, APIResponseObject<T>>(url, {
method,
body,
query,
});
if (!res.debug) {
res.debug = {};
}
res.debug.url = url;
res.debug.query = query;
res.debug.body = body;
return res;
}
@@ -0,0 +1,15 @@
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type { ApiReqParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function clientDeleteMediaHandler(params: ApiReqParams) {
const res = await fetchApi<ApiReqParams, APIResponseObject>(
`/api/admin/delete-media`,
{
method: "DELETE",
body: params,
},
);
return res;
}
@@ -0,0 +1,16 @@
import type { BUN_SQLITE_BSQLITE_MEDIA } from "@/db/types/db";
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type { ApiReqParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bun-sqlite";
export default async function clientUploadMediaHandler(params: ApiReqParams) {
const res = await fetchApi<
ApiReqParams,
APIResponseObject<BUN_SQLITE_BSQLITE_MEDIA>
>(`/api/admin/upload-media`, {
method: "POST",
body: params,
});
return res;
}
+121
View File
@@ -0,0 +1,121 @@
/**
* # Client-side actions for document/invoice generation & emailing.
* PDF endpoints return binary PDFs (not JSON), so plain fetch is used
* here instead of `fetchApi`.
*/
async function postAdminAction({
url,
body,
}: {
url: string;
body: any;
}): Promise<{ success: boolean; msg?: string }> {
try {
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body || {}),
});
const content_type = res.headers.get("Content-Type") || "";
if (content_type.includes("application/json")) {
const json = await res.json();
return {
success: Boolean(json.success),
msg: json.msg,
};
}
if (!res.ok) {
throw new Error(`Request failed with status ${res.status}`);
}
return {
success: true,
};
} catch (error: any) {
return {
success: false,
msg: error.message || "Request failed",
};
}
}
export async function downloadGeneratedPdf({
url,
body,
fallback_filename,
}: {
url: string;
body: any;
fallback_filename?: string;
}): Promise<{ success: boolean; msg?: string }> {
try {
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body || {}),
});
const content_type = res.headers.get("Content-Type") || "";
if (content_type.includes("application/json")) {
const json = await res.json();
return {
success: Boolean(json.success),
msg: json.msg,
};
}
if (!res.ok) {
throw new Error(`Request failed with status ${res.status}`);
}
const blob = await res.blob();
if (blob.size == 0) {
throw new Error(`Received an empty PDF`);
}
const disposition = res.headers.get("Content-Disposition") || "";
const filename_match = disposition.match(/filename="?([^";]+)"?/i);
const filename =
filename_match?.[1] || fallback_filename || "document.pdf";
const object_url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = object_url;
anchor.download = filename;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
setTimeout(() => {
URL.revokeObjectURL(object_url);
}, 1000);
return { success: true };
} catch (error: any) {
return {
success: false,
msg: error.message || "Couldn't download document",
};
}
}
export async function sendEmailRecord({
url,
body,
}: {
url: string;
body: any;
}) {
return await postAdminAction({ url, body });
}
@@ -0,0 +1,17 @@
import { AppData } from "@/src/data/app-data";
import type { SSOPendingLogin } from "@/src/types";
import EJSON from "@/src/utils/ejson";
export default function readSSOPendingLogin(): SSOPendingLogin | undefined {
try {
const stored = sessionStorage.getItem(
AppData["SSOPendingLoginStorageKey"],
);
return stored
? (EJSON.parse(stored) as SSOPendingLogin | undefined)
: undefined;
} catch {
return undefined;
}
}
@@ -0,0 +1,34 @@
import fetchApi from "@moduletrace/twui/utils/fetch/fetchApi";
import type useFormInit from "@/src/hooks/use-form-init";
import type { ApiReqParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
type Params = Pick<ReturnType<typeof useFormInit>, "setLoading" | "setStatus">;
export default async function resendLoginCode({
setLoading,
setStatus,
}: Params) {
setLoading(true);
const res = await fetchApi<ApiReqParams, APIResponseObject>(
`/api/auth/resend-login-code`,
{
method: "POST",
body: {},
},
);
setStatus(
res.success
? {
success: true,
msg: "A new code is on its way. Previous codes no longer work.",
}
: {
error: true,
msg: res.msg || "Couldn't resend the code. Please try again.",
},
);
setLoading(false);
}
@@ -0,0 +1,16 @@
import { AppData } from "@/src/data/app-data";
import type { SSOPendingLogin } from "@/src/types";
import EJSON from "@/src/utils/ejson";
/**
* Remembers which login is waiting for a code, so the SSO page can word
* itself to match. Best effort only.
*/
export default function saveSSOPendingLogin(pending: SSOPendingLogin) {
try {
sessionStorage.setItem(
AppData["SSOPendingLoginStorageKey"],
EJSON.stringify(pending) || "",
);
} catch {}
}