Skip to content

Commit

Permalink
Merge pull request #162 from UAlberta-CMPUT401/sprint-5
Browse files Browse the repository at this point in the history
Sprint 5
  • Loading branch information
monkey567567 authored Dec 3, 2024
2 parents 54a5381 + 3ca748e commit 0352557
Show file tree
Hide file tree
Showing 96 changed files with 3,846 additions and 1,020 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ venv

node_modules/

backend/firebase-credentials.json
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM node:20 as frontend-build
WORKDIR /usr/app/frontend
COPY frontend/package.json ./
RUN npm install
COPY frontend/ ./
RUN npm run build
FROM node:20
WORKDIR /usr/app/backend
COPY backend/package.json ./
RUN npm install
COPY backend/ ./
RUN npm run build
COPY --from=frontend-build /usr/app/frontend/dist ./dist/src/dist/
ENV NODE_ENV=production
ENV DOTENV_CONFIG_PATH=/usr/app/backend/.env.production.local
CMD ["node", "-r", "dotenv/config", "dist/src/server.js"]
7 changes: 6 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# port to run service on
# port to run backend service on
PORT=3000

# the lowest priority of logs that is displayed (usually `debug` for development and `warn` in production)
# log levels - error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
LOG_LEVEL=debug

# database host name (usually `localhost` for development and `db` in production)
Expand All @@ -21,3 +22,7 @@ DB_PASSWORD=password

# path to firebase credentials json file
GOOGLE_APPLICATION_CREDENTIALS=path/to/bleeding-heart-art-space-firebase-adminsdk.json

# sengrid email api
SENDGRID_API_KEY=SG.aBcDeFgHiJkLmNoPqRsTuVwXyZ
SENDGRID_FROM_EMAIL=[email protected]
40 changes: 39 additions & 1 deletion backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@jest/globals": "^29.7.0",
"@nestjs/common": "^10.4.6",
"@sendgrid/mail": "^8.1.4",
"axios": "^1.7.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
Expand Down Expand Up @@ -41,8 +42,8 @@
"@types/supertest": "^6.0.2",
"@types/swagger-ui-express": "^4.1.6",
"@types/yamljs": "^0.2.34",
"date-fns": "^3.0.0",
"cross-env": "^7.0.3",
"date-fns": "^3.0.0",
"jest": "^29.7.0",
"knex": "^3.1.0",
"nodemon": "^3.1.7",
Expand Down
19 changes: 14 additions & 5 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ export class App {

this.initializeFirebaseSDK();
this.initializeMiddlewares();

this.initializeRoutes(routes);
// serve react in production
if (NODE_ENV === 'production') {
this.app.use(express.static(path.join(__dirname, 'dist')))
this.app.get('*', (_req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
}

this.initializeErrorHandling(); // Add error-handling middleware
this.initializeCronJobs();
}
Expand All @@ -47,7 +56,7 @@ export class App {
this.app.use(loggerMiddleware);
this.app.use(express.json());
if (NODE_ENV === 'development') {
this.app.use(cors())
this.app.use(cors());
}
const swaggerDocument = YAML.load(this.apiSpecPath);
this.app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
Expand All @@ -62,14 +71,14 @@ export class App {
}

private initializeCronJobs() {
console.log('Starting cron job for aut-checkout every 5 minutes');
logger.info('Starting cron job for aut-checkout every 5 minutes');
cron.schedule('*/1 * * * *', async () => {
console.log('Running auto-checkout cron job...');
logger.info('Running auto-checkout cron job...');
try{
await this.shiftSignupService.autoCheckOut();
console.log('Auto-checkout cron job completed');
logger.info('Auto-checkout cron job completed');
} catch (error){
console.error('Error running auto-checkout cron job', error);
logger.error('Error running auto-checkout cron job', error);
}
})
}
Expand Down
4 changes: 2 additions & 2 deletions backend/src/common/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DB_NAME, DB_HOST, DB_USER, DB_PORT, DB_PASSWORD } from "@config/env";
import { promises as fs } from 'fs'
import * as path from 'path'
import { logger } from "@utils/logger";
import { RolesTable, UsersTable } from "@features/users/users.model";
import { RolesTable, UserEventRolesTable, UsersTable } from "@features/users/users.model";
import { VolunteerRolesTable } from "@/features/volunteerRoles/volunteerRoles.model";
import { EventsTable } from "@/features/events/events.model";
import { VolunteerShiftsTable } from "@/features/volunteerShifts/volunteerShifts.model";
Expand All @@ -22,7 +22,7 @@ export interface Database {
shift_signup: ShiftSignupTable
event_requests: EventRequestsTable
notifications: NotificationsTable

user_event_roles: UserEventRolesTable
}

// make sure that postgres 'numeric' types are numbers, not strings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export async function up(db: Kysely<any>): Promise<void> {
.notNull()
.references('users.id')
.onDelete('cascade'))
.addColumn('status', 'integer', col => col.notNull()) // 0 denied, 1 approved, 2 pending
.execute();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Kysely } from 'kysely'

export async function up(db: Kysely<any>): Promise<void> {

// Used to give permissions to users for a specific event (giving artist admin access for an event)
await db.schema
.createTable('user_event_roles')
.addColumn('user_id', 'integer', (col) => col
.references('users.id').onDelete('cascade')
.notNull()
)
.addColumn('event_id', 'integer', (col) => col
.references('events.id').onDelete('cascade')
.notNull()
)
.addColumn('role_id', 'integer', (col) => col
.references('roles.id').onDelete('cascade')
.notNull()
)
.addPrimaryKeyConstraint('user_event_roles_pkey', ['user_id', 'event_id', 'role_id'])
.execute();

}

export async function down(db: Kysely<any>): Promise<void> {

await db.schema.dropTable('user_event_roles').execute();

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ export const EventRequestsService = jest.fn().mockImplementation(() => ({
start: new Date(eventData.start),
end: new Date(eventData.end),
requester_id: 1,
status: 2,
}
return ret;
}),
getAllEventRequests: jest.fn().mockResolvedValue([
getPendingEventRequests: jest.fn().mockResolvedValue([
{
id: 1,
start: new Date('2024-10-15T09:00:00.000Z'),
Expand All @@ -34,6 +35,8 @@ export const EventRequestsService = jest.fn().mockImplementation(() => ({
uid: 'test_uid',
first_name: 'first',
last_name: 'last',
email: '[email protected]',
status: 2,
},
]),
getEventRequestById: jest.fn().mockImplementation(async (
Expand All @@ -50,6 +53,7 @@ export const EventRequestsService = jest.fn().mockImplementation(() => ({
address: 'Test Addr',
title: 'Test Title',
requester_id: 1,
status: 2,
}
return ret;
}),
Expand Down
27 changes: 25 additions & 2 deletions backend/src/features/eventRequests/eventRequests.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,22 @@ export class EventRequestsController {
* @route GET /api/event-requests
* @access Public (Adjust as needed)
*/
public getAllEventRequests = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
public getPendingEventRequests = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const eventRequests = await this.eventRequestsService.getAllEventRequests();
const eventRequests = await this.eventRequestsService.getPendingEventRequests();
res.json(eventRequests);
} catch (error) {
next(error);
}
}

public getUserEventRequests = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
if (!isAuthenticated(req)) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
const eventRequests = await this.eventRequestsService.getUserEventRequests(req.auth.uid);
res.json(eventRequests);
} catch (error) {
next(error);
Expand Down Expand Up @@ -75,6 +88,16 @@ export class EventRequestsController {
}
}

public denyEventRequest = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const eventRequestId = parseInt(req.params.id, 10);
await this.eventRequestsService.denyEventRequest(eventRequestId);
res.status(200).json({ message: 'Event request denied' });
} catch (error) {
next(error);
}
}

/**
* Update an existing event request
* @route PUT /api/event-requests/:id
Expand Down
1 change: 1 addition & 0 deletions backend/src/features/eventRequests/eventRequests.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface EventRequestsTable {
address: string; // Address of the event
title: string; // Title of the event
requester_id: number; // ID of the requester
status: number; // 0 denied, 1 approved, 2 pending
}

export type EventRequest = Selectable<EventRequestsTable>; // Type for selecting (reading) event records
Expand Down
6 changes: 4 additions & 2 deletions backend/src/features/eventRequests/eventRequests.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ export class EventRequestsRoute implements Routes {

private initializeRoutes() {
this.router.post(`${this.path}`, authMiddleware, this.eventsController.createEventRequest);
this.router.get(`${this.path}`, authMiddleware, this.eventsController.getAllEventRequests);
this.router.get(`${this.path}/:id`, authMiddleware, this.eventsController.getEventRequestById);
this.router.get(`${this.path}`, authMiddleware, isAdminMiddleware, this.eventsController.getPendingEventRequests);
this.router.get(`${this.path}/:id`, authMiddleware, isAdminMiddleware, this.eventsController.getEventRequestById);
this.router.get(`${this.path}_user`, authMiddleware, this.eventsController.getUserEventRequests);
this.router.delete(`${this.path}/:id`, authMiddleware, this.eventsController.deleteEventRequest);
this.router.put(`${this.path}/:id`, authMiddleware, this.eventsController.updateEventRequest);
this.router.post(`${this.path}/:id/confirm`, authMiddleware, isAdminMiddleware, this.eventsController.confirmEventRequest);
this.router.post(`${this.path}/:id/deny`, authMiddleware, isAdminMiddleware, this.eventsController.denyEventRequest);
}
}
Loading

0 comments on commit 0352557

Please sign in to comment.