Compare commits

..

2 Commits

Author SHA1 Message Date
rrryfoo 6cb4d56e3d Avoid repeated appearances of parse_date_string to reduce the amount of data downloaded 2026-03-10 01:59:47 +08:00
Maki caf6ae9416 Add session maxAge 2026-01-11 15:40:32 +08:00
10 changed files with 128 additions and 105 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "express-jtw-update-server", "name": "express-storage-site",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
+14
View File
@@ -0,0 +1,14 @@
import dotenv from 'dotenv'
dotenv.config()
const header_string = (page_title: string | boolean, notitle = false, add: string = null) => {
return `
<head>
${notitle ? "" : `<title>${process.env.servicename || "File Server"}${page_title ? ` | ${page_title}` : ""}</title>`}
${/*<link rel="icon" type="image/x-icon" href="/favicon.ico">*/""}
${add ? add : ""}
</head>
`
}
export default header_string;
+3 -1
View File
@@ -74,7 +74,7 @@ export var server: Server;
import moment from 'moment-timezone' import moment from 'moment-timezone'
import { Server } from "http"; import { Server } from "http";
import session from "express-session"; import session, { MemoryStore, Store } from "express-session";
var accessLogStream = fs.createWriteStream('./pvfiles/interactions.log', { flags: 'a' }); var accessLogStream = fs.createWriteStream('./pvfiles/interactions.log', { flags: 'a' });
@@ -93,10 +93,12 @@ try {
app.use( app.use(
session({ session({
name: "session", name: "session",
store: new MemoryStore(),
secret: process.env.session_secret, secret: process.env.session_secret,
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
cookie: { cookie: {
maxAge: Date.now() + (30 * 24 * 3600 * 1000),
httpOnly: true, httpOnly: true,
sameSite: "lax", sameSite: "lax",
secure: dev secure: dev
+1 -1
View File
@@ -91,7 +91,7 @@ export async function Main(app: Express) {
username: user.username username: user.username
}; };
res.redirect("/"); res.redirect(req.query?.next?.toString() ?? "/");
} }
); );
}); });
+13 -4
View File
@@ -162,13 +162,22 @@ async function posts_string(user?: UserSession): Promise<string> {
out += ` out += `
<div class="postlist" onClick="location.href='/posts/${p.hash_id}'"> <div class="postlist" onClick="location.href='/posts/${p.hash_id}'">
<a href="/posts/${p.hash_id}">${title}</a> ${p.visibility == "public" ? "" : `(${p.visibility})`} <br> <a href="/posts/${p.hash_id}">${title}</a> ${p.visibility == "public" ? "" : `(${p.visibility})`} <br>
<small>by ${await getUsernameById(p.user_id)} (<span id="time_span${times}"></span><script>${parse_date_string}; document.getElementById("time_span${times}").innerHTML = parseDate(new Date(toIso("${p.created_at.replace(" ", "T") + ".000Z"}")));</script>)</small><br><br> <small>by ${await getUsernameById(p.user_id)} (<span class="time" data-date="${p.created_at}" id="time_span${times}"></span>)</small><br><br>
${content} ${content}
</div> </div>
` `
}; };
resolve(out || "No posts yet."); resolve(out + `
<script>
document.addEventsListener("DOMContentLoaded", () => {
document.querySelectorAll(".time").forEach(span => {
const raw = span.dataset.date.replace(" ", "T") + ".000Z";
span.textContent = parseDate(new Date(toIso(raw)), false, true);
})
})
</script>
` || "No posts yet.");
}); });
}); });
} }
@@ -185,7 +194,7 @@ export async function Main(app: Express) {
<form action="/posts/new" method="GET"> <form action="/posts/new" method="GET">
<button class="tonly">New Post</button> <button class="tonly">New Post</button>
</form> </form>
` : `<p><a href="/login">Log in</a> to create posts.</p>`} ` : `<p><a href="/login?next=/posts/new">Log in</a> to create posts.</p>`}
<hr> <hr>
<br> <br>
<div class="postlist_r"> <div class="postlist_r">
@@ -202,7 +211,7 @@ export async function Main(app: Express) {
<p>You must be logged in to create a post.</p> <p>You must be logged in to create a post.</p>
</div> </div>
`, ` `, `
<a href="/login">Log in</a> | <a href="/register">Register</a><br><br> <a href="/login?next=/posts/new">Log in</a> | <a href="/register?next=/posts/new">Register</a><br><br>
`)) `))
else { else {
return res.send(newpost_string()); return res.send(newpost_string());
+9 -83
View File
@@ -78,94 +78,20 @@ export async function Main(app: Express) {
db.run( db.run(
`INSERT INTO users (username, password_hash) VALUES (?, ?)`, `INSERT INTO users (username, password_hash) VALUES (?, ?)`,
[username, passwordHash], [username, passwordHash],
(err: any) => { function (err: any) {
var isnameerror = false;
if (err) { if (err) {
if (err.message.includes("UNIQUE")) { if (err.message.includes("UNIQUE")) {
isnameerror = true; return res.send(register_string(`The username ${username} is already taken!`))
} }
} }
if (!isnameerror)
req.session.user = {
id: this.lastID,
username: username
}
c.log(`User registered: ${username}`); c.log(`User registered: ${username}`);
res.redirect(req.query?.next?.toString() ?? "/");
if (isnameerror)
res.send(register_string(`The username ${username} is already taken!`))
else
res.send(`
<!DOCTYPE html>
<body>
<style>
h1 {
font-size: 50
}
body {
margin-right: 10%;
margin-left: 10%;
margin-bottom: 10%;
text-align: left;
color: black;
font-family: Verdana, sans-serif;
}
a:link {
color: #3c72a3
}
a:visited {
color: #2a567d
}
@media screen and (prefers-color-scheme: dark) {
body {
background-color: #383737;
color: #b0acac;
}
}
@media screen and (prefers-color-scheme: light) {
body {
background-color: #e3e3e3;
color: black;
}
}
.description {
font-size: 32px
}
.details {
font-size: 20px
}
.extra {
font-size: 24px
}
</style>
<h1>${process.env.servicename || "File Server"}</h1>
<div class="details">
<p>Server region: ${'(alternate) ' + process.env.altregion}</p>
</div><hr><br>
<div class="description">
${process.env.description || "A simple file server built with Express."}
</div>
<div class="extra">
Account created successfully!<br>
${req.session.user ? `
<p>Logged in as ${req.session.user.username}<br>
<a href=/api/logout>log out</a></p>
` : `
<a href=/login>Log in</a> | <a href=/register>Register</a><br>
`}
<br><br>
Pages<br>
) <a href=/staticfiles>/staticfiles</a><br>
) <a href="/">Home</a>
</div>
</body>
`);
} }
); );
} catch (err) { } catch (err) {
+13 -6
View File
@@ -72,11 +72,7 @@ async function files_string(user: UserSession): Promise<string> {
out += ` out += `
<form method="POST" action="/delete" style="display:inline;"> <form method="POST" action="/delete" style="display:inline;">
${filect}) <a href="/uploads/${f.stored_name}" target="_blank">${f.original_name}</a> - uploaded on ${filect}) <a href="/uploads/${f.stored_name}" target="_blank">${f.original_name}</a> - uploaded on
<span id="time_span${times}"></span> <span class=".time" id="time_span${times}" data-date="${f.upload_date}></span>
<script>
${parse_date_string}
document.getElementById("time_span${times}").innerHTML = parseDate(new Date(toIso("${f.upload_date.replace(" ", "T") + ".000Z"}")), false, true);
</script>
<input type="hidden" name="fileId" value="${f.id}"> <input type="hidden" name="fileId" value="${f.id}">
| <button class="tonly" onclick="return confirm('Delete ${f.original_name}?')" type="submit">Delete</button> | <button class="tonly" onclick="return confirm('Delete ${f.original_name}?')" type="submit">Delete</button>
</form><br> </form><br>
@@ -84,6 +80,17 @@ async function files_string(user: UserSession): Promise<string> {
filect++; filect++;
}); });
out += `
<script>
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".time").forEach(span => {
const raw = span.dataset.date.replace(" ", "T") + ".000Z";
span.textContent = parseDate(new Date(toIso(raw)), false, true);
});
});
</script>
`
resolve(out); resolve(out);
} }
); );
@@ -145,7 +152,7 @@ export async function Main(app: Express) {
app.get("/upload", async (req: Request, res: Response) => { app.get("/upload", async (req: Request, res: Response) => {
if (!req.session.user) if (!req.session.user)
return res.status(401).send(upload_string("", "You must be <a href=/login>logged in</a> to use this feature.")) return res.status(401).send(upload_string("", "You must be <a href=/login?next=/upload>logged in</a> to use this feature."))
else { else {
const user = req.session.user; const user = req.session.user;
return res.send(await upload_string_home(null, null, user)) return res.send(await upload_string_home(null, null, user))
+1
View File
@@ -31,6 +31,7 @@ function getUploadByFilename(filename: string): Promise<Upload> {
}) })
} catch (err) { } catch (err) {
// if the cb fails somehow // if the cb fails somehow
// its not actually somehow it IS possible, like when a user is deleted from the users table or when a file is manually placed in the /uploads directory
return null return null
} }
} }
+64
View File
@@ -0,0 +1,64 @@
const p_404 = {
page: "The file you were looking for does not exist on the server."
}
const p_login = {
title: "Log in",
login: "Log in",
missing_credentials: "Missing username or password!",
wrong_credentials: "Invalid username or password."
}
const p_posts = {
title: "Posts",
by_user: "by",
new: {
post_title: "Post title:",
post_content: "Post content:",
new_post: "New Post",
visibility: "Visibility:",
public: "Public",
unlisted: "Unlisted",
private: "Private",
font: "Font:",
login: "You must be logged in to create a post.",
content_placeholder: "...",
post: "Post"
},
not_found: "Post not found.",
no_visible: "No posts yet.",
login: `<a href="/login?next=/posts/new">Log in</a> to create posts.`,
delete_post: "Delete Post"
}
const p_register = {
title: "Registration",
register: "Register",
name_taken: "The username $(var) is already taken!",
register_disabled: "$(var) does not support account registration."
}
const p_root = {
existing_session: "Logged in as $(var).",
logout_confirm: "Are you sure you want to log out of $(var)?"
}
const loc = {
username: "Username",
password: "Password",
default_title: "File Server",
default_description: "A simple file server built with Express.",
unknown_user: "Unknown",
log_in: "Login",
log_out: "Log out",
register: "Register",
back: "Back",
p_404, p_login, p_posts, p_register, p_root
}
export default loc