fix: audit fixes for registration flow
- loadProfile() now returns Promise (was missing return) - Avatar upload via COS instead of storing WeChat temp path - Add POST /user/avatar endpoint with multer + COS SDK - Incomplete registration detection: redirect to register if phone is empty
这个提交包含在:
+2
-1
@@ -26,9 +26,10 @@ App({
|
|||||||
|
|
||||||
loadProfile: function () {
|
loadProfile: function () {
|
||||||
var self = this
|
var self = this
|
||||||
http.get('/api/v1/user/profile').then(function (profile) {
|
return http.get('/api/v1/user/profile').then(function (profile) {
|
||||||
self.globalData.userInfo = profile
|
self.globalData.userInfo = profile
|
||||||
self.globalData.userId = String(profile.user_id)
|
self.globalData.userId = String(profile.user_id)
|
||||||
|
return profile
|
||||||
}).catch(function () {
|
}).catch(function () {
|
||||||
wx.removeStorageSync('token')
|
wx.removeStorageSync('token')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Page({
|
|||||||
|
|
||||||
app.doLogin().then(function (data) {
|
app.doLogin().then(function (data) {
|
||||||
self.setData({ loading: false })
|
self.setData({ loading: false })
|
||||||
if (data && data.is_new_user) {
|
if (data && (data.is_new_user || !data.user_info || !data.user_info.phone)) {
|
||||||
wx.redirectTo({ url: '/pages/register/register' })
|
wx.redirectTo({ url: '/pages/register/register' })
|
||||||
} else {
|
} else {
|
||||||
wx.switchTab({ url: '/pages/index/index' })
|
wx.switchTab({ url: '/pages/index/index' })
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var app = getApp()
|
var app = getApp()
|
||||||
var http = require('../../utils/request')
|
var http = require('../../utils/request')
|
||||||
|
var config = require('../../config/env')
|
||||||
|
|
||||||
var DEFAULT_AVATAR = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI9t2NHfLvvMiaiaCJQn3KP2P0vAHp1QBP0piavhGfhKCARlGd6z1EbR58ibJXKCp5WVlNnojwA/640'
|
var DEFAULT_AVATAR = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI9t2NHfLvvMiaiaCJQn3KP2P0vAHp1QBP0piavhGfhKCARlGd6z1EbR58ibJXKCp5WVlNnojwA/640'
|
||||||
|
|
||||||
@@ -65,6 +66,31 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
uploadAvatar: function (tempPath) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var token = wx.getStorageSync('token')
|
||||||
|
wx.uploadFile({
|
||||||
|
url: config.API_BASE + '/api/v1/user/avatar',
|
||||||
|
filePath: tempPath,
|
||||||
|
name: 'file',
|
||||||
|
header: { Authorization: 'Bearer ' + token },
|
||||||
|
success: function (res) {
|
||||||
|
try {
|
||||||
|
var data = JSON.parse(res.data)
|
||||||
|
if (data.code === 0 && data.data && data.data.avatar) {
|
||||||
|
resolve(data.data.avatar)
|
||||||
|
} else {
|
||||||
|
reject(new Error(data.message || '上传失败'))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
reject(new Error('上传失败'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: function () { reject(new Error('上传失败')) }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
onSubmit: function () {
|
onSubmit: function () {
|
||||||
var self = this
|
var self = this
|
||||||
var nickname = (self.data.nickname || '').trim()
|
var nickname = (self.data.nickname || '').trim()
|
||||||
@@ -82,9 +108,15 @@ Page({
|
|||||||
|
|
||||||
self.setData({ loading: true })
|
self.setData({ loading: true })
|
||||||
|
|
||||||
http.put('/api/v1/user/profile', {
|
var avatarPromise = (avatarUrl && avatarUrl !== DEFAULT_AVATAR)
|
||||||
nickname: nickname,
|
? self.uploadAvatar(avatarUrl)
|
||||||
avatar: avatarUrl !== DEFAULT_AVATAR ? avatarUrl : ''
|
: Promise.resolve('')
|
||||||
|
|
||||||
|
avatarPromise.then(function (permanentUrl) {
|
||||||
|
return http.put('/api/v1/user/profile', {
|
||||||
|
nickname: nickname,
|
||||||
|
avatar: permanentUrl || ''
|
||||||
|
})
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
return app.loadProfile()
|
return app.loadProfile()
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
|||||||
+123
@@ -14,6 +14,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-rate-limit": "^8.5.0",
|
"express-rate-limit": "^8.5.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^2.1.1",
|
||||||
"mysql2": "^3.11.3"
|
"mysql2": "^3.11.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -98,6 +99,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/asn1": {
|
"node_modules/asn1": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz",
|
||||||
@@ -215,6 +222,23 @@
|
|||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"streamsearch": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -271,6 +295,21 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/conf": {
|
"node_modules/conf": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/conf/-/conf-9.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/conf/-/conf-9.0.2.tgz",
|
||||||
@@ -1288,6 +1327,47 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/multer": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/multer/-/multer-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^1.6.0",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"type-is": "^1.6.18"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mysql2": {
|
"node_modules/mysql2": {
|
||||||
"version": "3.22.3",
|
"version": "3.22.3",
|
||||||
"resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.22.3.tgz",
|
"resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.22.3.tgz",
|
||||||
@@ -1546,6 +1626,20 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/request": {
|
"node_modules/request": {
|
||||||
"version": "2.88.2",
|
"version": "2.88.2",
|
||||||
"resolved": "https://registry.npmmirror.com/request/-/request-2.88.2.tgz",
|
"resolved": "https://registry.npmmirror.com/request/-/request-2.88.2.tgz",
|
||||||
@@ -1838,6 +1932,23 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strnum": {
|
"node_modules/strnum": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/strnum/-/strnum-1.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/strnum/-/strnum-1.1.2.tgz",
|
||||||
@@ -1929,6 +2040,12 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.19.2",
|
"version": "7.19.2",
|
||||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.19.2.tgz",
|
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.19.2.tgz",
|
||||||
@@ -1954,6 +2071,12 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.0",
|
||||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
|
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-rate-limit": "^8.5.0",
|
"express-rate-limit": "^8.5.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^2.1.1",
|
||||||
"mysql2": "^3.11.3"
|
"mysql2": "^3.11.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
const router = require('express').Router()
|
const router = require('express').Router()
|
||||||
|
const multer = require('multer')
|
||||||
|
const COS = require('cos-nodejs-sdk-v5')
|
||||||
const { ok, fail } = require('../lib/response')
|
const { ok, fail } = require('../lib/response')
|
||||||
const { requireUser } = require('../middleware/auth')
|
const { requireUser } = require('../middleware/auth')
|
||||||
const { getPhoneNumber } = require('../lib/wechat')
|
const { getPhoneNumber } = require('../lib/wechat')
|
||||||
|
const config = require('../config')
|
||||||
const userDao = require('../dao/user.dao')
|
const userDao = require('../dao/user.dao')
|
||||||
const deviceDao = require('../dao/device.dao')
|
const deviceDao = require('../dao/device.dao')
|
||||||
const logDao = require('../dao/log.dao')
|
const logDao = require('../dao/log.dao')
|
||||||
|
|
||||||
|
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 2 * 1024 * 1024 } })
|
||||||
const wrap = fn => (req, res, next) => fn(req, res, next).catch(next)
|
const wrap = fn => (req, res, next) => fn(req, res, next).catch(next)
|
||||||
|
|
||||||
router.get('/user/profile', requireUser, wrap(async (req, res) => {
|
router.get('/user/profile', requireUser, wrap(async (req, res) => {
|
||||||
@@ -32,6 +36,25 @@ router.put('/user/profile', requireUser, wrap(async (req, res) => {
|
|||||||
res.json(ok({ message: 'success' }))
|
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'))
|
||||||
|
const ext = (req.file.originalname || '').split('.').pop() || 'jpg'
|
||||||
|
const key = 'avatars/' + req.user.user_id + '_' + Date.now() + '.' + ext
|
||||||
|
const cos = new COS({ SecretId: config.cos.secretId, SecretKey: config.cos.secretKey })
|
||||||
|
const result = 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.bucket + '.cos.' + config.cos.region + '.myqcloud.com/' + key
|
||||||
|
await userDao.updateProfile(req.user.user_id, { nickname: null, avatar: avatarUrl, gender: null })
|
||||||
|
res.json(ok({ avatar: avatarUrl }))
|
||||||
|
}))
|
||||||
|
|
||||||
router.post('/user/phone', requireUser, wrap(async (req, res) => {
|
router.post('/user/phone', requireUser, wrap(async (req, res) => {
|
||||||
const code = String(req.body.code || '').trim()
|
const code = String(req.body.code || '').trim()
|
||||||
if (!code) return res.json(fail(2001, 'phone code required'))
|
if (!code) return res.json(fail(2001, 'phone code required'))
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户