今天是第十八天我們可以寫一個k8s 考古題資料庫管理系統,以下是我的程式碼
questions
表:儲存考古題的題目內容、類型、難度、答案等。categories
表:分類不同的考題主題。users
表:儲存使用者資訊(例如老師、學生)。CREATE TABLE questions (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
difficulty VARCHAR(50),
answer TEXT,
category_id INT,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
password VARCHAR(255),
role ENUM('teacher', 'student')
);
const express = require('express');
const mysql = require('mysql');
const app = express();
app.use(express.json());
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'k8s_exam_db'
});
db.connect(err => {
if (err) throw err;
console.log('MySQL Connected...');
});
// 查詢所有考古題
app.get('/questions', (req, res) => {
let sql = 'SELECT * FROM questions';
db.query(sql, (err, results) => {
if (err) throw err;
res.json(results);
});
});
// 新增考古題
app.post('/questions', (req, res) => {
const { title, description, difficulty, answer, category_id } = req.body;
let sql = 'INSERT INTO questions (title, description, difficulty, answer, category_id) VALUES (?, ?, ?, ?, ?)';
db.query(sql, [title, description, difficulty, answer, category_id], (err, result) => {
if (err) throw err;
res.json({ message: 'Question added', id: result.insertId });
});
});
// 刪除考古題
app.delete('/questions/:id', (req, res) => {
let sql = 'DELETE FROM questions WHERE id = ?';
db.query(sql, [req.params.id], (err, result) => {
if (err) throw err;
res.json({ message: 'Question deleted' });
});
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
import React, { useState, useEffect } from 'react';
function App() {
const [questions, setQuestions] = useState([]);
useEffect(() => {
fetch('http://localhost:3000/questions')
.then(res => res.json())
.then(data => setQuestions(data));
}, []);
return (
<div>
<h1>Kubernetes 考古題資料庫</h1>
<ul>
{questions.map(question => (
<li key={question.id}>
{question.title} - {question.difficulty}
</li>
))}
</ul>
</div>
);
}
export default App;
dockerfile
# 使用 Node.js 作為基礎映像
FROM node:14
# 設定工作目錄
WORKDIR /app
# 複製 package.json 和安裝依賴
COPY package*.json ./
RUN npm install
# 複製應用程式檔案
COPY . .
# 暴露應用程式運行的埠
EXPOSE 3000
# 啟動應用程式
CMD ["npm", "start"]
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-exam-deployment
spec:
replicas: 3
selector:
matchLabels:
app: k8s-exam
template:
metadata:
labels:
app: k8s-exam
spec:
containers:
- name: k8s-exam
image: your-docker-image
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: k8s-exam-service
spec:
selector:
app: k8s-exam
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
kubectl
部署:kubectl apply -f deployment.yaml
為了保護系統內的資料,使用者需要先登入,並根據其角色(教師或學生)獲得不同的操作權限。
JWT(JSON Web Token)驗證:
使用 JWT 來實現無狀態的身份驗證,讓後端可以驗證 API 請求的合法性。
後端 API(基於 JWT 的登入與註冊):
jsonwebtoken
套件來管理 JWT token。role
) 判斷不同操作權限。const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
// 註冊使用者
app.post('/register', async (req, res) => {
const { username, password, role } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
let sql = 'INSERT INTO users (username, password, role) VALUES (?, ?, ?)';
db.query(sql, [username, hashedPassword, role], (err, result) => {
if (err) throw err;
res.json({ message: 'User registered' });
});
});
// 登入使用者並生成 JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username], async (err, result) => {
if (err) throw err;
if (result.length === 0) return res.status(400).json({ message: 'User not found' });
const user = result[0];
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) return res.status(400).json({ message: 'Invalid credentials' });
// 生成 JWT
const token = jwt.sign({ id: user.id, role: user.role }, 'secretkey', { expiresIn: '1h' });
res.json({ token });
});
});
// JWT 驗證中間件
const authenticateJWT = (req, res, next) => {
const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
if (token == null) return res.sendStatus(401);
jwt.verify(token, 'secretkey', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
// 只有教師可以添加考古題
app.post('/questions', authenticateJWT, (req, res) => {
if (req.user.role !== 'teacher') return res.sendStatus(403);
const { title, description, difficulty, answer, category_id } = req.body;
let sql = 'INSERT INTO questions (title, description, difficulty, answer, category_id) VALUES (?, ?, ?, ?, ?)';
db.query(sql, [title, description, difficulty, answer, category_id], (err, result) => {
if (err) throw err;
res.json({ message: 'Question added', id: result.insertId });
});
});
增加篩選條件來讓使用者可以根據不同標準檢索考古題,如分類、難度、關鍵字等。
app.get('/questions', (req, res) => {
const { category_id, difficulty, keyword } = req.query;
let sql = 'SELECT * FROM questions WHERE 1=1';
if (category_id) sql += ` AND category_id = ${db.escape(category_id)}`;
if (difficulty) sql += ` AND difficulty = ${db.escape(difficulty)}`;
if (keyword) sql += ` AND title LIKE ${db.escape('%' + keyword + '%')}`;
db.query(sql, (err, results) => {
if (err) throw err;
res.json(results);
});
});
function App() {
const [questions, setQuestions] = useState([]);
const [category, setCategory] = useState('');
const [difficulty, setDifficulty] = useState('');
const [keyword, setKeyword] = useState('');
useEffect(() => {
const query = new URLSearchParams({ category, difficulty, keyword }).toString();
fetch(`http://localhost:3000/questions?${query}`)
.then(res => res.json())
.then(data => setQuestions(data));
}, [category, difficulty, keyword]);
return (
<div>
<h1>Kubernetes 考古題資料庫</h1>
{/* 篩選選項 */}
<label>Category:</label>
<input value={category} onChange={(e) => setCategory(e.target.value)} />
<label>Difficulty:</label>
<input value={difficulty} onChange={(e) => setDifficulty(e.target.value)} />
<label>Keyword:</label>
<input value={keyword} onChange={(e) => setKeyword(e.target.value)} />
<ul>
{questions.map(question => (
<li key={question.id}>
{question.title} - {question.difficulty}
</li>
))}
</ul>
</div>
);
}
為學生增加測驗功能,系統從資料庫中隨機抽取一定數量的題目,並提供批改測驗的功能。
app.get('/quiz', authenticateJWT, (req, res) => {
let sql = 'SELECT * FROM questions ORDER BY RAND() LIMIT 10';
db.query(sql, (err, results) => {
if (err) throw err;
res.json(results);
});
});
// 提交測驗並批改
app.post('/quiz/submit', authenticateJWT, (req, res) => {
const { answers } = req.body;
let correctAnswers = 0;
answers.forEach(answer => {
let sql = 'SELECT * FROM questions WHERE id = ? AND answer = ?';
db.query(sql, [answer.question_id, answer.answer], (err, result) => {
if (result.length > 0) correctAnswers++;
});
});
res.json({ score: correctAnswers });
});
在系統中記錄每位學生的測驗結果,方便教師查閱。
CREATE TABLE quiz_records (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
score INT,
date DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
app.post('/quiz/submit', authenticateJWT, (req, res) => {
const { answers } = req.body;
let correctAnswers = 0;
answers.forEach(answer => {
let sql = 'SELECT * FROM questions WHERE id = ? AND answer = ?';
db.query(sql, [answer.question_id, answer.answer], (err, result) => {
if (result.length > 0) correctAnswers++;
});
});
let sql = 'INSERT INTO quiz_records (user_id, score) VALUES (?, ?)';
db.query(sql, [req.user.id, correctAnswers], (err, result) => {
if (err) throw err;
res.json({ score: correctAnswers, message: 'Quiz result saved' });
});
});
為了確保資料庫的持久化,我們可以在 Kubernetes 集群中使用 Persistent Volume (PV) 和 Persistent Volume Claim (PVC) 來管理資料庫儲存。
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/data
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
使用 CI/CD 工具(如 Jenkins、GitLab CI、GitHub Actions)來自動
化部署流程,每次程式碼變更都自動部署最新版本至 Kubernetes 集群。
後端是用 Node.js 和 Express 框架來構建的,並使用了 MySQL 作為資料庫。這裡的後端 API 負責處理使用者的註冊、登入、考古題的管理、測驗以及身份驗證。
const express = require('express');
const app = express();
const mysql = require('mysql');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
express
:一個用於建立伺服器的 Node.js 框架。mysql
:用來連接和操作 MySQL 資料庫。jsonwebtoken
:用來生成和驗證 JWT(JSON Web Token),用於身份驗證。bcrypt
:用來加密使用者的密碼,以確保安全。app.post('/register', async (req, res) => {
const { username, password, role } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
let sql = 'INSERT INTO users (username, password, role) VALUES (?, ?, ?)';
db.query(sql, [username, hashedPassword, role], (err, result) => {
if (err) throw err;
res.json({ message: 'User registered' });
});
});
username
、password
和 role
(例如學生或教師)。req.body
中取得使用者的註冊資料。bcrypt.hash
來加密使用者密碼,保護密碼不以明文儲存。users
表中。app.post('/login', (req, res) => {
const { username, password } = req.body;
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username], async (err, result) => {
if (err) throw err;
if (result.length === 0) return res.status(400).json({ message: 'User not found' });
const user = result[0];
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) return res.status(400).json({ message: 'Invalid credentials' });
const token = jwt.sign({ id: user.id, role: user.role }, 'secretkey', { expiresIn: '1h' });
res.json({ token });
});
});
req.body
中取得使用者的登入資料。users
表,檢查該使用者是否存在。bcrypt.compare
比對密碼是否正確。如果正確,生成一個 JWT token,token 會包含使用者的 ID 和角色,並設置過期時間為 1 小時。const authenticateJWT = (req, res, next) => {
const token = req.headers.authorization && req.headers.authorization.split(' ')[1];
if (token == null) return res.sendStatus(401);
jwt.verify(token, 'secretkey', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
Authorization
標頭,確保其中包含合法的 JWT token。Authorization
標頭中提取 JWT token。jwt.verify
驗證 token 的合法性。如果驗證失敗,回應 403 Forbidden
錯誤;如果成功,將解碼的使用者資料附加到 req.user
,然後繼續執行下一個中間件。app.post('/questions', authenticateJWT, (req, res) => {
if (req.user.role !== 'teacher') return res.sendStatus(403);
const { title, description, difficulty, answer, category_id } = req.body;
let sql = 'INSERT INTO questions (title, description, difficulty, answer, category_id) VALUES (?, ?, ?, ?, ?)';
db.query(sql, [title, description, difficulty, answer, category_id], (err, result) => {
if (err) throw err;
res.json({ message: 'Question added', id: result.insertId });
});
});
authenticateJWT
中間件來驗證請求是否包含有效的 JWT token。req.user.role
是否為 teacher
,只有教師才可以新增題目。req.body
中提取題目的資訊,並將其插入資料庫中的 questions
表。app.get('/quiz', authenticateJWT, (req, res) => {
let sql = 'SELECT * FROM questions ORDER BY RAND() LIMIT 10';
db.query(sql, (err, results) => {
if (err) throw err;
res.json(results);
});
});
authenticateJWT
中間件來驗證請求。questions
表中隨機選取 10 道題目,並返回給前端。這個前端應用是用 React.js 來構建的,使用者可以在這裡搜尋考古題、進行測驗等。
function App() {
const [questions, setQuestions] = useState([]);
const [category, setCategory] = useState('');
const [difficulty, setDifficulty] = useState('');
const [keyword, setKeyword] = useState('');
useEffect(() => {
const query = new URLSearchParams({ category, difficulty, keyword }).toString();
fetch(`http://localhost:3000/questions?${query}`)
.then(res => res.json())
.then(data => setQuestions(data));
}, [category, difficulty, keyword]);
useState
) 和效果鉤子 (useEffect
) 來根據使用者選擇的篩選條件查詢考古題。useState
設置 questions
、category
、difficulty
和 keyword
來存放使用者輸入的篩選條件和查詢結果。useEffect
在每當篩選條件變化時觸發,會構建 API 查詢字串並向後端發送請求,將返回的考古題資料儲存到 questions
。 return (
<div>
<h1>Kubernetes 考古題資料庫</h1>
{/* 篩選選項 */}
<label>Category:</label>
<input value={category} onChange={(e) => setCategory(e.target.value)} />
<label>Difficulty:</label>
<input value={difficulty} onChange={(e) => setDifficulty(e.target.value)} />
<label>Keyword:</label>
<input value={keyword} onChange={(e) => setKeyword(e.target.value)} />
<ul>
{questions.map(question => (
<li key={question.id}>
{question.title} - {question.difficulty}
</li>
))}
</ul>
</div>
);
}
搜尋考古題的 UI,允許使用者依照分類、難度和關鍵字來進行篩選,並且在頁面上顯示搜尋結果。
category
)會被更新。questions.map()
來動態生成每個考古題的列表項目,顯示題目名稱和難度。這些程式碼示範了如何構建一個後端 API 和前端 React 應用,實現 Kubernetes 考古題資料庫管理系統。每個功能都設計為簡單明瞭,易於擴展和調整。