refactor: restructure entire project for human maintainability

Server:
- Add Express framework, replace custom router/request parser
- Create DAO layer (12 files) centralizing all 73 SQL queries
- Rewrite 7 route files as thin Express controllers calling DAOs
- Add SCF-to-Express adapter (lib/serverless.js)
- Add auth middleware (middleware/auth.js)
- Remove dead code from lib/auth.js

Admin console:
- Extract DataTable component (table + pagination)
- Extract ConfirmModal component (modal + form styles)
- Create listMixin for paginated list pages
- Move form styles to common.css for slot compatibility
- Refactor device + subscription pages as examples

Miniprogram:
- Split 734-line BLE monolith into 4 focused modules
  (protocol, connection, commands, barrel index)
- Create API module (utils/api.js) with named functions
- Create page utilities (utils/page.js)
- Refactor index + profile pages to use API module
这个提交包含在:
Guoguo
2026-04-29 05:58:20 -07:00
父节点 9f0e629c82
当前提交 bb4b80f867
修改 46 个文件,包含 4306 行新增2020 行删除
+1 -26
查看文件
@@ -2,7 +2,6 @@ const crypto = require('crypto')
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const config = require('../config')
const { one } = require('./db')
function hashPasswordLegacy(password, salt) {
return crypto.createHash('sha256').update(String(password) + ':' + salt).digest('hex')
@@ -34,28 +33,4 @@ function readBearer(headers) {
return match ? match[1] : ''
}
async function requireUser(ctx) {
const token = readBearer(ctx.headers)
if (!token) return null
try {
const payload = jwt.verify(token, config.jwt.secret)
if (payload.type !== 'user') return null
return await one('SELECT * FROM users WHERE user_id = :user_id AND status = 1', { user_id: payload.user_id })
} catch (err) {
return null
}
}
async function requireAdmin(ctx) {
const token = readBearer(ctx.headers)
if (!token) return null
try {
const payload = jwt.verify(token, config.jwt.adminSecret)
if (payload.type !== 'admin') return null
return await one('SELECT * FROM admin_accounts WHERE admin_id = :admin_id AND status = 1', { admin_id: payload.admin_id })
} catch (err) {
return null
}
}
module.exports = { hashPassword, hashPasswordLegacy, verifyPassword, randomHex, signUser, signAdmin, readBearer, requireUser, requireAdmin }
module.exports = { hashPassword, hashPasswordLegacy, verifyPassword, randomHex, signUser, signAdmin, readBearer }
+2 -16
查看文件
@@ -1,16 +1,2 @@
const { query } = require('./db')
async function writeLog(options) {
await query(
'INSERT INTO operation_logs (user_id, admin_id, action, detail, ip) VALUES (:user_id, :admin_id, :action, :detail, :ip)',
{
user_id: options.user_id || null,
admin_id: options.admin_id || null,
action: options.action,
detail: options.detail || '',
ip: options.ip || ''
}
)
}
module.exports = { writeLog }
const logDao = require('../dao/log.dao')
module.exports = { writeLog: logDao.write }
-45
查看文件
@@ -1,45 +0,0 @@
function normalizeHeaders(headers) {
const result = {}
Object.keys(headers || {}).forEach(key => {
result[key] = headers[key]
result[key.toLowerCase()] = headers[key]
})
return result
}
function parseBody(event) {
if (!event.body) return {}
if (typeof event.body === 'object') return event.body
const raw = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('utf8') : event.body
if (!raw) return {}
try { return JSON.parse(raw) } catch (err) { return {} }
}
function parseQuery(event) {
if (event.queryStringParameters) return event.queryStringParameters || {}
if (event.query) return event.query || {}
return {}
}
function getPath(event) {
return event.path || event.Path || event.requestContext && event.requestContext.path || '/'
}
function getMethod(event) {
return String(event.httpMethod || event.method || event.requestContext && event.requestContext.httpMethod || 'GET').toUpperCase()
}
function createContext(event) {
return {
event,
method: getMethod(event),
path: getPath(event),
headers: normalizeHeaders(event.headers),
query: parseQuery(event),
body: parseBody(event),
params: {},
ip: event.requestContext && event.requestContext.sourceIp || (event.headers && (event.headers['x-forwarded-for'] || event.headers['X-Forwarded-For'] || '').split(',')[0].trim()) || ''
}
}
module.exports = { createContext }
+1 -15
查看文件
@@ -6,18 +6,4 @@ function fail(code, message, data) {
return { code, message, data: data || {} }
}
function http(statusCode, body, headers) {
return {
isBase64Encoded: false,
statusCode,
headers: Object.assign({
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Device-Id, X-App-Version, X-Platform',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS'
}, headers || {}),
body: JSON.stringify(body)
}
}
module.exports = { ok, fail, http }
module.exports = { ok, fail }
-33
查看文件
@@ -1,33 +0,0 @@
class Router {
constructor() {
this.routes = []
}
add(method, pattern, handler) {
const keys = []
const regex = new RegExp('^' + pattern.replace(/\/:(\w+)/g, function (_, key) {
keys.push(key)
return '/([^/]+)'
}) + '$')
this.routes.push({ method, regex, keys, handler })
}
get(pattern, handler) { this.add('GET', pattern, handler) }
post(pattern, handler) { this.add('POST', pattern, handler) }
put(pattern, handler) { this.add('PUT', pattern, handler) }
delete(pattern, handler) { this.add('DELETE', pattern, handler) }
match(method, path) {
for (const route of this.routes) {
if (route.method !== method) continue
const match = path.match(route.regex)
if (!match) continue
const params = {}
route.keys.forEach((key, index) => { params[key] = decodeURIComponent(match[index + 1]) })
return { handler: route.handler, params }
}
return null
}
}
module.exports = Router
+63
查看文件
@@ -0,0 +1,63 @@
const http = require('http')
module.exports = function serverless(app) {
return async function handler(event) {
const method = String(event.httpMethod || event.method || 'GET').toUpperCase()
const path = event.path || '/'
const headers = event.headers || {}
const qs = event.queryStringParameters || {}
const qsStr = Object.keys(qs).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(qs[k])).join('&')
const url = path + (qsStr ? '?' + qsStr : '')
let rawBody = event.body || ''
if (event.isBase64Encoded && rawBody) rawBody = Buffer.from(rawBody, 'base64').toString('utf8')
return new Promise((resolve) => {
const req = new http.IncomingMessage()
req.method = method
req.url = url
req.headers = {}
Object.keys(headers).forEach(k => { req.headers[k.toLowerCase()] = headers[k] })
if (event.requestContext && event.requestContext.sourceIp) {
req.headers['x-forwarded-for'] = req.headers['x-forwarded-for'] || event.requestContext.sourceIp
}
const res = new http.ServerResponse(req)
let body = ''
const resHeaders = {}
res.writeHead = function (statusCode, reasonOrHeaders, maybeHeaders) {
res.statusCode = statusCode
const h = maybeHeaders || (typeof reasonOrHeaders === 'object' ? reasonOrHeaders : {})
Object.assign(resHeaders, h)
}
const originalSetHeader = res.setHeader.bind(res)
res.setHeader = function (name, value) {
resHeaders[name.toLowerCase()] = value
originalSetHeader(name, value)
}
res.end = function (chunk) {
if (chunk) body += chunk
resolve({
isBase64Encoded: false,
statusCode: res.statusCode || 200,
headers: Object.assign({
'content-type': 'application/json; charset=utf-8'
}, resHeaders),
body
})
}
res.write = function (chunk) { body += chunk }
if (rawBody) {
req.push(rawBody)
}
req.push(null)
app(req, res)
})
}
}