Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 115 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
<type>(<scope>): <description>

# 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
3 changes: 1 addition & 2 deletions functions/src/controllers/application_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,7 @@ function validateStringValue(fieldValue: string | any, question: Question) {
message: `Must be less than ${validation.maxLength} character(s)`,
});
}
// other string validation if needed
// ...

return errors;
}

Expand Down
14 changes: 2 additions & 12 deletions functions/src/controllers/auth_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,17 +445,7 @@ const createMailOptions = (email: string, link: string): MailOptions => ({
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #fff; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #1a1a1a;">
<div style="background-color: #2d2d2d; border-radius: 8px; padding: 30px; text-align: center; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
<div style="margin-bottom: 20px;">
<img
src="https://garudahacks.com/images/logo/ghq.png"
alt="Garuda Hacks Logo"
style="max-width: 80px;"
loading="eager"
decoding="async"
importance="high"
/>
</div>
<h1 style="color: #fff; margin-bottom: 20px; font-size: 24px;">Reset Your Password</h1>
<h1 style="color: #fff; margin-bottom: 20px; font-size: 32px;">Reset Your Password</h1>
<p style="color: #e2e8f0; margin-bottom: 25px;">You requested a password reset. Click the button below to choose a new password:</p>
<a href="${link}" style="background-color: #4299e1; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block; font-weight: bold; margin-bottom: 25px;">Reset Password</a>
<p style="color: #a0aec0; font-size: 14px; margin-top: 30px; border-top: 1px solid #4a5568; padding-top: 20px;">
Expand All @@ -469,7 +459,7 @@ const createMailOptions = (email: string, link: string): MailOptions => ({
<p>© ${new Date().getFullYear()} Garuda Hacks. All rights reserved.</p>
<p style="margin-top: 10px;">
<a href="https://garudahacks.com" style="color: #718096; text-decoration: none;">Visit our website</a> |
<a href="mailto:support@garudahacks.com" style="color: #718096; text-decoration: none;">Contact Support</a>
<a href="mailto:hiba@garudahacks.com" style="color: #718096; text-decoration: none;">Contact Support</a>
</p>
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion functions/src/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
Expand Down
2 changes: 1 addition & 1 deletion functions/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
16 changes: 9 additions & 7 deletions functions/src/types/application_types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {Request} from "express";
import { Request } from "express";

export enum APPLICATION_STATUS {
NOT_APPLICABLE = "not applicable",
DRAFT = "draft",
SUBMITTED = "submitted",
WAITLISTED = "waitlisted",
REJECTED = "rejected",
ACCEPTED = "accepted"
ACCEPTED = "accepted",
CONFIRMED_RSVP = "confirmed rsvp",
}

/**
Expand All @@ -24,13 +25,14 @@ export enum QUESTION_TYPE {
TEXTAREA = "textarea",
DATE = "datetime",
DROPDOWN = "dropdown",
FILE = "file"
FILE = "file",
}

export interface StringValidation {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: string;
}

export interface NumberValidation {
Expand Down Expand Up @@ -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 {
Expand All @@ -89,4 +91,4 @@ export interface FileData {

export interface ExtendedRequest extends Request {
rawBody?: Buffer;
}
}
14 changes: 14 additions & 0 deletions functions/src/utils/fake_data_populator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,21 @@ export class FakeDataPopulator {

// string example
q = {
id: faker.string.uuid(),
order: 1,
state: APPLICATION_STATES.PROFILE,
text: "Name",
type: QUESTION_TYPE.STRING,
validation: {
required: true,
pattern: "^[a-zA-Z\\s'-]+$",
},
};
await this.createQuestionDocument(q);

// number example
q = {
id: faker.string.uuid(),
order: 2,
state: APPLICATION_STATES.PROFILE,
text: "Age",
Expand All @@ -110,37 +113,43 @@ export class FakeDataPopulator {
required: true,
minValue: 16,
maxValue: 45,
pattern: "^[0-9]+$",
},
};
await this.createQuestionDocument(q);

// date example
q = {
id: faker.string.uuid(),
order: 3,
state: APPLICATION_STATES.PROFILE,
text: "Birthday",
type: QUESTION_TYPE.DATE,
validation: {
required: true,
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
},
};
await this.createQuestionDocument(q);

// dropdown example
q = {
id: faker.string.uuid(),
order: 4,
state: APPLICATION_STATES.PROFILE,
text: "Education Level",
type: QUESTION_TYPE.DROPDOWN,
validation: {
required: true,
pattern: "^(Undergraduate|High School)$",
},
options: ["Undergraduate", "High School"],
};
await this.createQuestionDocument(q);

// file example
q = {
id: faker.string.uuid(),
order: 4,
state: APPLICATION_STATES.PROFILE,
text: "Profile Photo",
Expand All @@ -149,30 +158,35 @@ export class FakeDataPopulator {
required: true,
allowedTypes: "image/jpg,image/jpeg,image/png",
maxSize: 5,
pattern: "\\.(jpg|jpeg|png)$",
},
};
await this.createQuestionDocument(q);

// string example
q = {
id: faker.string.uuid(),
order: 1,
state: APPLICATION_STATES.INQUIRY,
text: "What's your motivation in joining GarudaHacks?",
type: QUESTION_TYPE.TEXTAREA,
validation: {
required: true,
pattern: "^[\\w\\s\\.,!?-]+$",
},
};
await this.createQuestionDocument(q);

// 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?",
type: QUESTION_TYPE.TEXTAREA,
validation: {
required: true,
pattern: "^[\\w\\s\\.,!?-]+$",
},
};
await this.createQuestionDocument(q);
Expand Down