好,首先我們預期會讓很多使用者登入後上傳資料,所以我們需要搞個登入登出實作,預計會用 session 來弄
首先今天程式碼量比較多
以下路徑
src/routes/api/login/+server.ts
import type { RequestHandler } from './$types';
import { hashPassword } from '$lib/server/auth';
import type { ClientUsers } from '$lib/server/schema/ClientUsers';
export const POST: RequestHandler = async ({ request, cookies, platform }) => {
const { username, password } = await request.json();
const stmt = await platform.env.DB.prepare('SELECT * FROM client_users WHERE username = ?');
const { results } = await stmt.bind(username).run();
console.log(results);
if (!Array.isArray(results) || results.length === 0) {
return new Response(JSON.stringify({ message: 'User not found' }), { status: 401 });
}
const user = results[0] as ClientUsers;
const passwordHash = await hashPassword(password);
if (passwordHash !== user.password) {
return new Response(JSON.stringify({ error: 'Invalid' }), { status: 401 });
}
const sessionId = crypto.randomUUID();
cookies.set('session_id', sessionId, {
httpOnly: true,
path: '/',
secure: true,
sameSite: 'lax',
maxAge: 60 * 30
});
const insert_stmt = await platform.env.DB.prepare(
'INSERT INTO client_users_session(sid, user_id,expires_at) VALUES (?, ?,DATETIME("now", "+30 minutes"))'
);
await insert_stmt.bind(sessionId, user.id).run();
return new Response(JSON.stringify({ message: 'Login success' }));
};
src/routes/api/register/+server.ts
import type { RequestHandler } from './$types';
import { hashPassword } from '$lib/server/auth';
export const POST: RequestHandler = async ({ request, platform }) => {
const { username, password } = await request.json();
if (!username || !password) {
return new Response(JSON.stringify({ message: 'Username and password required' }), {
status: 400
});
}
// 檢查是否已存在
const query_stmt = await platform.env.DB.prepare('SELECT * FROM client_users WHERE username = ?');
const { results } = await query_stmt.bind(username).run();
if (Array.isArray(results) && results.length > 0) {
return new Response(JSON.stringify({ message: 'Username already exists' }), { status: 409 });
}
// hash 密碼
const passwordHash = await hashPassword(password);
// 寫入資料庫
const insert_stmt = await platform.env.DB.prepare(
'INSERT INTO client_users(username, password) VALUES (?, ?)'
);
await insert_stmt.bind(username, passwordHash).run();
return new Response(JSON.stringify({ message: 'Register success' }), { status: 201 });
};
src/routes/api/logout/+server.ts
import type { RequestHandler } from './$types';
export const DELETE: RequestHandler = async ({ cookies, platform }) => {
// 讀出 session_id
const sessionId = cookies.get('session_id');
if (sessionId) {
const insert_stmt = await platform.env.DB.prepare(
'DELETE FROM client_users_session where sid =?'
);
await insert_stmt.bind(sessionId).run();
}
// 清除 cookie
cookies.set('session_id', '', {
path: '/',
expires: new Date(0), // 立即過期
httpOnly: true,
secure: true,
sameSite: 'lax'
});
return new Response(JSON.stringify({ success: true }));
};
我講幾個雷點
1、lib 裡面的資料夾如果要給 server 使用,要先開個 server 資料夾,不能直接 call,他會前後不分
2、本來查到 node 可以用 bcrypt 或 crypto ,想說 crypto 原生內建,直接用好了,結果爆炸,因為 wokers 要這樣用,要特別設定,秉持著能原生就原生的精神,我們採用 Web Crypto API,連 import 都不需要可以直接使用,超讚
今天的情緒抒發:
突然發現是不是有點寫不完阿