iT邦幫忙

0

跟著AI一起:從零打造一個互動式網站 Day27

  • 分享至 

  • xImage
  •  

目標:完成互動式個人網站資訊架構(IA)、導覽設計、版型草圖、資料模型;規劃留言板 / 註冊登入 Demo 的後端 API。

成品清單

  • 網站資訊架構(頁面/區塊):首頁、關於我、作品集、文章/日記、聯絡我(含留言板)
  • 低保真 Wireframe(可用紙筆或 Figma)
  • 路由規劃://about/works/posts/contact
  • 元件清單:NavbarHeroWorkCardFooterContactFormToast
  • 色彩字體:Google Fonts + 2~3 色系(主色/輔色/灰階)

後端 API 規劃(Express)

資料模型

  • User: { id, username, passwordHash, createdAt }
  • Comment: { id, name, message, createdAt }

路由表

  • POST /api/auth/register → 註冊(Demo:不啟用 Email 驗證)
  • POST /api/auth/login → 登入(回傳 JWT 或 demo token)
  • GET /api/comments → 取得留言列表
  • POST /api/comments → 新增留言

需求細節

  • CORS 開啟(允許前端網域)
  • Rate limit(簡易)
  • 表單欄位驗證:最小長度、空值檢查

檔案結構

my-portfolio/
├─ client/            # 前端 (Vite/React 或原生 HTML+CSS+JS)
└─ server/            # 後端 (Express)
   ├─ db/             # SQLite / JSON 資料檔
   ├─ .env            # PORT, JWT_SECRET
   ├─ package.json
   └─ index.js

Day 27 延伸補充:加一個後端 API(留言板 / 註冊登入 Demo)

下面提供最小可行後端程式碼(可選 JSON 檔案或 SQLite):

方案 A:JSON 檔案(零安裝,快速上手)

server/index.js(精簡版)

import express from 'express';
import cors from 'cors';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';

const app = express();
app.use(cors());
app.use(express.json());

const dataPath = path.resolve('server/db');
const usersFile = path.join(dataPath, 'users.json');
const commentsFile = path.join(dataPath, 'comments.json');
if (!fs.existsSync(dataPath)) fs.mkdirSync(dataPath, { recursive: true });
if (!fs.existsSync(usersFile)) fs.writeFileSync(usersFile, '[]');
if (!fs.existsSync(commentsFile)) fs.writeFileSync(commentsFile, '[]');

const read = f => JSON.parse(fs.readFileSync(f, 'utf-8'));
const write = (f, data) => fs.writeFileSync(f, JSON.stringify(data, null, 2));

const hash = pw => crypto.createHash('sha256').update(pw).digest('hex');

app.post('/api/auth/register', (req, res) => {
  const { username, password } = req.body;
  if (!username || !password) return res.status(400).json({ error: '缺少欄位' });
  const users = read(usersFile);
  if (users.find(u => u.username === username)) return res.status(409).json({ error: '帳號已存在' });
  const user = { id: crypto.randomUUID(), username, passwordHash: hash(password), createdAt: Date.now() };
  users.push(user); write(usersFile, users);
  res.json({ ok: true });
});

app.post('/api/auth/login', (req, res) => {
  const { username, password } = req.body;
  const users = read(usersFile);
  const ok = users.find(u => u.username === username && u.passwordHash === hash(password));
  if (!ok) return res.status(401).json({ error: '帳密錯誤' });
  // Demo token(僅示範)
  res.json({ token: 'demo-' + crypto.randomBytes(6).toString('hex') });
});

app.get('/api/comments', (req, res) => {
  res.json(read(commentsFile));
});

app.post('/api/comments', (req, res) => {
  const { name, message } = req.body;
  if (!name || !message) return res.status(400).json({ error: '缺少欄位' });
  const list = read(commentsFile);
  const c = { id: crypto.randomUUID(), name, message, createdAt: Date.now() };
  list.push(c); write(commentsFile, list);
  res.json(c);
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => console.log('API on http://localhost:' + PORT));

啟動

cd server
npm init -y
npm i express cors
node index.js

方案 B:SQLite(單檔資料庫,易部署)

  • 套件:better-sqlite3
  • 表:users(id, username, passwordHash, createdAt)comments(id, name, message, createdAt)
  • 優點:查詢/篩選/排序更方便;部署雲端時穩定度較佳。

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言