| @@ -114,6 +114,9 @@ import * as ep___clips_list from './endpoints/clips/list.js'; | ||||
| import * as ep___clips_notes from './endpoints/clips/notes.js'; | ||||
| import * as ep___clips_show from './endpoints/clips/show.js'; | ||||
| import * as ep___clips_update from './endpoints/clips/update.js'; | ||||
| import * as ep___clips_favorite from './endpoints/clips/favorite.js'; | ||||
| import * as ep___clips_unfavorite from './endpoints/clips/unfavorite.js'; | ||||
| import * as ep___clips_myFavorites from './endpoints/clips/my-favorites.js'; | ||||
| import * as ep___drive from './endpoints/drive.js'; | ||||
| import * as ep___drive_files from './endpoints/drive/files.js'; | ||||
| import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; | ||||
| @@ -438,6 +441,9 @@ const $clips_list: Provider = { provide: 'ep:clips/list', useClass: ep___clips_l | ||||
| const $clips_notes: Provider = { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }; | ||||
| const $clips_show: Provider = { provide: 'ep:clips/show', useClass: ep___clips_show.default }; | ||||
| const $clips_update: Provider = { provide: 'ep:clips/update', useClass: ep___clips_update.default }; | ||||
| const $clips_favorite: Provider = { provide: 'ep:clips/favorite', useClass: ep___clips_favorite.default }; | ||||
| const $clips_unfavorite: Provider = { provide: 'ep:clips/unfavorite', useClass: ep___clips_unfavorite.default }; | ||||
| const $clips_myFavorites: Provider = { provide: 'ep:clips/my-favorites', useClass: ep___clips_myFavorites.default }; | ||||
| const $drive: Provider = { provide: 'ep:drive', useClass: ep___drive.default }; | ||||
| const $drive_files: Provider = { provide: 'ep:drive/files', useClass: ep___drive_files.default }; | ||||
| const $drive_files_attachedNotes: Provider = { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }; | ||||
| @@ -766,6 +772,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention | ||||
| 		$clips_notes, | ||||
| 		$clips_show, | ||||
| 		$clips_update, | ||||
| 		$clips_favorite, | ||||
| 		$clips_unfavorite, | ||||
| 		$clips_myFavorites, | ||||
| 		$drive, | ||||
| 		$drive_files, | ||||
| 		$drive_files_attachedNotes, | ||||
| @@ -1088,6 +1097,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention | ||||
| 		$clips_notes, | ||||
| 		$clips_show, | ||||
| 		$clips_update, | ||||
| 		$clips_favorite, | ||||
| 		$clips_unfavorite, | ||||
| 		$clips_myFavorites, | ||||
| 		$drive, | ||||
| 		$drive_files, | ||||
| 		$drive_files_attachedNotes, | ||||
|   | ||||
| @@ -114,6 +114,9 @@ import * as ep___clips_list from './endpoints/clips/list.js'; | ||||
| import * as ep___clips_notes from './endpoints/clips/notes.js'; | ||||
| import * as ep___clips_show from './endpoints/clips/show.js'; | ||||
| import * as ep___clips_update from './endpoints/clips/update.js'; | ||||
| import * as ep___clips_favorite from './endpoints/clips/favorite.js'; | ||||
| import * as ep___clips_unfavorite from './endpoints/clips/unfavorite.js'; | ||||
| import * as ep___clips_myFavorites from './endpoints/clips/my-favorites.js'; | ||||
| import * as ep___drive from './endpoints/drive.js'; | ||||
| import * as ep___drive_files from './endpoints/drive/files.js'; | ||||
| import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; | ||||
| @@ -436,6 +439,9 @@ const eps = [ | ||||
| 	['clips/notes', ep___clips_notes], | ||||
| 	['clips/show', ep___clips_show], | ||||
| 	['clips/update', ep___clips_update], | ||||
| 	['clips/favorite', ep___clips_favorite], | ||||
| 	['clips/unfavorite', ep___clips_unfavorite], | ||||
| 	['clips/my-favorites', ep___clips_myFavorites], | ||||
| 	['drive', ep___drive], | ||||
| 	['drive/files', ep___drive_files], | ||||
| 	['drive/files/attached-notes', ep___drive_files_attachedNotes], | ||||
|   | ||||
| @@ -106,6 +106,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				noteId: note.id, | ||||
| 				clipId: clip.id, | ||||
| 			}); | ||||
|  | ||||
| 			await this.clipsRepository.update(clip.id, { | ||||
| 				lastClippedAt: new Date(), | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				description: ps.description, | ||||
| 			}).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0])); | ||||
|  | ||||
| 			return await this.clipEntityService.pack(clip); | ||||
| 			return await this.clipEntityService.pack(clip, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										76
									
								
								packages/backend/src/server/api/endpoints/clips/favorite.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								packages/backend/src/server/api/endpoints/clips/favorite.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js'; | ||||
| import { IdService } from '@/core/IdService.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['clip'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
|  | ||||
| 	kind: 'write:clip-favorite', | ||||
|  | ||||
| 	errors: { | ||||
| 		noSuchClip: { | ||||
| 			message: 'No such clip.', | ||||
| 			code: 'NO_SUCH_CLIP', | ||||
| 			id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5', | ||||
| 		}, | ||||
|  | ||||
| 		alreadyFavorited: { | ||||
| 			message: 'The clip has already been favorited.', | ||||
| 			code: 'ALREADY_FAVORITED', | ||||
| 			id: '92658936-c625-4273-8326-2d790129256e', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		clipId: { type: 'string', format: 'misskey:id' }, | ||||
| 	}, | ||||
| 	required: ['clipId'], | ||||
| } as const; | ||||
|  | ||||
| // eslint-disable-next-line import/no-default-export | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 	constructor( | ||||
| 		@Inject(DI.clipsRepository) | ||||
| 		private clipsRepository: ClipsRepository, | ||||
|  | ||||
| 		@Inject(DI.clipFavoritesRepository) | ||||
| 		private clipFavoritesRepository: ClipFavoritesRepository, | ||||
|  | ||||
| 		private idService: IdService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const clip = await this.clipsRepository.findOneBy({ id: ps.clipId }); | ||||
| 			if (clip == null) { | ||||
| 				throw new ApiError(meta.errors.noSuchClip); | ||||
| 			} | ||||
| 			if ((clip.userId !== me.id) && !clip.isPublic) { | ||||
| 				throw new ApiError(meta.errors.noSuchClip); | ||||
| 			} | ||||
|  | ||||
| 			const exist = await this.clipFavoritesRepository.findOneBy({ | ||||
| 				clipId: clip.id, | ||||
| 				userId: me.id, | ||||
| 			}); | ||||
|  | ||||
| 			if (exist != null) { | ||||
| 				throw new ApiError(meta.errors.alreadyFavorited); | ||||
| 			} | ||||
|  | ||||
| 			await this.clipFavoritesRepository.insert({ | ||||
| 				id: this.idService.genId(), | ||||
| 				createdAt: new Date(), | ||||
| 				clipId: clip.id, | ||||
| 				userId: me.id, | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				userId: me.id, | ||||
| 			}); | ||||
|  | ||||
| 			return await Promise.all(clips.map(x => this.clipEntityService.pack(x))); | ||||
| 			return await this.clipEntityService.packMany(clips, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,52 @@ | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { ClipFavoritesRepository } from '@/models/index.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['account', 'clip'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
|  | ||||
| 	kind: 'read:clip-favorite', | ||||
|  | ||||
| 	res: { | ||||
| 		type: 'array', | ||||
| 		optional: false, nullable: false, | ||||
| 		items: { | ||||
| 			type: 'object', | ||||
| 			optional: false, nullable: false, | ||||
| 			ref: 'Clip', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 	}, | ||||
| 	required: [], | ||||
| } as const; | ||||
|  | ||||
| // eslint-disable-next-line import/no-default-export | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 	constructor( | ||||
| 		@Inject(DI.clipFavoritesRepository) | ||||
| 		private clipFavoritesRepository: ClipFavoritesRepository, | ||||
|  | ||||
| 		private clipEntityService: ClipEntityService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const query = this.clipFavoritesRepository.createQueryBuilder('favorite') | ||||
| 				.andWhere('favorite.userId = :meId', { meId: me.id }) | ||||
| 				.leftJoinAndSelect('favorite.clip', 'clip'); | ||||
|  | ||||
| 			const favorites = await query | ||||
| 				.getMany(); | ||||
|  | ||||
| 			return this.clipEntityService.packMany(favorites.map(x => x.clip!), me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				throw new ApiError(meta.errors.noSuchClip); | ||||
| 			} | ||||
|  | ||||
| 			return await this.clipEntityService.pack(clip); | ||||
| 			return await this.clipEntityService.pack(clip, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,65 @@ | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['clip'], | ||||
|  | ||||
| 	requireCredential: true, | ||||
|  | ||||
| 	kind: 'write:clip-favorite', | ||||
|  | ||||
| 	errors: { | ||||
| 		noSuchClip: { | ||||
| 			message: 'No such clip.', | ||||
| 			code: 'NO_SUCH_CLIP', | ||||
| 			id: '2603966e-b865-426c-94a7-af4a01241dc1', | ||||
| 		}, | ||||
|  | ||||
| 		notFavorited: { | ||||
| 			message: 'You have not favorited the clip.', | ||||
| 			code: 'NOT_FAVORITED', | ||||
| 			id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187', | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  | ||||
| export const paramDef = { | ||||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		clipId: { type: 'string', format: 'misskey:id' }, | ||||
| 	}, | ||||
| 	required: ['clipId'], | ||||
| } as const; | ||||
|  | ||||
| // eslint-disable-next-line import/no-default-export | ||||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 	constructor( | ||||
| 		@Inject(DI.clipsRepository) | ||||
| 		private clipsRepository: ClipsRepository, | ||||
|  | ||||
| 		@Inject(DI.clipFavoritesRepository) | ||||
| 		private clipFavoritesRepository: ClipFavoritesRepository, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const clip = await this.clipsRepository.findOneBy({ id: ps.clipId }); | ||||
| 			if (clip == null) { | ||||
| 				throw new ApiError(meta.errors.noSuchClip); | ||||
| 			} | ||||
|  | ||||
| 			const exist = await this.clipFavoritesRepository.findOneBy({ | ||||
| 				clipId: clip.id, | ||||
| 				userId: me.id, | ||||
| 			}); | ||||
|  | ||||
| 			if (exist == null) { | ||||
| 				throw new ApiError(meta.errors.notFavorited); | ||||
| 			} | ||||
|  | ||||
| 			await this.clipFavoritesRepository.delete(exist.id); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -64,7 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				isPublic: ps.isPublic, | ||||
| 			}); | ||||
|  | ||||
| 			return await this.clipEntityService.pack(clip.id); | ||||
| 			return await this.clipEntityService.pack(clip.id, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
| import { GetterService } from '@/server/api/GetterService.js'; | ||||
| import { ApiError } from '../../error.js'; | ||||
|  | ||||
| export const meta = { | ||||
| 	tags: ['clips', 'notes'], | ||||
| @@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				isPublic: true, | ||||
| 			}); | ||||
|  | ||||
| 			return await Promise.all(clips.map(x => this.clipEntityService.pack(x))); | ||||
| 			return await this.clipEntityService.packMany(clips, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -51,7 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				.take(ps.limit) | ||||
| 				.getMany(); | ||||
|  | ||||
| 			return await this.clipEntityService.packMany(clips); | ||||
| 			return await this.clipEntityService.packMany(clips, me); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo