kybernus 2.4.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/add-features/auth/java-spring/AuthController.java +54 -0
- package/add-features/auth/java-spring/AuthService.java +85 -0
- package/add-features/auth/java-spring/INSTRUCTIONS.md +119 -0
- package/add-features/auth/java-spring/dto/LoginRequest.java +22 -0
- package/add-features/auth/java-spring/dto/RegisterRequest.java +22 -0
- package/add-features/auth/java-spring/security/JwtRequestFilter.java +45 -0
- package/add-features/auth/java-spring/security/JwtUtil.java +59 -0
- package/add-features/auth/java-spring/security/SecurityConfig.java +39 -0
- package/add-features/auth/nestjs/INSTRUCTIONS.md +112 -0
- package/add-features/auth/nestjs/auth.controller.ts +27 -0
- package/add-features/auth/nestjs/auth.module.ts +20 -0
- package/add-features/auth/nestjs/auth.service.ts +81 -0
- package/add-features/auth/nestjs/dto/login.dto.ts +4 -0
- package/add-features/auth/nestjs/dto/register.dto.ts +4 -0
- package/add-features/auth/nestjs/jwt-auth.guard.ts +17 -0
- package/add-features/auth/nestjs/jwt.strategy.ts +24 -0
- package/add-features/auth/nextjs/INSTRUCTIONS.md +97 -0
- package/add-features/auth/nextjs/jwt.ts +21 -0
- package/add-features/auth/nextjs/middleware.ts +37 -0
- package/add-features/auth/nextjs/routes/login.ts +43 -0
- package/add-features/auth/nextjs/routes/register.ts +50 -0
- package/add-features/auth/nextjs/session.ts +28 -0
- package/add-features/auth/nodejs-express/INSTRUCTIONS.md +109 -0
- package/add-features/auth/nodejs-express/auth.controller.ts +59 -0
- package/add-features/auth/nodejs-express/auth.middleware.ts +38 -0
- package/add-features/auth/nodejs-express/auth.routes.ts +15 -0
- package/add-features/auth/nodejs-express/auth.service.ts +73 -0
- package/add-features/auth/nodejs-express/jwt.config.ts +17 -0
- package/add-features/auth/python-fastapi/INSTRUCTIONS.md +100 -0
- package/add-features/auth/python-fastapi/router.py +26 -0
- package/add-features/auth/python-fastapi/schemas.py +25 -0
- package/add-features/auth/python-fastapi/security.py +37 -0
- package/add-features/auth/python-fastapi/service.py +61 -0
- package/add-features/deploy/dockerfiles/Dockerfile.java +22 -0
- package/add-features/deploy/dockerfiles/Dockerfile.nextjs +32 -0
- package/add-features/deploy/dockerfiles/Dockerfile.nodejs +25 -0
- package/add-features/deploy/dockerfiles/Dockerfile.python +17 -0
- package/add-features/deploy/fly/INSTRUCTIONS.md +39 -0
- package/add-features/deploy/fly/java-spring.toml +21 -0
- package/add-features/deploy/fly/nextjs.toml +16 -0
- package/add-features/deploy/fly/nodejs.toml +21 -0
- package/add-features/deploy/fly/python-fastapi.toml +21 -0
- package/add-features/deploy/railway/INSTRUCTIONS.md +38 -0
- package/add-features/deploy/railway/java-spring.toml +16 -0
- package/add-features/deploy/railway/nextjs.toml +14 -0
- package/add-features/deploy/railway/nodejs.toml +14 -0
- package/add-features/deploy/railway/python-fastapi.toml +13 -0
- package/add-features/deploy/render/INSTRUCTIONS.md +35 -0
- package/add-features/deploy/render/java-spring.yaml +14 -0
- package/add-features/deploy/render/nextjs.yaml +17 -0
- package/add-features/deploy/render/nodejs.yaml +15 -0
- package/add-features/deploy/render/python-fastapi.yaml +13 -0
- package/add-features/deploy/vercel/INSTRUCTIONS.md +40 -0
- package/add-features/deploy/vercel/nextjs.json +16 -0
- package/add-features/deploy/vercel/nodejs-express.json +21 -0
- package/add-features/deploy/vercel/python-fastapi.json +21 -0
- package/add-features/husky/INSTRUCTIONS.md +52 -0
- package/add-features/husky/commit-msg +4 -0
- package/add-features/husky/commitlint.config.js +3 -0
- package/add-features/husky/pre-commit +4 -0
- package/add-features/redis/.env.snippet +1 -0
- package/add-features/redis/INSTRUCTIONS.md +64 -0
- package/add-features/redis/docker-compose.snippet.yml +18 -0
- package/add-features/redis/java-spring.java +27 -0
- package/add-features/redis/nextjs.ts +23 -0
- package/add-features/redis/nodejs.ts +14 -0
- package/add-features/redis/python.py +22 -0
- package/add-features/swagger/INSTRUCTIONS.md +53 -0
- package/add-features/swagger/java-spring.java +34 -0
- package/add-features/swagger/nestjs.ts +16 -0
- package/add-features/swagger/nextjs-route.ts +11 -0
- package/add-features/swagger/nextjs-swagger.ts +17 -0
- package/add-features/swagger/nodejs-express.ts +30 -0
- package/add-features/swagger/python-fastapi.py +21 -0
- package/add-features/websocket/INSTRUCTIONS.md +63 -0
- package/add-features/websocket/java-spring.java +60 -0
- package/add-features/websocket/nestjs.ts +38 -0
- package/add-features/websocket/nodejs-express.ts +38 -0
- package/add-features/websocket/python-fastapi.py +41 -0
- package/dist/cli/commands/add.d.ts +11 -0
- package/dist/cli/commands/add.d.ts.map +1 -0
- package/dist/cli/commands/add.js +102 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +11 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +71 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +10 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +110 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +110 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +1 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/prompts/wizard.d.ts +1 -0
- package/dist/cli/prompts/wizard.d.ts.map +1 -1
- package/dist/cli/prompts/wizard.js +23 -15
- package/dist/cli/prompts/wizard.js.map +1 -1
- package/dist/cli/utils/cli-helpers.d.ts +43 -0
- package/dist/cli/utils/cli-helpers.d.ts.map +1 -0
- package/dist/cli/utils/cli-helpers.js +107 -0
- package/dist/cli/utils/cli-helpers.js.map +1 -0
- package/dist/core/deploy/deploy-generator.d.ts +18 -0
- package/dist/core/deploy/deploy-generator.d.ts.map +1 -0
- package/dist/core/deploy/deploy-generator.js +155 -0
- package/dist/core/deploy/deploy-generator.js.map +1 -0
- package/dist/core/features/feature-generator.d.ts +26 -0
- package/dist/core/features/feature-generator.d.ts.map +1 -0
- package/dist/core/features/feature-generator.js +376 -0
- package/dist/core/features/feature-generator.js.map +1 -0
- package/dist/core/generator/project.d.ts.map +1 -1
- package/dist/core/generator/project.js +42 -2
- package/dist/core/generator/project.js.map +1 -1
- package/dist/core/templates/engine.d.ts.map +1 -1
- package/dist/core/templates/engine.js +4 -0
- package/dist/core/templates/engine.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/models/config.d.ts +1 -0
- package/dist/models/config.d.ts.map +1 -1
- package/package.json +14 -2
- package/templates/nestjs/clean/package.json.hbs +51 -41
- package/templates/nestjs/hexagonal/package.json.hbs +51 -41
- package/templates/nestjs/mvc/package.json.hbs +49 -39
- package/templates/nextjs/mvc/package.json.hbs +46 -36
- package/templates/nodejs-express/clean/package.json.hbs +69 -59
- package/templates/nodejs-express/hexagonal/package.json.hbs +69 -59
- package/templates/nodejs-express/mvc/package.json.hbs +67 -57
- /package/templates/nestjs/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
- /package/templates/nodejs-express/clean/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
- /package/templates/nodejs-express/hexagonal/prisma/{schema.prisma → schema.prisma.hbs} +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { generateToken } from './jwt.config';
|
|
2
|
+
import bcrypt from 'bcryptjs';
|
|
3
|
+
|
|
4
|
+
// ==========================================
|
|
5
|
+
// 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
|
|
6
|
+
// ==========================================
|
|
7
|
+
// This service currently uses an IN-MEMORY array to store users.
|
|
8
|
+
// You MUST replace the "mockDb" logic below with your actual ORM or Database provider.
|
|
9
|
+
//
|
|
10
|
+
// EXAMPLE WITH PRISMA:
|
|
11
|
+
// 1. import { PrismaClient } from '@prisma/client';
|
|
12
|
+
// const prisma = new PrismaClient();
|
|
13
|
+
// 2. To find user: const user = await prisma.user.findUnique({ where: { email } });
|
|
14
|
+
// 3. To create user: const user = await prisma.user.create({ data: { email, password: hashedPassword } });
|
|
15
|
+
// ==========================================
|
|
16
|
+
|
|
17
|
+
const mockDb: any[] = []; // 🚨 REPLACE THIS WITH REAL DB CALLS 🚨
|
|
18
|
+
|
|
19
|
+
export class AuthService {
|
|
20
|
+
|
|
21
|
+
async register(email: string, password: string) {
|
|
22
|
+
// 🚨 TODO: Change this to checking your real database!
|
|
23
|
+
const existingUser = mockDb.find((u) => u.email === email);
|
|
24
|
+
if (existingUser) {
|
|
25
|
+
throw new Error('User already exists');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const hashedPassword = await bcrypt.hash(password, 12);
|
|
29
|
+
|
|
30
|
+
// 🚨 TODO: Change this to inserting into your real database!
|
|
31
|
+
const newUser = {
|
|
32
|
+
id: Math.random().toString(36).substring(7), // Mock ID
|
|
33
|
+
email,
|
|
34
|
+
password: hashedPassword,
|
|
35
|
+
};
|
|
36
|
+
mockDb.push(newUser);
|
|
37
|
+
|
|
38
|
+
const token = generateToken({ userId: newUser.id, email: newUser.email });
|
|
39
|
+
|
|
40
|
+
// Do not return password hash
|
|
41
|
+
const { password: _, ...userWithoutPassword } = newUser;
|
|
42
|
+
return { user: userWithoutPassword, token };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async login(email: string, password: string) {
|
|
46
|
+
// 🚨 TODO: Change this to fetching from your real database!
|
|
47
|
+
const user = mockDb.find((u) => u.email === email);
|
|
48
|
+
|
|
49
|
+
if (!user) {
|
|
50
|
+
throw new Error('Invalid credentials');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const isMatch = await bcrypt.compare(password, user.password);
|
|
54
|
+
if (!isMatch) {
|
|
55
|
+
throw new Error('Invalid credentials');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const token = generateToken({ userId: user.id, email: user.email });
|
|
59
|
+
|
|
60
|
+
return { token };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async getUserProfile(userId: string) {
|
|
64
|
+
// 🚨 TODO: Change this to fetching from your real database!
|
|
65
|
+
const user = mockDb.find((u) => u.id === userId);
|
|
66
|
+
if (!user) {
|
|
67
|
+
throw new Error('User not found');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { password, ...profile } = user;
|
|
71
|
+
return profile;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
|
|
3
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'change-me-in-production';
|
|
4
|
+
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
|
|
5
|
+
|
|
6
|
+
export interface JwtPayload {
|
|
7
|
+
userId: string;
|
|
8
|
+
email: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function generateToken(payload: JwtPayload): string {
|
|
12
|
+
return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function verifyToken(token: string): JwtPayload {
|
|
16
|
+
return jwt.verify(token, JWT_SECRET) as JwtPayload;
|
|
17
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# 🔐 JWT Authentication Module — Python FastAPI
|
|
2
|
+
|
|
3
|
+
A complete, modular authentication flow was added to your project inside the `app/auth/` directory.
|
|
4
|
+
|
|
5
|
+
## 📁 Files generated:
|
|
6
|
+
|
|
7
|
+
- `router.py` — The FastAPI `APIRouter` exposing `/auth/login`, `/auth/register`, and `/auth/me`.
|
|
8
|
+
- `service.py` — Business logic (password validation, checking user). **🚨 ACTION REQUIRED HERE**
|
|
9
|
+
- `security.py` — JWT token generation and password hashing (passlib).
|
|
10
|
+
- `schemas.py` — Pydantic models for request/response validation.
|
|
11
|
+
|
|
12
|
+
## 📦 1. Install Dependencies
|
|
13
|
+
|
|
14
|
+
You need to install the cryptography and hashing libraries:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install python-jose[cryptography] passlib[bcrypt] python-multipart
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## ⚙️ 2. Environment Variables
|
|
21
|
+
|
|
22
|
+
Add these to your `.env` file at the root of your project:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
JWT_SECRET=your-super-secret-key-change-me
|
|
26
|
+
JWT_EXPIRES_MINUTES=10080
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> 💡 **Tip:** Generate a strong random secret by running this:
|
|
30
|
+
> `python -c "import secrets; print(secrets.token_hex(64))"`
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚨 3. MANDATORY ACTION: Connect to your Database
|
|
35
|
+
|
|
36
|
+
The generated `service.py` uses an **IN-MEMORY MOCK DATABASE** by default so that the API compiles and runs immediately.
|
|
37
|
+
You **MUST** replace this with calls to your actual Database (SQLAlchemy, SQLModel, Motor, etc).
|
|
38
|
+
|
|
39
|
+
**Open `app/auth/service.py` and look for the `🚨 TODO` blocks.**
|
|
40
|
+
|
|
41
|
+
### Example: How to connect it to SQLAlchemy:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
# Inside app/auth/service.py
|
|
45
|
+
|
|
46
|
+
from sqlalchemy.orm import Session
|
|
47
|
+
from app.db.models import User
|
|
48
|
+
|
|
49
|
+
# Add 'db: Session' parameter so router can inject it
|
|
50
|
+
def auth_register(user_data: UserCreate, db: Session) -> dict:
|
|
51
|
+
existing = db.query(User).filter(User.email == user_data.email).first()
|
|
52
|
+
if existing:
|
|
53
|
+
raise HTTPException(status_code=409, detail="User already exists")
|
|
54
|
+
|
|
55
|
+
hashed_password = hash_password(user_data.password)
|
|
56
|
+
|
|
57
|
+
new_user = User(email=user_data.email, password=hashed_password)
|
|
58
|
+
db.add(new_user)
|
|
59
|
+
db.commit()
|
|
60
|
+
db.refresh(new_user)
|
|
61
|
+
|
|
62
|
+
token = create_access_token(data={"sub": str(new_user.id), "email": new_user.email})
|
|
63
|
+
return {"user": new_user, "access_token": token, "token_type": "bearer"}
|
|
64
|
+
```
|
|
65
|
+
*(Remember to update `router.py` endpoints to pass the `db: Session = Depends(get_db)` to the service).*
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## ⚡ 4. Plug the Router into your App
|
|
70
|
+
|
|
71
|
+
Currently, the `router.py` exists, but your FastAPI app doesn't know about it.
|
|
72
|
+
You must include it in your main application instance.
|
|
73
|
+
|
|
74
|
+
**Open `app/main.py` and add:**
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from fastapi import FastAPI
|
|
78
|
+
from app.auth.router import router as auth_router # <-- Import this
|
|
79
|
+
|
|
80
|
+
app = FastAPI()
|
|
81
|
+
|
|
82
|
+
app.include_router(auth_router) # <-- Register it
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 🔒 5. How to protect other routes
|
|
86
|
+
|
|
87
|
+
You can secure any other route by using the `get_current_user` dependency:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from fastapi import APIRouter, Depends
|
|
91
|
+
from app.auth.router import get_current_user
|
|
92
|
+
|
|
93
|
+
router = APIRouter(prefix="/admin")
|
|
94
|
+
|
|
95
|
+
# The Depend() ensures a valid JWT is present before hitting this code
|
|
96
|
+
@router.get("/dashboard")
|
|
97
|
+
async def dashboard(user: dict = Depends(get_current_user)):
|
|
98
|
+
# You now have access to the logged-in user's payload!
|
|
99
|
+
return {"message": f"Welcome back, {user['email']}"}
|
|
100
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
2
|
+
from .schemas import UserCreate, UserLogin, TokenResponse, RegisterResponse, UserResponse
|
|
3
|
+
from .service import auth_register, auth_login, get_user_profile
|
|
4
|
+
from .security import oauth2_scheme, decode_token
|
|
5
|
+
|
|
6
|
+
router = APIRouter(prefix="/auth", tags=["Authentication"])
|
|
7
|
+
|
|
8
|
+
def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
|
|
9
|
+
"""Dependency for securing endpoints."""
|
|
10
|
+
payload = decode_token(token)
|
|
11
|
+
user_id = payload.get("sub")
|
|
12
|
+
if not user_id:
|
|
13
|
+
raise HTTPException(status_code=401, detail="Invalid token structure")
|
|
14
|
+
return {"user_id": user_id, "email": payload.get("email")}
|
|
15
|
+
|
|
16
|
+
@router.post("/register", response_model=RegisterResponse, status_code=201)
|
|
17
|
+
def register(user_data: UserCreate):
|
|
18
|
+
return auth_register(user_data)
|
|
19
|
+
|
|
20
|
+
@router.post("/login", response_model=TokenResponse)
|
|
21
|
+
def login(user_data: UserLogin):
|
|
22
|
+
return auth_login(user_data)
|
|
23
|
+
|
|
24
|
+
@router.get("/me", response_model=UserResponse)
|
|
25
|
+
def get_me(current_user: dict = Depends(get_current_user)):
|
|
26
|
+
return get_user_profile(current_user["user_id"])
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from pydantic import BaseModel, EmailStr
|
|
2
|
+
|
|
3
|
+
class UserBase(BaseModel):
|
|
4
|
+
email: EmailStr
|
|
5
|
+
|
|
6
|
+
class UserCreate(UserBase):
|
|
7
|
+
password: str
|
|
8
|
+
|
|
9
|
+
class UserLogin(UserBase):
|
|
10
|
+
password: str
|
|
11
|
+
|
|
12
|
+
class UserResponse(UserBase):
|
|
13
|
+
id: str
|
|
14
|
+
|
|
15
|
+
class Config:
|
|
16
|
+
from_attributes = True
|
|
17
|
+
|
|
18
|
+
class TokenResponse(BaseModel):
|
|
19
|
+
access_token: str
|
|
20
|
+
token_type: str = "bearer"
|
|
21
|
+
|
|
22
|
+
class RegisterResponse(BaseModel):
|
|
23
|
+
user: UserResponse
|
|
24
|
+
access_token: str
|
|
25
|
+
token_type: str = "bearer"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime, timedelta, timezone
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, HTTPException, status
|
|
6
|
+
from fastapi.security import OAuth2PasswordBearer
|
|
7
|
+
from jose import JWTError, jwt
|
|
8
|
+
from passlib.context import CryptContext
|
|
9
|
+
|
|
10
|
+
JWT_SECRET = os.getenv("JWT_SECRET", "change-me-in-production")
|
|
11
|
+
JWT_ALGORITHM = "HS256"
|
|
12
|
+
JWT_EXPIRES_MINUTES = int(os.getenv("JWT_EXPIRES_MINUTES", "10080")) # 7 days
|
|
13
|
+
|
|
14
|
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
15
|
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")
|
|
16
|
+
|
|
17
|
+
def hash_password(password: str) -> str:
|
|
18
|
+
return pwd_context.hash(password)
|
|
19
|
+
|
|
20
|
+
def verify_password(plain: str, hashed: str) -> bool:
|
|
21
|
+
return pwd_context.verify(plain, hashed)
|
|
22
|
+
|
|
23
|
+
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
|
|
24
|
+
to_encode = data.copy()
|
|
25
|
+
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=JWT_EXPIRES_MINUTES))
|
|
26
|
+
to_encode.update({"exp": expire})
|
|
27
|
+
return jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
|
|
28
|
+
|
|
29
|
+
def decode_token(token: str) -> dict:
|
|
30
|
+
try:
|
|
31
|
+
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
|
32
|
+
except JWTError:
|
|
33
|
+
raise HTTPException(
|
|
34
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
35
|
+
detail="Invalid or expired token",
|
|
36
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
37
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from fastapi import HTTPException
|
|
2
|
+
from .security import hash_password, verify_password, create_access_token
|
|
3
|
+
from .schemas import UserCreate, UserLogin
|
|
4
|
+
|
|
5
|
+
# ==========================================
|
|
6
|
+
# 🚨 TODO: DATABASE INTEGRATION REQUIRED 🚨
|
|
7
|
+
# ==========================================
|
|
8
|
+
# This service currently uses an IN-MEMORY list to store users.
|
|
9
|
+
# You MUST replace the "mock_db" logic below with your SQLAlchemy sessions
|
|
10
|
+
# or other ORM tool.
|
|
11
|
+
#
|
|
12
|
+
# EXAMPLE WITH SQLALCHEMY:
|
|
13
|
+
# def register_user(db: Session, user_data: UserCreate):
|
|
14
|
+
# existing = db.query(User).filter(User.email == user_data.email).first()
|
|
15
|
+
# if existing: ...
|
|
16
|
+
# ==========================================
|
|
17
|
+
|
|
18
|
+
mock_db = [] # 🚨 REPLACE THIS WITH REAL DB CALLS 🚨
|
|
19
|
+
|
|
20
|
+
def auth_register(user_data: UserCreate) -> dict:
|
|
21
|
+
# 🚨 TODO: Check real DB
|
|
22
|
+
existing_user = next((u for u in mock_db if u["email"] == user_data.email), None)
|
|
23
|
+
if existing_user:
|
|
24
|
+
raise HTTPException(status_code=409, detail="User already exists")
|
|
25
|
+
|
|
26
|
+
hashed_password = hash_password(user_data.password)
|
|
27
|
+
|
|
28
|
+
# 🚨 TODO: Insert into real DB
|
|
29
|
+
import uuid
|
|
30
|
+
new_user = {
|
|
31
|
+
"id": str(uuid.uuid4()),
|
|
32
|
+
"email": user_data.email,
|
|
33
|
+
"password": hashed_password
|
|
34
|
+
}
|
|
35
|
+
mock_db.append(new_user)
|
|
36
|
+
|
|
37
|
+
token = create_access_token(data={"sub": new_user["id"], "email": new_user["email"]})
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
"user": {"id": new_user["id"], "email": new_user["email"]},
|
|
41
|
+
"access_token": token,
|
|
42
|
+
"token_type": "bearer"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
def auth_login(user_data: UserLogin) -> dict:
|
|
46
|
+
# 🚨 TODO: Fetch from real DB
|
|
47
|
+
user = next((u for u in mock_db if u["email"] == user_data.email), None)
|
|
48
|
+
|
|
49
|
+
if not user or not verify_password(user_data.password, user["password"]):
|
|
50
|
+
raise HTTPException(status_code=401, detail="Invalid credentials")
|
|
51
|
+
|
|
52
|
+
token = create_access_token(data={"sub": user["id"], "email": user["email"]})
|
|
53
|
+
return {"access_token": token, "token_type": "bearer"}
|
|
54
|
+
|
|
55
|
+
def get_user_profile(user_id: str) -> dict:
|
|
56
|
+
# 🚨 TODO: Fetch from real DB
|
|
57
|
+
user = next((u for u in mock_db if u["id"] == user_id), None)
|
|
58
|
+
if not user:
|
|
59
|
+
raise HTTPException(status_code=404, detail="User not found")
|
|
60
|
+
|
|
61
|
+
return {"id": user["id"], "email": user["email"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ── Build stage ───────────────────────────────────────────────────
|
|
2
|
+
FROM maven:3.9-eclipse-temurin-21 AS builder
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
COPY pom.xml .
|
|
7
|
+
# Pre-download dependencies for caching
|
|
8
|
+
RUN mvn dependency:go-offline -B
|
|
9
|
+
|
|
10
|
+
COPY src ./src
|
|
11
|
+
RUN mvn package -DskipTests -B
|
|
12
|
+
|
|
13
|
+
# ── Runtime stage ─────────────────────────────────────────────────
|
|
14
|
+
FROM eclipse-temurin:21-jre-alpine
|
|
15
|
+
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
|
|
18
|
+
COPY --from=builder /app/target/*.jar app.jar
|
|
19
|
+
|
|
20
|
+
EXPOSE 8080
|
|
21
|
+
|
|
22
|
+
ENTRYPOINT ["java", "-jar", "app.jar"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
FROM node:20-alpine AS deps
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY package*.json ./
|
|
4
|
+
RUN npm ci
|
|
5
|
+
|
|
6
|
+
FROM node:20-alpine AS builder
|
|
7
|
+
WORKDIR /app
|
|
8
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
9
|
+
COPY . .
|
|
10
|
+
RUN npm run build
|
|
11
|
+
|
|
12
|
+
# ── Production runner ─────────────────────────────────────────────
|
|
13
|
+
FROM node:20-alpine AS runner
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
|
|
16
|
+
ENV NODE_ENV=production
|
|
17
|
+
|
|
18
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
19
|
+
RUN adduser --system --uid 1001 nextjs
|
|
20
|
+
|
|
21
|
+
COPY --from=builder /app/public ./public
|
|
22
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
23
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
24
|
+
|
|
25
|
+
USER nextjs
|
|
26
|
+
|
|
27
|
+
EXPOSE 3000
|
|
28
|
+
|
|
29
|
+
ENV PORT=3000
|
|
30
|
+
ENV HOSTNAME="0.0.0.0"
|
|
31
|
+
|
|
32
|
+
CMD ["node", "server.js"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
FROM node:20-alpine AS builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY package*.json ./
|
|
6
|
+
RUN npm ci
|
|
7
|
+
|
|
8
|
+
COPY . .
|
|
9
|
+
RUN npm run build
|
|
10
|
+
|
|
11
|
+
# ── Production image ──────────────────────────────────────────────
|
|
12
|
+
FROM node:20-alpine AS runner
|
|
13
|
+
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
|
|
16
|
+
ENV NODE_ENV=production
|
|
17
|
+
|
|
18
|
+
COPY --from=builder /app/package*.json ./
|
|
19
|
+
RUN npm ci --only=production
|
|
20
|
+
|
|
21
|
+
COPY --from=builder /app/dist ./dist
|
|
22
|
+
|
|
23
|
+
EXPOSE 3000
|
|
24
|
+
|
|
25
|
+
CMD ["node", "dist/index.js"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Install system dependencies
|
|
6
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
7
|
+
gcc \
|
|
8
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
9
|
+
|
|
10
|
+
COPY requirements.txt .
|
|
11
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
12
|
+
|
|
13
|
+
COPY . .
|
|
14
|
+
|
|
15
|
+
EXPOSE 8000
|
|
16
|
+
|
|
17
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 🚀 Deploy to Fly.io
|
|
2
|
+
|
|
3
|
+
Your deployment configuration has been generated for **Fly.io**.
|
|
4
|
+
|
|
5
|
+
## 📁 Files generated
|
|
6
|
+
|
|
7
|
+
- `fly.toml` — Fly.io app configuration
|
|
8
|
+
- `Dockerfile` — Container image definition
|
|
9
|
+
|
|
10
|
+
## 🔧 1. Install the Fly CLI
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# macOS
|
|
14
|
+
brew install flyctl
|
|
15
|
+
|
|
16
|
+
# Linux / WSL
|
|
17
|
+
curl -L https://fly.io/install.sh | sh
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 🔑 2. Set Secrets (Environment Variables)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
fly secrets set DATABASE_URL="your-db-connection-string"
|
|
24
|
+
fly secrets set JWT_SECRET="$(node -e "console.log(require('crypto').randomBytes(64).toString('hex'))")"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## ⚡ 3. Deploy
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
fly auth login
|
|
31
|
+
fly launch # First time: creates the app and allocates resources
|
|
32
|
+
fly deploy # Subsequent deploys
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> 💡 **Tip:** `fly launch` will walk you through a wizard. When it asks "Would you like to deploy now?", you can say Yes.
|
|
36
|
+
|
|
37
|
+
## 📖 More Info
|
|
38
|
+
- [Fly.io Docs](https://fly.io/docs)
|
|
39
|
+
- [fly.toml Reference](https://fly.io/docs/reference/configuration)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
app = "my-spring-app"
|
|
2
|
+
primary_region = "gru"
|
|
3
|
+
|
|
4
|
+
[build]
|
|
5
|
+
dockerfile = "Dockerfile"
|
|
6
|
+
|
|
7
|
+
[http_service]
|
|
8
|
+
internal_port = 8080
|
|
9
|
+
force_https = true
|
|
10
|
+
auto_stop_machines = true
|
|
11
|
+
auto_start_machines = true
|
|
12
|
+
|
|
13
|
+
[http_service.concurrency]
|
|
14
|
+
type = "connections"
|
|
15
|
+
hard_limit = 25
|
|
16
|
+
soft_limit = 20
|
|
17
|
+
|
|
18
|
+
[[vm]]
|
|
19
|
+
memory = "512mb"
|
|
20
|
+
cpu_kind = "shared"
|
|
21
|
+
cpus = 1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
app = "my-nextjs-app"
|
|
2
|
+
primary_region = "gru"
|
|
3
|
+
|
|
4
|
+
[build]
|
|
5
|
+
dockerfile = "Dockerfile"
|
|
6
|
+
|
|
7
|
+
[http_service]
|
|
8
|
+
internal_port = 3000
|
|
9
|
+
force_https = true
|
|
10
|
+
auto_stop_machines = true
|
|
11
|
+
auto_start_machines = true
|
|
12
|
+
|
|
13
|
+
[[vm]]
|
|
14
|
+
memory = "512mb"
|
|
15
|
+
cpu_kind = "shared"
|
|
16
|
+
cpus = 1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
app = "my-node-app"
|
|
2
|
+
primary_region = "gru"
|
|
3
|
+
|
|
4
|
+
[build]
|
|
5
|
+
dockerfile = "Dockerfile"
|
|
6
|
+
|
|
7
|
+
[http_service]
|
|
8
|
+
internal_port = 3000
|
|
9
|
+
force_https = true
|
|
10
|
+
auto_stop_machines = true
|
|
11
|
+
auto_start_machines = true
|
|
12
|
+
|
|
13
|
+
[http_service.concurrency]
|
|
14
|
+
type = "connections"
|
|
15
|
+
hard_limit = 25
|
|
16
|
+
soft_limit = 20
|
|
17
|
+
|
|
18
|
+
[[vm]]
|
|
19
|
+
memory = "256mb"
|
|
20
|
+
cpu_kind = "shared"
|
|
21
|
+
cpus = 1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
app = "my-fastapi-app"
|
|
2
|
+
primary_region = "gru"
|
|
3
|
+
|
|
4
|
+
[build]
|
|
5
|
+
dockerfile = "Dockerfile"
|
|
6
|
+
|
|
7
|
+
[http_service]
|
|
8
|
+
internal_port = 8000
|
|
9
|
+
force_https = true
|
|
10
|
+
auto_stop_machines = true
|
|
11
|
+
auto_start_machines = true
|
|
12
|
+
|
|
13
|
+
[http_service.concurrency]
|
|
14
|
+
type = "connections"
|
|
15
|
+
hard_limit = 25
|
|
16
|
+
soft_limit = 20
|
|
17
|
+
|
|
18
|
+
[[vm]]
|
|
19
|
+
memory = "256mb"
|
|
20
|
+
cpu_kind = "shared"
|
|
21
|
+
cpus = 1
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 🚀 Deploy to Railway
|
|
2
|
+
|
|
3
|
+
Your deployment configuration has been generated for **Railway**.
|
|
4
|
+
|
|
5
|
+
## 📁 Files generated
|
|
6
|
+
|
|
7
|
+
- `railway.toml` — Railway project configuration
|
|
8
|
+
|
|
9
|
+
## 🔑 1. Set Environment Variables
|
|
10
|
+
|
|
11
|
+
1. Go to [railway.app](https://railway.app) → Your Project → **Variables**
|
|
12
|
+
2. Add each variable from your `.env` file:
|
|
13
|
+
|
|
14
|
+
| Variable | Description |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `DATABASE_URL` | Your database connection string |
|
|
17
|
+
| `JWT_SECRET` | A strong, random secret for JWT signing |
|
|
18
|
+
| `NODE_ENV` / `PYTHON_ENV` | Set to `production` |
|
|
19
|
+
|
|
20
|
+
> 💡 Railway can also **provision a Postgres database** for you directly — click **New → Database → Postgres** in your project.
|
|
21
|
+
|
|
22
|
+
## ⚡ 2. Deploy
|
|
23
|
+
|
|
24
|
+
**Option A — GitHub Auto-Deploy (recommended):**
|
|
25
|
+
1. Go to [railway.app](https://railway.app) → **New Project** → **Deploy from GitHub repo**
|
|
26
|
+
2. Select your repo — Railway auto-detects `railway.toml` and deploys on every push to `main`
|
|
27
|
+
|
|
28
|
+
**Option B — Railway CLI:**
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g @railway/cli
|
|
31
|
+
railway login
|
|
32
|
+
railway link
|
|
33
|
+
railway up
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 📖 More Info
|
|
37
|
+
- [Railway Docs](https://docs.railway.app)
|
|
38
|
+
- [Railway CLI Reference](https://docs.railway.app/reference/cli-api)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build]
|
|
2
|
+
builder = "dockerfile"
|
|
3
|
+
dockerfilePath = "Dockerfile"
|
|
4
|
+
|
|
5
|
+
[deploy]
|
|
6
|
+
startCommand = "java -jar target/*.jar"
|
|
7
|
+
restartPolicyType = "on-failure"
|
|
8
|
+
restartPolicyMaxRetries = 5
|
|
9
|
+
healthcheckPath = "/actuator/health"
|
|
10
|
+
healthcheckTimeout = 100
|
|
11
|
+
|
|
12
|
+
[env]
|
|
13
|
+
# Add your env variables in the Railway dashboard under Variables
|
|
14
|
+
# DATABASE_URL =
|
|
15
|
+
# JWT_SECRET =
|
|
16
|
+
# SPRING_PROFILES_ACTIVE = "prod"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build]
|
|
2
|
+
builder = "nixpacks"
|
|
3
|
+
buildCommand = "npm install && npm run build"
|
|
4
|
+
|
|
5
|
+
[deploy]
|
|
6
|
+
startCommand = "npm start"
|
|
7
|
+
restartPolicyType = "on-failure"
|
|
8
|
+
restartPolicyMaxRetries = 5
|
|
9
|
+
|
|
10
|
+
[env]
|
|
11
|
+
# Add your env variables in the Railway dashboard under Variables
|
|
12
|
+
# DATABASE_URL =
|
|
13
|
+
# JWT_SECRET =
|
|
14
|
+
# NEXTAUTH_URL =
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build]
|
|
2
|
+
builder = "nixpacks"
|
|
3
|
+
buildCommand = "npm install && npm run build"
|
|
4
|
+
|
|
5
|
+
[deploy]
|
|
6
|
+
startCommand = "npm start"
|
|
7
|
+
restartPolicyType = "on-failure"
|
|
8
|
+
restartPolicyMaxRetries = 5
|
|
9
|
+
|
|
10
|
+
[env]
|
|
11
|
+
# Add your env variables in the Railway dashboard under Variables
|
|
12
|
+
# DATABASE_URL =
|
|
13
|
+
# JWT_SECRET =
|
|
14
|
+
# NODE_ENV = "production"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[build]
|
|
2
|
+
builder = "nixpacks"
|
|
3
|
+
buildCommand = "pip install -r requirements.txt"
|
|
4
|
+
|
|
5
|
+
[deploy]
|
|
6
|
+
startCommand = "uvicorn app.main:app --host 0.0.0.0 --port $PORT"
|
|
7
|
+
restartPolicyType = "on-failure"
|
|
8
|
+
restartPolicyMaxRetries = 5
|
|
9
|
+
|
|
10
|
+
[env]
|
|
11
|
+
# Add your env variables in the Railway dashboard under Variables
|
|
12
|
+
# DATABASE_URL =
|
|
13
|
+
# JWT_SECRET =
|