Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cb4d56e3d | |||
| caf6ae9416 |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
+1
-1
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
+13
-11
@@ -29,19 +29,19 @@ if (!fs.existsSync(path.join(process.cwd(), ".env"))) {
|
|||||||
const secret = PasswordGen(24)
|
const secret = PasswordGen(24)
|
||||||
|
|
||||||
const rString = `
|
const rString = `
|
||||||
port=80
|
port=80
|
||||||
altregion=us # the displayed region in each page's header
|
altregion=us # the displayed region in each page's header
|
||||||
|
|
||||||
servicename=File Server # the displayed service name in each page's header
|
servicename=File Server # the displayed service name in each page's header
|
||||||
description=A simple file server built with Express. # the displayed description in the home page
|
description=A simple file server built with Express. # the displayed description in the home page
|
||||||
|
|
||||||
bootmessage=Welcome!
|
bootmessage=Welcome!
|
||||||
|
|
||||||
session_secret=${secret} # the session secret used for logins. you can generate a new secret using the idGen command.
|
session_secret=${secret} # the session secret used for logins. you can generate a new secret using the idGen command.
|
||||||
enable_register=false # it's advised to keep this off unless you're hosting a public instance
|
enable_register=false # it's advised to keep this off unless you're hosting a public instance
|
||||||
|
|
||||||
postwrite_notice=Hi! # the notice on top of the post form
|
postwrite_notice=Hi! # the notice on top of the post form
|
||||||
postwrite_html=false # if enabled, users can use html in posts. this is unsafe! you can set it to true if you trust your users.
|
postwrite_html=false # if enabled, users can use html in posts. this is unsafe! you can set it to true if you trust your users.
|
||||||
`
|
`
|
||||||
|
|
||||||
fs.writeFileSync(path.join(process.cwd(), ".env"), rString);
|
fs.writeFileSync(path.join(process.cwd(), ".env"), rString);
|
||||||
@@ -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' });
|
||||||
|
|
||||||
@@ -88,15 +88,17 @@ try {
|
|||||||
filename: (req, file, cb) =>
|
filename: (req, file, cb) =>
|
||||||
cb(null, `${Date.now()}_${file.originalname}`)
|
cb(null, `${Date.now()}_${file.originalname}`)
|
||||||
})
|
})
|
||||||
upload = multer({storage})
|
upload = multer({ storage })
|
||||||
|
|
||||||
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
@@ -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
@@ -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());
|
||||||
|
|||||||
+8
-82
@@ -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)
|
|
||||||
c.log(`User registered: ${username}`);
|
|
||||||
|
|
||||||
if (isnameerror)
|
req.session.user = {
|
||||||
res.send(register_string(`The username ${username} is already taken!`))
|
id: this.lastID,
|
||||||
else
|
username: username
|
||||||
res.send(`
|
}
|
||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<body>
|
c.log(`User registered: ${username}`);
|
||||||
<style>
|
res.redirect(req.query?.next?.toString() ?? "/");
|
||||||
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
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user