minor: new nav system (#5)
All checks were successful
ci/woodpecker/push/push Pipeline was successful
All checks were successful
ci/woodpecker/push/push Pipeline was successful
Co-authored-by: Madison Grubb <madison@elastiflow.com> Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
export default defineEventHandler((event) => {
|
||||
const user = event.context.user
|
||||
if (!user) throw createError({ statusCode: 401, message: 'Unauthorized' })
|
||||
return { id: user.id, identifier: user.identifier, role: user.role, auth_provider: user.auth_provider ?? 'local' }
|
||||
return {
|
||||
id: user.id,
|
||||
identifier: user.identifier,
|
||||
role: user.role,
|
||||
auth_provider: user.auth_provider ?? 'local',
|
||||
avatar_url: user.avatar_path ? '/api/me/avatar' : null,
|
||||
}
|
||||
})
|
||||
|
||||
14
server/api/me/avatar.delete.js
Normal file
14
server/api/me/avatar.delete.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { unlink } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { getDb, getAvatarsDir } from '../../utils/db.js'
|
||||
import { requireAuth } from '../../utils/authHelpers.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = requireAuth(event)
|
||||
if (!user.avatar_path) return { ok: true }
|
||||
const path = join(getAvatarsDir(), user.avatar_path)
|
||||
await unlink(path).catch(() => {})
|
||||
const { run } = await getDb()
|
||||
await run('UPDATE users SET avatar_path = NULL WHERE id = ?', [user.id])
|
||||
return { ok: true }
|
||||
})
|
||||
23
server/api/me/avatar.get.js
Normal file
23
server/api/me/avatar.get.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { getAvatarsDir } from '../../utils/db.js'
|
||||
import { requireAuth } from '../../utils/authHelpers.js'
|
||||
|
||||
const MIME = Object.freeze({ jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png' })
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = requireAuth(event)
|
||||
if (!user.avatar_path) throw createError({ statusCode: 404, message: 'No avatar' })
|
||||
const path = join(getAvatarsDir(), user.avatar_path)
|
||||
const ext = user.avatar_path.split('.').pop()?.toLowerCase()
|
||||
const mime = MIME[ext] ?? 'application/octet-stream'
|
||||
try {
|
||||
const buf = await readFile(path)
|
||||
setResponseHeader(event, 'Content-Type', mime)
|
||||
setResponseHeader(event, 'Cache-Control', 'private, max-age=3600')
|
||||
return buf
|
||||
}
|
||||
catch {
|
||||
throw createError({ statusCode: 404, message: 'Avatar not found' })
|
||||
}
|
||||
})
|
||||
32
server/api/me/avatar.put.js
Normal file
32
server/api/me/avatar.put.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { writeFile, unlink } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { readMultipartFormData } from 'h3'
|
||||
import { getDb, getAvatarsDir } from '../../utils/db.js'
|
||||
import { requireAuth } from '../../utils/authHelpers.js'
|
||||
|
||||
const MAX_SIZE = 2 * 1024 * 1024
|
||||
const ALLOWED_TYPES = Object.freeze(['image/jpeg', 'image/png'])
|
||||
const EXT_BY_MIME = Object.freeze({ 'image/jpeg': 'jpg', 'image/png': 'png' })
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = requireAuth(event)
|
||||
const form = await readMultipartFormData(event)
|
||||
const file = form?.find(f => f.name === 'avatar' && f.data)
|
||||
if (!file || !file.filename) throw createError({ statusCode: 400, message: 'Missing avatar file' })
|
||||
if (file.data.length > MAX_SIZE) throw createError({ statusCode: 400, message: 'File too large' })
|
||||
const mime = file.type ?? ''
|
||||
if (!ALLOWED_TYPES.includes(mime)) throw createError({ statusCode: 400, message: 'Invalid type; use JPEG or PNG' })
|
||||
const ext = EXT_BY_MIME[mime] ?? 'jpg'
|
||||
const filename = `${user.id}.${ext}`
|
||||
const dir = getAvatarsDir()
|
||||
const path = join(dir, filename)
|
||||
await writeFile(path, file.data)
|
||||
const { run } = await getDb()
|
||||
const previous = user.avatar_path
|
||||
await run('UPDATE users SET avatar_path = ? WHERE id = ?', [filename, user.id])
|
||||
if (previous && previous !== filename) {
|
||||
const oldPath = join(dir, previous)
|
||||
await unlink(oldPath).catch(() => {})
|
||||
}
|
||||
return { ok: true }
|
||||
})
|
||||
Reference in New Issue
Block a user