Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fabf66f04 |
@@ -80,6 +80,7 @@ app.use(async (req, res, next) => {
|
|||||||
|
|
||||||
app.use('/api/auth', authRoutes)
|
app.use('/api/auth', authRoutes)
|
||||||
app.use('/api/games', gamesRoutes)
|
app.use('/api/games', gamesRoutes)
|
||||||
|
app.use('/api/topics', gamesRoutes)
|
||||||
app.use('/api/tierlists', tierListsRoutes)
|
app.use('/api/tierlists', tierListsRoutes)
|
||||||
app.use('/api/admin', adminRoutes)
|
app.use('/api/admin', adminRoutes)
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ const { createMemoryUpload, writeOptimizedImage, getImageOptimizationQueueState
|
|||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
|
function getTemplateIdParam(req) {
|
||||||
|
return req.params.templateId || req.params.gameId || ''
|
||||||
|
}
|
||||||
|
|
||||||
function buildUploadFilename(file) {
|
function buildUploadFilename(file) {
|
||||||
const ext = path.extname(file.originalname || '').toLowerCase()
|
const ext = path.extname(file.originalname || '').toLowerCase()
|
||||||
const safeExt = ext && /^[.a-z0-9]+$/.test(ext) ? ext : ''
|
const safeExt = ext && /^[.a-z0-9]+$/.test(ext) ? ext : ''
|
||||||
@@ -110,7 +114,7 @@ function canManageAdminRole(actingUser, primaryAdmin) {
|
|||||||
return !!actingUser?.isAdmin && primaryAdmin?.id === actingUser.id
|
return !!actingUser?.isAdmin && primaryAdmin?.id === actingUser.id
|
||||||
}
|
}
|
||||||
|
|
||||||
router.post('/games', requireAdmin, async (req, res) => {
|
router.post(['/games', '/templates'], requireAdmin, async (req, res) => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
id: z.string().min(1),
|
id: z.string().min(1),
|
||||||
name: z.string().min(1).max(60),
|
name: z.string().min(1).max(60),
|
||||||
@@ -126,24 +130,26 @@ router.post('/games', requireAdmin, async (req, res) => {
|
|||||||
const copiedThumb = await copyUploadIntoGameAsset(parsed.data.thumbnailSrc)
|
const copiedThumb = await copyUploadIntoGameAsset(parsed.data.thumbnailSrc)
|
||||||
await updateGameThumbnail(game.id, copiedThumb)
|
await updateGameThumbnail(game.id, copiedThumb)
|
||||||
}
|
}
|
||||||
res.json({ game: await findGameById(game.id) })
|
const template = await findGameById(game.id)
|
||||||
|
res.json({ game: template, template })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.patch('/games/:gameId', requireAdmin, async (req, res) => {
|
router.patch(['/games/:gameId', '/templates/:templateId'], requireAdmin, async (req, res) => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
isPublic: z.boolean(),
|
isPublic: z.boolean(),
|
||||||
})
|
})
|
||||||
const parsed = schema.safeParse(req.body)
|
const parsed = schema.safeParse(req.body)
|
||||||
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
||||||
|
|
||||||
const game = await findGameById(req.params.gameId)
|
const templateId = getTemplateIdParam(req)
|
||||||
|
const game = await findGameById(templateId)
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
|
|
||||||
const updated = await updateGameVisibility(game.id, parsed.data.isPublic)
|
const updated = await updateGameVisibility(game.id, parsed.data.isPublic)
|
||||||
res.json({ game: updated })
|
res.json({ game: updated, template: updated })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.patch('/games/display-order', requireAdmin, async (req, res) => {
|
router.patch(['/games/display-order', '/templates/display-order'], requireAdmin, async (req, res) => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
gameIds: z.array(z.string().min(1)).max(50),
|
gameIds: z.array(z.string().min(1)).max(50),
|
||||||
})
|
})
|
||||||
@@ -154,26 +160,28 @@ router.patch('/games/display-order', requireAdmin, async (req, res) => {
|
|||||||
const validGameIds = new Set(games.map((game) => game.id))
|
const validGameIds = new Set(games.map((game) => game.id))
|
||||||
const filteredIds = parsed.data.gameIds.filter((gameId) => validGameIds.has(gameId))
|
const filteredIds = parsed.data.gameIds.filter((gameId) => validGameIds.has(gameId))
|
||||||
const updatedGames = await updateGameDisplayOrder(filteredIds)
|
const updatedGames = await updateGameDisplayOrder(filteredIds)
|
||||||
res.json({ games: updatedGames })
|
res.json({ games: updatedGames, templates: updatedGames })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.patch('/games/:gameId/items/display-order', requireAdmin, async (req, res) => {
|
router.patch(['/games/:gameId/items/display-order', '/templates/:templateId/items/display-order'], requireAdmin, async (req, res) => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
itemIds: z.array(z.string().min(1)).min(1),
|
itemIds: z.array(z.string().min(1)).min(1),
|
||||||
})
|
})
|
||||||
const parsed = schema.safeParse(req.body)
|
const parsed = schema.safeParse(req.body)
|
||||||
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
||||||
|
|
||||||
const game = await findGameById(req.params.gameId)
|
const templateId = getTemplateIdParam(req)
|
||||||
|
const game = await findGameById(templateId)
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
|
|
||||||
const items = await updateGameItemDisplayOrder(game.id, parsed.data.itemIds)
|
const items = await updateGameItemDisplayOrder(game.id, parsed.data.itemIds)
|
||||||
res.json({ items })
|
res.json({ items })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/games/:gameId/thumbnail', requireAdmin, upload.single('thumbnail'), async (req, res) => {
|
router.post(['/games/:gameId/thumbnail', '/templates/:templateId/thumbnail'], requireAdmin, upload.single('thumbnail'), async (req, res) => {
|
||||||
if (!req.file) return res.status(400).json({ error: 'file_required' })
|
if (!req.file) return res.status(400).json({ error: 'file_required' })
|
||||||
const game = await findGameById(req.params.gameId)
|
const templateId = getTemplateIdParam(req)
|
||||||
|
const game = await findGameById(templateId)
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
|
|
||||||
const optimized = await writeOptimizedImage({
|
const optimized = await writeOptimizedImage({
|
||||||
@@ -185,14 +193,15 @@ router.post('/games/:gameId/thumbnail', requireAdmin, upload.single('thumbnail')
|
|||||||
quality: 84,
|
quality: 84,
|
||||||
})
|
})
|
||||||
|
|
||||||
const updated = await updateGameThumbnail(req.params.gameId, optimized.src)
|
const updated = await updateGameThumbnail(templateId, optimized.src)
|
||||||
res.json({ game: updated })
|
res.json({ game: updated, template: updated })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/games/:gameId/images', requireAdmin, upload.array('images', 50), async (req, res) => {
|
router.post(['/games/:gameId/images', '/templates/:templateId/images'], requireAdmin, upload.array('images', 50), async (req, res) => {
|
||||||
const files = Array.isArray(req.files) ? req.files : []
|
const files = Array.isArray(req.files) ? req.files : []
|
||||||
if (!files.length) return res.status(400).json({ error: 'file_required' })
|
if (!files.length) return res.status(400).json({ error: 'file_required' })
|
||||||
const game = await findGameById(req.params.gameId)
|
const templateId = getTemplateIdParam(req)
|
||||||
|
const game = await findGameById(templateId)
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
|
|
||||||
const labelsRaw = req.body?.labels
|
const labelsRaw = req.body?.labels
|
||||||
@@ -223,19 +232,19 @@ router.post('/games/:gameId/images', requireAdmin, upload.array('images', 50), a
|
|||||||
res.json({ item: items[0], items })
|
res.json({ item: items[0], items })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.delete('/games/:gameId/items/:itemId', requireAdmin, async (req, res) => {
|
router.delete(['/games/:gameId/items/:itemId', '/templates/:templateId/items/:itemId'], requireAdmin, async (req, res) => {
|
||||||
const game = await findGameById(req.params.gameId)
|
const game = await findGameById(getTemplateIdParam(req))
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
await deleteGameItem(req.params.itemId)
|
await deleteGameItem(req.params.itemId)
|
||||||
res.json({ ok: true })
|
res.json({ ok: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.patch('/games/:gameId/items/:itemId', requireAdmin, async (req, res) => {
|
router.patch(['/games/:gameId/items/:itemId', '/templates/:templateId/items/:itemId'], requireAdmin, async (req, res) => {
|
||||||
const schema = z.object({ label: z.string().trim().min(1).max(60) })
|
const schema = z.object({ label: z.string().trim().min(1).max(60) })
|
||||||
const parsed = schema.safeParse(req.body)
|
const parsed = schema.safeParse(req.body)
|
||||||
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
if (!parsed.success) return res.status(400).json({ error: 'bad_request' })
|
||||||
|
|
||||||
const game = await findGameById(req.params.gameId)
|
const game = await findGameById(getTemplateIdParam(req))
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
|
|
||||||
const updated = await updateGameItemLabel(req.params.itemId, parsed.data.label)
|
const updated = await updateGameItemLabel(req.params.itemId, parsed.data.label)
|
||||||
@@ -243,10 +252,11 @@ router.patch('/games/:gameId/items/:itemId', requireAdmin, async (req, res) => {
|
|||||||
res.json({ item: updated })
|
res.json({ item: updated })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.delete('/games/:gameId', requireAdmin, async (req, res) => {
|
router.delete(['/games/:gameId', '/templates/:templateId'], requireAdmin, async (req, res) => {
|
||||||
const game = await findGameById(req.params.gameId)
|
const templateId = getTemplateIdParam(req)
|
||||||
|
const game = await findGameById(templateId)
|
||||||
if (!game) return res.status(404).json({ error: 'not_found' })
|
if (!game) return res.status(404).json({ error: 'not_found' })
|
||||||
await deleteGame(req.params.gameId)
|
await deleteGame(templateId)
|
||||||
res.json({ ok: true })
|
res.json({ ok: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const router = express.Router()
|
|||||||
|
|
||||||
router.get('/', async (req, res) => {
|
router.get('/', async (req, res) => {
|
||||||
const games = await listGames(req.session?.userId || '', { includePrivate: !!req.session?.isAdmin })
|
const games = await listGames(req.session?.userId || '', { includePrivate: !!req.session?.isAdmin })
|
||||||
res.json({ games })
|
res.json({ games, topics: games })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/:gameId/favorite', requireAuth, async (req, res) => {
|
router.post('/:gameId/favorite', requireAuth, async (req, res) => {
|
||||||
@@ -15,7 +15,7 @@ router.post('/:gameId/favorite', requireAuth, async (req, res) => {
|
|||||||
await favoriteGame({ userId: req.session.userId, gameId: game.id })
|
await favoriteGame({ userId: req.session.userId, gameId: game.id })
|
||||||
const games = await listGames(req.session.userId)
|
const games = await listGames(req.session.userId)
|
||||||
const updated = games.find((entry) => entry.id === game.id) || { ...game, isFavorited: true }
|
const updated = games.find((entry) => entry.id === game.id) || { ...game, isFavorited: true }
|
||||||
res.json({ game: updated })
|
res.json({ game: updated, topic: updated })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.delete('/:gameId/favorite', requireAuth, async (req, res) => {
|
router.delete('/:gameId/favorite', requireAuth, async (req, res) => {
|
||||||
@@ -24,14 +24,14 @@ router.delete('/:gameId/favorite', requireAuth, async (req, res) => {
|
|||||||
await unfavoriteGame({ userId: req.session.userId, gameId: game.id })
|
await unfavoriteGame({ userId: req.session.userId, gameId: game.id })
|
||||||
const games = await listGames(req.session.userId)
|
const games = await listGames(req.session.userId)
|
||||||
const updated = games.find((entry) => entry.id === game.id) || { ...game, isFavorited: false }
|
const updated = games.find((entry) => entry.id === game.id) || { ...game, isFavorited: false }
|
||||||
res.json({ game: updated })
|
res.json({ game: updated, topic: updated })
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/:gameId', async (req, res) => {
|
router.get('/:gameId', async (req, res) => {
|
||||||
const detail = await getGameDetail(req.params.gameId)
|
const detail = await getGameDetail(req.params.gameId)
|
||||||
if (!detail) return res.status(404).json({ error: 'not_found' })
|
if (!detail) return res.status(404).json({ error: 'not_found' })
|
||||||
if (!detail.game.isPublic && !req.session?.isAdmin) return res.status(404).json({ error: 'not_found' })
|
if (!detail.game.isPublic && !req.session?.isAdmin) return res.status(404).json({ error: 'not_found' })
|
||||||
res.json({ game: detail.game, items: detail.items })
|
res.json({ game: detail.game, topic: detail.game, items: detail.items })
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# 의사결정 이력
|
# 의사결정 이력
|
||||||
|
|
||||||
|
## 2026-04-02 v1.4.12
|
||||||
|
- 프런트 이름만 바꾸는 단계가 끝난 뒤에는, 백엔드도 새 `/api/topics`, `/api/admin/templates` 경로를 열고 기존 `/games`는 호환용으로 남기는 점진 전환이 가장 안전하다고 판단했다.
|
||||||
|
|
||||||
## 2026-04-02 v1.4.11
|
## 2026-04-02 v1.4.11
|
||||||
- 백엔드 `/api/games` 경로를 바로 바꾸기보다, 프런트 API 객체에서 먼저 `topic/template` 의미 이름을 제공하고 호출부를 옮기는 편이 위험이 훨씬 낮다고 판단했다.
|
- 백엔드 `/api/games` 경로를 바로 바꾸기보다, 프런트 API 객체에서 먼저 `topic/template` 의미 이름을 제공하고 호출부를 옮기는 편이 위험이 훨씬 낮다고 판단했다.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# 할 일 및 이슈
|
# 할 일 및 이슈
|
||||||
|
|
||||||
## 단기 확인
|
## 단기 확인
|
||||||
|
- `/api/topics`, `/api/admin/templates` alias를 연 뒤 프런트 호출도 새 경로로 옮겼으므로, 실제 브라우저에서 주제 목록/즐겨찾기/주제 상세/관리자 템플릿 관리가 모두 같은 세션으로 자연스럽게 동작하는지 한 번 더 QA한다.
|
||||||
|
- 다음 마지막 단계에서는 DB 스키마와 백엔드 함수/변수명까지 실제로 옮길지, 아니면 현재 alias 구조를 안정판으로 남길지 최종 결정한다.
|
||||||
- 프런트 API 호출부는 `topic/template` 의미 이름으로 옮겼으므로, 다음 단계에서는 `api.js` 안의 레거시 alias를 얼마나 더 유지할지와 백엔드 API 경로를 실제로 바꿀지 범위를 정한다.
|
- 프런트 API 호출부는 `topic/template` 의미 이름으로 옮겼으므로, 다음 단계에서는 `api.js` 안의 레거시 alias를 얼마나 더 유지할지와 백엔드 API 경로를 실제로 바꿀지 범위를 정한다.
|
||||||
- 관리자 템플릿/주제 화면과 홈·에디터·즐겨찾기에서 새 API 이름층으로 바뀐 뒤에도 저장, 즐겨찾기, 템플릿 생성, 아이템 정렬 흐름이 자연스러운지 한 번 더 QA한다.
|
- 관리자 템플릿/주제 화면과 홈·에디터·즐겨찾기에서 새 API 이름층으로 바뀐 뒤에도 저장, 즐겨찾기, 템플릿 생성, 아이템 정렬 흐름이 자연스러운지 한 번 더 QA한다.
|
||||||
- `topicHub / topicId`를 기본 라우트 기준으로 세웠으므로, 기존 `/games/...` 북마크와 새 `/topics/...` 주소 양쪽에서 주제 상세와 에디터 진입이 모두 자연스럽게 이어지는지 한 번 더 QA한다.
|
- `topicHub / topicId`를 기본 라우트 기준으로 세웠으므로, 기존 `/games/...` 북마크와 새 `/topics/...` 주소 양쪽에서 주제 상세와 에디터 진입이 모두 자연스럽게 이어지는지 한 번 더 QA한다.
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# 업데이트 로그
|
# 업데이트 로그
|
||||||
|
|
||||||
|
## 2026-04-02 v1.4.12
|
||||||
|
- 백엔드에 `/api/topics`와 `/api/admin/templates` alias 경로를 추가하고, 주제/템플릿 응답도 `topic/topics`, `template/templates` 키를 함께 내려주도록 정리했다.
|
||||||
|
- 프런트의 새 의미 이름은 이제 실제로도 `/api/topics`, `/api/admin/templates`를 타도록 연결해, 경로 이름과 호출 이름이 다시 어긋나지 않게 맞췄다.
|
||||||
|
|
||||||
## 2026-04-02 v1.4.11
|
## 2026-04-02 v1.4.11
|
||||||
- 프런트 API 이름층을 한 단계 더 정리해 `listTopics / getTopic / favoriteTopic`, `updateAdminTemplate*`, `searchPublicTierListsByTopic` 같은 의미 기반 이름을 추가하고 실제 호출부도 이 기준으로 옮겼다.
|
- 프런트 API 이름층을 한 단계 더 정리해 `listTopics / getTopic / favoriteTopic`, `updateAdminTemplate*`, `searchPublicTierListsByTopic` 같은 의미 기반 이름을 추가하고 실제 호출부도 이 기준으로 옮겼다.
|
||||||
- 백엔드 경로와 응답 구조는 그대로 유지한 채 프런트에서 읽는 이름만 먼저 바꿔, 다음 단계의 API/모델 리네이밍 부담을 더 줄였다.
|
- 백엔드 경로와 응답 구조는 그대로 유지한 채 프런트에서 읽는 이름만 먼저 바꿔, 다음 단계의 API/모델 리네이밍 부담을 더 줄였다.
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export function useAdminGameManager({
|
|||||||
const preserveUploadState = !!options.preserveUploadState
|
const preserveUploadState = !!options.preserveUploadState
|
||||||
resetMessages()
|
resetMessages()
|
||||||
try {
|
try {
|
||||||
const res = await fetch(toApiUrl('/api/admin/games'), {
|
const res = await fetch(toApiUrl('/api/admin/templates'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -283,7 +283,7 @@ export function useAdminGameManager({
|
|||||||
fd.append('images', entry.file)
|
fd.append('images', entry.file)
|
||||||
fd.append('labels', entry.label.trim())
|
fd.append('labels', entry.label.trim())
|
||||||
})
|
})
|
||||||
const res = await fetch(toApiUrl(`/api/admin/games/${encodeURIComponent(selectedTemplateId.value)}/images`), {
|
const res = await fetch(toApiUrl(`/api/admin/templates/${encodeURIComponent(selectedTemplateId.value)}/images`), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: fd,
|
body: fd,
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ export const api = {
|
|||||||
login: ({ email, password }) => request('/api/auth/login', { method: 'POST', body: { email, password } }),
|
login: ({ email, password }) => request('/api/auth/login', { method: 'POST', body: { email, password } }),
|
||||||
logout: () => request('/api/auth/logout', { method: 'POST' }),
|
logout: () => request('/api/auth/logout', { method: 'POST' }),
|
||||||
|
|
||||||
listTopics: () => request('/api/games'),
|
listTopics: () => request('/api/topics'),
|
||||||
getTopic: (topicId) => request(`/api/games/${encodeURIComponent(topicId)}`),
|
getTopic: (topicId) => request(`/api/topics/${encodeURIComponent(topicId)}`),
|
||||||
favoriteTopic: (topicId) => request(`/api/games/${encodeURIComponent(topicId)}/favorite`, { method: 'POST' }),
|
favoriteTopic: (topicId) => request(`/api/topics/${encodeURIComponent(topicId)}/favorite`, { method: 'POST' }),
|
||||||
unfavoriteTopic: (topicId) => request(`/api/games/${encodeURIComponent(topicId)}/favorite`, { method: 'DELETE' }),
|
unfavoriteTopic: (topicId) => request(`/api/topics/${encodeURIComponent(topicId)}/favorite`, { method: 'DELETE' }),
|
||||||
updateAdminTemplateDisplayOrder: (payload) => request('/api/admin/games/display-order', { method: 'PATCH', body: payload }),
|
updateAdminTemplateDisplayOrder: (payload) => request('/api/admin/templates/display-order', { method: 'PATCH', body: payload }),
|
||||||
updateAdminTemplateItemDisplayOrder: (templateId, payload) =>
|
updateAdminTemplateItemDisplayOrder: (templateId, payload) =>
|
||||||
request(`/api/admin/games/${encodeURIComponent(templateId)}/items/display-order`, { method: 'PATCH', body: payload }),
|
request(`/api/admin/templates/${encodeURIComponent(templateId)}/items/display-order`, { method: 'PATCH', body: payload }),
|
||||||
updateAdminTemplate: (templateId, payload) =>
|
updateAdminTemplate: (templateId, payload) =>
|
||||||
request(`/api/admin/games/${encodeURIComponent(templateId)}`, { method: 'PATCH', body: payload }),
|
request(`/api/admin/templates/${encodeURIComponent(templateId)}`, { method: 'PATCH', body: payload }),
|
||||||
updateAdminTemplateItem: (templateId, itemId, payload) =>
|
updateAdminTemplateItem: (templateId, itemId, payload) =>
|
||||||
request(`/api/admin/games/${encodeURIComponent(templateId)}/items/${encodeURIComponent(itemId)}`, { method: 'PATCH', body: payload }),
|
request(`/api/admin/templates/${encodeURIComponent(templateId)}/items/${encodeURIComponent(itemId)}`, { method: 'PATCH', body: payload }),
|
||||||
listAdminCustomItems: ({ q = '', page = 1, limit = 50, filter = 'all' } = {}) =>
|
listAdminCustomItems: ({ q = '', page = 1, limit = 50, filter = 'all' } = {}) =>
|
||||||
request(
|
request(
|
||||||
`/api/admin/custom-items?q=${encodeURIComponent(q)}&page=${encodeURIComponent(page)}&limit=${encodeURIComponent(limit)}&filter=${encodeURIComponent(filter)}`
|
`/api/admin/custom-items?q=${encodeURIComponent(q)}&page=${encodeURIComponent(page)}&limit=${encodeURIComponent(limit)}&filter=${encodeURIComponent(filter)}`
|
||||||
|
|||||||
@@ -1148,7 +1148,7 @@ async function uploadThumbnail() {
|
|||||||
try {
|
try {
|
||||||
const fd = new FormData()
|
const fd = new FormData()
|
||||||
fd.append('thumbnail', thumbFile.value)
|
fd.append('thumbnail', thumbFile.value)
|
||||||
const res = await fetch(toApiUrl(`/api/admin/games/${encodeURIComponent(selectedTemplateId.value)}/thumbnail`), {
|
const res = await fetch(toApiUrl(`/api/admin/templates/${encodeURIComponent(selectedTemplateId.value)}/thumbnail`), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: fd,
|
body: fd,
|
||||||
@@ -1217,7 +1217,7 @@ async function removeTemplateItem(itemId) {
|
|||||||
resetMessages()
|
resetMessages()
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
toApiUrl(`/api/admin/games/${encodeURIComponent(selectedTemplateId.value)}/items/${encodeURIComponent(itemId)}`),
|
toApiUrl(`/api/admin/templates/${encodeURIComponent(selectedTemplateId.value)}/items/${encodeURIComponent(itemId)}`),
|
||||||
{
|
{
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -1263,7 +1263,7 @@ async function removeTemplate() {
|
|||||||
if (!ok) return
|
if (!ok) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(toApiUrl(`/api/admin/games/${encodeURIComponent(selectedTemplateId.value)}`), {
|
const res = await fetch(toApiUrl(`/api/admin/templates/${encodeURIComponent(selectedTemplateId.value)}`), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user