Frontend update
This commit is contained in:
87
client/src/api/client.js
Normal file
87
client/src/api/client.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// Thin fetch wrapper. Always sends cookies (httpOnly JWT) and talks to the
|
||||
// same-origin API (/api/v1) — proxied to the Express server in dev.
|
||||
const BASE = '/api/v1'
|
||||
|
||||
class ApiError extends Error {
|
||||
constructor(status, message, body) {
|
||||
super(message)
|
||||
this.status = status
|
||||
this.body = body
|
||||
}
|
||||
}
|
||||
|
||||
async function req(path, { method = 'GET', body, headers, raw } = {}) {
|
||||
const opts = { method, credentials: 'include', headers: { ...headers } }
|
||||
if (body !== undefined) {
|
||||
if (raw) {
|
||||
opts.body = body // FormData — let the browser set the content-type
|
||||
} else {
|
||||
opts.headers['Content-Type'] = 'application/json'
|
||||
opts.body = JSON.stringify(body)
|
||||
}
|
||||
}
|
||||
const res = await fetch(BASE + path, opts)
|
||||
const text = await res.text()
|
||||
const data = text ? safeParse(text) : null
|
||||
if (!res.ok) {
|
||||
const message = (data && data.message) || res.statusText || 'Request failed'
|
||||
throw new ApiError(res.status, message, data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function safeParse(text) {
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
export const api = {
|
||||
// ----- auth -----
|
||||
me: () => req('/auth/me'),
|
||||
login: (username, password) => req('/auth/login', { method: 'POST', body: { username, password } }),
|
||||
logout: () => req('/auth/logout', { method: 'POST' }),
|
||||
|
||||
// ----- public -----
|
||||
publicSettings: () => req('/public/settings'),
|
||||
status: () => req('/public/status'),
|
||||
posts: (category) => req(`/public/posts/${category}`),
|
||||
post: (category, idOrSlug) => req(`/public/posts/${category}/${idOrSlug}`),
|
||||
wiki: () => req('/public/wiki'),
|
||||
wikiPage: (slug) => req(`/public/wiki/${slug}`),
|
||||
contact: (payload) => req('/public/contact', { method: 'POST', body: payload }),
|
||||
|
||||
// ----- admin -----
|
||||
admin: {
|
||||
dashboard: () => req('/admin/dashboard'),
|
||||
setSiteMode: (mode) => req('/admin/site-mode', { method: 'PUT', body: { mode } }),
|
||||
listPosts: (category) => req(`/admin/posts${category ? `?category=${category}` : ''}`),
|
||||
getPost: (id) => req(`/admin/posts/${id}`),
|
||||
createPost: (data) => req('/admin/posts', { method: 'POST', body: data }),
|
||||
updatePost: (id, data) => req(`/admin/posts/${id}`, { method: 'PUT', body: data }),
|
||||
deletePost: (id) => req(`/admin/posts/${id}`, { method: 'DELETE' }),
|
||||
publishPost: (id, published) =>
|
||||
req(`/admin/posts/${id}/publish`, { method: 'PATCH', body: { published } }),
|
||||
uploadImage: (file) => {
|
||||
const fd = new FormData()
|
||||
fd.append('image', file)
|
||||
return req('/admin/posts/upload', { method: 'POST', body: fd, raw: true })
|
||||
},
|
||||
listWiki: () => req('/admin/wiki'),
|
||||
getWiki: (slug) => req(`/admin/wiki/${slug}`),
|
||||
createWiki: (data) => req('/admin/wiki', { method: 'POST', body: data }),
|
||||
updateWiki: (slug, data) => req(`/admin/wiki/${slug}`, { method: 'PUT', body: data }),
|
||||
deleteWiki: (slug) => req(`/admin/wiki/${slug}`, { method: 'DELETE' }),
|
||||
getSettings: () => req('/admin/settings'),
|
||||
updateSettings: (obj) => req('/admin/settings', { method: 'PUT', body: obj }),
|
||||
activity: (limit = 50) => req(`/admin/activity?limit=${limit}`),
|
||||
listUsers: () => req('/admin/users'),
|
||||
createUser: (data) => req('/admin/users', { method: 'POST', body: data }),
|
||||
updateUser: (id, data) => req(`/admin/users/${id}`, { method: 'PUT', body: data }),
|
||||
deleteUser: (id) => req(`/admin/users/${id}`, { method: 'DELETE' }),
|
||||
},
|
||||
}
|
||||
|
||||
export { ApiError }
|
||||
Reference in New Issue
Block a user