after biome unsafe, restart frontend, add superadmin

This commit is contained in:
creations 2025-04-13 10:22:47 -04:00
parent 25fcd99acf
commit c02b519eee
Signed by: creations
GPG key ID: 8F553AA4320FC711
41 changed files with 189 additions and 910 deletions

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
export const environment: Environment = { export const environment: Environment = {
port: Number.parseInt(process.env.PORT || "8080", 10), port: Number.parseInt(process.env.PORT || "8080", 10),

View file

@ -5,14 +5,14 @@ export const order: number = 6;
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS avatars ( CREATE TABLE IF NOT EXISTS avatars (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@ -28,7 +28,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -5,14 +5,14 @@ export const order: number = 5;
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS files ( CREATE TABLE IF NOT EXISTS files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@ -37,7 +37,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
); );
`; `;
const functionExists: { exists: boolean }[] = await reservation` const functionExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_proc SELECT 1 FROM pg_proc
JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid
@ -46,7 +46,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!functionExists[0].exists) { if (!functionExists[0].exists) {
await reservation` await activeReservation`
CREATE FUNCTION update_files_updated_at() CREATE FUNCTION update_files_updated_at()
RETURNS TRIGGER AS $$ RETURNS TRIGGER AS $$
BEGIN BEGIN
@ -57,7 +57,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
} }
const triggerExists: { exists: boolean }[] = await reservation` const triggerExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_trigger SELECT 1 FROM pg_trigger
WHERE tgname = 'trigger_update_files_updated_at' WHERE tgname = 'trigger_update_files_updated_at'
@ -65,7 +65,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!triggerExists[0].exists) { if (!triggerExists[0].exists) {
await reservation` await activeReservation`
CREATE TRIGGER trigger_update_files_updated_at CREATE TRIGGER trigger_update_files_updated_at
BEFORE UPDATE ON files BEFORE UPDATE ON files
FOR EACH ROW FOR EACH ROW
@ -80,7 +80,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -5,14 +5,14 @@ export const order: number = 4;
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS folders ( CREATE TABLE IF NOT EXISTS folders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, owner UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@ -26,7 +26,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
); );
`; `;
const functionExists: { exists: boolean }[] = await reservation` const functionExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_proc SELECT 1 FROM pg_proc
JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid
@ -35,7 +35,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!functionExists[0].exists) { if (!functionExists[0].exists) {
await reservation` await activeReservation`
CREATE FUNCTION update_folders_updated_at() CREATE FUNCTION update_folders_updated_at()
RETURNS TRIGGER AS $$ RETURNS TRIGGER AS $$
BEGIN BEGIN
@ -46,7 +46,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
} }
const triggerExists: { exists: boolean }[] = await reservation` const triggerExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_trigger SELECT 1 FROM pg_trigger
WHERE tgname = 'trigger_update_folders_updated_at' WHERE tgname = 'trigger_update_folders_updated_at'
@ -54,7 +54,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!triggerExists[0].exists) { if (!triggerExists[0].exists) {
await reservation` await activeReservation`
CREATE TRIGGER trigger_update_folders_updated_at CREATE TRIGGER trigger_update_folders_updated_at
BEFORE UPDATE ON folders BEFORE UPDATE ON folders
FOR EACH ROW FOR EACH ROW
@ -69,7 +69,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -5,14 +5,14 @@ export const order: number = 3;
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS invites ( CREATE TABLE IF NOT EXISTS invites (
id TEXT PRIMARY KEY NOT NULL UNIQUE, id TEXT PRIMARY KEY NOT NULL UNIQUE,
created_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, created_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@ -27,7 +27,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -21,14 +21,14 @@ const defaultSettings: Setting[] = [
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS settings ( CREATE TABLE IF NOT EXISTS settings (
"key" VARCHAR(64) PRIMARY KEY NOT NULL UNIQUE, "key" VARCHAR(64) PRIMARY KEY NOT NULL UNIQUE,
"value" TEXT NOT NULL, "value" TEXT NOT NULL,
@ -37,7 +37,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
); );
`; `;
const functionExists: { exists: boolean }[] = await reservation` const functionExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_proc SELECT 1 FROM pg_proc
JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid
@ -46,7 +46,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!functionExists[0].exists) { if (!functionExists[0].exists) {
await reservation` await activeReservation`
CREATE FUNCTION update_settings_updated_at() CREATE FUNCTION update_settings_updated_at()
RETURNS TRIGGER AS $$ RETURNS TRIGGER AS $$
BEGIN BEGIN
@ -57,7 +57,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
} }
const triggerExists: { exists: boolean }[] = await reservation` const triggerExists: { exists: boolean }[] = await activeReservation`
SELECT EXISTS ( SELECT EXISTS (
SELECT 1 FROM pg_trigger SELECT 1 FROM pg_trigger
WHERE tgname = 'trigger_update_settings_updated_at' WHERE tgname = 'trigger_update_settings_updated_at'
@ -65,7 +65,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
`; `;
if (!triggerExists[0].exists) { if (!triggerExists[0].exists) {
await reservation` await activeReservation`
CREATE TRIGGER trigger_update_settings_updated_at CREATE TRIGGER trigger_update_settings_updated_at
BEFORE UPDATE ON settings BEFORE UPDATE ON settings
FOR EACH ROW FOR EACH ROW
@ -74,7 +74,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
} }
for (const setting of defaultSettings) { for (const setting of defaultSettings) {
await reservation` await activeReservation`
INSERT INTO settings ("key", "value") INSERT INTO settings ("key", "value")
VALUES (${setting.key}, ${setting.value}) VALUES (${setting.key}, ${setting.value})
ON CONFLICT ("key") DO NOTHING; ON CONFLICT ("key") DO NOTHING;
@ -88,7 +88,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }
@ -100,15 +100,15 @@ export async function getSetting(
reservation?: ReservedSQL, reservation?: ReservedSQL,
): Promise<string | null> { ): Promise<string | null> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
const result: { value: string }[] = const result: { value: string }[] =
await reservation`SELECT value FROM settings WHERE "key" = ${key};`; await activeReservation`SELECT value FROM settings WHERE "key" = ${key};`;
if (result.length === 0) { if (result.length === 0) {
return null; return null;
@ -120,7 +120,7 @@ export async function getSetting(
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }
@ -131,14 +131,14 @@ export async function setSetting(
reservation?: ReservedSQL, reservation?: ReservedSQL,
): Promise<void> { ): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
INSERT INTO settings ("key", "value", updated_at) INSERT INTO settings ("key", "value", updated_at)
VALUES (${key}, ${value}, NOW()) VALUES (${key}, ${value}, NOW())
ON CONFLICT ("key") ON CONFLICT ("key")
@ -148,7 +148,7 @@ export async function setSetting(
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }
@ -158,20 +158,20 @@ export async function deleteSetting(
reservation?: ReservedSQL, reservation?: ReservedSQL,
): Promise<void> { ): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation`DELETE FROM settings WHERE "key" = ${key};`; await activeReservation`DELETE FROM settings WHERE "key" = ${key};`;
} catch (error) { } catch (error) {
logger.error(["Could not delete the setting:", error as Error]); logger.error(["Could not delete the setting:", error as Error]);
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }
@ -180,15 +180,15 @@ export async function getAllSettings(
reservation?: ReservedSQL, reservation?: ReservedSQL,
): Promise<{ key: string; value: string }[]> { ): Promise<{ key: string; value: string }[]> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
const result: { key: string; value: string }[] = const result: { key: string; value: string }[] =
await reservation`SELECT "key", "value" FROM settings;`; await activeReservation`SELECT "key", "value" FROM settings;`;
return result; return result;
} catch (error) { } catch (error) {
@ -196,7 +196,7 @@ export async function getAllSettings(
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -5,14 +5,14 @@ export const order: number = 1;
export async function createTable(reservation?: ReservedSQL): Promise<void> { export async function createTable(reservation?: ReservedSQL): Promise<void> {
let selfReservation = false; let selfReservation = false;
const activeReservation: ReservedSQL = reservation ?? (await sql.reserve());
if (!reservation) { if (!reservation) {
reservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
await reservation` await activeReservation`
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
authorization_token UUID NOT NULL UNIQUE DEFAULT gen_random_uuid(), authorization_token UUID NOT NULL UNIQUE DEFAULT gen_random_uuid(),
@ -32,7 +32,7 @@ export async function createTable(reservation?: ReservedSQL): Promise<void> {
throw error; throw error;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -26,6 +26,7 @@
}, },
"dependencies": { "dependencies": {
"ejs": "^3.1.10", "ejs": "^3.1.10",
"eta": "^3.5.0",
"exiftool-vendored": "^29.3.0", "exiftool-vendored": "^29.3.0",
"fast-jwt": "6.0.1", "fast-jwt": "6.0.1",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",

View file

@ -1,198 +0,0 @@
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
}
.content {
border: 1px solid var(--border);
background-color: var(--background-secondary);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
width: clamp(200px, 50%, 300px);
}
.content form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.auth-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(
135deg,
rgba(31 30 30 / 90%) 0%,
rgba(45 45 45 / 90%) 100%
);
}
.auth-logo {
text-align: center;
margin-bottom: 2rem;
}
.auth-logo h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
color: var(--accent);
}
.auth-logo p {
color: var(--text-secondary);
margin-top: 0.5rem;
}
.auth-card {
background-color: var(--background-secondary);
border-radius: 8px;
box-shadow: var(--card-shadow);
width: 100%;
max-width: 400px;
overflow: hidden;
animation: fade-in 0.5s ease;
}
.auth-header {
padding: 1.5rem;
text-align: center;
border-bottom: 1px solid var(--border);
background-color: rgba(0 0 0 / 10%);
}
.auth-header h2 {
margin: 0;
font-size: 1.5rem;
color: var(--text);
}
.auth-form {
padding: 1.5rem;
}
.auth-form form {
display: flex;
flex-direction: column;
width: 100%;
}
.auth-toggle {
text-align: center;
margin-top: 1.5rem;
font-size: 0.9rem;
color: var(--text-secondary);
}
.form-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
margin-top: 1rem;
}
.form-footer a {
color: var(--accent);
text-decoration: none;
}
.form-footer a:hover {
text-decoration: underline;
}
.form-footer label {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
white-space: nowrap;
}
.auth-form button {
margin-top: 0.5rem;
width: 100%;
}
.password-group {
position: relative;
width: 100%;
}
.password-wrapper {
position: relative;
width: 100%;
}
.password-wrapper input {
width: 100%;
padding-right: 2rem;
}
.toggle-password {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
width: 20px;
height: 20px;
fill: var(--text-secondary);
transition: fill 0.2s ease;
}
.toggle-password:hover {
fill: var(--text);
}
.error-message {
color: var(--error);
background-color: rgb(237 66 69 / 10%);
padding: 0.75rem;
margin-bottom: 1.5rem;
border-radius: 4px;
display: none;
font-size: 0.9rem;
text-align: center;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.auth-link {
color: var(--accent);
text-decoration: none;
font-weight: bold;
}
.auth-link:hover {
text-decoration: underline;
}
@media (width <= 480px) {
.auth-card {
max-width: 100%;
}
.auth-logo h1 {
font-size: 2rem;
}
}

View file

@ -1,79 +0,0 @@
body {
display: flex;
flex-direction: row;
}
/* sidebar */
.sidebar {
background-color: var(--background-secondary);
width: 220px;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
border-right: 1px solid var(--border);
box-sizing: border-box;
}
.sidebar .actions {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.sidebar .actions .action {
display: flex;
justify-content: flex-start;
gap: .5rem;
align-items: center;
padding: 1rem;
height: 3rem;
width: 100%;
transition: background-color 0.2s ease;
text-decoration: none;
color: var(--text);
box-sizing: border-box;
}
.sidebar .actions .action svg {
width: 15px;
height: 15px;
}
.sidebar .actions .action:hover {
background-color: var(--background);
}
.sidebar .actions .action.active {
background-color: var(--background);
}
.sidebar .actions .action.active:hover {
background-color: var(--background-secondary);
}
.sidebar .user-area {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
border-top: 1px solid var(--border);
background-color: rgba(0 0 0 / 10%);
}
.sidebar .user-area img {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
.sidebar .user-area .username {
margin-top: 1rem;
font-weight: bold;
color: var(--text);
}

View file

@ -46,82 +46,3 @@ body {
background-color: var(--background); background-color: var(--background);
color: var(--text); color: var(--text);
} }
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
input,
button,
textarea,
select {
font-family: inherit;
font-size: 1rem;
border-radius: 4px;
transition: all 0.2s ease;
}
button,
.button {
cursor: pointer;
padding: 0.75rem 1.5rem;
border: none;
background-color: var(--accent);
color: white;
font-weight: bold;
border-radius: 4px;
transition: background-color 0.2s ease;
}
button:hover,
.button:hover {
background-color: var(--accent-hover);
}
input,
textarea,
select {
padding: 0.75rem;
border: 1px solid var(--border);
background-color: var(--input-background);
color: var(--text);
width: 100%;
box-sizing: border-box;
}
input:focus,
textarea:focus,
select:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 2px rgb(88 101 242 / 30%);
}
.form-group {
margin-bottom: 1.5rem;
width: 100%;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
color: var(--text-secondary);
}
svg {
fill: var(--svg-fill);
transition: fill 0.2s ease;
}
svg.stroke-only {
fill: none;
stroke: var(--svg-fill);
stroke-width: 2;
}
svg:hover {
fill: var(--accent);
}

View file

@ -1,130 +0,0 @@
const loginForm = document.getElementById("login-form");
const registerForm = document.getElementById("register-form");
const errorMessage = document.getElementById("error-message");
const rememberMe = document.getElementById("remember-me");
const emailInput = document.getElementById("email");
if (emailInput && localStorage.getItem("email")) {
emailInput.value = localStorage.getItem("email");
}
if (loginForm) {
loginForm.addEventListener("submit", async (e) => {
e.preventDefault();
const email = emailInput?.value.trim();
const password = document.getElementById("password")?.value.trim();
if (!email || !password) {
if (errorMessage) {
errorMessage.style.display = "block";
errorMessage.textContent = "Please enter both email and password.";
}
return;
}
if (rememberMe?.checked) {
localStorage.setItem("email", email);
} else {
sessionStorage.setItem("email", email);
}
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({ email, password }),
});
const data = await response.json();
if (data.success) {
window.location.href = "/dashboard";
} else {
if (errorMessage) {
errorMessage.style.display = "block";
errorMessage.textContent =
data.error || "Invalid email or password. Please try again.";
}
}
} catch (error) {
console.error("Login error:", error);
if (errorMessage) {
errorMessage.style.display = "block";
errorMessage.textContent = "An error occurred. Please try again.";
}
}
});
} else if (registerForm) {
registerForm.addEventListener("submit", async (e) => {
e.preventDefault();
const email = emailInput?.value.trim();
const username = document.getElementById("username")?.value.trim();
const password = document.getElementById("password")?.value.trim();
const inviteCode = document.getElementById("invite-code")?.value.trim();
if (!email || !password) {
if (errorMessage) {
errorMessage.style.display = "block";
errorMessage.textContent = "Please enter email, password.";
}
return;
}
try {
const response = await fetch("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({
username,
email,
password,
invite: inviteCode,
}),
});
const data = await response.json();
if (data.success) {
window.location.href = "/dashboard";
} else {
if (errorMessage) {
errorMessage.style.display = "block";
if (Array.isArray(data.errors)) {
errorMessage.innerHTML = data.errors
.map((err) => `<p>${err}</p>`)
.join("");
} else {
errorMessage.textContent =
data.error || "An error occurred. Please try again.";
}
}
}
} catch (error) {
console.error("Register error:", error);
if (errorMessage) {
errorMessage.style.display = "block";
errorMessage.textContent = "An error occurred. Please try again.";
}
}
});
}
const passwordInput = document.getElementById("password");
const togglePassword = document.getElementById("toggle-password");
togglePassword.addEventListener("click", () => {
if (passwordInput.type === "password") {
passwordInput.type = "text";
togglePassword.innerHTML =
'<path d="M12 4.5c-5 0-9.27 3.11-11 7.5 1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 13c-3.03 0-5.5-2.47-5.5-5.5s2.47-5.5 5.5-5.5 5.5 2.47 5.5 5.5-2.47 5.5-5.5 5.5z"/>';
} else {
passwordInput.type = "password";
togglePassword.innerHTML =
'<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 13c-3.03 0-5.5-2.47-5.5-5.5s2.47-5.5 5.5-5.5 5.5 2.47 5.5 5.5-2.47 5.5-5.5 5.5zm0-9a3.5 3.5 0 100 7 3.5 3.5 0 000-7z"/>';
}
});

View file

@ -7,6 +7,7 @@ export async function authByToken(
reservation?: ReservedSQL, reservation?: ReservedSQL,
): Promise<ApiUserSession | null> { ): Promise<ApiUserSession | null> {
let selfReservation = false; let selfReservation = false;
let activeReservation: ReservedSQL | undefined = reservation;
const authorizationHeader: string | null = const authorizationHeader: string | null =
request.headers.get("Authorization"); request.headers.get("Authorization");
@ -17,14 +18,14 @@ export async function authByToken(
const authorizationToken: string = authorizationHeader.slice(7).trim(); const authorizationToken: string = authorizationHeader.slice(7).trim();
if (!authorizationToken || !isUUID(authorizationToken)) return null; if (!authorizationToken || !isUUID(authorizationToken)) return null;
if (!reservation) { if (!activeReservation) {
reservation = await sql.reserve(); activeReservation = await sql.reserve();
selfReservation = true; selfReservation = true;
} }
try { try {
const result: User[] = const result: User[] =
await reservation`SELECT * FROM users WHERE authorization_token = ${authorizationToken};`; await activeReservation`SELECT * FROM users WHERE authorization_token = ${authorizationToken};`;
if (result.length === 0) return null; if (result.length === 0) return null;
@ -44,7 +45,7 @@ export async function authByToken(
return null; return null;
} finally { } finally {
if (selfReservation) { if (selfReservation) {
reservation.release(); activeReservation.release();
} }
} }
} }

View file

@ -2,8 +2,8 @@ import { DateTime } from "luxon";
export function timestampToReadable(timestamp?: number): string { export function timestampToReadable(timestamp?: number): string {
const date: Date = const date: Date =
timestamp && !isNaN(timestamp) ? new Date(timestamp) : new Date(); timestamp && !Number.isNaN(timestamp) ? new Date(timestamp) : new Date();
if (isNaN(date.getTime())) return "Invalid Date"; if (Number.isNaN(date.getTime())) return "Invalid Date";
return date.toISOString().replace("T", " ").replace("Z", ""); return date.toISOString().replace("T", " ").replace("Z", "");
} }
@ -84,15 +84,13 @@ export function isValidTimezone(timezone: string): boolean {
} }
export function generateRandomString(length?: number): string { export function generateRandomString(length?: number): string {
if (!length) { const finalLength: number = length ?? Math.floor(Math.random() * 10) + 5;
length = length || Math.floor(Math.random() * 10) + 5;
}
const characters: string = const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = ""; let result = "";
for (let i = 0; i < length; i++) { for (let i = 0; i < finalLength; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length)); result += characters.charAt(Math.floor(Math.random() * characters.length));
} }

View file

@ -24,8 +24,7 @@ import { type ReservedSQL, sql } from "bun";
error.message.includes("foreign key constraint") error.message.includes("foreign key constraint")
) { ) {
console.error( console.error(
`Could not clear table "${table}" due to foreign key constraints.\n` + `Could not clear table "${table}" due to foreign key constraints.\nTry using --cascade if you want to remove dependent records.`,
"Try using --cascade if you want to remove dependent records.",
); );
} else { } else {
console.error("Could not clear table:", error); console.error("Could not clear table:", error);

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { renderFile } from "ejs"; import { renderFile } from "ejs";
export async function renderEjsTemplate( export async function renderEjsTemplate(

View file

@ -1,13 +1,13 @@
import type { Stats } from "fs"; import type { Stats } from "node:fs";
import { import {
type WriteStream, type WriteStream,
createWriteStream, createWriteStream,
existsSync, existsSync,
mkdirSync, mkdirSync,
statSync, statSync,
} from "fs"; } from "node:fs";
import { EOL } from "os"; import { EOL } from "node:os";
import { basename, join } from "path"; import { basename, join } from "node:path";
import { environment } from "@config/environment"; import { environment } from "@config/environment";
import { timestampToReadable } from "@helpers/char"; import { timestampToReadable } from "@helpers/char";

View file

@ -86,12 +86,12 @@ class RedisJson {
} }
return value; return value;
} else if (type === "STRING") { }
if (type === "STRING") {
const value: string | null = await this.client.get(key); const value: string | null = await this.client.get(key);
return value; return value;
} else {
throw new Error(`Invalid type: ${type}`);
} }
throw new Error(`Invalid type: ${type}`);
} catch (error) { } catch (error) {
logger.error(`Error getting value from Redis for key: ${key}`); logger.error(`Error getting value from Redis for key: ${key}`);
logger.error(error as Error); logger.error(error as Error);

View file

@ -50,7 +50,7 @@ class SessionManager {
const userSessions: string[] = await redis const userSessions: string[] = await redis
.getInstance() .getInstance()
.keys("session:*:" + token); .keys(`session:*:${token}`);
if (!userSessions.length) return null; if (!userSessions.length) return null;
const sessionData: unknown = await redis const sessionData: unknown = await redis
@ -76,7 +76,7 @@ class SessionManager {
const userSessions: string[] = await redis const userSessions: string[] = await redis
.getInstance() .getInstance()
.keys("session:*:" + token); .keys(`session:*:${token}`);
if (!userSessions.length) throw new Error("Session not found or expired"); if (!userSessions.length) throw new Error("Session not found or expired");
const sessionKey: string = userSessions[0]; const sessionKey: string = userSessions[0];
@ -96,7 +96,7 @@ class SessionManager {
public async verifySession(token: string): Promise<UserSession> { public async verifySession(token: string): Promise<UserSession> {
const userSessions: string[] = await redis const userSessions: string[] = await redis
.getInstance() .getInstance()
.keys("session:*:" + token); .keys(`session:*:${token}`);
if (!userSessions.length) throw new Error("Session not found or expired"); if (!userSessions.length) throw new Error("Session not found or expired");
const sessionData: unknown = await redis const sessionData: unknown = await redis
@ -122,7 +122,7 @@ class SessionManager {
const userSessions: string[] = await redis const userSessions: string[] = await redis
.getInstance() .getInstance()
.keys("session:*:" + token); .keys(`session:*:${token}`);
if (!userSessions.length) return; if (!userSessions.length) return;
await redis.getInstance().delete("JSON", userSessions[0]); await redis.getInstance().delete("JSON", userSessions[0]);

View file

@ -1,11 +1,11 @@
import { join, resolve } from "path"; import { join, resolve } from "node:path";
import { dataType } from "@config/environment.ts"; import { dataType } from "@config/environment.ts";
import { logger } from "@helpers/logger.ts"; import { logger } from "@helpers/logger.ts";
import { type BunFile, s3, sql } from "bun"; import { type BunFile, s3, sql } from "bun";
import ffmpeg from "fluent-ffmpeg"; import ffmpeg from "fluent-ffmpeg";
import imageThumbnail from "image-thumbnail"; import imageThumbnail from "image-thumbnail";
declare var self: Worker; declare let self: Worker;
async function generateVideoThumbnail( async function generateVideoThumbnail(
filePath: string, filePath: string,
@ -47,37 +47,34 @@ async function generateImageThumbnail(
thumbnailPath: string, thumbnailPath: string,
): Promise<ArrayBuffer> { ): Promise<ArrayBuffer> {
return new Promise( return new Promise(
async ( (
resolve: (value: ArrayBuffer) => void, resolve: (value: ArrayBuffer) => void,
reject: (reason: Error) => void, reject: (reason: Error) => void,
) => { ): void => {
try { const options = {
const options: {
responseType: "buffer";
height: number;
jpegOptions: {
force: boolean;
quality: number;
};
} = {
height: 320, height: 320,
responseType: "buffer", responseType: "buffer" as const,
jpegOptions: { jpegOptions: {
force: true, force: true,
quality: 60, quality: 60,
}, },
}; };
const thumbnailBuffer: Buffer = await imageThumbnail(filePath, options); imageThumbnail(filePath, options)
.then(
await Bun.write(thumbnailPath, thumbnailBuffer.buffer); (thumbnailBuffer: Buffer): Promise<ArrayBuffer> =>
resolve(await Bun.file(thumbnailPath).arrayBuffer()); Bun.write(thumbnailPath, thumbnailBuffer.buffer).then(
(): Promise<ArrayBuffer> => Bun.file(thumbnailPath).arrayBuffer(),
await Bun.file(filePath).unlink(); ),
await Bun.file(thumbnailPath).unlink(); )
} catch (error) { .then((arrayBuffer: ArrayBuffer) => {
reject(error as Error); resolve(arrayBuffer);
} return Promise.all([
Bun.file(filePath).unlink(),
Bun.file(thumbnailPath).unlink(),
]);
})
.catch(reject);
}, },
); );
} }

View file

@ -1,9 +1,9 @@
import { existsSync, mkdirSync } from "fs"; import { existsSync, mkdirSync } from "node:fs";
import { resolve } from "path"; import { readdir } from "node:fs/promises";
import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { logger } from "@helpers/logger"; import { logger } from "@helpers/logger";
import { type ReservedSQL, s3, sql } from "bun"; import { type ReservedSQL, s3, sql } from "bun";
import { readdir } from "fs/promises";
import { serverHandler } from "@/server"; import { serverHandler } from "@/server";
@ -38,7 +38,6 @@ async function initializeDatabase(): Promise<void> {
} }
async function main(): Promise<void> { async function main(): Promise<void> {
try {
try { try {
await sql`SELECT 1;`; await sql`SELECT 1;`;
@ -73,10 +72,7 @@ async function main(): Promise<void> {
await s3.write("test", "test"); await s3.write("test", "test");
await s3.delete("test"); await s3.delete("test");
logger.info([ logger.info(["Connected to S3 with bucket", `${process.env.S3_BUCKET}`]);
"Connected to S3 with bucket",
`${process.env.S3_BUCKET}`,
]);
} catch (error) { } catch (error) {
logger.error([ logger.error([
"Could not establish a connection to S3 bucket:", "Could not establish a connection to S3 bucket:",
@ -89,9 +85,6 @@ async function main(): Promise<void> {
await redis.initialize(); await redis.initialize();
serverHandler.initialize(); serverHandler.initialize();
await initializeDatabase(); await initializeDatabase();
} catch (error) {
throw error;
}
} }
main().catch((error: Error) => { main().catch((error: Error) => {

View file

@ -66,11 +66,11 @@ async function handler(
password ? { check: isValidPassword(password), field: "Password" } : null, password ? { check: isValidPassword(password), field: "Password" } : null,
].filter(Boolean) as UserValidation[]; ].filter(Boolean) as UserValidation[];
validations.forEach(({ check }: UserValidation): void => { for (const { check } of validations) {
if (!check.valid && check.error) { if (!check.valid && check.error) {
errors.push(check.error); errors.push(check.error);
} }
}); }
if (!username && !email) { if (!username && !email) {
errors.push("Either a username or an email is required."); errors.push("Either a username or an email is required.");

View file

@ -49,11 +49,11 @@ async function handler(
{ check: isValidPassword(password), field: "Password" }, { check: isValidPassword(password), field: "Password" },
]; ];
validations.forEach(({ check }: UserValidation): void => { for (const { check } of validations) {
if (!check.valid && check.error) { if (!check.valid && check.error) {
errors.push(check.error); errors.push(check.error);
} }
}); }
const normalizedUsername: string = username.normalize("NFC"); const normalizedUsername: string = username.normalize("NFC");
const reservation: ReservedSQL = await sql.reserve(); const reservation: ReservedSQL = await sql.reserve();

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { type SQLQuery, s3, sql } from "bun"; import { type SQLQuery, s3, sql } from "bun";

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { getSetting } from "@config/sql/settings"; import { getSetting } from "@config/sql/settings";
import { import {
@ -174,13 +174,9 @@ async function processFile(
let hashedPassword: string | null = null; let hashedPassword: string | null = null;
if (user_provided_password) { if (user_provided_password) {
try {
hashedPassword = await bunPassword.hash(user_provided_password, { hashedPassword = await bunPassword.hash(user_provided_password, {
algorithm: "argon2id", algorithm: "argon2id",
}); });
} catch (error) {
throw error;
}
} }
const randomUUID: string = randomUUIDv7(); const randomUUID: string = randomUUIDv7();
@ -217,7 +213,7 @@ async function processFile(
// ? Should work not sure about non-english characters // ? Should work not sure about non-english characters
const sanitizedFileName: string = rawName const sanitizedFileName: string = rawName
.normalize("NFD") .normalize("NFD")
.replace(/[\u0300-\u036f]/g, "") .replace(/\p{Mn}/gu, "")
.replace(/[^a-zA-Z0-9._-]/g, "_") .replace(/[^a-zA-Z0-9._-]/g, "_")
.toLowerCase(); .toLowerCase();
@ -276,7 +272,7 @@ async function processFile(
return; return;
} }
} else { } else {
path = "/uploads/" + uuidWithExtension; path = `/uploads/${uuidWithExtension}`;
try { try {
await s3.write(path, fileBuffer); await s3.write(path, fileBuffer);
@ -321,7 +317,7 @@ async function processFile(
return; return;
} }
if (uploadEntry.password) delete uploadEntry.password; if (uploadEntry.password) uploadEntry.password = undefined;
uploadEntry.url = `${userHeaderOptions.domain}/raw/${uploadEntry.name}`; uploadEntry.url = `${userHeaderOptions.domain}/raw/${uploadEntry.name}`;
successfulFiles.push(uploadEntry); successfulFiles.push(uploadEntry);

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { s3, sql } from "bun"; import { s3, sql } from "bun";
@ -113,7 +113,7 @@ async function handler(request: ExtendedRequest): Promise<Response> {
}, },
}, },
); );
} else { }
return Response.json( return Response.json(
{ {
success: true, success: true,
@ -122,7 +122,6 @@ async function handler(request: ExtendedRequest): Promise<Response> {
}, },
{ status: 200 }, { status: 200 },
); );
}
} catch (error) { } catch (error) {
logger.error(["Error processing delete request:", error as Error]); logger.error(["Error processing delete request:", error as Error]);

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { isValidTypeOrExtension } from "@config/sql/avatars"; import { isValidTypeOrExtension } from "@config/sql/avatars";
import { getSetting } from "@config/sql/settings"; import { getSetting } from "@config/sql/settings";
@ -201,7 +201,7 @@ async function handler(
}, },
}, },
); );
} else { }
return Response.json( return Response.json(
{ {
success: true, success: true,
@ -211,7 +211,6 @@ async function handler(
}, },
{ status: 200 }, { status: 200 },
); );
}
} catch (error) { } catch (error) {
logger.error(["Error processing file:", error as Error]); logger.error(["Error processing file:", error as Error]);

View file

@ -110,9 +110,9 @@ async function handler(request: ExtendedRequest): Promise<Response> {
); );
} }
delete user.password; user.password = undefined;
delete user.authorization_token; user.authorization_token = undefined;
if (!isSelf) delete user.email; if (!isSelf) user.email = undefined;
user.roles = user.roles ? user.roles[0].split(",") : []; user.roles = user.roles ? user.roles[0].split(",") : [];

View file

@ -1,24 +0,0 @@
import { getSetting } from "@config/sql/settings";
import { renderEjsTemplate } from "@helpers/ejs";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(request: ExtendedRequest): Promise<Response> {
if (request.session) return Response.redirect("/");
const instanceName: string =
(await getSetting("instance_name")) || "Unnamed Instance";
const ejsTemplateData: EjsTemplateData = {
title: `Login - ${instanceName}`,
instance_name: instanceName,
};
return await renderEjsTemplate("auth/login", ejsTemplateData);
}
export { handler, routeDef };

View file

@ -1,41 +0,0 @@
import { getSetting } from "@config/sql/settings";
import { renderEjsTemplate } from "@helpers/ejs";
import { type ReservedSQL, sql } from "bun";
import { logger } from "@/helpers/logger";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(request: ExtendedRequest): Promise<Response> {
if (request.session) return Response.redirect("/");
const reservation: ReservedSQL = await sql.reserve();
try {
const [firstUser] = await sql`SELECT COUNT(*) FROM users`;
const instanceName: string =
(await getSetting("instance_name", reservation)) || "Unnamed Instance";
const requiresInvite: boolean =
(await getSetting("enable_invitations", reservation)) === "true" &&
firstUser.count !== "0";
const ejsTemplateData: EjsTemplateData = {
title: `Register - ${instanceName}`,
instance_name: instanceName,
requires_invite: requiresInvite,
};
return await renderEjsTemplate("auth/register", ejsTemplateData);
} catch (error) {
logger.error(["Error rendering register page", error as Error]);
return Response.redirect("/");
} finally {
reservation.release();
}
}
export { handler, routeDef };

View file

@ -1,22 +0,0 @@
import { renderEjsTemplate } from "@helpers/ejs";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(request: ExtendedRequest): Promise<Response> {
// if (!request.session) {
// return Response.redirect("/auth/login");
// }
const ejsTemplateData: EjsTemplateData = {
title: "Hello, World!",
active: "dashboard",
};
return await renderEjsTemplate("dashboard/index.ejs", ejsTemplateData);
}
export { handler, routeDef };

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { type BunFile, type ReservedSQL, sql } from "bun"; import { type BunFile, type ReservedSQL, sql } from "bun";
@ -115,7 +115,7 @@ async function handler(request: ExtendedRequest): Promise<Response> {
} }
if (json === "true" || json === "1") { if (json === "true" || json === "1") {
delete fileData.password; fileData.password = undefined;
fileData.tags = fileData.tags = fileData.tags[0]?.trim() fileData.tags = fileData.tags = fileData.tags[0]?.trim()
? fileData.tags[0].split(",").filter((tag: string) => tag.trim()) ? fileData.tags[0].split(",").filter((tag: string) => tag.trim())
: []; : [];

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { dataType } from "@config/environment"; import { dataType } from "@config/environment";
import { isValidUsername } from "@config/sql/users"; import { isValidUsername } from "@config/sql/users";
import { type BunFile, type ReservedSQL, sql } from "bun"; import { type BunFile, type ReservedSQL, sql } from "bun";

View file

@ -1,4 +1,4 @@
import { resolve } from "path"; import { resolve } from "node:path";
import { environment } from "@config/environment"; import { environment } from "@config/environment";
import { logger } from "@helpers/logger"; import { logger } from "@helpers/logger";
import { import {
@ -41,10 +41,14 @@ class ServerHandler {
maxRequestBodySize: 10 * 1024 * 1024 * 1024, // 10GB ? will be changed to env var soon maxRequestBodySize: 10 * 1024 * 1024 * 1024, // 10GB ? will be changed to env var soon
}); });
logger.info( const accessUrls: string[] = [
`Server running at http://${server.hostname}:${server.port}`, `http://${server.hostname}:${server.port}`,
true, `http://localhost:${server.port}`,
); `http://127.0.0.1:${server.port}`,
];
logger.info(`Server running at ${accessUrls[0]}`);
logger.info(`Access via: ${accessUrls[1]} or ${accessUrls[2]}`, true);
this.logRoutes(); this.logRoutes();
} }
@ -82,10 +86,9 @@ class ServerHandler {
return new Response(fileContent, { return new Response(fileContent, {
headers: { "Content-Type": contentType }, headers: { "Content-Type": contentType },
}); });
} else { }
logger.warn(`File not found: ${filePath}`); logger.warn(`File not found: ${filePath}`);
return new Response("Not Found", { status: 404 }); return new Response("Not Found", { status: 404 });
}
} catch (error) { } catch (error) {
logger.error([`Error serving static file: ${pathname}`, error as Error]); logger.error([`Error serving static file: ${pathname}`, error as Error]);
return new Response("Internal Server Error", { status: 500 }); return new Response("Internal Server Error", { status: 500 });
@ -93,10 +96,11 @@ class ServerHandler {
} }
private async handleRequest( private async handleRequest(
request: ExtendedRequest, request: Request,
server: BunServer, server: BunServer,
): Promise<Response> { ): Promise<Response> {
request.startPerf = performance.now(); const extendedRequest: ExtendedRequest = request as ExtendedRequest;
extendedRequest.startPerf = performance.now();
const pathname: string = new URL(request.url).pathname; const pathname: string = new URL(request.url).pathname;
if (pathname.startsWith("/public") || pathname === "/favicon.ico") { if (pathname.startsWith("/public") || pathname === "/favicon.ico") {
@ -185,12 +189,12 @@ class ServerHandler {
{ status: 406 }, { status: 406 },
); );
} else { } else {
request.params = params; extendedRequest.params = params;
request.query = query; extendedRequest.query = query;
request.actualContentType = actualContentType; extendedRequest.actualContentType = actualContentType;
request.session = extendedRequest.session =
(await authByToken(request)) || (await authByToken(extendedRequest)) ||
(await sessionManager.getSession(request)); (await sessionManager.getSession(request));
response = await routeModule.handler(request, requestBody, server); response = await routeModule.handler(request, requestBody, server);
@ -242,7 +246,7 @@ class ServerHandler {
`(${response.status})`, `(${response.status})`,
[ [
request.url, request.url,
`${(performance.now() - request.startPerf).toFixed(2)}ms`, `${(performance.now() - extendedRequest.startPerf).toFixed(2)}ms`,
ip || "unknown", ip || "unknown",
], ],
"90", "90",

View file

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<%- include("../global", { styles: ["auth"], scripts: ["auth"] }) %>
</head>
<body>
<div class="auth-container">
<div class="auth-logo">
<h1><%= instance_name %></h1>
<p>Sign in to your account</p>
</div>
<div class="auth-card">
<div class="auth-header">
<h2>Welcome Back</h2>
</div>
<%- include("../partials/authForm", { pageType: "login" }) %>
</div>
</div>
</body>
</html>

View file

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<%- include("../global", { styles: ["auth"], scripts: ["auth"] }) %>
</head>
<body>
<div class="auth-container">
<div class="auth-logo">
<h1><%= instance_name %></h1>
<p>Create your account</p>
</div>
<div class="auth-card">
<div class="auth-header">
<h2>Join Us</h2>
</div>
<%- include("../partials/authForm", { pageType: "register" }) %>
</div>
</div>
</body>
</html>

View file

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<%- include("../global", { styles: ["dashboard/index"], scripts: [] }) %>
</head>
<body>
<%- include("../partials/sidebar") %>
<div class="content">
<h1>Dashboard</h1>
<p>Welcome to the dashboard!</p>
</div>
</body>
</html>

View file

@ -1,60 +0,0 @@
<div class="auth-form">
<div class="error-message" id="error-message">
<%= pageType==="register" ? "Registration failed. Please try again."
: "Invalid email or password. Please try again." %>
</div>
<form id="<%= pageType === "register" ? "register-form" : "login-form" %>" class="form">
<% if (pageType==="register") { %>
<div class="form-group">
<label for="username">Username</label>
<input type="text" name="username" id="username" required placeholder="Enter your username">
</div>
<% if (requires_invite === true) { %>
<div class="form-group">
<label for="invite">Invite Code</label>
<input type="text" name="invite" id="invite-code" required placeholder="Enter your invite code">
</div>
<% } %>
<% } %>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" id="email" required placeholder="Enter your email">
</div>
<div class="form-group password-group">
<label for="password">Password</label>
<div class="password-wrapper">
<input type="password" name="password" id="password" required placeholder="Enter your password">
<svg id="toggle-password" class="toggle-password" viewBox="0 0 24 24">
<path
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 13c-3.03 0-5.5-2.47-5.5-5.5s2.47-5.5 5.5-5.5 5.5 2.47 5.5 5.5-2.47 5.5-5.5 5.5zm0-9a3.5 3.5 0 100 7 3.5 3.5 0 000-7z" />
</svg>
</div>
</div>
<% if (pageType !=="register" ) { %>
<div class="form-footer">
<label>
<input type="checkbox" name="remember" id="remember-me">Remember me
</label>
<a href="/auth/forgot-password">Forgot password?</a>
</div>
<% } %>
<button type="submit">
<%= pageType==="register" ? "Register" : "Login" %>
</button>
</form>
<div class="auth-toggle">
<p>
<%= pageType==="register" ? "Already have an account?" : "Don't have an account?" %>
<a href="<%= pageType === 'register' ? '/auth/login' : '/auth/register' %>" class="auth-link">
<%= pageType==="register" ? "Login" : "Register" %>
</a>
</p>
</div>
</div>

View file

@ -1,12 +0,0 @@
<div class="sidebar">
<div class="actions">
<a href="/dashboard" class="action <%- active === 'dashboard' ? 'active' : '' %>">
<svg fill="" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" stroke=""><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M31.772 16.043l-15.012-15.724c-0.189-0.197-0.449-0.307-0.721-0.307s-0.533 0.111-0.722 0.307l-15.089 15.724c-0.383 0.398-0.369 1.031 0.029 1.414 0.399 0.382 1.031 0.371 1.414-0.029l1.344-1.401v14.963c0 0.552 0.448 1 1 1h6.986c0.551 0 0.998-0.445 1-0.997l0.031-9.989h7.969v9.986c0 0.552 0.448 1 1 1h6.983c0.552 0 1-0.448 1-1v-14.968l1.343 1.407c0.197 0.204 0.459 0.308 0.722 0.308 0.249 0 0.499-0.092 0.692-0.279 0.398-0.382 0.411-1.015 0.029-1.413zM26.985 14.213v15.776h-4.983v-9.986c0-0.552-0.448-1-1-1h-9.965c-0.551 0-0.998 0.445-1 0.997l-0.031 9.989h-4.989v-15.777c0-0.082-0.013-0.162-0.032-0.239l11.055-11.52 10.982 11.507c-0.021 0.081-0.036 0.165-0.036 0.252z"></path> </g></svg>
<span>Dashboard</span>
</a>
</div>
<div class="user-area">
<img src="https://cdn5.vectorstock.com/i/1000x1000/08/19/gray-photo-placeholder-icon-design-ui-vector-35850819.jpg" alt="User avatar">
<div class="username">John Doe</div>
</div>
</div>