feat(saves): Обновление существующих публикаций
This commit is contained in:
@ -175,6 +175,63 @@ export const savesController = new Elysia({ prefix: '/saves' })
|
||||
}
|
||||
)
|
||||
|
||||
.patch(
|
||||
'/:id',
|
||||
async ({ params: { id }, body, user, set }) => {
|
||||
if (!user) {
|
||||
set.status = 401;
|
||||
return { error: 'Unauthorized' };
|
||||
}
|
||||
|
||||
const saveId = Number(id);
|
||||
if (isNaN(saveId)) {
|
||||
set.status = 400;
|
||||
return { error: 'Invalid save ID' };
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await savesService.update(saveId, user.id, body);
|
||||
|
||||
return {
|
||||
id: updated.id,
|
||||
name: updated.name,
|
||||
type: updated.type,
|
||||
description: updated.description,
|
||||
tags: updated.tags,
|
||||
visibility: updated.visibility,
|
||||
shareUrl: updated.visibility === 'link' ? updated.shareUrl : undefined,
|
||||
updatedAt: updated.updatedAt.toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('not found')) {
|
||||
set.status = 404;
|
||||
return { error: 'Save not found or access denied' };
|
||||
}
|
||||
set.status = 500;
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Failed to update save'
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
params: t.Object({
|
||||
id: t.String(),
|
||||
}),
|
||||
body: t.Object({
|
||||
name: t.Optional(t.String()),
|
||||
description: t.Optional(t.String()),
|
||||
tags: t.Optional(t.Array(t.String())),
|
||||
visibility: t.Optional(t.Union([t.Literal('public'), t.Literal('link')])),
|
||||
}),
|
||||
detail: {
|
||||
tags: ['Saves'],
|
||||
summary: 'Update save',
|
||||
description: 'Updates save metadata (owner only)',
|
||||
},
|
||||
auth: true
|
||||
}
|
||||
)
|
||||
|
||||
.post(
|
||||
'/external',
|
||||
async ({ body, user, set }) => {
|
||||
|
||||
@ -8,6 +8,7 @@ import { nanoid } from 'nanoid';
|
||||
import type {
|
||||
Visibility,
|
||||
CreateSaveFromUrlRequest,
|
||||
UpdateSaveRequest,
|
||||
} from '@p1ctos4ve/shared-types';
|
||||
|
||||
class SavesService {
|
||||
@ -161,6 +162,47 @@ class SavesService {
|
||||
return publicSaves;
|
||||
}
|
||||
|
||||
async update(
|
||||
id: number,
|
||||
userId: string,
|
||||
data: UpdateSaveRequest
|
||||
): Promise<Save> {
|
||||
const savedItem = await this.getById(id, userId);
|
||||
|
||||
if (!savedItem || savedItem.userId !== userId) {
|
||||
throw new Error('Save not found or access denied');
|
||||
}
|
||||
|
||||
const updateData: Partial<NewSave> = {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
if (data.name !== undefined) updateData.name = data.name;
|
||||
if (data.description !== undefined) updateData.description = data.description;
|
||||
if (data.tags !== undefined) updateData.tags = data.tags;
|
||||
if (data.visibility !== undefined) {
|
||||
updateData.visibility = data.visibility;
|
||||
|
||||
if (data.visibility === 'link' && !savedItem.shareUrl) {
|
||||
updateData.shareUrl = this.generateShareUrl();
|
||||
}
|
||||
|
||||
if (data.visibility === 'public') {
|
||||
updateData.shareUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
const [updated] = await db
|
||||
.update(save)
|
||||
.set(updateData)
|
||||
.where(eq(save.id, id))
|
||||
.returning();
|
||||
|
||||
await this.invalidateCache(id, userId);
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
private hasAccess(
|
||||
savedItem: Save,
|
||||
requestUserId?: string,
|
||||
@ -185,6 +227,11 @@ class SavesService {
|
||||
return nanoid(16);
|
||||
}
|
||||
|
||||
private async invalidateCache(saveId: number, userId: string): Promise<void> {
|
||||
await redis.del(`save:${saveId}`);
|
||||
await this.invalidateUserCache(userId);
|
||||
}
|
||||
|
||||
private async invalidateUserCache(userId: string): Promise<void> {
|
||||
await redis.del(`user_saves:${userId}`);
|
||||
await redis.del(`public_saves:${userId}`);
|
||||
|
||||
Reference in New Issue
Block a user