From 14c3ca799a859a744193ebac0b675419d93a97bf Mon Sep 17 00:00:00 2001 From: Kezia Date: Fri, 16 May 2025 18:29:16 -0400 Subject: [PATCH 1/5] added new validation pattern --- .../src/controllers/application_controller.ts | 14 ++++++++++++-- functions/src/types/application_types.ts | 16 +++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/functions/src/controllers/application_controller.ts b/functions/src/controllers/application_controller.ts index def613a..898b1b3 100644 --- a/functions/src/controllers/application_controller.ts +++ b/functions/src/controllers/application_controller.ts @@ -462,8 +462,18 @@ function validateStringValue(fieldValue: string | any, question: Question) { message: `Must be less than ${validation.maxLength} character(s)`, }); } - // other string validation if needed - // ... + + // validate pattern + if (validation.pattern) { + const regex = new RegExp(validation.pattern); + if (!regex.test(fieldValue)) { + errors.push({ + field_id: `${question.id}`, + message: `Not a valid ${question.text}.`, + }); + } + } + return errors; } diff --git a/functions/src/types/application_types.ts b/functions/src/types/application_types.ts index 182ef08..0f1c82e 100644 --- a/functions/src/types/application_types.ts +++ b/functions/src/types/application_types.ts @@ -6,16 +6,17 @@ export enum APPLICATION_STATUS { SUBMITTED = "submitted", WAITLISTED = "waitlisted", REJECTED = "rejected", - ACCEPTED = "accepted" + ACCEPTED = "accepted", + CONFIRMED_RSVP = "confirmed rsvp", } /** * State for part to show in the web UI of GH Portal. */ export enum APPLICATION_STATES { - PROFILE = "PROFILE", - INQUIRY = "INQUIRY", - ADDITIONAL_QUESTION = "ADDITIONAL_QUESTION", + PROFILE = "profile", + INQUIRY = "inquiry", + ADDITIONAL_QUESTION = "additional", } export enum QUESTION_TYPE { @@ -31,6 +32,7 @@ export interface StringValidation { required?: boolean; minLength?: number; maxLength?: number; + pattern?: string; } export interface NumberValidation { @@ -64,14 +66,14 @@ export type ValidationTypeMap = { }; export interface Question { - id?: string; + id: string; order: number; - state: APPLICATION_STATES; text: string; type: QUESTION_TYPE; validation: ValidationTypeMap[Question["type"]]; - + placeholder?: string; options?: string[]; // for dropdown only + state: APPLICATION_STATES; } export interface FileInfo { From 63729c574d5c3c6811a44c1c37d1b7faf93e330c Mon Sep 17 00:00:00 2001 From: Hibatullah Fawwaz Hana Date: Sun, 18 May 2025 14:35:54 +0700 Subject: [PATCH 2/5] chore: update README --- README.md | 149 +++++++++++++++++++++++++++++++--------- functions/src/server.ts | 2 +- 2 files changed, 116 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 255b7cd..ed3ec38 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,147 @@ -# Garuda Hacks 6.0 Web Back-End +# Garuda Hacks Backend 🚀 -This repository contains Cloud Functions for Firebase, written in TypeScript. +The official backend service for Garuda Hacks 6.0, providing robust and scalable APIs for the hackathon platform. Built with Firebase Cloud Functions and TypeScript to ensure reliability and maintainability. -## Setup +## 🛠️ Tech Stack + +- **Backend** + + - Firebase Cloud Functions + - TypeScript + - Node.js + - Express.js + - Firebase Admin SDK + +- **Database** + + - Firebase Firestore + +- **Authentication** + + - Firebase Authentication + +- **Deployment** + - Firebase Hosting + - Firebase Cloud Functions + +## 🚀 Getting Started ### Prerequisites -- Node.js 20+ -- Firebase CLI installed (`npm install -g firebase-tools`) -- A Firebase project set up +- Node.js (v20 or higher) +- npm or yarn +- Firebase CLI (`npm install -g firebase-tools`) +- Firebase account ### Installation -```sh +1. Clone the repository + +```bash +git clone https://github.com/your-username/web-be.git +cd web-be +``` + +2. Install dependencies + +```bash npm install ``` -### Environment Variables +3. Set up environment variables -Create a `.env` file and add your environment variables as needed. +```bash +cp .env.example .env +``` -## Development +Fill in your Firebase configuration in `.env`: -### Lint +```bash +# Firebase Configuration +FIREBASE_PROJECT_ID=your-project-id +FIREBASE_PRIVATE_KEY=your-private-key +FIREBASE_CLIENT_EMAIL=your-client-email -```sh -npm run lint +# Other Configuration +NODE_ENV=development ``` -### Build +4. Start development server -```sh -npm run build +```bash +npm run serve ``` -### Watch Mode +### Building for Production -```sh -npm run build:watch +```bash +npm run build ``` -### Serve Locally +## 📁 Project Structure -```sh -npm run serve +``` +web-be/ +├── src/ +│ ├── functions/ # Cloud Functions +│ │ ├── auth/ # Authentication related functions +│ │ ├── users/ # User management functions +│ │ └── utils/ # Utility functions +│ ├── config/ # Configuration files +│ ├── types/ # TypeScript type definitions +│ └── utils/ # Helper functions +├── tests/ # Test files +└── firebase.json # Firebase configuration ``` -### Firebase Shell +## 🔧 Configuration -```sh -npm run shell -``` +### Environment Variables -## Deployment +Required environment variables: -```sh -npm run deploy -``` +- `FIREBASE_PROJECT_ID` +- `FIREBASE_PRIVATE_KEY` +- `FIREBASE_CLIENT_EMAIL` +- `NODE_ENV` -## Logs +## 🤝 Contributing -```sh -npm run logs -``` +1. Fork the repository +2. Create your feature branch (`git checkout -b feat/amazing-feature`) +3. Make your changes following our commit conventions: + + ```bash + # Format + (): + + # Examples + feat(auth): add user authentication middleware + fix(api): resolve CORS configuration + docs(readme): update deployment steps + style(code): improve error handling + refactor(functions): optimize database queries + test(auth): add authentication tests + chore(deps): update dependencies + ``` + + Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` + Scope: optional, indicates the module affected + +4. Push to the branch (`git push origin feat/amazing-feature`) +5. Open a Pull Request + +## 📝 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🙏 Acknowledgments + +- [Firebase](https://firebase.google.com/) +- [TypeScript](https://www.typescriptlang.org/) +- [Node.js](https://nodejs.org/) +- [Express.js](https://expressjs.com/) + +--- +Made with ❤️ by the Garuda Hacks Team diff --git a/functions/src/server.ts b/functions/src/server.ts index 671a9c8..3cdb290 100644 --- a/functions/src/server.ts +++ b/functions/src/server.ts @@ -13,8 +13,8 @@ const allowedOrigins = [ "http://localhost:3001", "http://localhost:5173", "https://garudahacks.com", - "https://portal-ochre-iota.vercel.app", "https://portal.garudahacks.com", + "https://preview.portal.garudahacks.com", ]; const corsOptions: CorsOptions = { From f696026e814870d020ff10eb1038828fce5c9714 Mon Sep 17 00:00:00 2001 From: Hibatullah Fawwaz Hana Date: Sun, 18 May 2025 23:12:29 +0700 Subject: [PATCH 3/5] fix: data populator --- functions/src/utils/fake_data_populator.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/functions/src/utils/fake_data_populator.ts b/functions/src/utils/fake_data_populator.ts index 9cb62dd..b17d614 100644 --- a/functions/src/utils/fake_data_populator.ts +++ b/functions/src/utils/fake_data_populator.ts @@ -90,6 +90,7 @@ export class FakeDataPopulator { // string example q = { + id: faker.string.uuid(), order: 1, state: APPLICATION_STATES.PROFILE, text: "Name", @@ -102,6 +103,7 @@ export class FakeDataPopulator { // number example q = { + id: faker.string.uuid(), order: 2, state: APPLICATION_STATES.PROFILE, text: "Age", @@ -116,6 +118,7 @@ export class FakeDataPopulator { // date example q = { + id: faker.string.uuid(), order: 3, state: APPLICATION_STATES.PROFILE, text: "Birthday", @@ -128,6 +131,7 @@ export class FakeDataPopulator { // dropdown example q = { + id: faker.string.uuid(), order: 4, state: APPLICATION_STATES.PROFILE, text: "Education Level", @@ -141,6 +145,7 @@ export class FakeDataPopulator { // file example q = { + id: faker.string.uuid(), order: 4, state: APPLICATION_STATES.PROFILE, text: "Profile Photo", @@ -155,6 +160,7 @@ export class FakeDataPopulator { // string example q = { + id: faker.string.uuid(), order: 1, state: APPLICATION_STATES.INQUIRY, text: "What's your motivation in joining GarudaHacks?", @@ -167,6 +173,7 @@ export class FakeDataPopulator { // string example q = { + id: faker.string.uuid(), order: 1, state: APPLICATION_STATES.INQUIRY, text: "Do you have any limitation that we should be concern about?", From 45ed8cdf242705b23920dc509d5222faaadf123e Mon Sep 17 00:00:00 2001 From: Hibatullah Fawwaz Hana Date: Mon, 19 May 2025 00:43:56 +0700 Subject: [PATCH 4/5] fix: application --- functions/src/controllers/application_controller.ts | 11 ----------- functions/src/types/application_types.ts | 12 ++++++------ functions/src/utils/fake_data_populator.ts | 7 +++++++ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/functions/src/controllers/application_controller.ts b/functions/src/controllers/application_controller.ts index 898b1b3..da9d151 100644 --- a/functions/src/controllers/application_controller.ts +++ b/functions/src/controllers/application_controller.ts @@ -463,17 +463,6 @@ function validateStringValue(fieldValue: string | any, question: Question) { }); } - // validate pattern - if (validation.pattern) { - const regex = new RegExp(validation.pattern); - if (!regex.test(fieldValue)) { - errors.push({ - field_id: `${question.id}`, - message: `Not a valid ${question.text}.`, - }); - } - } - return errors; } diff --git a/functions/src/types/application_types.ts b/functions/src/types/application_types.ts index 0f1c82e..a54595f 100644 --- a/functions/src/types/application_types.ts +++ b/functions/src/types/application_types.ts @@ -1,4 +1,4 @@ -import {Request} from "express"; +import { Request } from "express"; export enum APPLICATION_STATUS { NOT_APPLICABLE = "not applicable", @@ -14,9 +14,9 @@ export enum APPLICATION_STATUS { * State for part to show in the web UI of GH Portal. */ export enum APPLICATION_STATES { - PROFILE = "profile", - INQUIRY = "inquiry", - ADDITIONAL_QUESTION = "additional", + PROFILE = "PROFILE", + INQUIRY = "INQUIRY", + ADDITIONAL_QUESTION = "ADDITIONAL_QUESTION", } export enum QUESTION_TYPE { @@ -25,7 +25,7 @@ export enum QUESTION_TYPE { TEXTAREA = "textarea", DATE = "datetime", DROPDOWN = "dropdown", - FILE = "file" + FILE = "file", } export interface StringValidation { @@ -91,4 +91,4 @@ export interface FileData { export interface ExtendedRequest extends Request { rawBody?: Buffer; -} \ No newline at end of file +} diff --git a/functions/src/utils/fake_data_populator.ts b/functions/src/utils/fake_data_populator.ts index b17d614..ce6836d 100644 --- a/functions/src/utils/fake_data_populator.ts +++ b/functions/src/utils/fake_data_populator.ts @@ -97,6 +97,7 @@ export class FakeDataPopulator { type: QUESTION_TYPE.STRING, validation: { required: true, + pattern: "^[a-zA-Z\\s'-]+$", }, }; await this.createQuestionDocument(q); @@ -112,6 +113,7 @@ export class FakeDataPopulator { required: true, minValue: 16, maxValue: 45, + pattern: "^[0-9]+$", }, }; await this.createQuestionDocument(q); @@ -125,6 +127,7 @@ export class FakeDataPopulator { type: QUESTION_TYPE.DATE, validation: { required: true, + pattern: "^\\d{4}-\\d{2}-\\d{2}$", }, }; await this.createQuestionDocument(q); @@ -138,6 +141,7 @@ export class FakeDataPopulator { type: QUESTION_TYPE.DROPDOWN, validation: { required: true, + pattern: "^(Undergraduate|High School)$", }, options: ["Undergraduate", "High School"], }; @@ -154,6 +158,7 @@ export class FakeDataPopulator { required: true, allowedTypes: "image/jpg,image/jpeg,image/png", maxSize: 5, + pattern: "\\.(jpg|jpeg|png)$", }, }; await this.createQuestionDocument(q); @@ -167,6 +172,7 @@ export class FakeDataPopulator { type: QUESTION_TYPE.TEXTAREA, validation: { required: true, + pattern: "^[\\w\\s\\.,!?-]+$", }, }; await this.createQuestionDocument(q); @@ -180,6 +186,7 @@ export class FakeDataPopulator { type: QUESTION_TYPE.TEXTAREA, validation: { required: true, + pattern: "^[\\w\\s\\.,!?-]+$", }, }; await this.createQuestionDocument(q); From 4e46766c9e11dec421909c6e2d382ff6370c142d Mon Sep 17 00:00:00 2001 From: Hibatullah Fawwaz Hana Date: Thu, 22 May 2025 16:31:42 +0700 Subject: [PATCH 5/5] style: reset password contents --- functions/src/controllers/auth_controller.ts | 14 ++------------ functions/src/routes/auth.ts | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/functions/src/controllers/auth_controller.ts b/functions/src/controllers/auth_controller.ts index 395e1c0..8ba6210 100644 --- a/functions/src/controllers/auth_controller.ts +++ b/functions/src/controllers/auth_controller.ts @@ -445,17 +445,7 @@ const createMailOptions = (email: string, link: string): MailOptions => ({
-
- Garuda Hacks Logo -
-

Reset Your Password

+

Reset Your Password

You requested a password reset. Click the button below to choose a new password:

Reset Password

@@ -469,7 +459,7 @@ const createMailOptions = (email: string, link: string): MailOptions => ({

© ${new Date().getFullYear()} Garuda Hacks. All rights reserved.

Visit our website | - Contact Support + Contact Support

diff --git a/functions/src/routes/auth.ts b/functions/src/routes/auth.ts index 7d5f4de..ebab38e 100644 --- a/functions/src/routes/auth.ts +++ b/functions/src/routes/auth.ts @@ -12,7 +12,7 @@ const router = express.Router(); router.post("/login", (req: Request, res: Response) => login(req, res)); router.post("/register", (req: Request, res: Response) => register(req, res)); -router.post("/request-reset", requestPasswordReset); +router.post("/reset-password", requestPasswordReset); router.post("/session-login", (req: Request, res: Response) => sessionLogin(req, res) );