Skip to content

Commit eba6a5e

Browse files
committed
chore: add swagger image in readme and add env example file
1 parent eb4990c commit eba6a5e

7 files changed

Lines changed: 68 additions & 3 deletions

File tree

.env.example

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1-
DATABASE_URL=""
2-
SESSION_SECRET=""
3-
REPL_ID=""
1+
# databases
2+
DATABASE_URL=
3+
4+
# URIs
5+
SESSION_SECRET=
6+
API_PROD_API_URL=
7+
8+
9+
# email
10+
GMAIL_USER=
11+
GMAIL_APP_PASSWORD=
12+
GMAIL_APP_SUPPORT=
13+
14+
# cloudinary bucket
15+
CLOUDINARY_CLOUD_NAME=
16+
CLOUDINARY_API_KEY=
17+
CLOUDINARY_API_SECRET=

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
[![Node.js](https://img.shields.io/badge/Node.js-20%2B%20%7C%2022-green.svg)](https://nodejs.org/)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
77

8+
![Library Management REST API](public/images/lib_management_rest_api.png)
9+
810
A production-ready RESTful API for library content management—collections, stories, media, events, and user operations. Built with modern TypeScript, Express.js, and PostgreSQL. Designed for scalability, maintainability, and secure multi-tenant operations.
911

1012
**Repository:** [github.com/frckbrice/library-management-REST-API](https://github.com/frckbrice/library-management-REST-API)

config/database/storage.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface IStorage {
5353
getTotalLibraries(options?: { approved?: boolean; featured?: boolean; type?: string }): Promise<number>;
5454
createLibrary(library: InsertLibrary): Promise<Library>;
5555
updateLibrary(id: string, data: Partial<Library>): Promise<Library | undefined>;
56+
deleteLibrary(id: string): Promise<boolean>;
5657

5758
// Stories
5859
getStory(id: string): Promise<Story | undefined>;
@@ -1042,6 +1043,16 @@ export class MemStorage implements IStorage {
10421043
return updatedLibrary;
10431044
}
10441045

1046+
async deleteLibrary(id: string): Promise<boolean> {
1047+
const library = await this.getLibrary(id);
1048+
if (!library) return false;
1049+
for (const [, user] of this.users) {
1050+
if (user.libraryId === id) (user as any).libraryId = null;
1051+
}
1052+
this.librarys.delete(id);
1053+
return true;
1054+
}
1055+
10451056
// Story methods
10461057
async getStory(id: string): Promise<Story | undefined> {
10471058
return this.stories.get(id);

config/swagger.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ export const openApiDocument = {
223223
'500': errorResponse('Failed to upload image'),
224224
},
225225
},
226+
delete: {
227+
tags: ['Libraries'],
228+
summary: 'Delete library',
229+
description: 'Superadmin only. Permanently deletes a library. Unassigns linked users; stories, events, media, and contact messages for this library are removed (cascade).',
230+
security: [{ cookieAuth: [] }],
231+
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' } }],
232+
responses: {
233+
'204': { description: 'Library deleted successfully' },
234+
'401': errorResponse('Authentication required', 'AUTHENTICATION_ERROR', 'Authentication required'),
235+
'403': errorResponse('Insufficient permissions', 'AUTHORIZATION_ERROR', 'Insufficient permissions'),
236+
'404': errorResponse('Library not found', 'NOT_FOUND', 'Library not found'),
237+
'500': errorResponse('Internal server error', 'INTERNAL_ERROR', 'Internal server error'),
238+
},
239+
},
226240
},
227241
[`${apiPath}/stories`]: {
228242
get: {
527 KB
Loading

src/routes/libraries.routes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ export function registerLibrariesRoutes(app: Express, global_path: string) {
114114
});
115115
}));
116116

117+
// Delete library (superadmin only)
118+
app.delete(`${global_path}/libraries/:id`, requireSuperAdmin, apiHandler(async (req, res) => {
119+
const libraryId = req.params.id;
120+
const existing = await drizzleService.getLibrary(libraryId);
121+
if (!existing) throw new NotFoundError('Library');
122+
const deleted = await drizzleService.deleteLibrary(libraryId);
123+
if (!deleted) return res.status(500).json({ error: 'Failed to delete library' });
124+
return res.status(204).send();
125+
}));
126+
117127
// Librarys endpoints
118128
app.get(`${global_path}/libraries`, async (req, res) => {
119129
try {

src/services/drizzle-services.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,20 @@ export class DatabaseStorage implements IStorage {
237237
}
238238
}
239239

240+
/** Deletes a library by ID. Unassigns users linked to this library, then deletes (cascade removes stories, events, media, contact messages). */
241+
async deleteLibrary(id: string): Promise<boolean> {
242+
try {
243+
const [existing] = await db.select().from(libraries).where(eq(libraries.id, id)).limit(1);
244+
if (!existing) return false;
245+
await db.update(users).set({ libraryId: null }).where(eq(users.libraryId, id));
246+
const result = await db.delete(libraries).where(eq(libraries.id, id)).returning();
247+
return result.length > 0;
248+
} catch (error) {
249+
console.error('Error in deleteLibrary:', error);
250+
return false;
251+
}
252+
}
253+
240254
// Stories
241255
/** Fetches a story by ID. */
242256
async getStory(id: string): Promise<Story | undefined> {

0 commit comments

Comments
 (0)