Skip to content

Commit 645d4c0

Browse files
authored
Merge branch 'dev' into features/influcer-side-responsive
2 parents 9bc73c0 + 1ed46d2 commit 645d4c0

71 files changed

Lines changed: 2185 additions & 1773 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cd.yml

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,47 @@
1+
# name: CD
2+
3+
# on:
4+
# workflow_run:
5+
# workflows: ["CI"]
6+
# types:
7+
# - completed
8+
9+
# jobs:
10+
# deploy-core-api:
11+
# if: ${{ github.event.workflow_run.conclusion == 'success' }}
12+
# runs-on: ubuntu-latest
13+
14+
# steps:
15+
# - name: Deploy to EC2
16+
# uses: appleboy/ssh-action@v1.0.3
17+
# with:
18+
# host: ${{ secrets.EC2_HOST }}
19+
# username: ${{ secrets.EC2_USER }}
20+
# key: ${{ secrets.EC2_SSH_KEY }}
21+
# script: |
22+
# cd ${{ secrets.EC2_APP_PATH }}
23+
24+
# git pull origin main
25+
26+
# corepack enable
27+
# corepack prepare pnpm@latest --activate
28+
29+
# pnpm install --frozen-lockfile
30+
# pnpm --filter core-api build:prod
31+
32+
# pm2 restart core-api
33+
134
name: CD
235

336
on:
437
workflow_run:
538
workflows: ["CI"]
39+
branches: [main]
640
types:
741
- completed
842

943
jobs:
10-
deploy-core-api:
44+
deploy:
1145
if: ${{ github.event.workflow_run.conclusion == 'success' }}
1246
runs-on: ubuntu-latest
1347

@@ -18,15 +52,41 @@ jobs:
1852
host: ${{ secrets.EC2_HOST }}
1953
username: ${{ secrets.EC2_USER }}
2054
key: ${{ secrets.EC2_SSH_KEY }}
55+
2156
script: |
22-
cd ${{ secrets.EC2_APP_PATH }}
57+
set -e
2358
59+
cd ~/apps/noillin
60+
61+
echo "Pulling latest code..."
2462
git pull origin main
2563
64+
echo "Enabling corepack..."
2665
corepack enable
2766
corepack prepare pnpm@latest --activate
2867
68+
echo "Installing dependencies..."
2969
pnpm install --frozen-lockfile
30-
pnpm --filter core-api build:prod
3170
32-
pm2 restart core-api
71+
echo "Building shared..."
72+
pnpm --filter shared build
73+
74+
echo "Building core-api..."
75+
pnpm --filter core-api build
76+
77+
echo "Building realtime..."
78+
pnpm --filter realtime build
79+
80+
echo "Building worker..."
81+
pnpm --filter worker build
82+
83+
echo "Building web..."
84+
pnpm --filter web build
85+
86+
echo "Restarting PM2 services..."
87+
pm2 reload ecosystem.config.js --update-env || pm2 start ecosystem.config.js
88+
89+
echo "Saving PM2..."
90+
pm2 save
91+
92+
echo "Deployment completed successfully."

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ node_modules
33
.env.*
44
dist
55
.next
6-
!.env.example
7-
seed_test.ts
6+
!.env.example
7+
data.ms/
8+
data.ms/
9+
*.mdb

DEPLOYMENT.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Noillin Production Deployment Runbook
2+
3+
## Production URL
4+
5+
- App: https://noillin.in
6+
- API health: https://noillin.in/api/health
7+
- Socket.IO: https://noillin.in/socket.io/
8+
9+
## Infrastructure
10+
11+
- Server: AWS EC2 Ubuntu 24.04
12+
- Domain: noillin.in
13+
- DNS: GoDaddy → Elastic IP
14+
- Reverse proxy: Nginx
15+
- SSL: Let's Encrypt Certbot
16+
- Process manager: PM2
17+
- Database: MongoDB Atlas
18+
- Storage: AWS S3
19+
- Cache: Redis
20+
- Queue: RabbitMQ
21+
- Package manager: pnpm
22+
23+
## Services
24+
25+
| Service | Port | PM2 Name |
26+
| ------------------ | -------: | -------- |
27+
| Next.js web | 3000 | web |
28+
| Core API | 5000 | core-api |
29+
| Realtime Socket.IO | 6001 | realtime |
30+
| Worker | internal | worker |
31+
32+
## Important Server Paths
33+
34+
```bash
35+
/home/ubuntu/apps/noillin
36+
/etc/nginx/sites-available/noillin
37+
/etc/nginx/sites-enabled/noillin
38+
/home/ubuntu/.pm2
39+
/home/ubuntu/backups
40+
```

apps/core-api/.env.example

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
1-
PORT=5000
2-
REDIS_URL=redis://localhost:6379
3-
RABBIT_URL=amqp://localhost:5672
4-
MEILI_HOST=http://localhost:7700
5-
MONGO_URI=mongodb://localhost:27017/noillin
1+
PORT=5000
2+
REDIS_URL=redis://localhost:6379
3+
RABBIT_URL=amqp://localhost:5672
4+
MEILI_HOST=http://localhost:7700
5+
MEILI_KEY=your_master_key
6+
MONGO_URI=mongodb://localhost:27017/noillin
7+
JWT_ACCESS_SECRET=your_access_secret
8+
JWT_REFRESH_SECRET=your_refresh_secret
9+
10+
# Email
11+
EMAIL_USER=your_email@gmail.com
12+
EMAIL_PASS=your_email_password
13+
14+
# AWS S3
15+
AWS_ACCESS_KEY_ID=your_access_key
16+
AWS_SECRET_ACCESS_KEY=your_secret_key
17+
AWS_REGION=your_region
18+
AWS_BUCKET_NAME=your_bucket_name
19+
20+
# Payments
21+
STRIPE_SECRET_KEY=your_stripe_secret_key
22+
STRIPE_WEBHOOK_SECRET=your_stripe_webhook_secret
23+
STRIPE_MOCK_PAYOUT=true
24+
25+
# Feature Flags
26+
ENABLE_WEB_PUSH=true

apps/core-api/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
"type": "module",
77
"scripts": {
88
"dev": "nodemon --watch src --ext ts --exec tsx src/server.ts",
9-
"build": "tsc --noEmit",
10-
"build:prod": "tsc",
9+
"build": "tsc",
1110
"start": "ts-node src/server.ts",
11+
"start:prod": "node dist/server.js",
12+
"typecheck": "tsc --noEmit",
1213
"test": "echo \"Error: no test specified\" && exit 1"
1314
},
1415
"keywords": [],
@@ -24,6 +25,7 @@
2425
"cookie-parser": "^1.4.7",
2526
"dotenv": "^17.2.3",
2627
"express": "^5.2.1",
28+
"express-rate-limit": "^8.5.1",
2729
"ioredis": "^5.10.1",
2830
"jsonwebtoken": "^9.0.3",
2931
"meilisearch": "^0.55.0",

apps/core-api/src/cache/redis.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import IORedis from "ioredis";
2-
import type {Redis} from "ioredis";
1+
import { Redis } from "ioredis";
32

43
import { logger } from "../utils/logger.js";
54
const redis_url = process.env.REDIS_URL
65
if (!redis_url){
76
throw new Error("REDIS_URL is not defined in environment variables")
87
}
9-
export const redis:Redis = new IORedis.default(redis_url)
8+
export const redis = new Redis(redis_url);
109
redis.on("connect", ()=>{
1110
logger.info("Redis connected successfully")
1211
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Request, Response, NextFunction } from 'express';
2+
import { rateLimit, Options } from 'express-rate-limit';
3+
4+
export const authLimiter = rateLimit({
5+
windowMs: 15 * 60 * 1000, // 15 minutes
6+
max: 10, // Limit each IP to 10 requests per windowMs
7+
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
8+
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
9+
message: {
10+
success: false,
11+
message: "Too many authentication attempts. Please try again later.",
12+
},
13+
handler: (req: Request, res: Response, next: NextFunction, options: Options) => {
14+
res.status(429).json(options.message);
15+
},
16+
});

apps/core-api/src/routes/auth.routes.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ import { Router } from "express";
22

33
import { forgotPasswordController, loginController, logoutController, pendingProfileController, refreshTokenController, resendSignupOtpController, resetPasswordController, signupController, verifyResetOtpController, verifySignupOtpController } from "../controllers/auth.controller.js";
44
import { authenticate } from "../middlewares/auth.middleware.js";
5+
import { authLimiter } from "../middlewares/rateLimit.middleware.js";
56

67
const router: Router = Router()
7-
router.post("/login", loginController)
8-
router.post("/signup", signupController);
8+
router.post("/login", authLimiter, loginController)
9+
router.post("/signup", authLimiter, signupController);
910
router.post("/refresh", refreshTokenController);
1011
router.post("/logout", authenticate, logoutController);
11-
router.post("/verify-signup-otp", verifySignupOtpController);
12-
router.post("/resend-signup-otp", resendSignupOtpController);
13-
router.post("/forgot-password", forgotPasswordController);
14-
router.post("/verify-reset-otp", verifyResetOtpController);
15-
router.post("/reset-password", resetPasswordController);
12+
router.post("/verify-signup-otp", authLimiter, verifySignupOtpController);
13+
router.post("/resend-signup-otp", authLimiter, resendSignupOtpController);
14+
router.post("/forgot-password", authLimiter, forgotPasswordController);
15+
router.post("/verify-reset-otp", authLimiter, verifyResetOtpController);
16+
router.post("/reset-password", authLimiter, resetPasswordController);
1617
router.post("/pending-profile", pendingProfileController);
1718

1819

apps/core-api/tsconfig.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
11
{
22
"compilerOptions": {
3+
"rootDir": "./src",
34
"outDir": "./dist",
45

56
"module": "nodenext",
7+
"moduleResolution": "nodenext",
68
"target": "esnext",
79
"types": ["node"],
810

911
"sourceMap": true,
10-
"declaration": true,
11-
"declarationMap": true,
12-
1312
"noUncheckedIndexedAccess": true,
1413
"exactOptionalPropertyTypes": true,
1514

1615
"strict": true,
1716
"esModuleInterop": true,
1817
"allowSyntheticDefaultImports": true,
19-
"verbatimModuleSyntax": true,
2018
"isolatedModules": true,
2119
"noUncheckedSideEffectImports": true,
2220
"moduleDetection": "force",
2321
"skipLibCheck": true,
2422

25-
// 🔥 ADD THESE
2623
"baseUrl": ".",
2724
"paths": {
28-
"@shared/*": ["../shared/*"]
25+
"@shared/*": ["../../packages/shared/*"]
2926
}
30-
}
27+
},
28+
"exclude": ["node_modules", "dist"]
3129
}

apps/realtime/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
PORT=6001
2+
MONGO_URI=mongodb://localhost:27017/noillin
3+
INTERNAL_SECRET=your_internal_secret
4+
ENABLE_WEB_PUSH=true

0 commit comments

Comments
 (0)