feat: midterm shit done

This commit is contained in:
Tony Yang
2025-04-15 03:59:33 +08:00
parent f093df29a1
commit f7ee02586b
34 changed files with 1460 additions and 197 deletions
+30
View File
@@ -0,0 +1,30 @@
import { createErrorResponse } from '../../utils';
export async function onRequestGet(context) {
try {
const { env, params } = context;
const { filename } = params;
if (!filename) {
return createErrorResponse("Filename is required", 400);
}
const key = "avatars/" + filename;
const object = await env.MY_BUCKET.get(key);
if (object === null) {
return new Response("Object Not Found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("etag", object.httpEtag);
return new Response(object.body, {
headers,
});
} catch (error) {
console.error("Avatar retrieval error:", error);
return createErrorResponse("Server Error", 500);
}
}
+50
View File
@@ -0,0 +1,50 @@
import { verifyJWT } from '../../middleware/auth';
import { createErrorResponse, createSuccessResponse } from '../../utils';
import { fileTypeFromBuffer } from 'file-type';
export async function onRequestPut(context) {
try {
const { request, env } = context;
// Verify the JWT token
const authResult = await verifyJWT(context);
if (authResult) {
return authResult; // Return the error response from the middleware
}
const formData = await request.formData();
const avatar = formData.get('avatar');
if (!avatar) {
return createErrorResponse("Missing avatar", 400);
}
if (avatar.size > 2 * 1024 * 1024) {
return createErrorResponse("Avatar must be less than 2MB", 400);
}
const buffer = await avatar.arrayBuffer();
const fileTypeResult = await fileTypeFromBuffer(buffer);
if (!fileTypeResult) {
return createErrorResponse("Unsupported file type", 400);
}
if (fileTypeResult.mime !== 'image/jpeg' && fileTypeResult.mime !== 'image/png') {
return createErrorResponse("Avatar must be a JPG or PNG image", 400);
}
// Upload the avatar to R2
const fileExtension = fileTypeResult.ext;
const objectName = `avatars/${context.user.userId}.${fileExtension}`;
await env.MY_BUCKET.put(objectName, buffer);
// Store the filename in D1
await env.DB.prepare("UPDATE users SET avatar = ? WHERE id = ?").bind(objectName, context.user.userId).run();
return createSuccessResponse({ message: "Avatar uploaded successfully" });
} catch (error) {
console.error("Avatar upload error:", error);
return createErrorResponse("Avatar upload failed", 500);
}
}
+3
View File
@@ -0,0 +1,3 @@
export function onRequest(context) {
return new Response("Hello, world!")
}
+58
View File
@@ -0,0 +1,58 @@
import { SignJWT } from 'jose';
import { createSuccessResponse, createErrorResponse } from "../utils";
export async function onRequestPost(context) {
try {
const { request, env } = context;
const { username, password } = await request.json();
if (!username || !password) {
return createErrorResponse("Missing username or password", 400);
}
if (username.length < 3) {
return createErrorResponse("Username must be at least 3 characters", 400);
}
if (password.length < 8) {
return createErrorResponse("Password must be at least 8 characters", 400);
}
if (!/^[a-zA-Z0-9]+$/.test(username)) {
return createErrorResponse("Username must be alphanumeric", 400);
}
// Get the stored password from D1
const { results } = await env.DB.prepare("SELECT password FROM users WHERE username = ?").bind(username).all();
if (!results || results.length === 0) {
return new Response(JSON.stringify({"error": "Invalid username or password"}), { status: 403, headers: { 'Content-Type': 'application/json' } });
}
const storedPassword = results[0].password;
// Compare the password to the stored password
if (password !== storedPassword) {
return new Response(JSON.stringify({"error": "Invalid username or password"}), { status: 403, headers: { 'Content-Type': 'application/json' } });
}
// Get the user ID
const { results: userResults } = await env.DB.prepare("SELECT * FROM users WHERE username = ?").bind(username).all();
const jwtPayload = (({ id, username }) => ({ id, username }))(userResults[0]);
// Generate a JWT token
const jwt = await new SignJWT(jwtPayload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setIssuer('urn:example:issuer')
.setAudience('urn:example:audience')
.setExpirationTime('2h')
.sign(new TextEncoder().encode(env.JWT_SECRET));
return createSuccessResponse({ jwt });
} catch (error) {
console.error("Login error:", error);
return createErrorResponse("Login failed", 500);
}
}
+30
View File
@@ -0,0 +1,30 @@
import { verifyJWT } from "../middleware/auth";
import { createErrorResponse, createSuccessResponse } from "../utils";
export async function onRequest(context) {
try {
// Verify JWT token
const authResult = await verifyJWT(context);
if (authResult) {
return authResult; // Return error response if authentication fails
}
// Get user information from context
const { user } = context;
// Fetch user profile from D1 database
const { results } = await context.env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(user.userId).all();
if (!results || results.length === 0) {
// use 401 instead to redirect to login page
return createErrorResponse("User not found", 401);
}
const userProfile = results[0];
const { password, ...profile } = userProfile; // Exclude password from the profile
// Return the profile as a JSON response
return createSuccessResponse(profile, 200);
} catch (error) {
console.error("Error:", error);
return createErrorResponse("Internal Server Error", 500);
}
}
+81
View File
@@ -0,0 +1,81 @@
import { verifyJWT } from '../middleware/auth';
import { createErrorResponse, createSuccessResponse } from '../utils';
export async function onRequestGet(context) {
try {
const { env } = context;
// Get the messages from D1
const { results } = await env.DB.prepare("SELECT messages.id, userId, username, message, timestamp, users.avatar FROM messages LEFT JOIN users ON users.id = messages.userId").all();
return createSuccessResponse({ messages: results });
} catch (error) {
console.error("Get messages error:", error);
return createErrorResponse("Get messages failed", 500);
}
}
export async function onRequestPost(context) {
try {
const { request, env } = context;
// Verify the JWT token
const authResult = await verifyJWT(context);
if (authResult) {
return authResult; // Return the error response from the middleware
}
const { message } = await request.json();
if (!message) {
return new Response(JSON.stringify({"error": "Missing message"}), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
// Generate a unique ID for the message
const messageId = crypto.randomUUID();
// Store the message in D1
await env.DB.prepare("INSERT INTO messages (id, userId, message) VALUES (?, ?, ?)")
.bind(messageId, context.user.userId, message)
.run();
return new Response(JSON.stringify({ id: messageId, username: context.user.username, message }), {
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
console.error("Message posting error:", error);
return createErrorResponse("Message posting failed", 500);
}
}
export async function onRequestDelete(context) {
try {
const { request, env } = context;
// Verify the JWT token
const authResult = await verifyJWT(context);
if (authResult) {
return authResult; // Return the error response from the middleware
}
const { messageId } = await request.json();
if (!messageId) {
return new Response(JSON.stringify({"error": "Missing messageId"}), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
// Delete the message from D1
await env.DB.prepare("DELETE FROM messages WHERE id = ?").bind(messageId).run();
return createSuccessResponse({ message: "Message deleted successfully" });
} catch (error) {
console.error("Message deletion error:", error);
return createErrorResponse("Message deletion failed", 500);
}
}
+52
View File
@@ -0,0 +1,52 @@
import { createErrorResponse } from '../utils';
export async function onRequestPost(context) {
try {
const { request, env } = context;
const { username, password } = await request.json();
if (!username || !password) {
return createErrorResponse("Missing username or password", 400);
}
if (username.length < 3) {
return createErrorResponse("Username must be at least 3 characters", 400);
}
if (password.length < 8) {
return createErrorResponse("Password must be at least 8 characters", 400);
}
if (!/^[a-zA-Z0-9]+$/.test(username)) {
return createErrorResponse("Username must be alphanumeric", 400);
}
// Check if the username already exists
const { results: existingUsers } = await env.DB.prepare("SELECT id FROM users WHERE username = ?").bind(username).all();
if (existingUsers.length > 0) {
return createErrorResponse("Username already exists", 400);
}
// Store the username and password in D1
await env.DB.prepare("INSERT INTO users (username, password, avatar) VALUES (?, ?, ?)").bind(username, password, "avatars/default.png").run();
// Get the user ID
const { results } = await env.DB.prepare("SELECT id FROM users WHERE username = ?").bind(username).all();
const userId = results[0].id;
// Registration successful, return success response
return new Response(
JSON.stringify({
success: true,
message: "Registration successful. Please login.",
}),
{
headers: { "Content-Type": "application/json" },
}
);
} catch (error) {
console.error("Registration error:", error);
return createErrorResponse("Server Error", 500);
}
}
-47
View File
@@ -1,47 +0,0 @@
/**
* @typedef {import('@cloudflare/workers-types').D1Database} D1Database
*/
export async function onRequestGet(context) {
const DB = context.env.DB;
// return all users
const stmt = await DB.prepare("SELECT * FROM users");
const users = (await stmt.run()).results;
return new Response(JSON.stringify(users), {
headers: { "Content-Type": "application/json" },
});
}
export async function onRequestPost(context) {
/**
* @type {D1Database}
*/
const DB = context.env.DB;
const { name } = await context.request.json();
// create a new user
const stmt = DB.prepare("INSERT INTO users (name) VALUES (?)").bind(name);
try {
const result = await stmt.run();
if (!result.success) {
throw new Error("Failed to create user");
}
const userId = result.meta.last_row_id;
const stmt2 = await DB.prepare("SELECT * FROM users WHERE id = ?").bind(userId);
const userResult = await stmt2.run();
const user = userResult.results[0];
return new Response(JSON.stringify(user), {
headers: { "Content-Type": "application/json" },
status: 201,
});
} catch (error) {
console.error("Error creating user:", error);
return new Response("Error creating user", { status: 500 });
}
}
+26
View File
@@ -0,0 +1,26 @@
import * as jose from 'jose';
import { createErrorResponse } from "../utils";
export async function verifyJWT(context) {
const { request, env } = context;
// Check for a valid JWT token
const authHeader = request.headers.get("Authorization");
if (!authHeader) {
return createErrorResponse("Missing Authorization header", 401);
}
const token = authHeader.split(" ")[1];
try {
// Verify the token
const { payload, protectedHeader } = await jose.jwtVerify(token, new TextEncoder().encode(env.JWT_SECRET), {
issuer: 'urn:example:issuer',
audience: 'urn:example:audience',
});
context.user = { userId: payload.id, username: payload.username };
return; // Continue to the next middleware or function
} catch (error) {
return createErrorResponse("Invalid or expired token", 401);
}
}
+13
View File
@@ -0,0 +1,13 @@
export function createErrorResponse(message, status) {
return new Response(JSON.stringify({"error": message}), {
status: status,
headers: { 'Content-Type': 'application/json' },
});
}
export function createSuccessResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status: status,
headers: { 'Content-Type': 'application/json' },
});
}