diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml new file mode 100644 index 0000000..47d1803 --- /dev/null +++ b/.github/workflows/develop.yml @@ -0,0 +1,73 @@ +name: Deploy to Railway + +on: + push: + branches: + - develop + paths-ignore: + - 'README.md' + +env: + AZURE_WEBAPP_NAME: bookify + NODE_VERSION: '20.x' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout Source + uses: actions/checkout@v3 + + - name: Setup Node.js version + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Create .env file + run: | + echo "SQLITE_DB=${{ secrets.SQLITE_DB }}" >> .env + echo "TYPEORM_CLI=${{ secrets.TYPEORM_CLI }}" >> .env + echo "APP_PORT=${{ secrets.APP_PORT }}" >> .env + echo "NODE_ENV=${{ secrets.NODE_ENV }}" >> .env + echo "OAUTH_CLIENT_SECRET=${{ secrets.OAUTH_CLIENT_SECRET }}" >> .env + echo "OAUTH_CLIENT_ID=${{ secrets.OAUTH_CLIENT_ID }}" >> .env + echo "OAUTH_REDIRECT_URL=${{ secrets.OAUTH_REDIRECT_URL }}" >> .env + echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> .env + echo "${{ secrets.ROOMS }}" > ./src/config/rooms.ts + + - name: Install Dependencies + run: npm install + + - name: Run database migrations + run: npm run migration:run + + - name: Build project + run: npm run build + + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v4 + with: + name: node-app + path: . + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: 'production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v4 + with: + name: node-app + + - name: Install Railway + run: npm i -g @railway/cli + + - name: Deploy + run: railway up --service bookify + env: + RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5bb83d4..b1d9d63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,13 +37,14 @@ jobs: echo "${{ secrets.ROOMS }}" > ./src/config/rooms.ts - name: Install Dependencies - run: | - npm install - npm run build --if-present + run: npm install - name: Run database migrations run: npm run migration:run + - name: Build project + run: npm run build + - name: Upload artifact for deployment job uses: actions/upload-artifact@v4 with: diff --git a/client/script.js b/client/script.js index 3209204..b6931d3 100644 --- a/client/script.js +++ b/client/script.js @@ -4,7 +4,7 @@ let floor = 1; const CLIENT_ID = '1043931677993-j15eelb1golb8544ehi2meeru35q3fo4.apps.googleusercontent.com'; const REDIRECT_URI = window.location.origin; -const BACKEND_ENDPOINT = 'http://localhost:3000'; +const BACKEND_ENDPOINT = REDIRECT_URI; async function openPage(pageName, elmnt) { var i, tabcontent, tablinks; @@ -119,12 +119,17 @@ async function bookRoom() { const startTimeSelect = document.getElementById('startTime'); const startTime = startTimeSelect.value; + const date = new Date(Date.now()).toISOString().split('T')[0]; + const formattedStartTime = convertToRFC3339(date, startTime); + + console.log('formattedStartTime', formattedStartTime); + const duration = document.getElementById('duration').textContent; const seats = document.getElementById('seat_text').textContent; const floor = document.getElementById('floor_text').textContent; const res = await makeRequest('/room', 'POST', { - startTime, + startTime: formattedStartTime, duration: parseInt(duration), seats: parseInt(seats), floor: parseInt(floor), @@ -135,7 +140,7 @@ async function bookRoom() { return; } - createRoomAlert(res.room, res.start, res.end, res.summary, 'info'); + createRoomAlert(res.room, convertToLocaleTime(res.start), convertToLocaleTime(res.end), res.summary, 'info'); } async function makeRequest(path, method, body) { @@ -340,7 +345,7 @@ const populateEvents = async () => {
- ${event.start} - ${event.end} + ${convertToLocaleTime(event.start)} - ${convertToLocaleTime(event.end)}
@@ -381,3 +386,30 @@ async function removeEvent(id) { eventElement.remove(); } } + +function convertToRFC3339(dateString, timeString, timeZoneOffset = '+06:00') { + const date = new Date(`${dateString} ${timeString}`); + + const [offsetSign, offsetHours, offsetMinutes] = timeZoneOffset.match(/([+-])(\d{2}):(\d{2})/).slice(1); + + const offsetInMinutes = (parseInt(offsetHours) * 60 + parseInt(offsetMinutes)) * (offsetSign === '+' ? 1 : -1); + date.setMinutes(date.getMinutes() + offsetInMinutes); + + const isoString = date.toISOString(); + const [isoDate, isoTime] = isoString.split('T'); + + // Return the formatted date and time in RFC 3339 format + return `${isoDate}T${isoTime.split('.')[0]}${timeZoneOffset}`; +} + +function convertToLocaleTime(dateStr) { + const date = new Date(dateStr); + + const options = { + hour: '2-digit', + minute: '2-digit', + hour12: true, + }; + + return date.toLocaleTimeString('en-US', options); +} diff --git a/src/calender/calender.controller.ts b/src/calender/calender.controller.ts index 7d4c953..661bcca 100644 --- a/src/calender/calender.controller.ts +++ b/src/calender/calender.controller.ts @@ -4,7 +4,6 @@ import { CalenderService } from './calender.service'; import { AuthGuard } from '../auth/auth.guard'; import { _OAuth2Client } from '../auth/decorators'; import { EventResponse, RoomResponse } from './dto'; -import { convertToRFC3339 } from './util/calender.util'; import { DeleteResponse } from './dto/delete.response'; @Controller() @@ -29,16 +28,12 @@ export class CalenderController { @Body('floor') floor?: number, @Body('attendees') attendees?: string[], ): Promise { - // start time - const date = new Date(Date.now()).toISOString().split('T')[0]; - const formattedStartTime = convertToRFC3339(date, startTime); - // end time - const startDate = new Date(formattedStartTime); + const startDate = new Date(startTime); startDate.setMinutes(startDate.getMinutes() + durationInMins); const endTime = startDate.toISOString(); - const event = await this.calenderService.createEvent(client, formattedStartTime, endTime, seats, createConference, title, floor, attendees); + const event = await this.calenderService.createEvent(client, startTime, endTime, seats, createConference, title, floor, attendees); return event; } diff --git a/src/calender/calender.service.ts b/src/calender/calender.service.ts index 9bf10e3..47d81ab 100644 --- a/src/calender/calender.service.ts +++ b/src/calender/calender.service.ts @@ -1,10 +1,10 @@ import { OAuth2Client } from 'google-auth-library'; import { ConflictException, ForbiddenException, Inject, Injectable, InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { ConfigType } from '@nestjs/config'; -import { calendar_v3, google } from 'googleapis'; +import { google } from 'googleapis'; import appConfig from '../config/env/app.config'; import { EventResponse, RoomResponse } from './dto'; -import { convertToLocaleTime, isRoomAvailable, parseLocation } from './util/calender.util'; +import { isRoomAvailable, parseLocation } from './util/calender.util'; import { Room } from './interfaces/room.interface'; import { AuthService } from '../auth/auth.service'; import { rooms } from '../config/rooms'; @@ -27,6 +27,11 @@ export class CalenderService { floor?: number, attendees?: string[], ): Promise { + console.log('current server date', new Date().toISOString()); + + console.log('startTime', startTime); + console.log('endTime', endTime); + const room = await this.getAvailableRoom(client, startTime, endTime, seats, floor); if (!room) { @@ -90,8 +95,8 @@ export class CalenderService { return { summary: result.data.summary, meet: result.data.hangoutLink, - start: convertToLocaleTime(result.data.start.dateTime), - end: convertToLocaleTime(result.data.end.dateTime), + start: result.data.start.dateTime, + end: result.data.end.dateTime, room: formattedRoom, }; } @@ -101,7 +106,7 @@ export class CalenderService { const calendar = google.calendar({ version: 'v3', auth: client }); const filteredRoomIds = []; for (const room of rooms) { - if (room.seats >= minSeats && (!floor || room.floor === floor)) { + if (room.seats >= minSeats && room.floor === floor) { filteredRoomIds.push(room.id); } } @@ -111,9 +116,9 @@ export class CalenderService { timeMin: start, timeMax: end, timeZone: 'Asia/Dhaka', - items: rooms.map((room) => { + items: filteredRoomIds.map((id) => { return { - id: room.id, + id }; }), }, @@ -156,8 +161,8 @@ export class CalenderService { room: parseLocation(event.location), id: event.id, title: event.summary, - start: convertToLocaleTime(event.start.dateTime), - end: convertToLocaleTime(event.end.dateTime), + start: event.start.dateTime, + end: event.end.dateTime, } as RoomResponse; }); diff --git a/src/calender/util/calender.util.ts b/src/calender/util/calender.util.ts index 474593a..2fe1993 100644 --- a/src/calender/util/calender.util.ts +++ b/src/calender/util/calender.util.ts @@ -21,37 +21,3 @@ export function parseLocation(location: string) { const parsedLocation = location.split(','); return parsedLocation.length > 0 ? parsedLocation[0] : undefined; } - -/** - * - * @param dateString in the format '2024-08-31' - * @param timeString in the format '11:30 AM' - * @param timeZoneOffset - * @returns - */ -export function convertToRFC3339(dateString: string, timeString: string, timeZoneOffset = '+06:00') { - const date = new Date(`${dateString} ${timeString}`); - - const [offsetSign, offsetHours, offsetMinutes] = timeZoneOffset.match(/([+-])(\d{2}):(\d{2})/).slice(1); - - const offsetInMinutes = (parseInt(offsetHours) * 60 + parseInt(offsetMinutes)) * (offsetSign === '+' ? 1 : -1); - date.setMinutes(date.getMinutes() + offsetInMinutes); - - const isoString = date.toISOString(); - const [isoDate, isoTime] = isoString.split('T'); - - // Return the formatted date and time in RFC 3339 format - return `${isoDate}T${isoTime.split('.')[0]}${timeZoneOffset}`; -} - -export function convertToLocaleTime(dateStr: string) { - const date = new Date(dateStr); - - const options: Intl.DateTimeFormatOptions = { - hour: '2-digit', - minute: '2-digit', - hour12: true, - }; - - return date.toLocaleTimeString('en-US', options); -}