Files
zhuomi-hui/server/lib/db.js
T
Michelle ZanandCursor cd05113755 Add 卓米会 mini program and admin server.
Initial commit of the WeChat mini program, Express backend, and admin console.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-29 19:01:36 +08:00

110 lines
2.8 KiB
JavaScript

const fs = require('fs')
const path = require('path')
const passwords = require('./passwords')
const file = process.env.DB_FILE
? path.resolve(process.env.DB_FILE)
: path.join(__dirname, '../data/db.json')
const SEED = {
users: [],
admins: [{ username: 'admin', password: '123456', token: '' }],
adminAllowlist: [],
activities: [
{
id: '93',
title: '2026年卓米第93期卓米会',
brandName: '卓米会 ZHUOMI',
brandDesc: '专注于品牌增长与企业服务的共创平台,连接资源、方法与同行者。',
cover: '',
video: '',
startAt: '2026-06-18 14:00',
address: '上海市浦东新区世纪大道 100 号 卓米中心 3F',
latitude: 31.235929,
longitude: 121.506321,
status: 'current',
isCurrent: true,
pinned: false,
pinnedAt: '',
theme: 0,
createdAt: '2026-06-01T00:00:00.000Z',
updatedAt: '2026-06-01T00:00:00.000Z'
}
],
posters: [],
signups: [],
checkinCodes: []
}
const COLLECTIONS = ['users', 'admins', 'adminAllowlist', 'activities', 'posters', 'signups', 'checkinCodes']
let cache = null
// Returns true when the stored shape changed and must be persisted.
function normalize(data) {
let dirty = false
COLLECTIONS.forEach((key) => {
if (!Array.isArray(data[key])) {
data[key] = []
dirty = true
}
})
// The allowlist is the only source of truth for mini-program admins.
data.users.forEach((user) => {
// Client temp paths (http://tmp/..., wxfile://...) were saved by older builds and never resolve.
if (user.avatarUrl && !user.avatarUrl.startsWith('/static/uploads/')) {
user.avatarUrl = ''
dirty = true
}
if (!('role' in user)) return
if (user.role === 'admin' && !data.adminAllowlist.includes(user.openid)) {
data.adminAllowlist.push(user.openid)
}
delete user.role
dirty = true
})
data.admins.forEach((admin) => {
if (!passwords.isHashed(admin.password)) {
admin.password = passwords.hash(admin.password || '')
dirty = true
}
})
return dirty
}
function persist(data) {
fs.mkdirSync(path.dirname(file), { recursive: true })
const tmp = `${file}.${process.pid}.tmp`
fs.writeFileSync(tmp, JSON.stringify(data, null, 2))
fs.renameSync(tmp, file)
}
function load() {
if (cache) return cache
const data = fs.existsSync(file)
? JSON.parse(fs.readFileSync(file, 'utf8'))
: JSON.parse(JSON.stringify(SEED))
if (normalize(data) || !fs.existsSync(file)) persist(data)
cache = data
return cache
}
function read() {
return load()
}
function update(mutator) {
const data = load()
const result = mutator(data)
persist(data)
return result
}
function reset() {
cache = null
}
module.exports = { read, update, reset, file, SEED }