feat(backend): Updates for account pages
This commit is contained in:
parent
4744759d0b
commit
5ee9491485
5 changed files with 118 additions and 19 deletions
|
@ -117,6 +117,18 @@ UsersController.prototype.profile = async (req, res, tools) => {
|
|||
return User.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all user data
|
||||
*
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UsersController.prototype.allData = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
await User.allData(req)
|
||||
|
||||
return User.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether a submitted username is available
|
||||
*
|
||||
|
|
|
@ -9,15 +9,20 @@ import { decorateModel } from '../utils/model-decorator.mjs'
|
|||
* @param {tools} object - A set of tools loaded in src/index.js
|
||||
* @returns {ApikeyModel} object - The ApikeyModel
|
||||
*/
|
||||
export function ApikeyModel(tools) {
|
||||
export function ApikeyModel(tools, models) {
|
||||
/*
|
||||
* See utils/model-decorator.mjs for details
|
||||
*/
|
||||
return decorateModel(this, tools, {
|
||||
name: 'apikey',
|
||||
encryptedFields: ['name'],
|
||||
models: ['user'],
|
||||
})
|
||||
return decorateModel(
|
||||
this,
|
||||
tools,
|
||||
{
|
||||
name: 'apikey',
|
||||
encryptedFields: ['name'],
|
||||
models: ['user'],
|
||||
},
|
||||
models
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -167,12 +172,18 @@ ApikeyModel.prototype.userApikeys = async function (uid) {
|
|||
/*
|
||||
* Keys are an array, remove sercrets with map() and decrypt prior to returning
|
||||
*/
|
||||
return keys.map((key) => {
|
||||
delete key.secret
|
||||
key.name = this.decrypt(key.name)
|
||||
return keys.map((key) => this.asKeyData(key))
|
||||
}
|
||||
|
||||
return key
|
||||
})
|
||||
/*
|
||||
* Takes non-instatiated key data and prepares it so it can be returned
|
||||
*/
|
||||
ApikeyModel.prototype.asKeyData = async function (key) {
|
||||
delete key.secret
|
||||
delete key.aud
|
||||
key.name = this.decrypt(key.name)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -51,6 +51,43 @@ UserModel.prototype.profile = async function ({ params }) {
|
|||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a user from the database based on the where clause you pass it
|
||||
* In addition prepares it for returning all account data
|
||||
* This is guarded so it enforces access control and validates input
|
||||
* This is an anonymous route returning limited info (profile data)
|
||||
*
|
||||
* @param {params} object - The request (URL) parameters
|
||||
* @returns {UserModel} object - The UserModel
|
||||
*/
|
||||
UserModel.prototype.allData = async function ({ params }) {
|
||||
/*
|
||||
* Is id set?
|
||||
*/
|
||||
if (typeof params.id === 'undefined') return this.setResponse(403, 'idMissing')
|
||||
|
||||
/*
|
||||
* Try to find the record in the database
|
||||
* Note that find checks lusername, ehash, and id but we
|
||||
* pass it in the username value as that's what the login
|
||||
* rout does
|
||||
*/
|
||||
await this.read(
|
||||
{ id: Number(params.id) },
|
||||
{ apikeys: true, bookmarks: true, patterns: true, sets: true }
|
||||
)
|
||||
|
||||
/*
|
||||
* If it does not exist, return 404
|
||||
*/
|
||||
if (!this.exists) return this.setResponse(404)
|
||||
|
||||
return this.setResponse200({
|
||||
result: 'success',
|
||||
data: this.asData(),
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a user from the database based on the where clause you pass it
|
||||
* In addition prepares it for returning the account data
|
||||
|
@ -857,15 +894,21 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
|||
/*
|
||||
* Image (img)
|
||||
*/
|
||||
if (typeof body.img === 'string')
|
||||
data.img = await replaceImage({
|
||||
if (typeof body.img === 'string') {
|
||||
const imgData = {
|
||||
id: `user-${this.record.ihash}`,
|
||||
metadata: {
|
||||
user: user.uid,
|
||||
ihash: this.record.ihash,
|
||||
},
|
||||
b64: body.img,
|
||||
})
|
||||
}
|
||||
/*
|
||||
* Allow both a base64 encoded binary image or an URL
|
||||
*/
|
||||
if (body.img.slice(0, 4) === 'http') imgData.url = body.img
|
||||
else imgData.b64 = body.img
|
||||
data.img = await replaceImage(imgData)
|
||||
}
|
||||
|
||||
/*
|
||||
* Now update the database record
|
||||
|
@ -1161,6 +1204,7 @@ UserModel.prototype.asProfile = function () {
|
|||
id: this.record.id,
|
||||
bio: this.clear.bio,
|
||||
img: this.clear.img,
|
||||
ihash: this.record.ihash,
|
||||
patron: this.record.patron,
|
||||
role: this.record.role,
|
||||
username: this.record.username,
|
||||
|
@ -1185,7 +1229,7 @@ UserModel.prototype.asAccount = function () {
|
|||
createdAt: this.record.createdAt,
|
||||
email: this.clear.email,
|
||||
data: this.clear.data,
|
||||
ihash: this.ihash,
|
||||
ihash: this.record.ihash,
|
||||
img: this.clear.img,
|
||||
imperial: this.record.imperial,
|
||||
initial: this.clear.initial,
|
||||
|
@ -1208,6 +1252,31 @@ UserModel.prototype.asAccount = function () {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all user data (that is not included in the account data)
|
||||
*
|
||||
* @return {account} object - The account data as a plain object
|
||||
*/
|
||||
UserModel.prototype.asData = function () {
|
||||
/*
|
||||
* Nothing to do here but construct the object to return
|
||||
*/
|
||||
return {
|
||||
apikeys: this.record.apikeys
|
||||
? this.record.apikeys.map((key) => {
|
||||
delete key.secret
|
||||
delete key.aud
|
||||
key.name = this.decrypt(key.name)
|
||||
|
||||
return key
|
||||
})
|
||||
: [],
|
||||
bookmarks: this.record.bookmarks || [],
|
||||
patterns: this.record.patterns || [],
|
||||
sets: this.record.sets || [],
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of records as search results
|
||||
* Typically used by admin search
|
||||
|
|
|
@ -55,6 +55,14 @@ export function usersRoutes(tools) {
|
|||
Users.isUsernameAvailable(req, res, tools)
|
||||
)
|
||||
|
||||
// Load full user data
|
||||
app.get('/users/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Users.allData(req, res, tools)
|
||||
)
|
||||
app.get('/users/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Users.allData(req, res, tools)
|
||||
)
|
||||
|
||||
// Load a user profile
|
||||
app.get('/users/:id', (req, res) => Users.profile(req, res, tools))
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ export async function replaceImage(props, isTest = false) {
|
|||
const form = getFormData(props)
|
||||
// Ignore errors on delete, probably means the image does not exist
|
||||
try {
|
||||
await axios.delete(`${config.api}/${props.id}`)
|
||||
await axios.delete(`${config.api}/${props.id}`, { headers })
|
||||
} catch (err) {
|
||||
// It's fine
|
||||
log.info(`Could not delete image ${props.id}`)
|
||||
|
@ -58,10 +58,9 @@ export async function replaceImage(props, isTest = false) {
|
|||
result = await axios.post(config.api, form, { headers })
|
||||
} catch (err) {
|
||||
console.log('Failed to replace image on cloudflare', err)
|
||||
console.log(err.response.data)
|
||||
}
|
||||
|
||||
return result.data?.result?.id ? result.data.result.id : false
|
||||
return result?.data?.result?.id ? result.data.result.id : false
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue