iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
佛心分享-SideProject30

最近的工程師真沒用系列 第 18

第十八天-註冊登入登出後端實作

  • 分享至 

  • xImage
  •  

好,首先我們預期會讓很多使用者登入後上傳資料,所以我們需要搞個登入登出實作,預計會用 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 都不需要可以直接使用,超讚

今天的情緒抒發:
突然發現是不是有點寫不完阿


上一篇
第十七天-本地 DB 程式讀取
下一篇
第十九天-註冊登入登出前後端串接
系列文
最近的工程師真沒用19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言