feat: remove OTP gate from transactions, fix categories auth, add implementation plan

- Remove OtpGateGuard from transactions controller (OTP verified at login)
- Fix categories controller to use authenticated user instead of TEMP_USER_ID
- Add comprehensive implementation plan document
- Update .env.example with WEB_APP_URL
- Prepare for admin dashboard development
This commit is contained in:
dwindown
2025-10-11 14:00:11 +07:00
parent 0da6071eb3
commit 249f3a9d7d
159 changed files with 13748 additions and 3369 deletions

133
RESEND_OTP_FIX.md Normal file
View File

@@ -0,0 +1,133 @@
# 🔧 Resend OTP Fix - Public Endpoint
## 🐛 **Problem:**
Resend OTP endpoint was failing with `ERR_CONNECTION_REFUSED` because:
1. The entire `OtpController` has `@UseGuards(AuthGuard)` at class level
2. The resend endpoint requires a temp token, not a full JWT
3. AuthGuard was rejecting the request
## ✅ **Solution:**
Made the resend endpoint public by:
1. Adding `@Public()` decorator
2. Updating `AuthGuard` to respect public routes
3. Endpoint verifies temp token manually
---
## 📝 **Changes Made:**
### 1. **Updated OtpController** (`otp.controller.ts`):
```typescript
// Added Public decorator
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// Marked resend endpoint as public
@Public()
@Post('email/resend')
async resendEmailOtp(@Body() body: { tempToken: string }) {
try {
// Verify temp token manually
const payload = this.jwtService.verify(body.tempToken);
if (!payload.temp) {
throw new UnauthorizedException('Invalid token type');
}
const userId = payload.userId || payload.sub;
// Send OTP
return this.otpService.sendEmailOtp(userId);
} catch (error) {
throw new UnauthorizedException('Invalid or expired token');
}
}
```
### 2. **Updated AuthGuard** (`auth.guard.ts`):
```typescript
import { Reflector } from '@nestjs/core';
@Injectable()
export class AuthGuard extends PassportAuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
// Check if route is marked as public
const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true; // Skip authentication
}
return super.canActivate(context);
}
}
```
---
## 🔄 **How It Works:**
1. **Frontend** calls `/api/otp/email/resend` with temp token
2. **AuthGuard** checks for `@Public()` decorator
3. **If public**: Skips JWT validation, allows request through
4. **Endpoint** manually verifies temp token
5. **Extracts** userId from temp token
6. **Sends** OTP email
7. **Returns** success
---
## 🧪 **Testing:**
### **Test Resend:**
1. ✅ Login with email OTP enabled
2. ✅ On OTP page, wait 30 seconds
3. ✅ Click "Resend Code"
4.**Should work now!**
5. ✅ Check console for new OTP code
6. ✅ Enter code and login
---
## ⚠️ **Current Status:**
Backend needs to restart to apply changes. If backend is not responding:
```bash
# Kill existing process
pkill -f "nest start"
# Restart
cd apps/api
npm run dev
```
---
## 📊 **Files Modified:**
1. **`apps/api/src/otp/otp.controller.ts`**
- Added `Public` decorator
- Marked `email/resend` as public
- Injected `JwtService`
2. **`apps/api/src/auth/auth.guard.ts`**
- Injected `Reflector`
- Added public route check
- Skip auth for public routes
3. **`apps/web/src/components/pages/OtpVerification.tsx`**
- Already updated to use `/api/otp/email/resend`
---
**Once backend restarts, resend should work!** 🚀