generated from nhcarrigan/template
feat: multiple improvements to library functionality #50
@@ -7,4 +7,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
coverageDirectory: '../coverage/api',
|
coverageDirectory: '../coverage/api',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ describe('GET /', () => {
|
|||||||
url: '/',
|
url: '/',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(response.json()).toEqual({ message: 'Hello API' });
|
expect(response.json()).toEqual({ version: expect.any(String) });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-4
@@ -13,8 +13,8 @@ export async function app(fastify: FastifyInstance, opts: AppOptions) {
|
|||||||
// Log CSRF validation failures
|
// Log CSRF validation failures
|
||||||
if (error.code === 'FST_CSRF_INVALID_TOKEN' || error.code === 'FST_CSRF_MISSING_SECRET') {
|
if (error.code === 'FST_CSRF_INVALID_TOKEN' || error.code === 'FST_CSRF_MISSING_SECRET') {
|
||||||
await AuditService.log({
|
await AuditService.log({
|
||||||
action: AuditAction.CSRF_VALIDATION_FAILED,
|
action: AuditAction.csrfValidationFailed,
|
||||||
category: AuditCategory.SECURITY,
|
category: AuditCategory.security,
|
||||||
details: `CSRF validation failed: ${error.message}, URL: ${request.url}`,
|
details: `CSRF validation failed: ${error.message}, URL: ${request.url}`,
|
||||||
success: false,
|
success: false,
|
||||||
}, request).catch(() => {
|
}, request).catch(() => {
|
||||||
@@ -25,8 +25,8 @@ export async function app(fastify: FastifyInstance, opts: AppOptions) {
|
|||||||
// Log unauthorized access attempts
|
// Log unauthorized access attempts
|
||||||
if (error.statusCode === 401 || error.statusCode === 403) {
|
if (error.statusCode === 401 || error.statusCode === 403) {
|
||||||
await AuditService.log({
|
await AuditService.log({
|
||||||
action: AuditAction.UNAUTHORIZED_ACCESS,
|
action: AuditAction.unauthorizedAccess,
|
||||||
category: AuditCategory.SECURITY,
|
category: AuditCategory.security,
|
||||||
details: `Unauthorized access attempt: ${error.message}, URL: ${request.url}`,
|
details: `Unauthorized access attempt: ${error.message}, URL: ${request.url}`,
|
||||||
success: false,
|
success: false,
|
||||||
}, request).catch(() => {
|
}, request).catch(() => {
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ const authPlugin: FastifyPluginAsync = async (app) => {
|
|||||||
try {
|
try {
|
||||||
await request.jwtVerify();
|
await request.jwtVerify();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw app.httpErrors.unauthorized("Invalid token");
|
const error = new Error("Invalid token");
|
||||||
|
(error as any).statusCode = 401;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const rateLimitPlugin: FastifyPluginAsync = async (app) => {
|
|||||||
errorResponseBuilder: (request) => {
|
errorResponseBuilder: (request) => {
|
||||||
// Log rate limit exceeded event
|
// Log rate limit exceeded event
|
||||||
AuditService.log({
|
AuditService.log({
|
||||||
action: AuditAction.RATE_LIMIT_EXCEEDED,
|
action: AuditAction.rateLimitExceeded,
|
||||||
category: AuditCategory.SECURITY,
|
category: AuditCategory.security,
|
||||||
details: `Rate limit exceeded for URL: ${request.url}`,
|
details: `Rate limit exceeded for URL: ${request.url}`,
|
||||||
success: false,
|
success: false,
|
||||||
}, request).catch(() => {
|
}, request).catch(() => {
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const art = await artService.createArt(request.body);
|
const art = await artService.createArt(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: art.id,
|
resourceId: art.id,
|
||||||
details: `Created art: ${art.title}`,
|
details: `Created art: ${art.title}`,
|
||||||
@@ -74,8 +74,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const art = await artService.updateArt(id, request.body);
|
const art = await artService.updateArt(id, request.body);
|
||||||
if (art) {
|
if (art) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated art: ${art.title}`,
|
details: `Updated art: ${art.title}`,
|
||||||
@@ -98,8 +98,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await artService.deleteArt(id);
|
await artService.deleteArt(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted art with ID: ${id}`,
|
details: `Deleted art with ID: ${id}`,
|
||||||
@@ -133,8 +133,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForArt(id, userId, request.body);
|
const comment = await commentService.createCommentForArt(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to art`,
|
details: `Added comment to art`,
|
||||||
@@ -169,8 +169,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on art`,
|
details: `Updated comment ${commentId} on art`,
|
||||||
@@ -205,8 +205,8 @@ const artRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "art",
|
resourceType: "art",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from art`,
|
details: `Deleted comment ${commentId} from art`,
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ const authRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
// Log successful login
|
// Log successful login
|
||||||
await AuditService.log({
|
await AuditService.log({
|
||||||
action: AuditAction.LOGIN,
|
action: AuditAction.login,
|
||||||
category: AuditCategory.AUTH,
|
category: AuditCategory.auth,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
details: `User ${user.username} logged in via Discord`,
|
details: `User ${user.username} logged in via Discord`,
|
||||||
success: true,
|
success: true,
|
||||||
@@ -114,8 +114,8 @@ const authRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Log failed login attempt
|
// Log failed login attempt
|
||||||
await AuditService.log({
|
await AuditService.log({
|
||||||
action: AuditAction.LOGIN_FAILED,
|
action: AuditAction.loginFailed,
|
||||||
category: AuditCategory.SECURITY,
|
category: AuditCategory.security,
|
||||||
details: error instanceof Error ? error.message : String(error),
|
details: error instanceof Error ? error.message : String(error),
|
||||||
success: false,
|
success: false,
|
||||||
}, request);
|
}, request);
|
||||||
@@ -229,8 +229,8 @@ const authRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const user = request.user as { id?: string; username?: string };
|
const user = request.user as { id?: string; username?: string };
|
||||||
if (user?.id) {
|
if (user?.id) {
|
||||||
await AuditService.log({
|
await AuditService.log({
|
||||||
action: AuditAction.LOGOUT,
|
action: AuditAction.logout,
|
||||||
category: AuditCategory.AUTH,
|
category: AuditCategory.auth,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
details: `User ${user.username ?? "unknown"} logged out`,
|
details: `User ${user.username ?? "unknown"} logged out`,
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const book = await bookService.createBook(request.body);
|
const book = await bookService.createBook(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: book.id,
|
resourceId: book.id,
|
||||||
details: `Created book: ${book.title}`,
|
details: `Created book: ${book.title}`,
|
||||||
@@ -74,8 +74,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const book = await bookService.updateBook(id, request.body);
|
const book = await bookService.updateBook(id, request.body);
|
||||||
if (book) {
|
if (book) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated book: ${book.title}`,
|
details: `Updated book: ${book.title}`,
|
||||||
@@ -98,8 +98,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await bookService.deleteBook(id);
|
await bookService.deleteBook(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted book with ID: ${id}`,
|
details: `Deleted book with ID: ${id}`,
|
||||||
@@ -133,8 +133,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForBook(id, userId, request.body);
|
const comment = await commentService.createCommentForBook(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to book`,
|
details: `Added comment to book`,
|
||||||
@@ -169,8 +169,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on book`,
|
details: `Updated comment ${commentId} on book`,
|
||||||
@@ -205,8 +205,8 @@ const booksRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "book",
|
resourceType: "book",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from book`,
|
details: `Deleted comment ${commentId} from book`,
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const game = await gameService.createGame(request.body);
|
const game = await gameService.createGame(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: game.id,
|
resourceId: game.id,
|
||||||
details: `Created game: ${game.title}`,
|
details: `Created game: ${game.title}`,
|
||||||
@@ -66,8 +66,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const game = await gameService.updateGame(id, request.body);
|
const game = await gameService.updateGame(id, request.body);
|
||||||
if (game) {
|
if (game) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated game: ${game.title}`,
|
details: `Updated game: ${game.title}`,
|
||||||
@@ -88,8 +88,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await gameService.deleteGame(id);
|
await gameService.deleteGame(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted game with ID: ${id}`,
|
details: `Deleted game with ID: ${id}`,
|
||||||
@@ -119,8 +119,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForGame(id, userId, request.body);
|
const comment = await commentService.createCommentForGame(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to game`,
|
details: `Added comment to game`,
|
||||||
@@ -153,8 +153,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on game`,
|
details: `Updated comment ${commentId} on game`,
|
||||||
@@ -187,8 +187,8 @@ const gamesRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "game",
|
resourceType: "game",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from game`,
|
details: `Deleted comment ${commentId} from game`,
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const manga = await mangaService.createManga(request.body);
|
const manga = await mangaService.createManga(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: manga.id,
|
resourceId: manga.id,
|
||||||
details: `Created manga: ${manga.title}`,
|
details: `Created manga: ${manga.title}`,
|
||||||
@@ -62,8 +62,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const manga = await mangaService.updateManga(id, request.body);
|
const manga = await mangaService.updateManga(id, request.body);
|
||||||
if (manga) {
|
if (manga) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated manga: ${manga.title}`,
|
details: `Updated manga: ${manga.title}`,
|
||||||
@@ -83,8 +83,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await mangaService.deleteManga(id);
|
await mangaService.deleteManga(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted manga with ID: ${id}`,
|
details: `Deleted manga with ID: ${id}`,
|
||||||
@@ -112,8 +112,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForManga(id, userId, request.body);
|
const comment = await commentService.createCommentForManga(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to manga`,
|
details: `Added comment to manga`,
|
||||||
@@ -145,8 +145,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on manga`,
|
details: `Updated comment ${commentId} on manga`,
|
||||||
@@ -178,8 +178,8 @@ const mangaRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "manga",
|
resourceType: "manga",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from manga`,
|
details: `Deleted comment ${commentId} from manga`,
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const music = await musicService.createMusic(request.body);
|
const music = await musicService.createMusic(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: music.id,
|
resourceId: music.id,
|
||||||
details: `Created music: ${music.title}`,
|
details: `Created music: ${music.title}`,
|
||||||
@@ -74,8 +74,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const music = await musicService.updateMusic(id, request.body);
|
const music = await musicService.updateMusic(id, request.body);
|
||||||
if (music) {
|
if (music) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated music: ${music.title}`,
|
details: `Updated music: ${music.title}`,
|
||||||
@@ -98,8 +98,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await musicService.deleteMusic(id);
|
await musicService.deleteMusic(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted music with ID: ${id}`,
|
details: `Deleted music with ID: ${id}`,
|
||||||
@@ -133,8 +133,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForMusic(id, userId, request.body);
|
const comment = await commentService.createCommentForMusic(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to music`,
|
details: `Added comment to music`,
|
||||||
@@ -169,8 +169,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on music`,
|
details: `Updated comment ${commentId} on music`,
|
||||||
@@ -205,8 +205,8 @@ const musicRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "music",
|
resourceType: "music",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from music`,
|
details: `Deleted comment ${commentId} from music`,
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
async (request) => {
|
async (request) => {
|
||||||
const show = await showService.createShow(request.body);
|
const show = await showService.createShow(request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: show.id,
|
resourceId: show.id,
|
||||||
details: `Created show: ${show.title}`,
|
details: `Created show: ${show.title}`,
|
||||||
@@ -62,8 +62,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const show = await showService.updateShow(id, request.body);
|
const show = await showService.updateShow(id, request.body);
|
||||||
if (show) {
|
if (show) {
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated show: ${show.title}`,
|
details: `Updated show: ${show.title}`,
|
||||||
@@ -83,8 +83,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await showService.deleteShow(id);
|
await showService.deleteShow(id);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted show with ID: ${id}`,
|
details: `Deleted show with ID: ${id}`,
|
||||||
@@ -112,8 +112,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
const userId = request.user.id;
|
const userId = request.user.id;
|
||||||
const comment = await commentService.createCommentForShow(id, userId, request.body);
|
const comment = await commentService.createCommentForShow(id, userId, request.body);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_CREATE,
|
action: AuditAction.commentCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Added comment to show`,
|
details: `Added comment to show`,
|
||||||
@@ -145,8 +145,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
const comment = await commentService.updateComment(commentId, request.body.content);
|
const comment = await commentService.updateComment(commentId, request.body.content);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_UPDATE,
|
action: AuditAction.commentUpdate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Updated comment ${commentId} on show`,
|
details: `Updated comment ${commentId} on show`,
|
||||||
@@ -178,8 +178,8 @@ const showsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
|
|
||||||
await commentService.deleteComment(commentId);
|
await commentService.deleteComment(commentId);
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.COMMENT_DELETE,
|
action: AuditAction.commentDelete,
|
||||||
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin && verification.comment?.userId !== userId ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "show",
|
resourceType: "show",
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
details: `Deleted comment ${commentId} from show`,
|
details: `Deleted comment ${commentId} from show`,
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ export default async function (app: FastifyInstance): Promise<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_CREATE,
|
action: AuditAction.entryCreate,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: "Suggestion",
|
resourceType: "Suggestion",
|
||||||
resourceId: suggestion.id,
|
resourceId: suggestion.id,
|
||||||
details: `Created ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
details: `Created ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
||||||
@@ -115,8 +115,8 @@ export default async function (app: FastifyInstance): Promise<void> {
|
|||||||
const suggestion = await SuggestionService.acceptSuggestion(id);
|
const suggestion = await SuggestionService.acceptSuggestion(id);
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.ADMIN,
|
category: AuditCategory.admin,
|
||||||
resourceType: "Suggestion",
|
resourceType: "Suggestion",
|
||||||
resourceId: suggestion.id,
|
resourceId: suggestion.id,
|
||||||
details: `Accepted ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
details: `Accepted ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
||||||
@@ -146,8 +146,8 @@ export default async function (app: FastifyInstance): Promise<void> {
|
|||||||
const suggestion = await SuggestionService.acceptSuggestionWithEdits(id, editedData);
|
const suggestion = await SuggestionService.acceptSuggestionWithEdits(id, editedData);
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.ADMIN,
|
category: AuditCategory.admin,
|
||||||
resourceType: "Suggestion",
|
resourceType: "Suggestion",
|
||||||
resourceId: suggestion.id,
|
resourceId: suggestion.id,
|
||||||
details: `Accepted ${suggestion.entityType} suggestion with edits: ${suggestion.title}`,
|
details: `Accepted ${suggestion.entityType} suggestion with edits: ${suggestion.title}`,
|
||||||
@@ -177,8 +177,8 @@ export default async function (app: FastifyInstance): Promise<void> {
|
|||||||
const suggestion = await SuggestionService.declineSuggestion(id, reason);
|
const suggestion = await SuggestionService.declineSuggestion(id, reason);
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_UPDATE,
|
action: AuditAction.entryUpdate,
|
||||||
category: AuditCategory.ADMIN,
|
category: AuditCategory.admin,
|
||||||
resourceType: "Suggestion",
|
resourceType: "Suggestion",
|
||||||
resourceId: suggestion.id,
|
resourceId: suggestion.id,
|
||||||
details: `Declined ${suggestion.entityType} suggestion: ${suggestion.title}${reason ? ` (Reason: ${reason})` : ""}`,
|
details: `Declined ${suggestion.entityType} suggestion: ${suggestion.title}${reason ? ` (Reason: ${reason})` : ""}`,
|
||||||
@@ -209,8 +209,8 @@ export default async function (app: FastifyInstance): Promise<void> {
|
|||||||
const suggestion = await SuggestionService.deleteSuggestion(id, userId, isAdmin);
|
const suggestion = await SuggestionService.deleteSuggestion(id, userId, isAdmin);
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.ENTRY_DELETE,
|
action: AuditAction.entryDelete,
|
||||||
category: isAdmin ? AuditCategory.ADMIN : AuditCategory.CONTENT,
|
category: isAdmin ? AuditCategory.admin : AuditCategory.content,
|
||||||
resourceType: "Suggestion",
|
resourceType: "Suggestion",
|
||||||
resourceId: suggestion.id,
|
resourceId: suggestion.id,
|
||||||
details: `Deleted ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
details: `Deleted ${suggestion.entityType} suggestion: ${suggestion.title}`,
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ const usersRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.USER_BAN,
|
action: AuditAction.userBan,
|
||||||
category: AuditCategory.ADMIN,
|
category: AuditCategory.admin,
|
||||||
targetUserId: id,
|
targetUserId: id,
|
||||||
details: `Banned user: ${user.username}`,
|
details: `Banned user: ${user.username}`,
|
||||||
});
|
});
|
||||||
@@ -78,8 +78,8 @@ const usersRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await AuditService.logFromRequest(request, {
|
await AuditService.logFromRequest(request, {
|
||||||
action: AuditAction.USER_UNBAN,
|
action: AuditAction.userUnban,
|
||||||
category: AuditCategory.ADMIN,
|
category: AuditCategory.admin,
|
||||||
targetUserId: id,
|
targetUserId: id,
|
||||||
details: `Unbanned user: ${user.username}`,
|
details: `Unbanned user: ${user.username}`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const AuditService = {
|
|||||||
request: FastifyRequest,
|
request: FastifyRequest,
|
||||||
data: Omit<AuditLogData, "userId">
|
data: Omit<AuditLogData, "userId">
|
||||||
) {
|
) {
|
||||||
const userId = (request.user as { id?: string } | undefined)?.id;
|
const userId = ((request as any).user as { id?: string } | undefined)?.id;
|
||||||
|
|
||||||
return this.log(
|
return this.log(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export class LikeService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await AuditService.logFromRequest(req, {
|
await AuditService.logFromRequest(req, {
|
||||||
action: AuditAction.UNLIKE,
|
action: AuditAction.unlike,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: entityType,
|
resourceType: entityType,
|
||||||
resourceId: entityId,
|
resourceId: entityId,
|
||||||
details: `Unliked ${entityType}`
|
details: `Unliked ${entityType}`
|
||||||
@@ -52,8 +52,8 @@ export class LikeService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await AuditService.logFromRequest(req, {
|
await AuditService.logFromRequest(req, {
|
||||||
action: AuditAction.LIKE,
|
action: AuditAction.like,
|
||||||
category: AuditCategory.CONTENT,
|
category: AuditCategory.content,
|
||||||
resourceType: entityType,
|
resourceType: entityType,
|
||||||
resourceId: entityId,
|
resourceId: entityId,
|
||||||
details: `Liked ${entityType}`
|
details: `Liked ${entityType}`
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set required environment variables for tests
|
||||||
|
process.env.JWT_SECRET = 'test-secret';
|
||||||
|
process.env.DISCORD_CLIENT_ID = 'test-client-id';
|
||||||
|
process.env.DISCORD_CLIENT_SECRET = 'test-client-secret';
|
||||||
|
process.env.DOMAIN = 'http://localhost:3000';
|
||||||
|
process.env.API_URL = 'http://localhost:3000/api';
|
||||||
|
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test';
|
||||||
|
process.env.BASE_URL = 'http://localhost:4200';
|
||||||
|
process.env.NODE_ENV = 'test';
|
||||||
@@ -206,8 +206,8 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
|||||||
}
|
}
|
||||||
|
|
||||||
@if (showDeclineModal()) {
|
@if (showDeclineModal()) {
|
||||||
<div class="modal-overlay" (click)="closeDeclineModal()">
|
<div class="modal-overlay" (click)="closeDeclineModal()" (keyup.escape)="closeDeclineModal()" tabindex="0" role="button">
|
||||||
<div class="modal" (click)="$event.stopPropagation()">
|
<div class="modal" (click)="$event.stopPropagation()" (keyup)="$event.stopPropagation()" tabindex="-1">
|
||||||
<h3>Decline Suggestion</h3>
|
<h3>Decline Suggestion</h3>
|
||||||
<p>Are you sure you want to decline "{{ decliningsuggestion()?.title }}"?</p>
|
<p>Are you sure you want to decline "{{ decliningsuggestion()?.title }}"?</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -229,8 +229,8 @@ import { Suggestion, SuggestionStatus, SuggestionEntity } from '@library/shared-
|
|||||||
}
|
}
|
||||||
|
|
||||||
@if (showEditModal()) {
|
@if (showEditModal()) {
|
||||||
<div class="modal-overlay" (click)="closeEditModal()">
|
<div class="modal-overlay" (click)="closeEditModal()" (keyup.escape)="closeEditModal()" tabindex="0" role="button">
|
||||||
<div class="modal edit-modal" (click)="$event.stopPropagation()">
|
<div class="modal edit-modal" (click)="$event.stopPropagation()" (keyup)="$event.stopPropagation()" tabindex="-1">
|
||||||
<h3>Review & Edit Before Accepting</h3>
|
<h3>Review & Edit Before Accepting</h3>
|
||||||
<p>Review and edit the details before adding to your collection.</p>
|
<p>Review and edit the details before adding to your collection.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newArt.tags; track tag; let i = $index) {
|
@for (tag of newArt.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -123,8 +122,7 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newArt.links; track link.url; let i = $index) {
|
@for (link of newArt.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -280,8 +278,7 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editArt.tags; track tag; let i = $index) {
|
@for (tag of editArt.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -298,8 +295,7 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editArt.links; track link.url; let i = $index) {
|
@for (link of editArt.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -401,6 +397,10 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
[alt]="art.description || art.title"
|
[alt]="art.description || art.title"
|
||||||
class="art-image"
|
class="art-image"
|
||||||
(click)="openLightbox(art)"
|
(click)="openLightbox(art)"
|
||||||
|
(keyup.enter)="openLightbox(art)"
|
||||||
|
(keyup.space)="openLightbox(art)"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -536,8 +536,8 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
}
|
}
|
||||||
|
|
||||||
@if (lightboxArt()) {
|
@if (lightboxArt()) {
|
||||||
<div class="lightbox" (click)="closeLightbox()">
|
<div class="lightbox" (click)="closeLightbox()" (keyup.escape)="closeLightbox()" tabindex="0" role="button">
|
||||||
<div class="lightbox-content" (click)="$event.stopPropagation()">
|
<div class="lightbox-content" (click)="$event.stopPropagation()" (keyup)="$event.stopPropagation()" tabindex="-1">
|
||||||
<button class="lightbox-close" (click)="closeLightbox()">×</button>
|
<button class="lightbox-close" (click)="closeLightbox()">×</button>
|
||||||
<img [src]="lightboxArt()!.imageUrl" [alt]="lightboxArt()!.description || lightboxArt()!.title">
|
<img [src]="lightboxArt()!.imageUrl" [alt]="lightboxArt()!.description || lightboxArt()!.title">
|
||||||
<div class="lightbox-info">
|
<div class="lightbox-info">
|
||||||
|
|||||||
@@ -126,8 +126,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newBook.tags; track tag; let i = $index) {
|
@for (tag of newBook.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -144,8 +143,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newBook.links; track link.url; let i = $index) {
|
@for (link of newBook.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -269,8 +267,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editBook.tags; track tag; let i = $index) {
|
@for (tag of editBook.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -287,8 +284,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editBook.links; track link.url; let i = $index) {
|
@for (link of editBook.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -421,8 +417,7 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
@if (showFilters()) {
|
@if (showFilters()) {
|
||||||
<div class="advanced-filters">
|
<div class="advanced-filters">
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label>Filter by Tags:</label>
|
<div class="tags-filter" aria-label="Filter by Tags">
|
||||||
<div class="tags-filter">
|
|
||||||
@for (tag of allTags(); track tag) {
|
@for (tag of allTags(); track tag) {
|
||||||
<label class="tag-checkbox">
|
<label class="tag-checkbox">
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -114,8 +114,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newGame.tags; track tag; let i = $index) {
|
@for (tag of newGame.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -132,8 +131,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newGame.links; track link.url; let i = $index) {
|
@for (link of newGame.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -245,8 +243,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editGame.tags; track tag; let i = $index) {
|
@for (tag of editGame.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -263,8 +260,7 @@ import { Game, GameStatus, CreateGameDto, UpdateGameDto, Comment, SuggestionEnti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editGame.links; track link.url; let i = $index) {
|
@for (link of editGame.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newManga.tags; track tag; let i = $index) {
|
@for (tag of newManga.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -133,8 +132,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newManga.links; track link.url; let i = $index) {
|
@for (link of newManga.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -247,8 +245,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editManga.tags; track tag; let i = $index) {
|
@for (tag of editManga.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -265,8 +262,7 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editManga.links; track link.url; let i = $index) {
|
@for (link of editManga.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
|
|||||||
@@ -124,8 +124,7 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newMusic.tags; track tag; let i = $index) {
|
@for (tag of newMusic.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -142,8 +141,7 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newMusic.links; track link.url; let i = $index) {
|
@for (link of newMusic.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -265,8 +263,7 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editMusicData.tags; track tag; let i = $index) {
|
@for (tag of editMusicData.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -283,8 +280,7 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editMusicData.links; track link.url; let i = $index) {
|
@for (link of editMusicData.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of newShow.tags; track tag; let i = $index) {
|
@for (tag of newShow.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -131,8 +130,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of newShow.links; track link.url; let i = $index) {
|
@for (link of newShow.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
@@ -243,8 +241,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Tags</label>
|
<div class="tags-input-container" aria-label="Tags">
|
||||||
<div class="tags-input-container">
|
|
||||||
@for (tag of editShow.tags; track tag; let i = $index) {
|
@for (tag of editShow.tags; track tag; let i = $index) {
|
||||||
<span class="tag">
|
<span class="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
@@ -261,8 +258,7 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" aria-label="External Links">
|
||||||
<label>External Links</label>
|
|
||||||
<div class="links-list">
|
<div class="links-list">
|
||||||
@for (link of editShow.links; track link.url; let i = $index) {
|
@for (link of editShow.links; track link.url; let i = $index) {
|
||||||
<div class="link-item">
|
<div class="link-item">
|
||||||
|
|||||||
@@ -19,14 +19,12 @@ import { environment } from '../../environments/environment';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthInterceptor implements HttpInterceptor {
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
private router = inject(Router);
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
|
||||||
private isRefreshing = false;
|
private isRefreshing = false;
|
||||||
private refreshTokenSubject = new BehaviorSubject<boolean | null>(null);
|
private refreshTokenSubject = new BehaviorSubject<boolean | null>(null);
|
||||||
|
|
||||||
constructor(
|
|
||||||
private router: Router,
|
|
||||||
private http: HttpClient
|
|
||||||
) {}
|
|
||||||
|
|
||||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
// Clone the request to add withCredentials
|
// Clone the request to add withCredentials
|
||||||
const authReq = request.clone({
|
const authReq = request.clone({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Art, CreateArtDto, UpdateArtDto } from '@library/shared-types';
|
import { Art, CreateArtDto, UpdateArtDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Art, CreateArtDto, UpdateArtDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ArtService {
|
export class ArtService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllArt(): Observable<Art[]> {
|
getAllArt(): Observable<Art[]> {
|
||||||
return this.api.get<Art[]>('/art');
|
return this.api.get<Art[]>('/art');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable, signal } from '@angular/core';
|
import { Injectable, signal, inject } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Observable, tap, catchError, switchMap, throwError, of } from 'rxjs';
|
import { Observable, tap, catchError, switchMap, throwError, of } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
@@ -16,16 +16,14 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
private api = inject(ApiService);
|
||||||
|
private router = inject(Router);
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
|
||||||
private currentUser = signal<User | null>(null);
|
private currentUser = signal<User | null>(null);
|
||||||
public readonly user = this.currentUser.asReadonly();
|
public readonly user = this.currentUser.asReadonly();
|
||||||
private refreshing = false;
|
private refreshing = false;
|
||||||
|
|
||||||
constructor(
|
|
||||||
private api: ApiService,
|
|
||||||
private router: Router,
|
|
||||||
private http: HttpClient
|
|
||||||
) {}
|
|
||||||
|
|
||||||
login(): void {
|
login(): void {
|
||||||
// Redirect to API login endpoint
|
// Redirect to API login endpoint
|
||||||
window.location.href = `${environment.apiUrl}/auth/login`;
|
window.location.href = `${environment.apiUrl}/auth/login`;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Book, CreateBookDto, UpdateBookDto } from '@library/shared-types';
|
import { Book, CreateBookDto, UpdateBookDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Book, CreateBookDto, UpdateBookDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class BooksService {
|
export class BooksService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllBooks(): Observable<Book[]> {
|
getAllBooks(): Observable<Book[]> {
|
||||||
return this.api.get<Book[]>('/books');
|
return this.api.get<Book[]>('/books');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Comment, CreateCommentDto } from '@library/shared-types';
|
import { Comment, CreateCommentDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Comment, CreateCommentDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CommentsService {
|
export class CommentsService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getCommentsForGame(gameId: string): Observable<Comment[]> {
|
getCommentsForGame(gameId: string): Observable<Comment[]> {
|
||||||
return this.api.get<Comment[]>(`/games/${gameId}/comments`);
|
return this.api.get<Comment[]>(`/games/${gameId}/comments`);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Game, CreateGameDto, UpdateGameDto } from '@library/shared-types';
|
import { Game, CreateGameDto, UpdateGameDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Game, CreateGameDto, UpdateGameDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class GamesService {
|
export class GamesService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllGames(): Observable<Game[]> {
|
getAllGames(): Observable<Game[]> {
|
||||||
return this.api.get<Game[]>('/games');
|
return this.api.get<Game[]>('/games');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Manga, CreateMangaDto, UpdateMangaDto } from '@library/shared-types';
|
import { Manga, CreateMangaDto, UpdateMangaDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Manga, CreateMangaDto, UpdateMangaDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MangaService {
|
export class MangaService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllManga(): Observable<Manga[]> {
|
getAllManga(): Observable<Manga[]> {
|
||||||
return this.api.get<Manga[]>('/manga');
|
return this.api.get<Manga[]>('/manga');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Music, CreateMusicDto, UpdateMusicDto } from '@library/shared-types';
|
import { Music, CreateMusicDto, UpdateMusicDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Music, CreateMusicDto, UpdateMusicDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MusicService {
|
export class MusicService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllMusic(): Observable<Music[]> {
|
getAllMusic(): Observable<Music[]> {
|
||||||
return this.api.get<Music[]>('/music');
|
return this.api.get<Music[]>('/music');
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Show, CreateShowDto, UpdateShowDto } from '@library/shared-types';
|
import { Show, CreateShowDto, UpdateShowDto } from '@library/shared-types';
|
||||||
@@ -13,7 +13,8 @@ import { Show, CreateShowDto, UpdateShowDto } from '@library/shared-types';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ShowsService {
|
export class ShowsService {
|
||||||
constructor(private api: ApiService) {}
|
private api = inject(ApiService);
|
||||||
|
|
||||||
|
|
||||||
getAllShows(): Observable<Show[]> {
|
getAllShows(): Observable<Show[]> {
|
||||||
return this.api.get<Show[]>('/shows');
|
return this.api.get<Show[]>('/shows');
|
||||||
|
|||||||
+11
-11
@@ -1,17 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
export * from "./lib/game.types";
|
export type * from "./lib/art.types";
|
||||||
export * from "./lib/book.types";
|
|
||||||
export * from "./lib/music.types";
|
|
||||||
export * from "./lib/art.types";
|
|
||||||
export * from "./lib/show.types";
|
|
||||||
export * from "./lib/manga.types";
|
|
||||||
export type * from "./lib/auth.types";
|
|
||||||
export * from "./lib/comment.types";
|
|
||||||
export * from "./lib/audit.types";
|
export * from "./lib/audit.types";
|
||||||
|
export type * from "./lib/auth.types";
|
||||||
|
export * from "./lib/book.types";
|
||||||
|
export type * from "./lib/comment.types";
|
||||||
|
export type * from "./lib/common.types";
|
||||||
|
export * from "./lib/game.types";
|
||||||
|
export type * from "./lib/like.types";
|
||||||
|
export * from "./lib/manga.types";
|
||||||
|
export * from "./lib/music.types";
|
||||||
|
export * from "./lib/show.types";
|
||||||
export * from "./lib/suggestion.types";
|
export * from "./lib/suggestion.types";
|
||||||
export * from "./lib/common.types";
|
|
||||||
export * from "./lib/like.types";
|
|
||||||
@@ -1,30 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
export interface Art {
|
interface Art {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
dateAdded: Date;
|
dateAdded: Date;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateArtDto {
|
interface CreateArtDto {
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateArtDto extends Partial<CreateArtDto> {}
|
type UpdateArtDto = Partial<CreateArtDto>;
|
||||||
|
|
||||||
|
export type { Art, CreateArtDto, UpdateArtDto };
|
||||||
|
|||||||
@@ -1,36 +1,42 @@
|
|||||||
export enum AuditAction {
|
/**
|
||||||
LOGIN = "LOGIN",
|
* @copyright NHCarrigan
|
||||||
LOGOUT = "LOGOUT",
|
* @license Naomi's Public License
|
||||||
LOGIN_FAILED = "LOGIN_FAILED",
|
* @author Naomi Carrigan
|
||||||
COMMENT_CREATE = "COMMENT_CREATE",
|
*/
|
||||||
COMMENT_UPDATE = "COMMENT_UPDATE",
|
|
||||||
COMMENT_DELETE = "COMMENT_DELETE",
|
enum AuditAction {
|
||||||
ENTRY_CREATE = "ENTRY_CREATE",
|
login = "LOGIN",
|
||||||
ENTRY_UPDATE = "ENTRY_UPDATE",
|
logout = "LOGOUT",
|
||||||
ENTRY_DELETE = "ENTRY_DELETE",
|
loginFailed = "LOGIN_FAILED",
|
||||||
LIKE = "LIKE",
|
commentCreate = "COMMENT_CREATE",
|
||||||
UNLIKE = "UNLIKE",
|
commentUpdate = "COMMENT_UPDATE",
|
||||||
USER_BAN = "USER_BAN",
|
commentDelete = "COMMENT_DELETE",
|
||||||
USER_UNBAN = "USER_UNBAN",
|
entryCreate = "ENTRY_CREATE",
|
||||||
RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED",
|
entryUpdate = "ENTRY_UPDATE",
|
||||||
CSRF_VALIDATION_FAILED = "CSRF_VALIDATION_FAILED",
|
entryDelete = "ENTRY_DELETE",
|
||||||
UNAUTHORIZED_ACCESS = "UNAUTHORIZED_ACCESS",
|
like = "LIKE",
|
||||||
|
unlike = "UNLIKE",
|
||||||
|
userBan = "USER_BAN",
|
||||||
|
userUnban = "USER_UNBAN",
|
||||||
|
rateLimitExceeded = "RATE_LIMIT_EXCEEDED",
|
||||||
|
csrfValidationFailed = "CSRF_VALIDATION_FAILED",
|
||||||
|
unauthorizedAccess = "UNAUTHORIZED_ACCESS",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AuditCategory {
|
enum AuditCategory {
|
||||||
AUTH = "AUTH",
|
auth = "AUTH",
|
||||||
CONTENT = "CONTENT",
|
content = "CONTENT",
|
||||||
ADMIN = "ADMIN",
|
admin = "ADMIN",
|
||||||
SECURITY = "SECURITY",
|
security = "SECURITY",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditLogUser {
|
interface AuditLogUser {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditLog {
|
interface AuditLog {
|
||||||
id: string;
|
id: string;
|
||||||
action: AuditAction;
|
action: AuditAction;
|
||||||
category: AuditCategory;
|
category: AuditCategory;
|
||||||
@@ -46,7 +52,7 @@ export interface AuditLog {
|
|||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuditLogFilters {
|
interface AuditLogFilters {
|
||||||
action?: AuditAction;
|
action?: AuditAction;
|
||||||
category?: AuditCategory;
|
category?: AuditCategory;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
@@ -56,3 +62,11 @@ export interface AuditLogFilters {
|
|||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
AuditAction,
|
||||||
|
AuditCategory,
|
||||||
|
type AuditLog,
|
||||||
|
type AuditLogFilters,
|
||||||
|
type AuditLogUser,
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -18,7 +18,8 @@ export interface User {
|
|||||||
isStaff: boolean;
|
isStaff: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JwtPayload {
|
interface JwtPayload {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User id.
|
* User id.
|
||||||
*/
|
*/
|
||||||
@@ -30,7 +31,9 @@ export interface JwtPayload {
|
|||||||
exp?: number;
|
exp?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthResponse {
|
interface AuthResponse {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type { AuthResponse, JwtPayload, User };
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @copyright 2026 NHCarrigan
|
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum BookStatus {
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
|
enum BookStatus {
|
||||||
reading = "READING",
|
reading = "READING",
|
||||||
finished = "FINISHED",
|
finished = "FINISHED",
|
||||||
toRead = "TO_READ",
|
toRead = "TO_READ",
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
interface Book {
|
||||||
|
|
||||||
export interface Book {
|
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
@@ -23,13 +23,13 @@ export interface Book {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateBookDto {
|
interface CreateBookDto {
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
isbn?: string;
|
isbn?: string;
|
||||||
@@ -37,10 +37,12 @@ export interface CreateBookDto {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateBookDto extends Partial<CreateBookDto> {
|
interface UpdateBookDto extends Partial<CreateBookDto> {
|
||||||
dateFinished?: Date;
|
dateFinished?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { type Book, BookStatus, type CreateBookDto, type UpdateBookDto };
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface CommentUser {
|
interface CommentUser {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
@@ -14,7 +14,7 @@ export interface CommentUser {
|
|||||||
isStaff?: boolean;
|
isStaff?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Comment {
|
interface Comment {
|
||||||
id: string;
|
id: string;
|
||||||
content: string;
|
content: string;
|
||||||
rawContent?: string;
|
rawContent?: string;
|
||||||
@@ -30,6 +30,8 @@ export interface Comment {
|
|||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateCommentDto {
|
interface CreateCommentDto {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type { Comment, CommentUser, CreateCommentDto };
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
export interface Link {
|
export interface Link {
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @copyright 2026 NHCarrigan
|
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum GameStatus {
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
|
enum GameStatus {
|
||||||
playing = "PLAYING",
|
playing = "PLAYING",
|
||||||
completed = "COMPLETED",
|
completed = "COMPLETED",
|
||||||
backlog = "BACKLOG",
|
backlog = "BACKLOG",
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
interface Game {
|
||||||
|
|
||||||
export interface Game {
|
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
platform?: string;
|
platform?: string;
|
||||||
@@ -22,23 +22,25 @@ export interface Game {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateGameDto {
|
interface CreateGameDto {
|
||||||
title: string;
|
title: string;
|
||||||
platform?: string;
|
platform?: string;
|
||||||
status: GameStatus;
|
status: GameStatus;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateGameDto extends Partial<CreateGameDto> {
|
interface UpdateGameDto extends Partial<CreateGameDto> {
|
||||||
dateCompleted?: Date;
|
dateCompleted?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { type CreateGameDto, type Game, GameStatus, type UpdateGameDto };
|
||||||
|
|||||||
@@ -1,32 +1,38 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface Like {
|
interface Like {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
entityType: 'book' | 'game' | 'show' | 'manga' | 'music' | 'art';
|
entityType: "book" | "game" | "show" | "manga" | "music" | "art";
|
||||||
entityId: string;
|
entityId: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateLikeDto = Pick<Like, 'entityType' | 'entityId'>;
|
type CreateLikeDto = Pick<Like, "entityType" | "entityId">;
|
||||||
|
|
||||||
export interface LikeCountDto {
|
interface LikeCountDto {
|
||||||
entityId: string;
|
entityId: string;
|
||||||
entityType: string;
|
entityType: string;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LikedItemDto {
|
interface LikedItemDto {
|
||||||
like: Like;
|
like: Like;
|
||||||
item: any; // This will be the actual entity (Book, Game, etc.)
|
|
||||||
|
/**
|
||||||
|
* This will be the actual entity (Book, Game, etc.).
|
||||||
|
*/
|
||||||
|
item: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response types
|
// Response types
|
||||||
export interface LikeResponse {
|
interface LikeResponse {
|
||||||
liked: boolean;
|
liked: boolean;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type { CreateLikeDto, Like, LikeCountDto, LikedItemDto, LikeResponse };
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum MangaStatus {
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
|
enum MangaStatus {
|
||||||
reading = "READING",
|
reading = "READING",
|
||||||
completed = "COMPLETED",
|
completed = "COMPLETED",
|
||||||
wantToRead = "WANT_TO_READ",
|
wantToRead = "WANT_TO_READ",
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
interface Manga {
|
||||||
|
|
||||||
export interface Manga {
|
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
@@ -22,23 +22,26 @@ export interface Manga {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateMangaDto {
|
interface CreateMangaDto {
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
status: MangaStatus;
|
status: MangaStatus;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateMangaDto extends Partial<CreateMangaDto> {
|
interface UpdateMangaDto extends Partial<CreateMangaDto> {
|
||||||
dateCompleted?: Date;
|
dateCompleted?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { MangaStatus };
|
||||||
|
export type { CreateMangaDto, Manga, UpdateMangaDto };
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @copyright 2026 NHCarrigan
|
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum MusicType {
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
|
enum MusicType {
|
||||||
album = "ALBUM",
|
album = "ALBUM",
|
||||||
single = "SINGLE",
|
single = "SINGLE",
|
||||||
ep = "EP",
|
ep = "EP",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MusicStatus {
|
enum MusicStatus {
|
||||||
listening = "LISTENING",
|
listening = "LISTENING",
|
||||||
completed = "COMPLETED",
|
completed = "COMPLETED",
|
||||||
wantToListen = "WANT_TO_LISTEN",
|
wantToListen = "WANT_TO_LISTEN",
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
interface Music {
|
||||||
|
|
||||||
export interface Music {
|
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
@@ -29,13 +29,13 @@ export interface Music {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverArt?: string;
|
coverArt?: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateMusicDto {
|
interface CreateMusicDto {
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
type: MusicType;
|
type: MusicType;
|
||||||
@@ -43,10 +43,13 @@ export interface CreateMusicDto {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverArt?: string;
|
coverArt?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateMusicDto extends Partial<CreateMusicDto> {
|
interface UpdateMusicDto extends Partial<CreateMusicDto> {
|
||||||
dateCompleted?: Date;
|
dateCompleted?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { MusicStatus, MusicType };
|
||||||
|
export type { CreateMusicDto, Music, UpdateMusicDto };
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
/**
|
/**
|
||||||
* @copyright 2026 NHCarrigan
|
* @copyright NHCarrigan
|
||||||
* @license Naomi's Public License
|
* @license Naomi's Public License
|
||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum ShowType {
|
import type { Link } from "./common.types";
|
||||||
|
|
||||||
|
enum ShowType {
|
||||||
tvSeries = "TV_SERIES",
|
tvSeries = "TV_SERIES",
|
||||||
anime = "ANIME",
|
anime = "ANIME",
|
||||||
film = "FILM",
|
film = "FILM",
|
||||||
documentary = "DOCUMENTARY",
|
documentary = "DOCUMENTARY",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ShowStatus {
|
enum ShowStatus {
|
||||||
watching = "WATCHING",
|
watching = "WATCHING",
|
||||||
completed = "COMPLETED",
|
completed = "COMPLETED",
|
||||||
wantToWatch = "WANT_TO_WATCH",
|
wantToWatch = "WANT_TO_WATCH",
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Link } from "./common.types";
|
interface Show {
|
||||||
|
|
||||||
export interface Show {
|
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: ShowType;
|
type: ShowType;
|
||||||
@@ -29,23 +29,26 @@ export interface Show {
|
|||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags: string[];
|
tags: Array<string>;
|
||||||
links: Link[];
|
links: Array<Link>;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateShowDto {
|
interface CreateShowDto {
|
||||||
title: string;
|
title: string;
|
||||||
type: ShowType;
|
type: ShowType;
|
||||||
status: ShowStatus;
|
status: ShowStatus;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Link[];
|
links?: Array<Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateShowDto extends Partial<CreateShowDto> {
|
interface UpdateShowDto extends Partial<CreateShowDto> {
|
||||||
dateCompleted?: Date;
|
dateCompleted?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { ShowStatus, ShowType };
|
||||||
|
export type { CreateShowDto, Show, UpdateShowDto };
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
import type { CreateGameDto } from "./game.types";
|
/**
|
||||||
import type { CreateBookDto } from "./book.types";
|
* @copyright NHCarrigan
|
||||||
import type { CreateMusicDto } from "./music.types";
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
import type { CreateArtDto } from "./art.types";
|
import type { CreateArtDto } from "./art.types";
|
||||||
import type { CreateShowDto } from "./show.types";
|
import type { CreateBookDto } from "./book.types";
|
||||||
|
import type { CreateGameDto } from "./game.types";
|
||||||
import type { CreateMangaDto } from "./manga.types";
|
import type { CreateMangaDto } from "./manga.types";
|
||||||
|
import type { CreateMusicDto } from "./music.types";
|
||||||
|
import type { CreateShowDto } from "./show.types";
|
||||||
|
|
||||||
export enum SuggestionEntity {
|
enum SuggestionEntity {
|
||||||
GAME = "GAME",
|
game = "GAME",
|
||||||
BOOK = "BOOK",
|
book = "BOOK",
|
||||||
MUSIC = "MUSIC",
|
music = "MUSIC",
|
||||||
ART = "ART",
|
art = "ART",
|
||||||
SHOW = "SHOW",
|
show = "SHOW",
|
||||||
MANGA = "MANGA",
|
manga = "MANGA",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SuggestionStatus {
|
enum SuggestionStatus {
|
||||||
UNREVIEWED = "UNREVIEWED",
|
unreviewed = "UNREVIEWED",
|
||||||
ACCEPTED = "ACCEPTED",
|
accepted = "ACCEPTED",
|
||||||
DECLINED = "DECLINED",
|
declined = "DECLINED",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SuggestionUser {
|
interface SuggestionUser {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
@@ -30,7 +36,7 @@ export interface SuggestionUser {
|
|||||||
isStaff: boolean;
|
isStaff: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Suggestion {
|
interface Suggestion {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
user: SuggestionUser;
|
user: SuggestionUser;
|
||||||
@@ -48,16 +54,16 @@ export interface Suggestion {
|
|||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateGameSuggestionDto {
|
interface CreateGameSuggestionDto {
|
||||||
entityType: SuggestionEntity.GAME;
|
entityType: SuggestionEntity.game;
|
||||||
title: string;
|
title: string;
|
||||||
platform?: string;
|
platform?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateBookSuggestionDto {
|
interface CreateBookSuggestionDto {
|
||||||
entityType: SuggestionEntity.BOOK;
|
entityType: SuggestionEntity.book;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
isbn?: string;
|
isbn?: string;
|
||||||
@@ -65,8 +71,8 @@ export interface CreateBookSuggestionDto {
|
|||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateMusicSuggestionDto {
|
interface CreateMusicSuggestionDto {
|
||||||
entityType: SuggestionEntity.MUSIC;
|
entityType: SuggestionEntity.music;
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -74,31 +80,31 @@ export interface CreateMusicSuggestionDto {
|
|||||||
coverArt?: string;
|
coverArt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateArtSuggestionDto {
|
interface CreateArtSuggestionDto {
|
||||||
entityType: SuggestionEntity.ART;
|
entityType: SuggestionEntity.art;
|
||||||
title: string;
|
title: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateShowSuggestionDto {
|
interface CreateShowSuggestionDto {
|
||||||
entityType: SuggestionEntity.SHOW;
|
entityType: SuggestionEntity.show;
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
type: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateMangaSuggestionDto {
|
interface CreateMangaSuggestionDto {
|
||||||
entityType: SuggestionEntity.MANGA;
|
entityType: SuggestionEntity.manga;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateSuggestionDto =
|
type CreateSuggestionDto =
|
||||||
| CreateGameSuggestionDto
|
| CreateGameSuggestionDto
|
||||||
| CreateBookSuggestionDto
|
| CreateBookSuggestionDto
|
||||||
| CreateMusicSuggestionDto
|
| CreateMusicSuggestionDto
|
||||||
@@ -106,11 +112,11 @@ export type CreateSuggestionDto =
|
|||||||
| CreateShowSuggestionDto
|
| CreateShowSuggestionDto
|
||||||
| CreateMangaSuggestionDto;
|
| CreateMangaSuggestionDto;
|
||||||
|
|
||||||
export interface DeclineSuggestionDto {
|
interface DeclineSuggestionDto {
|
||||||
reason?: string;
|
reason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AcceptWithEditsDto {
|
interface AcceptWithEditsDto {
|
||||||
title?: string;
|
title?: string;
|
||||||
platform?: string;
|
platform?: string;
|
||||||
author?: string;
|
author?: string;
|
||||||
@@ -122,6 +128,21 @@ export interface AcceptWithEditsDto {
|
|||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
coverArt?: string;
|
coverArt?: string;
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
tags?: string[];
|
tags?: Array<string>;
|
||||||
links?: Array<{ label: string; url: string }>;
|
links?: Array<{ label: string; url: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { SuggestionEntity, SuggestionStatus };
|
||||||
|
export type {
|
||||||
|
AcceptWithEditsDto,
|
||||||
|
CreateArtSuggestionDto,
|
||||||
|
CreateBookSuggestionDto,
|
||||||
|
CreateGameSuggestionDto,
|
||||||
|
CreateMangaSuggestionDto,
|
||||||
|
CreateMusicSuggestionDto,
|
||||||
|
CreateShowSuggestionDto,
|
||||||
|
CreateSuggestionDto,
|
||||||
|
DeclineSuggestionDto,
|
||||||
|
Suggestion,
|
||||||
|
SuggestionUser,
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user