const router = require('express').Router() const multer = require('multer') const COS = require('cos-nodejs-sdk-v5') const { ok, fail } = require('../lib/response') const { requireUser } = require('../middleware/auth') const { getPhoneNumber } = require('../lib/wechat') const config = require('../config') const userDao = require('../dao/user.dao') const deviceDao = require('../dao/device.dao') const logDao = require('../dao/log.dao') const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 2 * 1024 * 1024 }, fileFilter: (req, file, cb) => { cb(null, ALLOWED_IMAGE_TYPES.includes(file.mimetype)) } }) const wrap = fn => (req, res, next) => fn(req, res, next).catch(next) router.get('/user/profile', requireUser, wrap(async (req, res) => { const user = req.user const devices = await deviceDao.listByUser(user.user_id) res.json(ok({ user_id: String(user.user_id), nickname: user.nickname || '用户' + String(user.user_id), avatar: user.avatar || '', phone: user.phone || '', gender: user.gender || 0, bind_time: null, device_count: devices.length })) })) router.put('/user/profile', requireUser, wrap(async (req, res) => { await userDao.updateProfile(req.user.user_id, { nickname: req.body.nickname || null, avatar: req.body.avatar || req.body.avatar_url || null, gender: req.body.gender === undefined ? null : req.body.gender }) await logDao.write({ user_id: req.user.user_id, action: 'user_update', detail: '更新用户资料', ip: req.ip }) res.json(ok({ message: 'success' })) })) router.post('/user/avatar', requireUser, upload.single('file'), wrap(async (req, res) => { if (!req.file) return res.json(fail(2001, 'file required, only jpg/png/gif/webp allowed')) const ALLOWED_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'webp'] const ext = (req.file.originalname || '').split('.').pop().toLowerCase().replace(/[^a-z]/g, '') || 'jpg' if (!ALLOWED_EXTS.includes(ext)) return res.json(fail(2001, 'unsupported image format')) const key = 'avatars/' + req.user.user_id + '_' + Date.now() + '.' + ext const cos = new COS({ SecretId: config.cos.secretId, SecretKey: config.cos.secretKey }) await new Promise((resolve, reject) => { cos.putObject({ Bucket: config.cos.bucket, Region: config.cos.region, Key: key, Body: req.file.buffer, ContentType: req.file.mimetype }, (err, data) => err ? reject(err) : resolve(data)) }) const avatarUrl = 'https://' + (config.cos.cdnDomain || 'tx.vsai.net.cn') + '/' + key res.json(ok({ avatar: avatarUrl })) })) router.post('/user/phone', requireUser, wrap(async (req, res) => { const code = String(req.body.code || '').trim() if (!code) return res.json(fail(2001, 'phone code required')) const phoneInfo = await getPhoneNumber(code) if (!phoneInfo || !phoneInfo.phoneNumber) return res.json(fail(2001, 'phone authorization failed')) await userDao.updatePhone(req.user.user_id, phoneInfo.phoneNumber) await logDao.write({ user_id: req.user.user_id, action: 'user_phone_bind', detail: '授权手机号', ip: req.ip }) res.json(ok({ phone: phoneInfo.phoneNumber, pure_phone_number: phoneInfo.purePhoneNumber || '', country_code: phoneInfo.countryCode || '' })) })) module.exports = router