714 lines
19 KiB
JavaScript
714 lines
19 KiB
JavaScript
import { useMemo } from 'react'
|
|
import { urls } from '@freesewing/config'
|
|
import { RestClient } from '@freesewing/react/lib/RestClient'
|
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
|
|
/*
|
|
* Get backend URL from config
|
|
*/
|
|
const { backend } = urls
|
|
|
|
/**
|
|
* The useBackend hook
|
|
*
|
|
* This hook provides access to the FreeSewing backend
|
|
*/
|
|
export function useBackend() {
|
|
/*
|
|
* Load the token via the useAccount hook
|
|
*/
|
|
const { token } = useAccount()
|
|
|
|
/*
|
|
* Memoize this call for efficiency
|
|
*/
|
|
const backend = useMemo(() => new Backend(token), [token])
|
|
|
|
return backend
|
|
}
|
|
|
|
/**
|
|
* This helper function creates the authentication headers
|
|
*
|
|
* @param {string} token - The JSON Web Token to authenticate to the backend
|
|
* @return {object} headers - An object holding headers for the REST API call
|
|
*/
|
|
function authenticationHeaders(token) {
|
|
return token ? { Authorization: 'Bearer ' + token } : {}
|
|
}
|
|
|
|
/**
|
|
* This creates a backend instance and stores the authentication data
|
|
*
|
|
* @param {string} token - The JWT token to use for authentication to the backend
|
|
*/
|
|
function Backend(token) {
|
|
this.token = token
|
|
this.headers = authenticationHeaders(token)
|
|
this.restClient = new RestClient(backend, this.headers)
|
|
this.delete = this.restClient.delete
|
|
this.get = this.restClient.get
|
|
this.patch = this.restClient.patch
|
|
this.put = this.restClient.put
|
|
this.post = this.restClient.post
|
|
}
|
|
|
|
/**
|
|
* Admin: Search user
|
|
*
|
|
* @param {string} q - The search query
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.adminSearchUsers = async function (q) {
|
|
return await this.post('/admin/search/users/jwt', { q })
|
|
}
|
|
|
|
/*
|
|
* Admin: Load user
|
|
*
|
|
* @param {number} id - The user ID to load
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.adminLoadUser = async function (id) {
|
|
return await this.get(`/admin/user/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Admin: Update user
|
|
*
|
|
* @param {object} data
|
|
* @param {number} data.id - The user ID to update
|
|
* @param {object} data.data - The data for the API request
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.adminUpdateUser = async function ({ id, data }) {
|
|
return await this.patch(`/admin/user/${id}/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Admin: Impersonate user
|
|
*
|
|
* @param {number} id - The user ID to impersonate
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.adminImpersonateUser = async function (id) {
|
|
return await this.get(`/admin/impersonate/${id}/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Load newsletter subscribers (admin method)
|
|
*/
|
|
Backend.prototype.adminLoadSubscribers = async function () {
|
|
return await this.get(`/admin/subscribers/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Verify an admin account while impersonating another user
|
|
*/
|
|
Backend.prototype.adminPing = async function (token) {
|
|
console.log('admin ping called', token)
|
|
return await this.get(`/whoami/jwt`, { Authorization: `Bearer: ${token}` })
|
|
}
|
|
/*
|
|
* Create a curated set from a suggested measurements set
|
|
*/
|
|
Backend.prototype.acceptCset = async function (id) {
|
|
return await this.post(`/curated-sets/from/${id}/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Confirm MFA
|
|
*
|
|
* @param {object} data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.confirmMfa = async function (data) {
|
|
return await this.post(`/account/mfa/jwt`, { ...data, mfa: true })
|
|
}
|
|
|
|
/*
|
|
* Confirm a signup
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.id - The confirmation ID
|
|
* @param {string} data.consent - The consent data
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.confirmSignup = async function ({ id, consent }) {
|
|
return await this.post(`/confirm/signup/${id}`, { consent })
|
|
}
|
|
|
|
/**
|
|
* Create API key
|
|
*
|
|
* @param {object} data - The data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createApikey = async function (data) {
|
|
return await this.post(`/apikeys/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Create bookmark
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createBookmark = async function (data) {
|
|
return await this.post(`/bookmarks/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Generate a social media image
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createSocialImage = async function (data) {
|
|
return await this.post('/img', data)
|
|
}
|
|
|
|
/**
|
|
* Create GitHub issue
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createIssue = async function (data) {
|
|
return await this.post(`/issues`, data)
|
|
}
|
|
|
|
/**
|
|
* Create pattern
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createPattern = async function (data) {
|
|
return await this.post(`/patterns/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Create a pull request for a showcase or blog post
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createPostPr = async function (type, data) {
|
|
return await this.post(`/flows/pr/${type}/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Create measurements set
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.createSet = async function (data) {
|
|
return await this.post(`/sets/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Disable MFA for the current user
|
|
*
|
|
* @param {object} data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.disableMfa = async function (data) {
|
|
return await this.post(`/account/mfa/jwt`, { ...data, mfa: false })
|
|
}
|
|
|
|
/**
|
|
* Enable MFA for the current user
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.enableMfa = async function () {
|
|
return await this.post(`/account/mfa/jwt`, { mfa: true })
|
|
}
|
|
|
|
/**
|
|
* Export account data for the current user
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.exportAccount = async function () {
|
|
return await this.get(`/account/export/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get API key
|
|
*
|
|
* @param {string} id - The API Key ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getApikey = async function (id) {
|
|
return await this.get(`/apikeys/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get API keys for the current user
|
|
*/
|
|
Backend.prototype.getApikeys = async function () {
|
|
return await this.get(`/apikeys/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get bookmark
|
|
*
|
|
* @param {string} id - The bookmark ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getBookmark = async function (id) {
|
|
return await this.get(`/bookmarks/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get bookmarks (for the current user)
|
|
*/
|
|
Backend.prototype.getBookmarks = async function () {
|
|
return await this.get(`/bookmarks/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get data for a confirmation
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.id - The confirmation ID
|
|
* @param {string} data.check - The confirmation check value
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getConfirmation = async function ({ id, check }) {
|
|
return await this.get(`/confirmations/${id}/${check}`)
|
|
}
|
|
|
|
/**
|
|
* Get curated measurements set
|
|
*
|
|
* @param {number} id - The curated measurements set ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getCuratedSet = async function (id) {
|
|
return await this.get(`/curated-sets/${id}`)
|
|
}
|
|
|
|
/**
|
|
* Get curated measurements sets
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getCuratedSets = async function () {
|
|
return await this.get(`/curated-sets`)
|
|
}
|
|
|
|
/**
|
|
* Get pattern
|
|
*
|
|
* @param {number} id - The pattern ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getPattern = async function (id) {
|
|
return await this.get(`/patterns/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get patterns for the current user
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getPatterns = async function () {
|
|
return await this.get(`/patterns/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get public pattern
|
|
*
|
|
* @param {number} id - The pattern ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getPublicPattern = async function (id) {
|
|
return await this.get(`/patterns/${id}.json`)
|
|
}
|
|
|
|
/**
|
|
* Get public measurements set
|
|
*
|
|
* @param {number} id - The public measurements set ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getPublicSet = async function (id) {
|
|
return await this.get(`/sets/${id}.json`)
|
|
}
|
|
|
|
/**
|
|
* Get measurements set
|
|
*
|
|
* @param {number} id - The measurements set ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getSet = async function (id) {
|
|
return await this.get(`/sets/${id}/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Get measurements sets for the current user
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getSets = async function () {
|
|
return await this.get(`/sets/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Get stats (info about how many users, patterns, and so on)
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getStats = async function () {
|
|
return await this.get(`/info/stats`)
|
|
}
|
|
|
|
/*
|
|
* Get option packs suggested for curation
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getSuggestedPacks = async function () {
|
|
return await this.get(`/suggested-packs/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get measurements sets suggested for curation
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getSuggestedSets = async function () {
|
|
return await this.get(`/suggested-sets/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get user count (how many users FreeSewing has)
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getUserCount = async function () {
|
|
return await this.get(`/info/users`)
|
|
}
|
|
|
|
/**
|
|
* Get user data
|
|
*
|
|
* @param {number} uid - The user ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getUserData = async function (uid) {
|
|
return await this.get(`/users/${uid}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Get user profile
|
|
*
|
|
* @param {number} uid - The user ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.getUserProfile = async function (uid) {
|
|
return await this.get(`/users/${uid}`)
|
|
}
|
|
|
|
/*
|
|
* Check whether a slug for a blog or showcase post is available
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.slug - The slug to check
|
|
* @param {string} data.type - One of 'blog' or 'showcase'
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.isPostSlugAvailable = async function ({ slug, type }) {
|
|
const response = await this.get(`/slugs/${type}/${slug}/jwt`, this.auth)
|
|
|
|
// 404 means username is available, which is success in this case
|
|
return response[0] === 404
|
|
? { success: true, data: false, available: true, response }
|
|
: { success: false, available: false, response }
|
|
}
|
|
|
|
/**
|
|
* Checks whether a username is available
|
|
*
|
|
* @param {string} username - The username to check
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.isUsernameAvailable = async function (username) {
|
|
const response = await this.post(`/available/username/jwt`, { username })
|
|
|
|
/*
|
|
* Status 404 means username is available, which is success in this case
|
|
*/
|
|
return response[0] === 404
|
|
? { success: true, data: false, available: true, response }
|
|
: { success: false, available: false, response }
|
|
}
|
|
|
|
/**
|
|
/*
|
|
* Confirm newsletter subscribe
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.id - The confirmation ID
|
|
* @param {string} data.ehash - The ehash value
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.newsletterConfirmSubscribe = async function ({ id, ehash }) {
|
|
return await this.put('/subscriber', { id, ehash })
|
|
}
|
|
|
|
/**
|
|
* Subscribe to newsletter
|
|
*
|
|
* @param {string} email - The email to subscribe
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.newsletterSubscribe = async function (email) {
|
|
return await this.post('/subscriber', { email, language: 'en' })
|
|
}
|
|
|
|
/*
|
|
* Newsletter unsubscribe
|
|
*
|
|
* @param {string} ehash - The ehash to unsubscribe
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.newsletterUnsubscribe = async function (ehash) {
|
|
return await this.delete(`/subscriber/${ehash}`)
|
|
}
|
|
|
|
/*
|
|
* Newsletter start unsubscribe flow
|
|
*
|
|
* @param {string} ehash - The email address for which to start the unsubscribe flow
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.newsletterStartUnsubscribe = async function (email) {
|
|
return await this.post(`/subscriber/remove`, { email })
|
|
}
|
|
|
|
/**
|
|
* Ping backend to see if current token is still valid
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.ping = async function () {
|
|
console.log('ping called')
|
|
return await this.get(`/whoami/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Reload account - Useful for when local state gets out of sync
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.reloadAccount = async function () {
|
|
console.log('reloadAccount called')
|
|
return await this.get(`/whoami/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Remove account (the current logged in user)
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeAccount = async function () {
|
|
return await this.delete(`/account/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Remove API key
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeApikey = async function (id) {
|
|
return await this.delete(`/apikeys/${id}/jwt`, this.auth)
|
|
}
|
|
|
|
/*
|
|
* Remove bookmark
|
|
*
|
|
* @param {string} id - The bookmark ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeBookmark = async function (id) {
|
|
return await this.delete(`/bookmarks/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Remove curated measurements set
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeCuratedSet = async function (id) {
|
|
return await this.delete(`/curated-sets/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Remove an uploaded image
|
|
*
|
|
* @param {string} id - The image ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeImage = async function (id) {
|
|
return await this.delete(`/images/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Remove pattern
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removePattern = async function (id) {
|
|
return await this.delete(`/patterns/${id}/jwt`, this.auth)
|
|
}
|
|
|
|
/**
|
|
* Remove measurements set
|
|
*
|
|
* @param {string} id - The measurements set ID
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.removeSet = async function (id) {
|
|
return await this.delete(`/sets/${id}/jwt`)
|
|
}
|
|
|
|
/*
|
|
* Remove suggested measurements set
|
|
*/
|
|
Backend.prototype.removeSuggestedSet = async function (id) {
|
|
return await this.delete(`/suggested-sets/${id}/jwt`)
|
|
}
|
|
|
|
/**
|
|
* Restrict processing of account data
|
|
*
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.restrictAccount = async function () {
|
|
return await this.get(`/account/restrict/jwt`)
|
|
}
|
|
|
|
/**
|
|
* User signin/login
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.username - The account username
|
|
* @param {string} data.password - The account password
|
|
* @param {string} data.token - The (optional) MFA token
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.signIn = async function ({ username, password = false, token = false }) {
|
|
return password === false
|
|
? await this.post('/signinlink', { username })
|
|
: await this.post('/signin', { username, password, token })
|
|
}
|
|
|
|
/**
|
|
* Trade in sign-in link id & check for JWT token
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.id - The confirmation ID
|
|
* @param {string} data.check - The confirmation check value
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.signInFromLink = async function ({ id, check, token }) {
|
|
return await this.post(`/signinlink/${id}/${check}`, { token })
|
|
}
|
|
|
|
/**
|
|
* User signup
|
|
*
|
|
* @param {object} data
|
|
* @param {string} data.email - The Email address to sign up
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.signUp = async function ({ email }) {
|
|
return await this.post('/signup', { email, language: 'en' })
|
|
}
|
|
|
|
/*
|
|
* Suggest a measurements set for curation
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.suggestCset = async function (data) {
|
|
return await this.post(`/curated-sets/suggest/jwt`, data)
|
|
}
|
|
|
|
/*
|
|
* Suggest an option pack for curation
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.suggestOpack = async function (data) {
|
|
return await this.post(`/option-packs/suggest/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Generic update account method
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.updateAccount = async function (data) {
|
|
return await this.patch(`/account/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Generic update curated measurements set method
|
|
*
|
|
* @param {object} consent
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.updateCuratedSet = async function (id, data) {
|
|
return await this.patch(`/curated-sets/${id}/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Update consent (uses the jwt-guest middleware)
|
|
*
|
|
* @param {object} consent
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.updateConsent = async function (consent) {
|
|
return await this.patch(`/consent/jwt`, { consent })
|
|
}
|
|
|
|
/**
|
|
* Generic update pattern set method
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.updatePattern = async function (id, data) {
|
|
return await this.patch(`/patterns/${id}/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Generic update measurements set method
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.updateSet = async function (id, data) {
|
|
return await this.patch(`/sets/${id}/jwt`, data)
|
|
}
|
|
|
|
/**
|
|
* Upload an image
|
|
*
|
|
* @param {object} data - Data for the API call
|
|
* @return {array} result - The REST response, a [status, data] array
|
|
*/
|
|
Backend.prototype.uploadImage = async function (data) {
|
|
return await this.post('/images/jwt', data)
|
|
}
|
|
|