add clearTable command, simple webpage start

This commit is contained in:
creations 2025-03-18 21:07:15 -04:00
parent f14daf041a
commit 9a93e6269d
Signed by: creations
GPG key ID: 8F553AA4320FC711
10 changed files with 156 additions and 11 deletions

View file

@ -205,3 +205,23 @@ export function supportsExif(mimeType: string, extension: string): boolean {
export function supportsThumbnail(mimeType: string): boolean {
return /^(image\/(?!svg+xml)|video\/)/i.test(mimeType);
}
// Commands
export function parseArgs(): Record<string, string | boolean> {
const args: string[] = process.argv.slice(2);
const parsedArgs: Record<string, string | boolean> = {};
for (let i: number = 0; i < args.length; i++) {
if (args[i].startsWith("--")) {
const key: string = args[i].slice(2);
const value: string | boolean =
args[i + 1] && !args[i + 1].startsWith("--")
? args[i + 1]
: true;
parsedArgs[key] = value;
if (value !== true) i++;
}
}
return parsedArgs;
}

View file

@ -0,0 +1,40 @@
import { parseArgs } from "@helpers/char";
import { type ReservedSQL, sql } from "bun";
(async (): Promise<void> => {
try {
const args: Record<string, string | boolean> = parseArgs();
const table: string | undefined = args.table as string | undefined;
const cascade: boolean = args.cascade === true;
if (!table) {
throw new Error("Missing required argument: --table <table_name>");
}
const reservation: ReservedSQL = await sql.reserve();
try {
await reservation`TRUNCATE TABLE ${sql(table)} ${cascade ? sql`CASCADE` : sql``};`;
console.log(
`Table ${table} has been cleared${cascade ? " with CASCADE" : ""}.`,
);
} catch (error) {
if (
error instanceof Error &&
error.message.includes("foreign key constraint")
) {
console.error(
`Could not clear table "${table}" due to foreign key constraints.\n` +
"Try using --cascade if you want to remove dependent records.",
);
} else {
console.error("Could not clear table:", error);
}
} finally {
reservation.release();
}
} catch (error) {
console.error("Unexpected error:", error);
process.exit(1);
}
})();

View file

@ -48,12 +48,7 @@ async function handler(
);
}
if (
typeof key !== "string" ||
(typeof value !== "string" &&
typeof value !== "boolean" &&
typeof value !== "number")
) {
if (!["string", "boolean", "number"].includes(typeof value)) {
return Response.json(
{
success: false,

20
src/routes/auth/login.ts Normal file
View file

@ -0,0 +1,20 @@
import { getSetting } from "@config/sql/settings";
import { renderEjsTemplate } from "@helpers/ejs";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(): Promise<Response> {
const ejsTemplateData: EjsTemplateData = {
title: "Hello, World!",
instance_name:
(await getSetting("instance_name")) || "Unnamed Instance",
};
return await renderEjsTemplate("auth/login", ejsTemplateData);
}
export { handler, routeDef };

View file

@ -6,7 +6,11 @@ const routeDef: RouteDef = {
returns: "text/html",
};
async function handler(): Promise<Response> {
async function handler(request: ExtendedRequest): Promise<Response> {
if (!request.session) {
return Response.redirect("/auth/login");
}
const ejsTemplateData: EjsTemplateData = {
title: "Hello, World!",
};

View file

@ -3,7 +3,7 @@ import { isValidUsername } from "@config/sql/users";
import { type BunFile, type ReservedSQL, sql } from "bun";
import { resolve } from "path";
import { isUUID, nameWithoutExtension } from "@/helpers/char";
import { getBaseUrl, isUUID, nameWithoutExtension } from "@/helpers/char";
import { logger } from "@/helpers/logger";
const routeDef: RouteDef = {
@ -70,7 +70,13 @@ async function handler(request: ExtendedRequest): Promise<Response> {
if (json === "true" || json === "1") {
return Response.json(
{ success: true, code: 200, data: avatar },
{
success: true, code: 200,
avatar: {
...avatar,
url: `${getBaseUrl(request)}/user/avatar/${user.id}`,
}
},
{ status: 200 },
);
}

28
src/views/auth/login.ejs Normal file
View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<%- include("../global", { styles: ["auth/login"], scripts: [] }) %>
</head>
<body>
<div class="container">
<div class="header">
<h1><%= instance_name %></h1>
</div>
<div class="content">
<form id="login-form">
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" id="email" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
</div>
<div class="form-group">
<button type="submit">Login</button>
</div>
</form>
</div>
</div>
</body>
</html>