This commit is contained in:
Ho Ngoc Hai
2025-12-27 01:31:10 +07:00
commit 4da46b5b8e
205 changed files with 21063 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,572 @@
---
name: comment-code
description: Add bilingual code comments in Vietnamese and English for better documentation. Use when adding comments to code, documenting functions/classes, or when user requests Vietnamese/English documentation.
---
# Bilingual Code Comments
Add comprehensive code comments in both Vietnamese and English to improve code readability for international and Vietnamese teams.
## When to Use
- Adding comments to new code
- Documenting existing code
- Creating JSDoc/TSDoc documentation
- Writing function/class descriptions
- Explaining complex logic
- Adding inline comments
## Comment Format
### Single-line Comments
```typescript
// EN: Initialize database connection
// VI: Khởi tạo kết nối database
const db = await createConnection();
```
### Multi-line Comments
```typescript
/**
* EN: Validates user credentials and returns JWT token
* VI: Xác thực thông tin đăng nhập và trả về JWT token
*
* @param email - User email address / Địa chỉ email người dùng
* @param password - User password / Mật khẩu người dùng
* @returns JWT token / Mã JWT token
* @throws AuthenticationError if credentials invalid / Lỗi xác thực nếu thông tin không hợp lệ
*/
async function login(email: string, password: string): Promise<string> {
// Implementation
}
```
## Language-Specific Patterns
### TypeScript/JavaScript
#### Function Documentation
```typescript
/**
* EN: Calculates the total price including tax and discount
* VI: Tính tổng giá bao gồm thuế và giảm giá
*
* @param basePrice - Original price / Giá gốc
* @param taxRate - Tax rate (0-1) / Tỷ lệ thuế (0-1)
* @param discount - Discount amount / Số tiền giảm giá
* @returns Final price / Giá cuối cùng
*/
function calculateTotal(
basePrice: number,
taxRate: number,
discount: number
): number {
// EN: Apply discount first
// VI: Áp dụng giảm giá trước
const discountedPrice = basePrice - discount;
// EN: Then calculate tax
// VI: Sau đó tính thuế
const tax = discountedPrice * taxRate;
return discountedPrice + tax;
}
```
#### Class Documentation
```typescript
/**
* EN: Handles user authentication and authorization
* VI: Xử lý xác thực và phân quyền người dùng
*/
export class AuthService {
/**
* EN: JWT secret key from environment
* VI: Khóa bí mật JWT từ biến môi trường
*/
private readonly jwtSecret: string;
/**
* EN: Initialize auth service with configuration
* VI: Khởi tạo service xác thực với cấu hình
*
* @param config - Authentication configuration / Cấu hình xác thực
*/
constructor(config: AuthConfig) {
this.jwtSecret = config.jwtSecret;
}
/**
* EN: Verify JWT token and return user payload
* VI: Xác minh JWT token và trả về thông tin người dùng
*
* @param token - JWT token to verify / JWT token cần xác minh
* @returns User payload / Thông tin người dùng
* @throws TokenExpiredError if token expired / Lỗi token hết hạn
*/
async verifyToken(token: string): Promise<UserPayload> {
// Implementation
}
}
```
#### Interface/Type Documentation
```typescript
/**
* EN: User data transfer object
* VI: Đối tượng truyền dữ liệu người dùng
*/
interface UserDto {
/** EN: Unique user identifier / VI: Mã định danh duy nhất */
id: string;
/** EN: User email address / VI: Địa chỉ email người dùng */
email: string;
/** EN: User display name / VI: Tên hiển thị người dùng */
name: string;
/** EN: User role for authorization / VI: Vai trò người dùng để phân quyền */
role: 'admin' | 'user' | 'guest';
}
```
#### Complex Logic Comments
```typescript
async function processPayment(order: Order): Promise<PaymentResult> {
// EN: Step 1: Validate order data
// VI: Bước 1: Xác thực dữ liệu đơn hàng
if (!order.items.length) {
throw new Error('Order must have items / Đơn hàng phải có sản phẩm');
}
// EN: Step 2: Calculate total amount
// VI: Bước 2: Tính tổng số tiền
const total = order.items.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
// EN: Step 3: Process payment through gateway
// VI: Bước 3: Xử lý thanh toán qua cổng thanh toán
try {
const result = await paymentGateway.charge({
amount: total,
currency: 'VND',
orderId: order.id,
});
// EN: Step 4: Update order status on success
// VI: Bước 4: Cập nhật trạng thái đơn hàng khi thành công
await updateOrderStatus(order.id, 'paid');
return result;
} catch (error) {
// EN: Log error and mark order as failed
// VI: Ghi log lỗi và đánh dấu đơn hàng thất bại
logger.error('Payment failed', { orderId: order.id, error });
await updateOrderStatus(order.id, 'failed');
throw error;
}
}
```
### React/Next.js Components
```typescript
/**
* EN: User profile card component
* VI: Component thẻ hồ sơ người dùng
*
* @param user - User data to display / Dữ liệu người dùng để hiển thị
* @param onEdit - Callback when edit button clicked / Callback khi nhấn nút chỉnh sửa
*/
export function UserCard({ user, onEdit }: UserCardProps) {
// EN: Local state for loading status
// VI: State cục bộ cho trạng thái loading
const [isLoading, setIsLoading] = useState(false);
/**
* EN: Handle user profile update
* VI: Xử lý cập nhật hồ sơ người dùng
*/
const handleUpdate = async () => {
setIsLoading(true);
try {
await updateUser(user.id, user);
onEdit?.();
} finally {
setIsLoading(false);
}
};
return (
<div className="user-card">
{/* EN: Display user avatar / VI: Hiển thị avatar người dùng */}
<img src={user.avatar} alt={user.name} />
{/* EN: User information section / VI: Phần thông tin người dùng */}
<div className="user-info">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
</div>
);
}
```
### Prisma Schema
```prisma
/// EN: User model for authentication and profile
/// VI: Model người dùng cho xác thực và hồ sơ
model User {
/// EN: Unique identifier
/// VI: Mã định danh duy nhất
id String @id @default(cuid())
/// EN: User email (unique)
/// VI: Email người dùng (duy nhất)
email String @unique
/// EN: Hashed password
/// VI: Mật khẩu đã mã hóa
password String
/// EN: Display name
/// VI: Tên hiển thị
name String
/// EN: Account creation timestamp
/// VI: Thời gian tạo tài khoản
createdAt DateTime @default(now())
/// EN: Last update timestamp
/// VI: Thời gian cập nhật cuối
updatedAt DateTime @updatedAt
@@map("users")
}
```
### Configuration Files
```typescript
// config/database.config.ts
/**
* EN: Database configuration for Prisma and Neon PostgreSQL
* VI: Cấu hình database cho Prisma và Neon PostgreSQL
*/
export const databaseConfig = {
/**
* EN: Database connection URL from environment
* VI: URL kết nối database từ biến môi trường
*/
url: process.env.DATABASE_URL,
/**
* EN: Connection pool settings
* VI: Cài đặt connection pool
*/
pool: {
// EN: Minimum connections in pool
// VI: Số kết nối tối thiểu trong pool
min: 2,
// EN: Maximum connections in pool
// VI: Số kết nối tối đa trong pool
max: 10,
},
/**
* EN: Enable query logging in development
* VI: Bật ghi log truy vấn trong môi trường phát triển
*/
logging: process.env.NODE_ENV === 'development',
};
```
### API Routes/Controllers
```typescript
/**
* EN: User management controller
* VI: Controller quản lý người dùng
*/
export class UserController {
/**
* EN: Get user by ID
* VI: Lấy thông tin người dùng theo ID
*
* GET /api/users/:id
*/
async getById(req: Request, res: Response) {
try {
// EN: Extract user ID from params
// VI: Lấy ID người dùng từ params
const { id } = req.params;
// EN: Fetch user from database
// VI: Lấy người dùng từ database
const user = await this.userService.findById(id);
// EN: Return 404 if user not found
// VI: Trả về 404 nếu không tìm thấy người dùng
if (!user) {
return res.status(404).json({
success: false,
error: {
code: 'USER_NOT_FOUND',
message: 'User not found / Không tìm thấy người dùng',
},
});
}
// EN: Return user data
// VI: Trả về dữ liệu người dùng
return res.json({
success: true,
data: user,
});
} catch (error) {
// EN: Handle unexpected errors
// VI: Xử lý lỗi không mong đợi
logger.error('Failed to get user', { error, userId: req.params.id });
return res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'Internal server error / Lỗi máy chủ nội bộ',
},
});
}
}
}
```
### Middleware
```typescript
/**
* EN: Authentication middleware to verify JWT tokens
* VI: Middleware xác thực để kiểm tra JWT token
*/
export function authMiddleware(
req: Request,
res: Response,
next: NextFunction
) {
// EN: Extract token from Authorization header
// VI: Lấy token từ header Authorization
const authHeader = req.headers.authorization;
const token = authHeader?.replace('Bearer ', '');
// EN: Return 401 if no token provided
// VI: Trả về 401 nếu không có token
if (!token) {
return res.status(401).json({
success: false,
error: {
code: 'NO_TOKEN',
message: 'Authentication required / Yêu cầu xác thực',
},
});
}
try {
// EN: Verify token and extract payload
// VI: Xác minh token và lấy payload
const payload = jwt.verify(token, JWT_SECRET);
// EN: Attach user info to request
// VI: Gắn thông tin người dùng vào request
req.user = payload;
next();
} catch (error) {
// EN: Return 401 if token invalid or expired
// VI: Trả về 401 nếu token không hợp lệ hoặc hết hạn
return res.status(401).json({
success: false,
error: {
code: 'INVALID_TOKEN',
message: 'Invalid or expired token / Token không hợp lệ hoặc hết hạn',
},
});
}
}
```
## Best Practices
### 1. Comment Placement
- Place bilingual comments together (EN first, then VI)
- Keep comments close to the code they describe
- Use JSDoc format for functions and classes
### 2. Comment Content
- **DO**: Explain WHY, not WHAT (code shows what)
- **DO**: Document complex logic and business rules
- **DO**: Include parameter descriptions and return types
- **DO**: Document error conditions and exceptions
- **DON'T**: State the obvious
- **DON'T**: Write redundant comments
### 3. Language Guidelines
- **English**: Use clear, concise technical English
- **Vietnamese**: Use proper Vietnamese technical terms
- Keep translations accurate and natural
- Use consistent terminology across codebase
### 4. Special Cases
#### TODO Comments
```typescript
// TODO EN: Implement caching for better performance
// TODO VI: Triển khai caching để cải thiện hiệu suất
```
#### FIXME Comments
```typescript
// FIXME EN: This causes memory leak, needs refactoring
// FIXME VI: Đoạn này gây rò rỉ bộ nhớ, cần refactor
```
#### WARNING Comments
```typescript
// WARNING EN: Do not modify this without updating the database schema
// WARNING VI: Không sửa đổi phần này mà không cập nhật schema database
```
### 5. Documentation Priority
**High Priority** (Always document):
- Public APIs and exported functions
- Complex algorithms and business logic
- Security-critical code
- Configuration and environment setup
- Error handling strategies
**Medium Priority** (Document when helpful):
- Helper functions with non-obvious behavior
- Data transformations
- Integration points with external services
**Low Priority** (Optional):
- Simple getters/setters
- Self-explanatory code
- Standard CRUD operations
## Examples by Use Case
### Authentication Flow
```typescript
/**
* EN: Complete authentication flow with refresh token
* VI: Luồng xác thực hoàn chỉnh với refresh token
*/
export class AuthFlow {
/**
* EN: Login user and generate token pair
* VI: Đăng nhập người dùng và tạo cặp token
*/
async login(credentials: LoginDto) {
// EN: Step 1: Validate credentials
// VI: Bước 1: Xác thực thông tin đăng nhập
const user = await this.validateCredentials(credentials);
// EN: Step 2: Generate access token (15min expiry)
// VI: Bước 2: Tạo access token (hết hạn sau 15 phút)
const accessToken = this.generateAccessToken(user);
// EN: Step 3: Generate refresh token (7 days expiry)
// VI: Bước 3: Tạo refresh token (hết hạn sau 7 ngày)
const refreshToken = this.generateRefreshToken(user);
// EN: Step 4: Store refresh token in database
// VI: Bước 4: Lưu refresh token vào database
await this.storeRefreshToken(user.id, refreshToken);
return { accessToken, refreshToken };
}
}
```
### Database Transaction
```typescript
/**
* EN: Transfer money between accounts with transaction
* VI: Chuyển tiền giữa các tài khoản với transaction
*/
async function transferMoney(
fromAccountId: string,
toAccountId: string,
amount: number
) {
// EN: Use transaction to ensure atomicity
// VI: Sử dụng transaction để đảm bảo tính nguyên tử
return await prisma.$transaction(async (tx) => {
// EN: Deduct from sender account
// VI: Trừ tiền từ tài khoản người gửi
await tx.account.update({
where: { id: fromAccountId },
data: { balance: { decrement: amount } },
});
// EN: Add to receiver account
// VI: Cộng tiền vào tài khoản người nhận
await tx.account.update({
where: { id: toAccountId },
data: { balance: { increment: amount } },
});
// EN: Create transaction record
// VI: Tạo bản ghi giao dịch
return await tx.transaction.create({
data: {
fromAccountId,
toAccountId,
amount,
type: 'TRANSFER',
},
});
});
}
```
## Integration with Project Rules
When commenting code in this project:
- Follow the code organization from `project-rules` skill
- Use consistent terminology with project documentation
- Align with the API response format standards
- Document according to the testing standards
- Include security considerations where relevant
## Quick Reference
### Function Comment Template
```typescript
/**
* EN: [Brief description in English]
* VI: [Mô tả ngắn gọn bằng tiếng Việt]
*
* @param paramName - EN description / VI mô tả
* @returns EN description / VI mô tả
* @throws ErrorType EN when / VI khi nào
*/
```
### Inline Comment Template
```typescript
// EN: [English explanation]
// VI: [Giải thích tiếng Việt]
```
### Complex Block Template
```typescript
// EN: Step N: [What this block does]
// VI: Bước N: [Block này làm gì]
```

View File

@@ -0,0 +1,476 @@
---
name: project-rules
description: GoodGo Microservices Platform coding standards, architecture patterns, and development guidelines. Use when working with this monorepo to ensure code follows project conventions for services, apps, packages, infrastructure, or when making architectural decisions.
---
# GoodGo Project Rules
## Architecture Overview
This is an enterprise-grade microservices monorepo with:
- **Apps**: Next.js (web-admin, web-client) + Flutter (app-admin, app-client)
- **Services**: Node.js/TypeScript microservices with Express
- **Packages**: Shared libraries (logger, types, http-client, auth-sdk, tracing, config)
- **Infrastructure**: Traefik, Redis, Neon PostgreSQL, Observability stack
- **Deployments**: Local (Docker Compose), Staging/Production (Kubernetes)
## Tech Stack Requirements
### Frontend
- **Web**: Next.js 14+ with App Router, TypeScript, Tailwind CSS, Zustand
- **Mobile**: Flutter 3.x with Provider pattern
- Use shared types from `@goodgo/types` package
- API calls via `@goodgo/http-client`
### Backend Services
- **Runtime**: Node.js 20+, TypeScript 5+
- **Framework**: Express with modular architecture
- **Database**: Prisma ORM with Neon PostgreSQL
- **Validation**: Zod for DTOs
- **Logging**: Use `@goodgo/logger` package
- **Tracing**: Use `@goodgo/tracing` with OpenTelemetry
- **Auth**: JWT tokens, use `@goodgo/auth-sdk`
### Infrastructure
- **API Gateway**: Traefik with path-based routing
- **Caching**: Redis for sessions/cache
- **Monitoring**: Prometheus + Grafana + Loki
- **Containerization**: Docker with multi-stage builds
## Code Organization Standards
### Service Structure
```
services/service-name/
├── src/
│ ├── config/ # Configuration files
│ ├── modules/ # Feature modules
│ │ └── feature/
│ │ ├── feature.controller.ts
│ │ ├── feature.service.ts
│ │ ├── feature.dto.ts
│ │ └── feature.module.ts
│ ├── middlewares/ # Express middlewares
│ ├── routes/ # Route definitions
│ └── main.ts # Entry point
├── prisma/
│ ├── schema.prisma
│ └── seed.ts
├── Dockerfile
├── package.json
└── tsconfig.json
```
### Package Structure
```
packages/package-name/
├── src/
│ └── index.ts # Main export
├── package.json
├── tsconfig.json
└── README.md
```
### App Structure
```
apps/web-*/
├── src/
│ ├── app/ # Next.js App Router
│ ├── services/api/ # API clients
│ └── stores/ # Zustand stores
├── Dockerfile
└── package.json
```
## Naming Conventions
### Files & Folders
- **Services**: `kebab-case` (e.g., `auth-service`, `user-service`)
- **Packages**: `kebab-case` (e.g., `http-client`, `auth-sdk`)
- **Files**: `kebab-case.type.ts` (e.g., `user.controller.ts`, `auth.service.ts`)
- **Components**: `PascalCase.tsx` for React, `snake_case.dart` for Flutter
### Code
- **Classes**: `PascalCase` (e.g., `UserService`, `AuthController`)
- **Functions/Methods**: `camelCase` (e.g., `getUserById`, `validateToken`)
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `JWT_SECRET`, `API_VERSION`)
- **Interfaces/Types**: `PascalCase` with descriptive names (e.g., `UserResponse`, `LoginDto`)
### Package Names
- Use `@goodgo/` scope for all packages
- Format: `@goodgo/package-name`
## Development Workflow
### Creating New Service
1. Copy `services/_template/` as starting point
2. Update `package.json` with correct name: `@goodgo/service-name`
3. Add Prisma schema if database needed
4. Create modules following modular pattern
5. Add health check endpoint
6. Create Dockerfile with multi-stage build
7. Add to `turbo.json` if needed
8. Update documentation
### Creating New Package
1. Create in `packages/` directory
2. Use TypeScript with strict mode
3. Export from `src/index.ts`
4. Add to `pnpm-workspace.yaml`
5. Document usage in README.md
6. Version with semantic versioning
### Adding Dependencies
```bash
# Service/App dependency
pnpm --filter @goodgo/service-name add package-name
# Workspace package dependency
pnpm --filter @goodgo/service-name add @goodgo/logger
# Dev dependency
pnpm --filter @goodgo/service-name add -D @types/package-name
# Root dependency
pnpm add -w package-name
```
### Database Workflow
1. Update Prisma schema in service
2. Generate migration: `pnpm --filter @goodgo/service-name prisma migrate dev`
3. Generate client: `pnpm --filter @goodgo/service-name prisma generate`
4. Use Neon PostgreSQL (no local PostgreSQL needed)
5. Connection pooling via Prisma
## Code Quality Standards
### TypeScript
- Enable strict mode
- No `any` types (use `unknown` if needed)
- Define interfaces for all DTOs
- Use Zod for runtime validation
- Export types from `@goodgo/types` when shared
### Error Handling
- Use custom error classes
- Implement global error middleware
- Log errors with context using `@goodgo/logger`
- Return consistent error responses:
```typescript
{
success: false,
error: {
code: 'ERROR_CODE',
message: 'Human readable message',
details?: any
}
}
```
### API Response Format
```typescript
// Success
{
success: true,
data: any
}
// Error
{
success: false,
error: {
code: string,
message: string,
details?: any
}
}
```
### Logging
```typescript
import { logger } from '@goodgo/logger';
logger.info('Message', { context: 'data' });
logger.error('Error', { error, context: 'data' });
logger.debug('Debug info', { data });
```
### Environment Variables
- Use `.env.example` as template
- Never commit `.env` files
- Validate env vars at startup
- Use Zod for env validation
- Document all env vars in README
## Testing Standards
### Unit Tests
- Place tests next to source: `feature.service.test.ts`
- Use Jest as test runner
- Mock external dependencies
- Aim for >80% coverage
### Integration Tests
- Test API endpoints end-to-end
- Use test database (Neon branch)
- Clean up test data after tests
### Test Commands
```bash
pnpm test # All tests
pnpm --filter @goodgo/service-name test # Service tests
pnpm test:coverage # With coverage
```
## Docker Standards
### Multi-stage Builds
```dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
COPY . .
RUN pnpm build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/main.js"]
```
### Image Naming
- Format: `goodgo/service-name:version`
- Use semantic versioning
- Tag latest for production
## Git Workflow
### Branch Naming
- Feature: `feature/description`
- Bug fix: `fix/description`
- Hotfix: `hotfix/description`
- Release: `release/version`
### Commit Messages
Follow Conventional Commits:
```
type(scope): subject
body (optional)
footer (optional)
```
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
Examples:
- `feat(auth): add refresh token endpoint`
- `fix(user): resolve email validation bug`
- `docs(readme): update installation steps`
### Pull Requests
- Use PR template
- Link related issues
- Request review from team
- Ensure CI passes
- Squash merge to main
## CI/CD Standards
### GitHub Actions
- Run on PR: lint, test, build
- Deploy to staging on merge to `develop`
- Deploy to production on merge to `main`
- Use secrets for sensitive data
### Deployment Checklist
- [ ] All tests pass
- [ ] No linter errors
- [ ] Environment variables configured
- [ ] Database migrations applied
- [ ] Documentation updated
- [ ] Monitoring configured
## Security Guidelines
### Authentication
- Use JWT with short expiry (15min access, 7d refresh)
- Store tokens securely (httpOnly cookies for web)
- Implement refresh token rotation
- Use `@goodgo/auth-sdk` for consistency
### Authorization
- Implement RBAC (Role-Based Access Control)
- Check permissions at service level
- Use middleware for route protection
### Data Protection
- Hash passwords with bcrypt (cost factor 12)
- Encrypt sensitive data at rest
- Use HTTPS in production
- Sanitize user inputs
- Validate all DTOs with Zod
### Secrets Management
- Use environment variables
- Kubernetes secrets for production
- GitHub secrets for CI/CD
- Never hardcode secrets
- Rotate secrets regularly
## Performance Guidelines
### Backend
- Use Redis for caching
- Implement database connection pooling
- Add pagination for list endpoints
- Use database indexes
- Implement rate limiting
### Frontend
- Use Next.js Image optimization
- Implement code splitting
- Lazy load components
- Use React.memo for expensive renders
- Optimize bundle size
### Database
- Use Prisma query optimization
- Add indexes for frequent queries
- Use database transactions
- Implement soft deletes
- Regular backups (Neon handles this)
## Monitoring & Observability
### Metrics
- Use Prometheus for metrics
- Track: request count, duration, errors
- Set up alerts for critical metrics
### Logging
- Structured logging with `@goodgo/logger`
- Include trace IDs for correlation
- Log levels: error, warn, info, debug
- Aggregate logs with Loki
### Tracing
- Use OpenTelemetry via `@goodgo/tracing`
- Trace cross-service requests
- Include context in traces
## Documentation Standards
### Code Documentation
- JSDoc for public APIs
- Inline comments for complex logic
- README.md for each service/package
- Keep docs up to date
### API Documentation
- OpenAPI/Swagger specs in `docs/api/openapi/`
- Document all endpoints
- Include request/response examples
- Document error codes
### Architecture Documentation
- System design in `docs/architecture/`
- Service communication patterns
- Data flow diagrams
- Decision records (ADRs)
## Common Patterns
### Module Pattern
```typescript
// feature.module.ts
export class FeatureModule {
controller: FeatureController;
service: FeatureService;
constructor() {
this.service = new FeatureService();
this.controller = new FeatureController(this.service);
}
}
```
### DTO Pattern
```typescript
// feature.dto.ts
import { z } from 'zod';
export const CreateFeatureDto = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export type CreateFeatureDto = z.infer<typeof CreateFeatureDto>;
```
### Controller Pattern
```typescript
// feature.controller.ts
export class FeatureController {
constructor(private service: FeatureService) {}
async create(req: Request, res: Response) {
try {
const dto = CreateFeatureDto.parse(req.body);
const result = await this.service.create(dto);
res.json({ success: true, data: result });
} catch (error) {
next(error);
}
}
}
```
### Service Pattern
```typescript
// feature.service.ts
export class FeatureService {
constructor(private prisma: PrismaClient) {}
async create(dto: CreateFeatureDto) {
return this.prisma.feature.create({ data: dto });
}
}
```
## Troubleshooting
### Common Issues
- **Port conflicts**: Check `deployments/local/docker-compose.yml`
- **Database connection**: Verify Neon DATABASE_URL in `.env.local`
- **Module not found**: Run `pnpm install` in root
- **Build errors**: Clear `dist/` and rebuild
- **Type errors**: Run `pnpm --filter @goodgo/service-name prisma generate`
### Debug Commands
```bash
# Check service logs
docker-compose logs -f service-name
# Check database connection
pnpm --filter @goodgo/service-name prisma studio
# Rebuild containers
docker-compose up -d --build
# Check running services
docker-compose ps
```
## Resources
- [Architecture Docs](../../docs/architecture/)
- [API Specs](../../docs/api/openapi/)
- [Development Guide](../../docs/guides/development.md)
- [Deployment Guide](../../docs/guides/deployment.md)
- [Neon Database Guide](../../docs/guides/neon-database.md)
- [Contributing Guide](../../CONTRIBUTING.md)

73
.github/workflows/ci-auth-service.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
name: Auth Service CI
on:
push:
paths:
- 'services/auth-service/**'
- 'packages/**'
pull_request:
paths:
- 'services/auth-service/**'
- 'packages/**'
jobs:
lint-and-test:
runs-on: ubuntu-latest
# Use Neon test database if available, otherwise use PostgreSQL service
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate Prisma Client
run: pnpm --filter @goodgo/auth-service prisma:generate
env:
# Use Neon test DB if available, otherwise fallback to local PostgreSQL
DATABASE_URL: ${{ secrets.NEON_DATABASE_URL_TEST != '' && secrets.NEON_DATABASE_URL_TEST || 'postgresql://testuser:testpass@localhost:5432/test_db' }}
- name: Run migrations
run: pnpm --filter @goodgo/auth-service prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.NEON_DATABASE_URL_TEST != '' && secrets.NEON_DATABASE_URL_TEST || 'postgresql://testuser:testpass@localhost:5432/test_db' }}
- name: Lint
run: pnpm --filter @goodgo/auth-service lint
- name: Type check
run: pnpm --filter @goodgo/auth-service typecheck
- name: Build
run: pnpm --filter @goodgo/auth-service build
- name: Test
run: pnpm --filter @goodgo/auth-service test
env:
DATABASE_URL: ${{ secrets.NEON_DATABASE_URL_TEST != '' && secrets.NEON_DATABASE_URL_TEST || 'postgresql://testuser:testpass@localhost:5432/test_db' }}

57
.github/workflows/ci-mobile.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Mobile Apps CI
on:
push:
paths:
- 'apps/app-*/**'
- 'packages/**'
pull_request:
paths:
- 'apps/app-*/**'
- 'packages/**'
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.16.0'
channel: 'stable'
- name: Install Flutter dependencies (Admin)
run: |
cd apps/app-admin
flutter pub get
- name: Install Flutter dependencies (Client)
run: |
cd apps/app-client
flutter pub get
- name: Analyze Flutter code (Admin)
run: |
cd apps/app-admin
flutter analyze
- name: Analyze Flutter code (Client)
run: |
cd apps/app-client
flutter analyze

49
.github/workflows/ci-web.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Web Apps CI
on:
push:
paths:
- 'apps/web-*/**'
- 'packages/**'
pull_request:
paths:
- 'apps/web-*/**'
- 'packages/**'
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint Web Admin
run: pnpm --filter @goodgo/web-admin lint || echo "Skipped"
- name: Lint Web Client
run: pnpm --filter @goodgo/web-client lint || echo "Skipped"
- name: Type check Web Admin
run: pnpm --filter @goodgo/web-admin typecheck || echo "Skipped"
- name: Type check Web Client
run: pnpm --filter @goodgo/web-client typecheck || echo "Skipped"
- name: Build Web Admin
run: pnpm --filter @goodgo/web-admin build || echo "Skipped"
- name: Build Web Client
run: pnpm --filter @goodgo/web-client build || echo "Skipped"

58
.github/workflows/deploy-production.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Deploy to Production
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run database migrations
run: |
cd services/auth-service
pnpm prisma generate
pnpm prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.NEON_DATABASE_URL_PRODUCTION }}
- name: Setup kubectl
uses: azure/setup-kubectl@v3
- name: Configure kubectl
run: |
echo "${{ secrets.KUBECONFIG_PRODUCTION }}" | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
- name: Deploy Auth Service
run: |
export KUBECONFIG=./kubeconfig
kubectl apply -f deployments/production/kubernetes/auth-service.yaml
kubectl apply -f deployments/production/kubernetes/configmap.yaml
kubectl apply -f deployments/production/kubernetes/ingress.yaml
kubectl rollout status deployment/auth-service -n production
- name: Deploy Web App
run: |
export KUBECONFIG=./kubeconfig
kubectl apply -f deployments/production/kubernetes/web-app.yaml || echo "Web app deployment not configured"
kubectl rollout status deployment/web-app -n production || echo "Web app deployment not configured"

57
.github/workflows/deploy-staging.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Deploy to Staging
on:
push:
branches:
- develop
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run database migrations
run: |
cd services/auth-service
pnpm prisma generate
pnpm prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.NEON_DATABASE_URL_STAGING }}
- name: Setup kubectl
uses: azure/setup-kubectl@v3
- name: Configure kubectl
run: |
echo "${{ secrets.KUBECONFIG_STAGING }}" | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
- name: Deploy Auth Service
run: |
export KUBECONFIG=./kubeconfig
kubectl apply -f deployments/staging/kubernetes/auth-service.yaml
kubectl apply -f deployments/staging/kubernetes/configmap.yaml
kubectl apply -f deployments/staging/kubernetes/ingress.yaml
kubectl rollout status deployment/auth-service -n staging
- name: Deploy Web App
run: |
export KUBECONFIG=./kubeconfig
kubectl apply -f deployments/staging/kubernetes/web-app.yaml || echo "Web app deployment not configured"
kubectl rollout status deployment/web-app -n staging || echo "Web app deployment not configured"

87
.github/workflows/docker-build.yml vendored Normal file
View File

@@ -0,0 +1,87 @@
name: Docker Build
on:
push:
branches:
- main
- develop
paths:
- 'services/auth-service/**'
- 'apps/web-*/**'
workflow_dispatch:
jobs:
build-auth-service:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Auth Service
uses: docker/build-push-action@v5
with:
context: ./services/auth-service
push: true
tags: |
goodgo/auth-service:latest
goodgo/auth-service:${{ github.sha }}
cache-from: type=registry,ref=goodgo/auth-service:buildcache
cache-to: type=registry,ref=goodgo/auth-service:buildcache,mode=max
build-web-admin:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Web Admin
uses: docker/build-push-action@v5
with:
context: ./apps/web-admin
push: true
tags: |
goodgo/web-admin:latest
goodgo/web-admin:${{ github.sha }}
cache-from: type=registry,ref=goodgo/web-admin:buildcache
cache-to: type=registry,ref=goodgo/web-admin:buildcache,mode=max
build-web-client:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Web Client
uses: docker/build-push-action@v5
with:
context: ./apps/web-client
push: true
tags: |
goodgo/web-client:latest
goodgo/web-client:${{ github.sha }}
cache-from: type=registry,ref=goodgo/web-client:buildcache
cache-to: type=registry,ref=goodgo/web-client:buildcache,mode=max

34
.github/workflows/pr-checks.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint all
run: pnpm lint
- name: Type check all
run: pnpm typecheck
- name: Build all
run: pnpm build

82
.gitignore vendored Normal file
View File

@@ -0,0 +1,82 @@
# Dependencies
node_modules/
.pnp
.pnp.js
# Testing
coverage/
*.lcov
.nyc_output
# Production
dist/
build/
.next/
out/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*.local
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.project
.classpath
.settings/
# Prisma
prisma/migrations/*.sql
# Docker
.dockerignore
# Secrets
infra/secrets/**/*
!infra/secrets/**/.env.example
!infra/secrets/**/.gitignore
# Temporary files
*.tmp
*.temp
.cache/
.turbo/
# Database
*.db
*.sqlite
*.sqlite3
# Certificates
*.pem
*.key
*.crt
infra/traefik/certs/*
# Build artifacts
*.tsbuildinfo

93
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,93 @@
# Contributing Guide
Thank you for considering contributing to GoodGo Microservices Platform!
## Development Workflow
### Branch Strategy
We follow Git Flow:
- `main` - Production-ready code
- `develop` - Integration branch for features
- `feature/*` - New features
- `bugfix/*` - Bug fixes
- `hotfix/*` - Critical production fixes
### Commit Convention
We use [Conventional Commits](https://www.conventionalcommits.org/):
```bash
feat: add login endpoint
fix: resolve token expiration issue
docs: update API documentation
refactor: restructure auth module
test: add unit tests for auth service
chore: update dependencies
ci: add GitHub Actions workflow
perf: optimize database queries
```
### Pull Request Process
1. Create feature branch from `develop`
2. Implement changes with tests
3. Run linter and tests locally
4. Push and create PR
5. CI/CD will auto-run checks
6. Code review required
7. Merge into `develop` (auto-deploy to staging)
8. QA testing on staging
9. Merge into `main` (manual deploy to production)
### Code Standards
- **TypeScript**: Strict mode enabled
- **Linting**: ESLint with shared config
- **Formatting**: Prettier
- **Testing**: Minimum 80% coverage
- **Documentation**: JSDoc for public APIs
### Running Tests
```bash
# All tests
pnpm test
# Specific package
pnpm --filter @goodgo/auth-service test
# With coverage
pnpm --filter @goodgo/auth-service test:coverage
```
### Code Review Checklist
- [ ] Code follows style guidelines
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] No console.logs or debug code
- [ ] Environment variables documented
- [ ] Breaking changes documented
## Adding a New Service
1. Copy `services/_template` to `services/new-service`
2. Update package.json name and dependencies
3. Implement service logic
4. Add tests
5. Update documentation
6. Create CI/CD workflow
## Adding a New Package
1. Create package in `packages/new-package`
2. Add to workspace
3. Export from index.ts
4. Add tests
5. Document usage
## Questions?
Feel free to open an issue or contact the team.

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 GoodGo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

200
README.md Normal file
View File

@@ -0,0 +1,200 @@
# GoodGo Microservices Platform
Enterprise-grade microservices monorepo built with modern technologies and best practices.
## 🏗️ Architecture
This monorepo follows a microservices architecture pattern with:
- **Apps**: Frontend applications (Web + Mobile)
- **Services**: Backend microservices (starting with Auth Service)
- **Packages**: Shared libraries and utilities
- **Infrastructure**: Traefik, Observability, Databases
- **Deployments**: Environment-specific configurations
## 📁 Project Structure
```
├── apps/ # Frontend applications
│ ├── web-admin/ # Next.js admin web application
│ ├── web-client/ # Next.js client web application
│ ├── app-admin/ # Flutter admin mobile application
│ └── app-client/ # Flutter client mobile application
├── services/ # Backend microservices
│ ├── auth-service/ # Authentication service
│ └── _template/ # Service template
├── packages/ # Shared libraries
│ ├── logger/ # Centralized logging
│ ├── types/ # TypeScript types
│ ├── config/ # Shared configs
│ ├── http-client/ # API client wrapper
│ ├── auth-sdk/ # Auth utilities
│ └── tracing/ # OpenTelemetry setup
├── infra/ # Infrastructure as Code
│ ├── traefik/ # Traefik configuration
│ ├── docker/ # Docker configs
│ ├── observability/ # Monitoring stack
│ └── databases/ # Database configs
├── deployments/ # Environment configs
│ ├── local/ # Local development
│ ├── staging/ # Staging environment
│ └── production/ # Production environment
├── .github/ # CI/CD workflows
├── scripts/ # Automation scripts
└── docs/ # Documentation
```
## 🚀 Getting Started
### Prerequisites
- Node.js >= 20.0.0
- pnpm >= 8.0.0
- Docker & Docker Compose
- Neon account (https://neon.tech) - for PostgreSQL database
### Installation
```bash
# Install dependencies
pnpm install
# Start all services (development)
pnpm dev
# Start specific service
pnpm --filter @goodgo/auth-service dev
# Build all
pnpm build
# Run tests
pnpm test
# Run linter
pnpm lint
```
### Local Development
```bash
# Setup Neon database (first time only)
./scripts/db/setup-neon.sh
# Or manually: Create .env.local with Neon DATABASE_URL
# Start infrastructure (Redis, Traefik - no PostgreSQL needed)
cd deployments/local
docker-compose up -d
# Run migrations
./scripts/db/migrate.sh auth-service dev
# Start services
pnpm dev
```
**Note**: This project uses Neon PostgreSQL. No local PostgreSQL setup needed!
## 🛠️ Tech Stack
**Frontend:**
- Web: Next.js 14+ (App Router) - Admin & Client portals
- Mobile: Flutter 3.x - Admin & Client apps
- TypeScript / Dart
- Tailwind CSS
- Zustand / Provider
**Backend:**
- Node.js + TypeScript
- Express/NestJS
- Prisma ORM
- PostgreSQL (Neon - serverless)
- Redis
**Infrastructure:**
- Traefik (API Gateway)
- Docker & Docker Compose
- Kubernetes (production)
- Prometheus + Grafana + Loki
- OpenTelemetry
**DevOps:**
- GitHub Actions
- PNPM Workspaces
- Turborepo
## 📚 Documentation
- [Architecture Overview](docs/architecture/system-design.md)
- [API Documentation](docs/api/openapi/)
- [Development Guide](docs/guides/development.md)
- [Deployment Guide](docs/guides/deployment.md)
- [Contributing Guide](CONTRIBUTING.md)
## 🔐 Environment Variables
### Database (Neon)
All environments use Neon PostgreSQL:
- **Dev**: Set `DATABASE_URL` in `deployments/local/.env.local`
- **Staging/Prod**: Set in Kubernetes secrets or GitHub Secrets
See `infra/databases/neon/README.md` for setup instructions.
### Other Variables
Copy `.env.example` files in each service/app and configure accordingly.
**Important**: Never commit `.env` files. Use `.env.example` as templates.
## 🧪 Testing
```bash
# Run all tests
pnpm test
# Run tests for specific package
pnpm --filter @goodgo/auth-service test
# Run with coverage
pnpm --filter @goodgo/auth-service test:coverage
```
## 📦 Building
```bash
# Build all packages and services
pnpm build
# Build specific service
pnpm --filter @goodgo/auth-service build
```
## 🚢 Deployment
See [Deployment Guide](docs/guides/deployment.md) for detailed instructions.
### Quick Deploy
```bash
# Local
cd deployments/local && docker-compose up -d
# Staging (Kubernetes)
cd deployments/staging && kubectl apply -f kubernetes/
# Production
cd deployments/production && kubectl apply -f kubernetes/
```
## 🤝 Contributing
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 👥 Team
Built with ❤️ by the GoodGo team

265
SETUP_GUIDE.md Normal file
View File

@@ -0,0 +1,265 @@
# Environment Setup Guide - Hướng Dẫn Thiết Lập Môi Trường
## 🎯 Tổng Quan
Dự án sử dụng **Hybrid Environment Configuration** với 2 levels:
1. **Shared Environment** (`deployments/local/.env.local`) - Configs dùng chung
2. **Service-Specific Environment** (`services/<service>/.env.local`) - Configs riêng
### Lợi Ích
-**JWT secrets giống nhau** → Services có thể verify tokens của nhau
-**Mỗi service có database riêng** → Đúng microservices pattern
-**Override configs dễ dàng** → REDIS_HOST khác nhau cho Docker/Native
-**Không duplicate** → Maintain dễ hơn
## 🚀 Quick Start
### Option 1: Sử Dụng Script (Khuyến nghị)
```bash
# Tự động tạo tất cả env files
./scripts/dev/setup-env.sh
```
### Option 2: Setup Thủ Công
**Step 1: Shared Environment**
```bash
cp deployments/local/env.local.example deployments/local/.env.local
```
**Step 2: Service Environment**
```bash
cp services/auth-service/env.local.example services/auth-service/.env.local
```
## 📝 Cấu Hình Chi Tiết
### 1. Shared Environment (`deployments/local/.env.local`)
Chứa configs dùng chung cho TẤT CẢ services:
```bash
# JWT Secrets - PHẢI GIỐNG NHAU cho tất cả services
JWT_SECRET=dev-jwt-secret-change-in-production-min-32-chars
JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production-min-32-chars
# Redis (Docker hostname)
REDIS_HOST=redis
REDIS_PORT=6379
# Common
NODE_ENV=development
LOG_LEVEL=debug
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
```
### 2. Service Environment (`services/auth-service/.env.local`)
Chứa configs RIÊNG cho từng service:
```bash
# Database riêng cho service này
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/goodgo_auth_dev?sslmode=require&pgbouncer=true
# Service configs
PORT=5001
SERVICE_NAME=auth-service
# Override cho native dev (Redis trong Docker)
REDIS_HOST=localhost
```
## 🗄️ Database Setup
### Tạo Databases Trong Neon
Mỗi service cần database riêng:
1. Truy cập: https://console.neon.tech
2. Tạo databases:
- `goodgo_auth_dev` → auth-service
- `goodgo_user_dev` → user-service
- `goodgo_product_dev` → product-service
- etc.
3. Copy connection string vào `services/<service>/.env.local`:
```bash
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/goodgo_auth_dev?sslmode=require&pgbouncer=true
```
### Run Migrations
```bash
# Auth service
./scripts/db/migrate.sh auth-service dev
# Seed data (optional)
./scripts/db/seed.sh auth-service
```
## 🔧 Cách Hoạt Động
### Loading Order
Services load environment theo thứ tự:
1. **Shared env** (`deployments/local/.env.local`)
- JWT_SECRET, JWT_REFRESH_SECRET
- REDIS_HOST=redis, REDIS_PORT
- NODE_ENV, LOG_LEVEL, CORS_ORIGIN
2. **Service env** (`.env.local`)
- DATABASE_URL (riêng cho service)
- PORT (riêng cho service)
- REDIS_HOST=localhost (override cho native dev)
### Package.json Configuration
```json
{
"scripts": {
"dev": "dotenv -e ../../deployments/local/.env.local -e .env.local -- tsx watch src/main.ts"
}
}
```
Sử dụng `dotenv-cli` để load multiple env files.
## 📂 File Structure
```
Base/
├── deployments/local/
│ ├── env.local.example # Template cho shared env
│ └── .env.local # Shared env (gitignored)
├── services/
│ ├── auth-service/
│ │ ├── env.example # Old template (deprecated)
│ │ ├── env.local.example # New template cho service env
│ │ └── .env.local # Service env (gitignored)
│ │
│ └── user-service/
│ ├── env.local.example
│ └── .env.local
└── scripts/dev/
└── setup-env.sh # Auto setup script
```
## ⚠️ Important Notes
### 1. JWT Secrets
**MUST BE IDENTICAL** across all services:
- Services cần verify JWT tokens của nhau
- Nếu khác nhau → authentication sẽ fail
### 2. Database URLs
**MUST BE DIFFERENT** for each service:
- Mỗi service có database riêng (microservices pattern)
- Ví dụ:
- auth-service → `goodgo_auth_dev`
- user-service → `goodgo_user_dev`
### 3. Redis Host
**Khác nhau tùy môi trường:**
- **Shared env**: `REDIS_HOST=redis` (Docker hostname)
- **Service env**: `REDIS_HOST=localhost` (override cho native dev)
Khi chạy native, Redis trong Docker → dùng `localhost`
### 4. Git Ignore
Tất cả `.env.local` files đã được gitignored:
- `deployments/local/.env.local`
- `services/*/.env.local`
**NEVER commit** actual env files!
## 🎓 Examples
### Example 1: Auth Service Native Dev
```bash
# 1. Setup env
cp services/auth-service/env.local.example services/auth-service/.env.local
# 2. Edit .env.local
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/goodgo_auth_dev?sslmode=require
PORT=5001
REDIS_HOST=localhost # Override cho native dev
# 3. Start Redis in Docker
cd deployments/local && docker-compose up -d redis
# 4. Run service
pnpm --filter @goodgo/auth-service dev
```
### Example 2: Multiple Services
```bash
# 1. Setup all envs
./scripts/dev/setup-env.sh
# 2. Edit each service's .env.local with different DATABASE_URL
# 3. Start infrastructure
cd deployments/local && docker-compose up -d redis traefik
# 4. Run all services
pnpm dev
```
## 🔍 Troubleshooting
### Database Connection Failed
```bash
# Check if .env.local exists
ls -la services/auth-service/.env.local
# Check DATABASE_URL
cat services/auth-service/.env.local | grep DATABASE_URL
# Test connection
cd services/auth-service && pnpm prisma db pull
```
### JWT Secret Warning
Nếu thấy warning "Using default JWT_SECRET":
- Check `deployments/local/.env.local` có JWT_SECRET chưa
- Check service có load đúng env files chưa
### Redis Connection Failed
Khi chạy native:
- Check Redis container đang chạy: `docker ps | grep redis`
- Check REDIS_HOST=localhost trong service `.env.local`
## 📚 Tài Liệu Thêm
- [Local Development Guide](docs/vi/guides/local-development.md)
- [Neon Database Guide](docs/vi/guides/neon-database.md)
- [Service README](services/auth-service/README.md)
## 🎉 Ready to Go!
Sau khi setup xong:
```bash
# Start everything
./scripts/dev/start-all.sh
# Or selective
pnpm --filter @goodgo/auth-service dev
```
Happy coding! 🚀

44
apps/app-admin/.gitignore vendored Normal file
View File

@@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

20
apps/app-admin/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# Flutter build stage
FROM ghcr.io/cirruslabs/flutter:stable AS build
WORKDIR /app
# Copy pubspec files
COPY pubspec.yaml pubspec.lock ./
# Get dependencies
RUN flutter pub get
# Copy source code
COPY . .
# Build APK (for CI/CD purposes)
# Note: Actual mobile builds require native tooling
RUN flutter build apk --release || echo "Build skipped - requires Android SDK"
# For CI/CD testing only
CMD ["flutter", "test"]

43
apps/app-admin/README.md Normal file
View File

@@ -0,0 +1,43 @@
# App Admin (Flutter)
Flutter mobile application for GoodGo Platform Admin Panel.
## Features
- Flutter 3.x
- Material Design 3
- GoRouter for navigation
- Provider for state management
- API integration
## Prerequisites
- Flutter SDK >= 3.0.0
- Dart SDK >= 3.0.0
- Android Studio / Xcode
## Development
```bash
# Install dependencies
flutter pub get
# Run on Android
flutter run
# Run on iOS
flutter run -d ios
# Build APK
flutter build apk
# Build iOS
flutter build ios
```
## Environment Variables
Create `.env` file:
```
API_URL=http://localhost/api/v1
```

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'GoodGo Admin',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
routerConfig: _router,
);
}
}
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
),
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) {
return const LoginScreen();
},
),
],
);
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GoodGo Admin'),
),
body: const Center(
child: Text('Welcome to GoodGo Admin'),
),
);
}
}
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: const Center(
child: Text('Login Screen'),
),
);
}
}

View File

@@ -0,0 +1,24 @@
name: app_admin
description: GoodGo Platform Admin Mobile Application (Flutter)
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.6
http: ^1.1.0
shared_preferences: ^2.2.2
provider: ^6.1.1
go_router: ^13.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true

View File

@@ -0,0 +1,20 @@
# Flutter build stage
FROM ghcr.io/cirruslabs/flutter:stable AS build
WORKDIR /app
# Copy pubspec files
COPY pubspec.yaml pubspec.lock ./
# Get dependencies
RUN flutter pub get
# Copy source code
COPY . .
# Build APK (for CI/CD purposes)
# Note: Actual mobile builds require native tooling
RUN flutter build apk --release || echo "Build skipped - requires Android SDK"
# For CI/CD testing only
CMD ["flutter", "test"]

43
apps/app-client/README.md Normal file
View File

@@ -0,0 +1,43 @@
# App Client (Flutter)
Flutter mobile application for GoodGo Platform Client Portal.
## Features
- Flutter 3.x
- Material Design 3
- GoRouter for navigation
- Provider for state management
- API integration
## Prerequisites
- Flutter SDK >= 3.0.0
- Dart SDK >= 3.0.0
- Android Studio / Xcode
## Development
```bash
# Install dependencies
flutter pub get
# Run on Android
flutter run
# Run on iOS
flutter run -d ios
# Build APK
flutter build apk
# Build iOS
flutter build ios
```
## Environment Variables
Create `.env` file:
```
API_URL=http://localhost/api/v1
```

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'GoodGo Client',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
routerConfig: _router,
);
}
}
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
),
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) {
return const LoginScreen();
},
),
],
);
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GoodGo Client'),
),
body: const Center(
child: Text('Welcome to GoodGo Client'),
),
);
}
}
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: const Center(
child: Text('Login Screen'),
),
);
}
}

View File

@@ -0,0 +1,24 @@
name: app_client
description: GoodGo Platform Client Mobile Application (Flutter)
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.6
http: ^1.1.0
shared_preferences: ^2.2.2
provider: ^6.1.1
go_router: ^13.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true

50
apps/web-admin/Dockerfile Normal file
View File

@@ -0,0 +1,50 @@
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Builder stage
FROM base AS builder
RUN corepack enable pnpm
# Copy workspace configuration
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
# Create directory structure and copy all package.json files
RUN mkdir -p packages apps services
COPY packages/auth-sdk/package.json ./packages/auth-sdk/
COPY packages/http-client/package.json ./packages/http-client/
COPY packages/logger/package.json ./packages/logger/
COPY packages/tracing/package.json ./packages/tracing/
COPY packages/types/package.json ./packages/types/
COPY packages/config/eslint-config/package.json ./packages/config/eslint-config/
COPY packages/config/prettier-config/package.json ./packages/config/prettier-config/
COPY packages/config/tsconfig/package.json ./packages/config/tsconfig/
COPY apps/web-client/package.json ./apps/web-client/
COPY apps/web-admin/package.json ./apps/web-admin/
COPY services/auth-service/package.json ./services/auth-service/
# Install all dependencies for entire monorepo
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# Copy all source code
COPY packages ./packages
COPY apps/web-admin ./apps/web-admin
COPY turbo.json ./
# Build using turbo from root (handles dependency order automatically)
RUN pnpm turbo build --filter=web-admin
# Production stage
FROM base AS runner
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy the entire workspace to preserve pnpm structure
COPY --from=builder --chown=nextjs:nodejs /app /app
WORKDIR /app/apps/web-admin
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", ".next/standalone/apps/web-admin/server.js"]

31
apps/web-admin/README.md Normal file
View File

@@ -0,0 +1,31 @@
# Web Admin Application
Next.js web application for GoodGo Platform Admin Panel.
## Features
- Next.js 14 with App Router
- TypeScript
- Tailwind CSS
- Zustand for state management
- API integration with auth service
## Development
```bash
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Build for production
pnpm build
# Start production server
pnpm start
```
## Environment Variables
- `NEXT_PUBLIC_API_URL` - API base URL (default: http://localhost/api/v1)

5
apps/web-admin/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

View File

@@ -0,0 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
output: 'standalone',
env: {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost/api/v1',
},
};
module.exports = nextConfig;

View File

@@ -0,0 +1,35 @@
{
"name": "@goodgo/web-admin",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@goodgo/types": "workspace:*",
"@goodgo/http-client": "workspace:*",
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.4.7",
"axios": "^1.6.5"
},
"devDependencies": {
"@goodgo/eslint-config": "workspace:*",
"@goodgo/tsconfig": "workspace:*",
"@goodgo/prettier-config": "workspace:*",
"@types/node": "^20.11.0",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"typescript": "^5.3.3",
"tailwindcss": "^3.4.1",
"postcss": "^8.4.33",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "^14.1.0"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1 @@
# Public assets

View File

@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

View File

@@ -0,0 +1,19 @@
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'GoodGo Platform',
description: 'Enterprise microservices platform',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

View File

@@ -0,0 +1,60 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuthStore } from '@/stores/auth.store';
export default function LoginPage() {
const router = useRouter();
const { login, isLoading } = useAuthStore();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
await login(email, password);
router.push('/');
} catch (err: any) {
setError(err.message || 'Login failed');
}
};
return (
<div className="min-h-screen flex items-center justify-center">
<form onSubmit={handleSubmit} className="bg-white p-8 rounded shadow-md w-96">
<h2 className="text-2xl font-bold mb-4">Login</h2>
{error && <div className="text-red-500 mb-4">{error}</div>}
<div className="mb-4">
<label className="block mb-2">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full p-2 border rounded"
required
/>
</div>
<div className="mb-4">
<label className="block mb-2">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 border rounded"
required
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Login'}
</button>
</form>
</div>
);
}

View File

@@ -0,0 +1,34 @@
'use client';
import { useAuthStore } from '@/stores/auth.store';
import { useEffect } from 'react';
export default function Home() {
const { user, isAuthenticated, isLoading, fetchUser } = useAuthStore();
useEffect(() => {
if (!isAuthenticated && !isLoading) {
fetchUser();
}
}, [isAuthenticated, isLoading, fetchUser]);
if (isLoading) {
return <div className="p-8">Loading...</div>;
}
return (
<main className="min-h-screen p-8">
<h1 className="text-4xl font-bold mb-4">GoodGo Platform</h1>
{isAuthenticated && user ? (
<div>
<p>Welcome, {user.email}!</p>
<p>Role: {user.role}</p>
</div>
) : (
<div>
<p>Please log in to continue.</p>
</div>
)}
</main>
);
}

View File

@@ -0,0 +1,45 @@
import { apiClient } from './client';
import { LoginDto, RegisterDto, AuthResponse, ApiResponse, UserResponse } from '@goodgo/types';
export const authApi = {
register: async (data: RegisterDto): Promise<ApiResponse<AuthResponse>> => {
return apiClient.post('/auth/register', data);
},
login: async (data: LoginDto): Promise<ApiResponse<AuthResponse>> => {
const response = await apiClient.post('/auth/login', data);
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
if (typeof window !== 'undefined') {
localStorage.setItem('refreshToken', response.data.refreshToken);
}
}
return response;
},
logout: async (): Promise<ApiResponse> => {
const refreshToken = typeof window !== 'undefined' ? localStorage.getItem('refreshToken') : null;
const response = await apiClient.post('/auth/logout', { refreshToken });
apiClient.removeAuthToken();
if (typeof window !== 'undefined') {
localStorage.removeItem('refreshToken');
}
return response;
},
refreshToken: async (refreshToken: string): Promise<ApiResponse<{ accessToken: string }>> => {
const response = await apiClient.post('/auth/refresh', { refreshToken });
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
}
return response;
},
getMe: async (): Promise<ApiResponse<UserResponse>> => {
return apiClient.get('/users/me');
},
changePassword: async (currentPassword: string, newPassword: string): Promise<ApiResponse> => {
return apiClient.put('/auth/password', { currentPassword, newPassword });
},
};

View File

@@ -0,0 +1,8 @@
import { createHttpClient } from '@goodgo/http-client';
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost/api/v1';
export const apiClient = createHttpClient({
baseURL: API_URL,
timeout: 30000,
});

View File

@@ -0,0 +1,103 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { UserResponse } from '@goodgo/types';
import { authApi } from '../services/api/auth.api';
interface AuthState {
user: UserResponse | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string, confirmPassword: string) => Promise<void>;
logout: () => Promise<void>;
fetchUser: () => Promise<void>;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
isAuthenticated: false,
isLoading: false,
login: async (email: string, password: string) => {
set({ isLoading: true });
try {
const response = await authApi.login({ email, password });
if (response.success && response.data) {
set({
user: response.data.user,
isAuthenticated: true,
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Login failed');
}
} catch (error) {
set({ isLoading: false });
throw error;
}
},
register: async (email: string, password: string, confirmPassword: string) => {
set({ isLoading: true });
try {
const response = await authApi.register({ email, password, confirmPassword });
if (response.success && response.data) {
set({
user: response.data.user,
isAuthenticated: true,
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Registration failed');
}
} catch (error) {
set({ isLoading: false });
throw error;
}
},
logout: async () => {
try {
await authApi.logout();
} finally {
set({
user: null,
isAuthenticated: false,
});
}
},
fetchUser: async () => {
set({ isLoading: true });
try {
const response = await authApi.getMe();
if (response.success && response.data) {
set({
user: response.data,
isAuthenticated: true,
isLoading: false,
});
} else {
set({
user: null,
isAuthenticated: false,
isLoading: false,
});
}
} catch (error) {
set({
user: null,
isAuthenticated: false,
isLoading: false,
});
}
},
}),
{
name: 'auth-storage',
partialize: (state) => ({ user: state.user, isAuthenticated: state.isAuthenticated }),
}
)
);

View File

@@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -0,0 +1,21 @@
{
"extends": "@goodgo/tsconfig/nextjs.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
},
"isolatedModules": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,50 @@
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Builder stage
FROM base AS builder
RUN corepack enable pnpm
# Copy workspace configuration
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
# Create directory structure and copy all package.json files
RUN mkdir -p packages apps services
COPY packages/auth-sdk/package.json ./packages/auth-sdk/
COPY packages/http-client/package.json ./packages/http-client/
COPY packages/logger/package.json ./packages/logger/
COPY packages/tracing/package.json ./packages/tracing/
COPY packages/types/package.json ./packages/types/
COPY packages/config/eslint-config/package.json ./packages/config/eslint-config/
COPY packages/config/prettier-config/package.json ./packages/config/prettier-config/
COPY packages/config/tsconfig/package.json ./packages/config/tsconfig/
COPY apps/web-client/package.json ./apps/web-client/
COPY apps/web-admin/package.json ./apps/web-admin/
COPY services/auth-service/package.json ./services/auth-service/
# Install all dependencies for entire monorepo
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# Copy all source code
COPY packages ./packages
COPY apps/web-client ./apps/web-client
COPY turbo.json ./
# Build using turbo from root (handles dependency order automatically)
RUN pnpm turbo build --filter=web-client
# Production stage
FROM base AS runner
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy the entire workspace to preserve pnpm structure
COPY --from=builder --chown=nextjs:nodejs /app /app
WORKDIR /app/apps/web-client
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", ".next/standalone/apps/web-client/server.js"]

31
apps/web-client/README.md Normal file
View File

@@ -0,0 +1,31 @@
# Web Client Application
Next.js web application for GoodGo Platform Client Portal.
## Features
- Next.js 14 with App Router
- TypeScript
- Tailwind CSS
- Zustand for state management
- API integration with auth service
## Development
```bash
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Build for production
pnpm build
# Start production server
pnpm start
```
## Environment Variables
- `NEXT_PUBLIC_API_URL` - API base URL (default: http://localhost/api/v1)

5
apps/web-client/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

View File

@@ -0,0 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
output: 'standalone',
env: {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost/api/v1',
},
};
module.exports = nextConfig;

View File

@@ -0,0 +1,35 @@
{
"name": "@goodgo/web-client",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@goodgo/types": "workspace:*",
"@goodgo/http-client": "workspace:*",
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.4.7",
"axios": "^1.6.5"
},
"devDependencies": {
"@goodgo/eslint-config": "workspace:*",
"@goodgo/tsconfig": "workspace:*",
"@goodgo/prettier-config": "workspace:*",
"@types/node": "^20.11.0",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"typescript": "^5.3.3",
"tailwindcss": "^3.4.1",
"postcss": "^8.4.33",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "^14.1.0"
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1 @@
# Public assets

View File

@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

View File

@@ -0,0 +1,19 @@
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'GoodGo Platform',
description: 'Enterprise microservices platform',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

View File

@@ -0,0 +1,60 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuthStore } from '@/stores/auth.store';
export default function LoginPage() {
const router = useRouter();
const { login, isLoading } = useAuthStore();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
await login(email, password);
router.push('/');
} catch (err: any) {
setError(err.message || 'Login failed');
}
};
return (
<div className="min-h-screen flex items-center justify-center">
<form onSubmit={handleSubmit} className="bg-white p-8 rounded shadow-md w-96">
<h2 className="text-2xl font-bold mb-4">Login</h2>
{error && <div className="text-red-500 mb-4">{error}</div>}
<div className="mb-4">
<label className="block mb-2">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full p-2 border rounded"
required
/>
</div>
<div className="mb-4">
<label className="block mb-2">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 border rounded"
required
/>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Login'}
</button>
</form>
</div>
);
}

View File

@@ -0,0 +1,34 @@
'use client';
import { useAuthStore } from '@/stores/auth.store';
import { useEffect } from 'react';
export default function Home() {
const { user, isAuthenticated, isLoading, fetchUser } = useAuthStore();
useEffect(() => {
if (!isAuthenticated && !isLoading) {
fetchUser();
}
}, [isAuthenticated, isLoading, fetchUser]);
if (isLoading) {
return <div className="p-8">Loading...</div>;
}
return (
<main className="min-h-screen p-8">
<h1 className="text-4xl font-bold mb-4">GoodGo Platform</h1>
{isAuthenticated && user ? (
<div>
<p>Welcome, {user.email}!</p>
<p>Role: {user.role}</p>
</div>
) : (
<div>
<p>Please log in to continue.</p>
</div>
)}
</main>
);
}

View File

@@ -0,0 +1,45 @@
import { apiClient } from './client';
import { LoginDto, RegisterDto, AuthResponse, ApiResponse, UserResponse } from '@goodgo/types';
export const authApi = {
register: async (data: RegisterDto): Promise<ApiResponse<AuthResponse>> => {
return apiClient.post('/auth/register', data);
},
login: async (data: LoginDto): Promise<ApiResponse<AuthResponse>> => {
const response = await apiClient.post('/auth/login', data);
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
if (typeof window !== 'undefined') {
localStorage.setItem('refreshToken', response.data.refreshToken);
}
}
return response;
},
logout: async (): Promise<ApiResponse> => {
const refreshToken = typeof window !== 'undefined' ? localStorage.getItem('refreshToken') : null;
const response = await apiClient.post('/auth/logout', { refreshToken });
apiClient.removeAuthToken();
if (typeof window !== 'undefined') {
localStorage.removeItem('refreshToken');
}
return response;
},
refreshToken: async (refreshToken: string): Promise<ApiResponse<{ accessToken: string }>> => {
const response = await apiClient.post('/auth/refresh', { refreshToken });
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
}
return response;
},
getMe: async (): Promise<ApiResponse<UserResponse>> => {
return apiClient.get('/users/me');
},
changePassword: async (currentPassword: string, newPassword: string): Promise<ApiResponse> => {
return apiClient.put('/auth/password', { currentPassword, newPassword });
},
};

View File

@@ -0,0 +1,8 @@
import { createHttpClient } from '@goodgo/http-client';
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost/api/v1';
export const apiClient = createHttpClient({
baseURL: API_URL,
timeout: 30000,
});

View File

@@ -0,0 +1,103 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { UserResponse } from '@goodgo/types';
import { authApi } from '../services/api/auth.api';
interface AuthState {
user: UserResponse | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string, confirmPassword: string) => Promise<void>;
logout: () => Promise<void>;
fetchUser: () => Promise<void>;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
isAuthenticated: false,
isLoading: false,
login: async (email: string, password: string) => {
set({ isLoading: true });
try {
const response = await authApi.login({ email, password });
if (response.success && response.data) {
set({
user: response.data.user,
isAuthenticated: true,
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Login failed');
}
} catch (error) {
set({ isLoading: false });
throw error;
}
},
register: async (email: string, password: string, confirmPassword: string) => {
set({ isLoading: true });
try {
const response = await authApi.register({ email, password, confirmPassword });
if (response.success && response.data) {
set({
user: response.data.user,
isAuthenticated: true,
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Registration failed');
}
} catch (error) {
set({ isLoading: false });
throw error;
}
},
logout: async () => {
try {
await authApi.logout();
} finally {
set({
user: null,
isAuthenticated: false,
});
}
},
fetchUser: async () => {
set({ isLoading: true });
try {
const response = await authApi.getMe();
if (response.success && response.data) {
set({
user: response.data,
isAuthenticated: true,
isLoading: false,
});
} else {
set({
user: null,
isAuthenticated: false,
isLoading: false,
});
}
} catch (error) {
set({
user: null,
isAuthenticated: false,
isLoading: false,
});
}
},
}),
{
name: 'auth-storage',
partialize: (state) => ({ user: state.user, isAuthenticated: state.isAuthenticated }),
}
)
);

View File

@@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -0,0 +1,21 @@
{
"extends": "@goodgo/tsconfig/nextjs.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
},
"isolatedModules": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,81 @@
# Local Development Setup
Docker Compose configuration for local development.
## Prerequisites
- Docker & Docker Compose installed
- Neon account (https://neon.tech) - for database
- Ports available: 80, 6379, 5001, 3000, 3001, 8080
## Initial Setup
### 1. Setup Neon Database
```bash
# Run setup script
./scripts/db/setup-neon.sh
# Or manually:
# 1. Create Neon project at https://neon.tech
# 2. Get connection string from main branch
# 3. Create deployments/local/.env.local:
# DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
See [Neon Setup Guide](../../infra/databases/neon/README.md) for details.
### 2. Start Services
```bash
# Start infrastructure (Redis, Traefik - no PostgreSQL needed)
docker-compose up -d
# Run migrations
./scripts/db/migrate.sh auth-service dev
# Seed database (optional)
./scripts/db/seed.sh auth-service
```
## Usage
```bash
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
# Stop and remove volumes (clean slate)
docker-compose down -v
```
## Services
- **Neon Database**: Cloud-hosted (no local container)
- **Redis**: `localhost:6379`
- **Auth Service**: `localhost:5001`
- **Web Admin**: `http://localhost:3000` or `http://admin.localhost`
- **Web Client**: `http://localhost:3001` or `http://localhost`
- **Traefik Dashboard**: `http://localhost:8080`
## Access
- API Gateway: `http://localhost/api/v1`
- Web Admin: `http://admin.localhost` (via Traefik) or `http://localhost:3000` (direct)
- Web Client: `http://localhost` (via Traefik) or `http://localhost:3001` (direct)
- Traefik Dashboard: `http://localhost:8080`
## Environment Variables
Copy `env.local.example` to `.env.local` and add your Neon DATABASE_URL:
```bash
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
**Note**: PostgreSQL is not included in Docker Compose. All environments use Neon database.

View File

@@ -0,0 +1,120 @@
version: '3.8'
# NOTE: This setup uses Neon PostgreSQL database
# Setup Neon database URL in .env.local file
# See infra/databases/neon/README.md for setup instructions
services:
redis:
image: redis:7-alpine
container_name: redis-cache-local
command: redis-server /etc/redis/redis.conf
ports:
- "6379:6379"
volumes:
- redis_data:/data
- ../../infra/databases/redis/redis.conf:/etc/redis/redis.conf
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- microservices-network
auth-service:
build:
context: ../..
dockerfile: services/auth-service/Dockerfile
container_name: auth-service-local
env_file:
- .env.local
environment:
- NODE_ENV=development
- PORT=5001
# DATABASE_URL should be set in .env.local (Neon database URL)
- REDIS_HOST=redis
- REDIS_PORT=6379
- JWT_SECRET=${JWT_SECRET:-dev-jwt-secret-change-in-production}
- JWT_EXPIRES_IN=15m
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-dev-refresh-secret-change-in-production}
- JWT_REFRESH_EXPIRES_IN=7d
- CORS_ORIGIN=http://localhost:3000,http://localhost:3001
- LOG_LEVEL=debug
- SERVICE_NAME=auth-service
ports:
- "5001:5001"
depends_on:
redis:
condition: service_healthy
networks:
- microservices-network
web-admin:
build:
context: ../..
dockerfile: apps/web-admin/Dockerfile
container_name: web-admin-local
environment:
- NODE_ENV=development
- NEXT_PUBLIC_API_URL=http://localhost/api/v1
ports:
- "3000:3000"
depends_on:
- auth-service
networks:
- microservices-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.web-admin.rule=Host(`admin.localhost`)"
- "traefik.http.routers.web-admin.entrypoints=web"
- "traefik.http.services.web-admin.loadbalancer.server.port=3000"
web-client:
build:
context: ../..
dockerfile: apps/web-client/Dockerfile
container_name: web-client-local
environment:
- NODE_ENV=development
- NEXT_PUBLIC_API_URL=http://localhost/api/v1
ports:
- "3001:3000"
depends_on:
- auth-service
networks:
- microservices-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.web-client.rule=Host(`localhost`)"
- "traefik.http.routers.web-client.entrypoints=web"
- "traefik.http.services.web-client.loadbalancer.server.port=3000"
traefik:
image: traefik:v2.10
container_name: traefik-local
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--log.level=INFO"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ../../infra/traefik:/etc/traefik
networks:
- microservices-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.traefik.entrypoints=web"
volumes:
redis_data:
networks:
microservices-network:
driver: bridge

View File

@@ -0,0 +1,54 @@
# Local Development Environment Variables (Shared Configs)
# Shared Environment Variables - Shared across all services
# Copy this file to .env.local and fill in your values
#
# Note: Service-specific configs (DATABASE_URL, PORT) should be in services/<service-name>/.env.local
# =============================================================================
# SHARED SECRETS - Must be same across all services for JWT token verification
# =============================================================================
JWT_SECRET=dev-jwt-secret-change-in-production-min-32-chars
JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production-min-32-chars
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# =============================================================================
# SHARED INFRASTRUCTURE - Redis, Traefik
# =============================================================================
# Redis (Docker container name when using docker-compose)
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
# =============================================================================
# COMMON CONFIGURATION
# =============================================================================
NODE_ENV=development
LOG_LEVEL=debug
# CORS - Allowed origins for all services
CORS_ORIGIN=http://localhost:3000,http://localhost:3001,http://admin.localhost
# =============================================================================
# MONITORING & TRACING (Optional)
# =============================================================================
TRACING_ENABLED=false
JAEGER_ENDPOINT=http://jaeger:14268/api/traces
# =============================================================================
# EXTERNAL SERVICES (Optional)
# =============================================================================
EMAIL_FROM=noreply@goodgo.vn
# =============================================================================
# NOTES
# =============================================================================
# - Each service should have its own .env.local for service-specific configs:
# * DATABASE_URL (each service has its own database)
# * PORT (each service has different port)
# * SERVICE_NAME
# * REDIS_HOST=localhost (override when running native, Redis in Docker)
#
# - Get Neon database URLs from: https://console.neon.tech
# - Create separate databases for each service (microservices pattern)
# - JWT secrets MUST be identical across all services

View File

@@ -0,0 +1,19 @@
# Production Environment Variables
# Use these values to create Kubernetes secrets
# Neon Database (Production branch)
DATABASE_URL=postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
# JWT Secrets (use strong random strings - min 32 chars)
JWT_SECRET=your-production-jwt-secret-min-32-chars
JWT_REFRESH_SECRET=your-production-refresh-secret-min-32-chars
# Redis
REDIS_HOST=redis-service
REDIS_PORT=6379
# Notes:
# - Store these in Kubernetes secrets or GitHub Secrets
# - Never commit actual values to Git
# - Use kubectl create secret or GitHub Secrets for CI/CD
# - Rotate secrets regularly

View File

@@ -0,0 +1,91 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth-service
image: goodgo/auth-service:latest
imagePullPolicy: Always
ports:
- containerPort: 5001
envFrom:
- configMapRef:
name: auth-service-config
- secretRef:
name: auth-service-secrets
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 5001
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 5001
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: production
spec:
selector:
app: auth-service
ports:
- protocol: TCP
port: 5001
targetPort: 5001
type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: auth-service-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: auth-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-service-config
namespace: production
data:
NODE_ENV: "production"
PORT: "5001"
API_VERSION: "v1"
CORS_ORIGIN: "https://goodgo.vn"
LOG_LEVEL: "warn"
SERVICE_NAME: "auth-service"
TRACING_ENABLED: "true"
# Note: DATABASE_URL is stored in secrets (auth-service-secrets)
# DATABASE_URL should point to Neon production branch

View File

@@ -0,0 +1,32 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefix
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: traefik
tls:
- hosts:
- api.goodgo.vn
secretName: api-tls-cert
rules:
- host: api.goodgo.vn
http:
paths:
- path: /api/v1/auth
pathType: Prefix
backend:
service:
name: auth-service
port:
number: 5001
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: auth-service
port:
number: 5001

View File

@@ -0,0 +1,34 @@
# Kubernetes Secrets Template for Production
# DO NOT commit actual secrets to Git
# Use this as a template to create secrets
# Create secret using kubectl:
# kubectl create secret generic auth-service-secrets \
# --from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
# --from-literal=jwt-secret='your-production-jwt-secret-min-32-chars' \
# --from-literal=jwt-refresh-secret='your-production-refresh-secret-min-32-chars' \
# --from-literal=redis-password='' \
# -n production
# Or use GitHub Secrets in CI/CD:
# - NEON_DATABASE_URL_PRODUCTION
# - JWT_SECRET_PRODUCTION
# - JWT_REFRESH_SECRET_PRODUCTION
apiVersion: v1
kind: Secret
metadata:
name: auth-service-secrets
namespace: production
type: Opaque
stringData:
# Neon Database URL (Production branch)
# Format: postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
database-url: "postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true"
# JWT Secrets (use strong random strings, min 32 characters)
jwt-secret: "your-production-jwt-secret-min-32-chars"
jwt-refresh-secret: "your-production-refresh-secret-min-32-chars"
# Redis (if password protected)
redis-password: ""

View File

@@ -0,0 +1,18 @@
# Staging Environment Variables
# Use these values to create Kubernetes secrets
# Neon Database (Staging branch)
DATABASE_URL=postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
# JWT Secrets (use strong random strings)
JWT_SECRET=your-staging-jwt-secret-min-32-chars
JWT_REFRESH_SECRET=your-staging-refresh-secret-min-32-chars
# Redis
REDIS_HOST=redis-service
REDIS_PORT=6379
# Notes:
# - Store these in Kubernetes secrets or GitHub Secrets
# - Never commit actual values to Git
# - Use kubectl create secret or GitHub Secrets for CI/CD

View File

@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: staging
spec:
replicas: 2
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
spec:
containers:
- name: auth-service
image: goodgo/auth-service:latest
ports:
- containerPort: 5001
env:
- name: NODE_ENV
value: "staging"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: auth-service-secrets
key: database-url
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: auth-service-secrets
key: jwt-secret
- name: REDIS_HOST
value: "redis-service"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: 5001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 5001
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: staging
spec:
selector:
app: auth-service
ports:
- protocol: TCP
port: 5001
targetPort: 5001
type: ClusterIP

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-service-config
namespace: staging
data:
NODE_ENV: "staging"
PORT: "5001"
API_VERSION: "v1"
CORS_ORIGIN: "https://staging.goodgo.vn"
LOG_LEVEL: "info"
SERVICE_NAME: "auth-service"
# Note: DATABASE_URL is stored in secrets (auth-service-secrets)
# DATABASE_URL should point to Neon staging branch

View File

@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: staging
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefix
spec:
ingressClassName: traefik
rules:
- host: api.staging.goodgo.vn
http:
paths:
- path: /api/v1/auth
pathType: Prefix
backend:
service:
name: auth-service
port:
number: 5001
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: auth-service
port:
number: 5001

View File

@@ -0,0 +1,34 @@
# Kubernetes Secrets Template for Staging
# DO NOT commit actual secrets to Git
# Use this as a template to create secrets
# Create secret using kubectl:
# kubectl create secret generic auth-service-secrets \
# --from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
# --from-literal=jwt-secret='your-staging-jwt-secret-min-32-chars' \
# --from-literal=jwt-refresh-secret='your-staging-refresh-secret-min-32-chars' \
# --from-literal=redis-password='' \
# -n staging
# Or use GitHub Secrets in CI/CD:
# - NEON_DATABASE_URL_STAGING
# - JWT_SECRET_STAGING
# - JWT_REFRESH_SECRET_STAGING
apiVersion: v1
kind: Secret
metadata:
name: auth-service-secrets
namespace: staging
type: Opaque
stringData:
# Neon Database URL (Staging branch)
# Format: postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
database-url: "postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true"
# JWT Secrets (use strong random strings, min 32 characters)
jwt-secret: "your-staging-jwt-secret-min-32-chars"
jwt-refresh-secret: "your-staging-refresh-secret-min-32-chars"
# Redis (if password protected)
redis-password: ""

45
docs/README.md Normal file
View File

@@ -0,0 +1,45 @@
# Documentation
This directory contains documentation for the GoodGo Microservices Platform, organized by language.
## Structure
```
docs/
├── en/ # English documentation
│ ├── api/
│ ├── architecture/
│ ├── guides/
│ ├── onboarding/
│ └── runbooks/
├── vi/ # Vietnamese documentation (Tiếng Việt)
│ ├── api/
│ ├── architecture/
│ ├── guides/
│ ├── onboarding/
│ └── runbooks/
└── README.md # This file
```
## Available Documentation
### English (`/en`)
- **API**: OpenAPI specifications
- **Architecture**: System design and service communication patterns
- **Guides**: Development, deployment, getting started, troubleshooting
- **Onboarding**: New developer guide
- **Runbooks**: Incident response and rollback procedures
### Vietnamese (`/vi`)
- **API**: OpenAPI specifications
- **Architecture**: Thiết kế hệ thống và các mẫu giao tiếp service
- **Guides**: Development, deployment, bắt đầu, xử lý sự cố
- **Onboarding**: Hướng dẫn cho developer mới
- **Runbooks**: Phản ứng sự cố và quy trình rollback
## Contributing
When adding new documentation:
1. Add the English version to `/en`
2. Add the Vietnamese translation to `/vi`
3. Keep both versions in sync

View File

@@ -0,0 +1,102 @@
openapi: 3.0.0
info:
title: Auth Service API
version: 1.0.0
description: Authentication and Authorization Service API
servers:
- url: http://localhost/api/v1
description: Local development
- url: https://api.goodgo.vn/api/v1
description: Production
paths:
/auth/register:
post:
summary: Register new user
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
format: email
password:
type: string
minLength: 6
confirmPassword:
type: string
responses:
'201':
description: User registered successfully
'400':
description: Validation error
/auth/login:
post:
summary: Login user
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
format: email
password:
type: string
responses:
'200':
description: Login successful
'401':
description: Invalid credentials
/auth/logout:
post:
summary: Logout user
security:
- bearerAuth: []
responses:
'200':
description: Logout successful
/auth/refresh:
post:
summary: Refresh access token
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
refreshToken:
type: string
responses:
'200':
description: Token refreshed
'401':
description: Invalid refresh token
/users/me:
get:
summary: Get current user
security:
- bearerAuth: []
responses:
'200':
description: User information
'401':
description: Unauthorized
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@@ -0,0 +1,58 @@
# Service Communication
## Communication Patterns
### Synchronous Communication (HTTP/REST)
Services communicate synchronously via HTTP REST APIs through Traefik API Gateway.
**Example:**
```typescript
// Web App -> Auth Service
const response = await fetch('http://api.goodgo.vn/api/v1/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
```
### Service-to-Service Communication
Services can communicate directly via internal network:
```typescript
// Auth Service -> Notification Service (future)
const response = await fetch('http://notification-service:5003/api/v1/notifications', {
method: 'POST',
headers: { 'X-Service-Auth': process.env.INTERNAL_API_KEY },
body: JSON.stringify({ userId, message }),
});
```
## API Gateway Routing
Traefik routes requests based on:
- Host header (`api.goodgo.vn`)
- Path prefix (`/api/v1/auth`)
## Error Handling
All services follow consistent error response format:
```json
{
"success": false,
"error": {
"code": "AUTH_001",
"message": "Invalid credentials",
"details": {}
},
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
## Retry and Circuit Breaker
Future implementation:
- Exponential backoff for retries
- Circuit breaker pattern for fault tolerance
- Fallback mechanisms

View File

@@ -0,0 +1,81 @@
# System Design
## Overview
GoodGo Microservices Platform is built using a microservices architecture pattern with the following principles:
- **Service Independence**: Each service has its own database and can be deployed independently
- **API Gateway**: Traefik handles routing, load balancing, and cross-cutting concerns
- **Shared Libraries**: Common functionality is extracted into shared packages
- **Infrastructure as Code**: All infrastructure configurations are versioned
- **Observability**: Full monitoring, logging, and tracing capabilities
## Architecture Diagram
```
┌─────────────┐ ┌─────────────┐
│ Web App │ │ Mobile App │
│ (Next.js) │ │ (React Native)
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬────────┘
┌────────▼────────┐
│ Traefik │
│ (API Gateway) │
└────────┬─────────┘
┌─────────────┼─────────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌───▼────┐
│ Auth │ │ Future │ │ Future │
│Service │ │Service │ │Service │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└────────────┼────────────┘
┌────────────┼────────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌───▼────┐
│Postgres│ │ Redis │ │Prometheus│
└────────┘ └────────┘ └─────────┘
```
## Components
### Frontend Layer
- **Web App**: Next.js application with App Router
- **Mobile App**: React Native application
### API Gateway
- **Traefik**: Reverse proxy, load balancer, SSL termination
### Services Layer
- **Auth Service**: Authentication and authorization
- **Future Services**: Payment, Order, Notification, etc.
### Infrastructure Layer
- **PostgreSQL**: Primary database
- **Redis**: Caching and session storage
- **Prometheus**: Metrics collection
- **Grafana**: Metrics visualization
- **Loki**: Log aggregation
## Communication Patterns
- **Synchronous**: HTTP/REST for request-response patterns
- **Asynchronous**: Message queues (future implementation)
- **Service Discovery**: Docker networking and Kubernetes DNS
## Data Management
- **Database per Service**: Each service owns its data
- **API Composition**: Services expose APIs for data access
- **Event Sourcing**: Future consideration for audit trails
## Security
- **Authentication**: JWT tokens with refresh token rotation
- **Authorization**: Role-based access control (RBAC)
- **Network Security**: TLS/SSL, rate limiting, CORS
- **Secrets Management**: Environment variables, Kubernetes secrets

View File

@@ -0,0 +1,106 @@
# Deployment Guide
## Database Setup (Neon)
All environments use **Neon PostgreSQL**. Setup once before deployment:
1. Create Neon project at https://neon.tech
2. Create branches: `main` (dev), `staging`, `production`
3. Get connection strings for each branch
4. Configure in environment variables (see below)
See [Neon Setup Guide](../../infra/databases/neon/README.md) for details.
## Local Deployment
```bash
# Setup Neon database URL
cp deployments/local/env.local.example deployments/local/.env.local
# Edit .env.local and add your Neon DATABASE_URL
# Start services (no PostgreSQL container needed)
cd deployments/local
docker-compose up -d
```
## Staging Deployment
### Prerequisites
- Kubernetes cluster access
- kubectl configured
- KUBECONFIG set
- Neon staging branch created
- GitHub Secrets configured:
- `NEON_DATABASE_URL_STAGING`
- `KUBECONFIG_STAGING`
### Setup Secrets
```bash
# Create Kubernetes secret
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
--from-literal=jwt-secret='your-staging-jwt-secret' \
--from-literal=jwt-refresh-secret='your-staging-refresh-secret' \
-n staging
```
### Deploy
```bash
./scripts/deploy/deploy-staging.sh
```
Or manually:
```bash
kubectl apply -f deployments/staging/kubernetes/
```
**Note**: Migrations run automatically in CI/CD before deployment.
## Production Deployment
### Prerequisites
- Production Kubernetes cluster
- kubectl configured with production context
- Neon production branch created
- GitHub Secrets configured:
- `NEON_DATABASE_URL_PRODUCTION`
- `KUBECONFIG_PRODUCTION`
### Setup Secrets
```bash
# Create Kubernetes secret
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
--from-literal=jwt-secret='your-production-jwt-secret' \
--from-literal=jwt-refresh-secret='your-production-refresh-secret' \
-n production
```
### Deploy
```bash
./scripts/deploy/deploy-prod.sh
```
**Note**: Migrations run automatically in CI/CD before deployment (with approval).
### Rollback
```bash
kubectl rollout undo deployment/auth-service -n production
```
## Health Checks
- Liveness: `GET /health/live`
- Readiness: `GET /health/ready`
- Health: `GET /health`
## Monitoring
- Prometheus: http://prometheus:9090
- Grafana: http://grafana:3000
- Traefik Dashboard: http://traefik:8080

View File

@@ -0,0 +1,87 @@
# Development Guide
## Project Structure
```
├── apps/ # Frontend applications
├── services/ # Backend microservices
├── packages/ # Shared libraries
├── infra/ # Infrastructure configs
├── deployments/ # Deployment configs
├── scripts/ # Automation scripts
└── docs/ # Documentation
```
## Development Workflow
### 1. Create a Feature Branch
```bash
git checkout -b feature/my-feature
```
### 2. Make Changes
- Write code following TypeScript strict mode
- Add tests for new functionality
- Update documentation if needed
### 3. Run Tests Locally
```bash
# All tests
pnpm test
# Specific service
pnpm --filter @goodgo/auth-service test
```
### 4. Lint and Format
```bash
pnpm lint
pnpm format
```
### 5. Create Pull Request
- Push your branch
- Create PR targeting `develop`
- CI/CD will run automatically
## Adding a New Service
1. Use the template:
```bash
./scripts/utils/create-service.sh my-new-service
```
2. Update service configuration
3. Implement business logic
4. Add tests
5. Update documentation
## Adding a New Package
1. Create package in `packages/new-package`
2. Add to workspace in `pnpm-workspace.yaml`
3. Export from `index.ts`
4. Add tests
5. Document usage
## Database Migrations
```bash
# Create migration
cd services/auth-service
pnpm prisma migrate dev --name add_new_field
# Apply migrations (production)
pnpm prisma migrate deploy
```
## Debugging
- Use logger from `@goodgo/logger`
- Check Traefik logs: `docker logs traefik-local`
- Check service logs: `./scripts/dev/logs.sh auth-service`

View File

@@ -0,0 +1,81 @@
# Getting Started
## Prerequisites
- Node.js >= 20.0.0
- PNPM >= 8.0.0
- Docker & Docker Compose
- Git
- Neon account (https://neon.tech) - for database
## Initial Setup
1. **Clone the repository**
```bash
git clone <repository-url>
cd Base
```
2. **Setup Neon Database**
```bash
# Run setup script
./scripts/db/setup-neon.sh
# Or manually:
# 1. Create Neon project at https://neon.tech
# 2. Create branches: main (dev), staging, production
# 3. Get connection strings
# 4. Update deployments/local/.env.local
```
See [Neon Setup Guide](../../infra/databases/neon/README.md) for details.
3. **Initialize the project**
```bash
./scripts/setup/init-project.sh
```
4. **Start infrastructure** (Redis, Traefik - no PostgreSQL needed)
```bash
cd deployments/local
docker-compose up -d
cd ../..
```
5. **Run database migrations**
```bash
./scripts/db/migrate.sh auth-service dev
```
6. **Seed the database**
```bash
./scripts/db/seed.sh auth-service
```
7. **Start all services**
```bash
./scripts/dev/start-all.sh
```
## Access Points
- **API Gateway**: http://localhost/api/v1
- **Auth Service**: http://localhost:5001
- **Web Admin**: http://admin.localhost or http://localhost:3000
- **Web Client**: http://localhost or http://localhost:3001
- **Traefik Dashboard**: http://localhost:8080
## Database
This project uses **Neon PostgreSQL** for all environments:
- **Development**: Neon main branch
- **Staging**: Neon staging branch
- **Production**: Neon production branch
No local PostgreSQL needed! See [Neon Setup](../../infra/databases/neon/README.md) for details.
## Next Steps
- Read [Development Guide](development.md)
- Check [API Documentation](../api/openapi/)
- Review [Architecture Overview](../architecture/system-design.md)

View File

@@ -0,0 +1,476 @@
# Local Development Guide
Comprehensive guide for running and developing the project locally with real-time hot reload.
## System Requirements
- **Node.js**: >= 20.0.0
- **PNPM**: >= 8.0.0
- **Docker & Docker Compose**: Latest version
- **Git**: For cloning repository
- **Neon Account**: https://neon.tech (for database)
## Initial Setup
### 1. Clone Repository
```bash
git clone <repository-url>
cd Base
```
### 2. Install Dependencies
```bash
pnpm install
```
### 3. Setup Database (Neon)
Create environment configuration file:
```bash
cp deployments/local/env.local.example deployments/local/.env.local
```
Edit `.env.local` file and add your Neon DATABASE_URL:
```bash
# Get connection string from Neon Console: https://console.neon.tech
DATABASE_URL=postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
# JWT Secrets (can keep defaults for dev)
JWT_SECRET=dev-jwt-secret-change-in-production
JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production
```
**Note**: See [Neon Database Guide](neon-database.md) for detailed setup instructions.
### 4. Run Database Migrations
```bash
./scripts/db/migrate.sh auth-service dev
```
### 5. Seed Database (Optional)
```bash
./scripts/db/seed.sh auth-service
```
## Ways to Run the Project
### Method 1: Run All Services (Recommended)
Best for full-stack development or testing the entire system:
```bash
./scripts/dev/start-all.sh
```
This script will:
1. Check if Docker is running
2. Verify DATABASE_URL is configured
3. Start infrastructure (Redis, Traefik)
4. Start all services with hot reload
**Or run manually:**
```bash
# Step 1: Start infrastructure
cd deployments/local
docker-compose up -d
cd ../..
# Step 2: Start all services
pnpm dev
```
### Method 2: Run Specific Service
Best when working on a single service:
```bash
# Using script
./scripts/dev/start-service.sh auth-service
# Or run directly
cd services/auth-service
pnpm dev
```
### Method 3: Run Service Groups
```bash
# Run only backend services
pnpm --filter "./services/*" dev
# Run only frontend apps
pnpm --filter "./apps/*" dev
# Run specific service
pnpm --filter @goodgo/auth-service dev
```
### Method 4: Run With Docker Compose (Full Stack)
```bash
cd deployments/local
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
```
## Access Points
When services are running, you can access:
| Service | URL | Description |
|---------|-----|-------------|
| **API Gateway** | http://localhost/api/v1 | Main entry point via Traefik |
| **Auth Service** | http://localhost:5001 | Direct auth service access |
| **Auth API** | http://localhost/api/v1/auth | Auth API via gateway |
| **Web Admin** | http://admin.localhost or http://localhost:3000 | Admin dashboard |
| **Web Client** | http://localhost or http://localhost:3001 | Client web app |
| **Traefik Dashboard** | http://localhost:8080 | View routing and services |
## Hot Reload & Live Development
### Backend Services (TypeScript)
Backend services use `tsx watch` or `nodemon` for automatic restart on code changes:
```bash
# In services/auth-service/package.json
"scripts": {
"dev": "tsx watch src/index.ts"
}
```
**When you change:**
- `.ts` files → Service auto-restarts (1-2 seconds)
- `.env` files → Manual restart required
- `prisma/schema.prisma` → Need to run migration
### Frontend Apps (Next.js)
Frontend apps use Next.js Fast Refresh:
```bash
# In apps/web-admin/package.json
"scripts": {
"dev": "next dev"
}
```
**When you change:**
- React components → Updates instantly (no page reload)
- CSS/Tailwind → Updates instantly
- `next.config.js` → Restart required
### Shared Packages
When changing shared packages (in `packages/`):
```bash
# Packages auto-rebuild with Turbo watch mode
pnpm --filter @goodgo/logger dev
```
## Real-World Development Workflow
### Setup 3 Terminals
**Terminal 1: Run Services**
```bash
./scripts/dev/start-all.sh
# Or: pnpm dev
```
**Terminal 2: View Logs**
```bash
# View specific service logs
./scripts/dev/logs.sh auth-service
# Or view Docker logs
docker logs -f redis-cache-local
docker logs -f traefik-local
```
**Terminal 3: Development Tasks**
```bash
# Run tests
pnpm --filter @goodgo/auth-service test --watch
# Run migrations
./scripts/db/migrate.sh auth-service dev
# Format code
pnpm format
```
## Health Checks
### Health Endpoints
```bash
# Check API Gateway
curl http://localhost/api/v1/health
# Check Auth Service directly
curl http://localhost:5001/health
# Check Redis
docker exec redis-cache-local redis-cli ping
```
### Traefik Dashboard
Access http://localhost:8080 to view:
- All active routes
- Registered services
- Service health status
## Database Development
### Schema Changes
```bash
# 1. Edit prisma/schema.prisma
# 2. Create and apply migration
cd services/auth-service
pnpm prisma migrate dev --name add_new_field
# 3. Prisma Client auto-regenerates
```
### Reset Database (Development Only!)
```bash
cd services/auth-service
pnpm prisma migrate reset
```
### View Database
```bash
# Open Prisma Studio
cd services/auth-service
pnpm prisma studio
# Access: http://localhost:5555
```
## Debugging
### VS Code Debugging
Create `.vscode/launch.json`:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Auth Service",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@goodgo/auth-service", "dev"],
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal"
}
]
}
```
### View Detailed Logs
```bash
# Service logs
./scripts/dev/logs.sh auth-service
# Docker logs
docker logs -f auth-service-local
docker logs -f redis-cache-local
docker logs -f traefik-local
# All logs
docker-compose -f deployments/local/docker-compose.yml logs -f
```
## Troubleshooting
### Port Already in Use
```bash
# Find process using port
lsof -i :5001 # Auth service
lsof -i :3000 # Web admin
lsof -i :6379 # Redis
lsof -i :80 # Traefik
# Kill process
kill -9 <PID>
```
### Docker Not Running
```bash
# Check Docker
docker info
# Start Docker Desktop (macOS)
open -a Docker
# Restart Docker services
docker-compose -f deployments/local/docker-compose.yml restart
```
### Database Connection Failed
```bash
# Check DATABASE_URL
cat deployments/local/.env.local | grep DATABASE_URL
# Test connection
cd services/auth-service
pnpm prisma db pull
```
### Module Not Found
```bash
# Cleanup and reinstall
./scripts/utils/cleanup.sh
pnpm install
# Or just cleanup node_modules
rm -rf node_modules
rm -rf services/*/node_modules
rm -rf apps/*/node_modules
rm -rf packages/*/node_modules
pnpm install
```
### Hot Reload Not Working
```bash
# Restart service
# Press Ctrl+C to stop, then:
pnpm dev
# Or restart Docker container
docker-compose -f deployments/local/docker-compose.yml restart auth-service
```
## Tips & Best Practices
### 1. Use Turbo Cache
Turbo cache speeds up builds:
```bash
# First run will be slow
pnpm dev
# Subsequent runs will be faster thanks to cache
# Cache stored in node_modules/.cache/turbo
```
### 2. Dev Selective Services
No need to run everything if working on one service:
```bash
# Run only auth-service
pnpm --filter @goodgo/auth-service dev
# Run auth-service with dependencies
pnpm --filter @goodgo/auth-service... dev
```
### 3. Watch Tests
```bash
# Run tests automatically on code changes
pnpm --filter @goodgo/auth-service test --watch
```
### 4. Auto-format Code
Install Prettier extension in VS Code and enable format on save.
### 5. Use Git Hooks
```bash
# Pre-commit hook will auto-format and lint
git commit -m "feat: add new feature"
```
## Environment Variables
### Development (.env.local)
```bash
# Database
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require&pgbouncer=true
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
# JWT
JWT_SECRET=dev-jwt-secret
JWT_REFRESH_SECRET=dev-refresh-secret
# Service
NODE_ENV=development
LOG_LEVEL=debug
```
### Override for Specific Service
Create `.env.local` file in service directory:
```bash
# services/auth-service/.env.local
PORT=5001
LOG_LEVEL=debug
```
## Useful Commands
```bash
# Development
pnpm dev # Run all services
pnpm build # Build all
pnpm test # Test all
pnpm lint # Lint all
pnpm format # Format code
# Cleanup
pnpm clean # Remove build artifacts
./scripts/utils/cleanup.sh # Full cleanup
# Database
./scripts/db/migrate.sh auth-service dev # Migration
./scripts/db/seed.sh auth-service # Seed data
./scripts/db/backup.sh auth-service # Backup
# Docker
docker-compose -f deployments/local/docker-compose.yml up -d # Start
docker-compose -f deployments/local/docker-compose.yml down # Stop
docker-compose -f deployments/local/docker-compose.yml logs -f # Logs
docker-compose -f deployments/local/docker-compose.yml restart # Restart
```
## Additional Resources
- [Getting Started](getting-started.md) - Initial setup
- [Development Guide](development.md) - Development workflow
- [Neon Database Guide](neon-database.md) - Database guide
- [Troubleshooting](troubleshooting.md) - Problem solving

View File

@@ -0,0 +1,215 @@
# Neon Database Guide
This project uses [Neon PostgreSQL](https://neon.tech) for all environments.
## Why Neon?
-**Serverless**: No infrastructure management
-**Branching**: Separate databases for dev/staging/prod
-**Auto-scaling**: Handles traffic spikes automatically
-**Point-in-time restore**: Easy recovery from mistakes
-**Free tier**: Perfect for development
-**Connection pooling**: Built-in PgBouncer support
## Quick Start
### 1. Create Neon Account
1. Sign up at https://neon.tech
2. Create a new project: `goodgo-platform`
### 2. Create Branches
In Neon Console, create branches:
- `main` (development) - already exists
- `staging` - create from main
- `production` - create from main
### 3. Get Connection Strings
For each branch, copy the connection string:
- Format: `postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require`
- Add `?pgbouncer=true` for connection pooling (recommended)
### 4. Configure Local Development
```bash
# Create .env.local
cp deployments/local/env.local.example deployments/local/.env.local
# Edit .env.local and add:
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
### 5. Run Migrations
```bash
./scripts/db/migrate.sh auth-service dev
```
## Connection String Format
```
postgresql://[user]:[password]@[endpoint]/[dbname]?sslmode=require&pgbouncer=true
```
**Parameters**:
- `sslmode=require` - Required for Neon
- `pgbouncer=true` - Enable connection pooling (recommended)
## Environment Configuration
### Local Development
File: `deployments/local/.env.local`
```bash
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
### Staging
Store in GitHub Secrets: `NEON_DATABASE_URL_STAGING`
Or in Kubernetes:
```bash
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://...' \
-n staging
```
### Production
Store in GitHub Secrets: `NEON_DATABASE_URL_PRODUCTION`
Or in Kubernetes:
```bash
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://...' \
-n production
```
## Migrations
### Development
```bash
# Create new migration
./scripts/db/migrate.sh auth-service dev
# This will:
# 1. Create migration file
# 2. Apply to database
# 3. Update Prisma Client
```
### Staging/Production
Migrations run automatically in CI/CD:
- Before deployment to staging
- Before deployment to production (with approval)
Manual migration:
```bash
./scripts/db/migrate.sh auth-service deploy
```
## Backup & Restore
### Automatic Backups
Neon provides automatic backups. Access via Neon Console:
- Point-in-time restore
- Branch restore
- Export data
### Manual Backup
```bash
./scripts/db/backup.sh auth-service
```
This creates a SQL dump file in `backups/` directory.
### Restore
```bash
# From Neon Console (recommended)
# Or using psql:
psql $DATABASE_URL < backup.sql
```
## Monitoring
Monitor your databases via Neon Console:
- Connection metrics
- Query performance
- Storage usage
- Branch status
## Troubleshooting
### Connection Issues
1. **Check connection string format**
- Must include `?sslmode=require`
- Verify credentials
2. **Check IP allowlist**
- Neon may restrict IPs
- Add your IP in Neon Console
3. **Check branch status**
- Ensure branch is active
- Check for maintenance
### Migration Issues
1. **DATABASE_URL not set**
```bash
export DATABASE_URL="your-neon-url"
```
2. **Schema mismatch**
```bash
# Reset and re-migrate (dev only!)
pnpm prisma migrate reset
```
3. **Connection timeout**
- Add `?pgbouncer=true` for pooling
- Check Neon console for limits
### Performance Issues
1. **Enable connection pooling**
- Add `?pgbouncer=true` to connection string
2. **Check query performance**
- Use Neon Console query analyzer
- Review slow queries
3. **Optimize indexes**
- Review Prisma schema
- Add indexes for frequent queries
## Cost Optimization
- **Free tier**: 0.5 GB storage, sufficient for dev
- **Staging**: Use free tier or minimal paid plan
- **Production**: Scale based on usage
- **Branching**: Free branches for testing
## Best Practices
1. **Always use connection pooling**: `?pgbouncer=true`
2. **Use SSL**: `?sslmode=require`
3. **Separate branches**: One per environment
4. **Regular backups**: Use Neon's automatic backups
5. **Monitor usage**: Check Neon Console regularly
## Resources
- [Neon Documentation](https://neon.tech/docs)
- [Neon Console](https://console.neon.tech)
- [Prisma + Neon Guide](https://neon.tech/docs/guides/prisma)

View File

@@ -0,0 +1,57 @@
# Troubleshooting Guide
## Common Issues
### Database Connection Failed
**Symptoms**: Service can't connect to database
**Solutions**:
1. Check if PostgreSQL is running: `docker ps`
2. Verify DATABASE_URL in .env
3. Check network connectivity: `docker network ls`
4. Review logs: `docker logs postgres-auth-local`
### Port Already in Use
**Symptoms**: Service fails to start with port error
**Solutions**:
1. Find process using port: `lsof -i :5001`
2. Kill process or change PORT in .env
3. Check docker-compose for port conflicts
### Prisma Client Not Generated
**Symptoms**: Import errors for Prisma Client
**Solutions**:
```bash
cd services/auth-service
pnpm prisma generate
```
### Build Failures
**Symptoms**: TypeScript or build errors
**Solutions**:
1. Clean build artifacts: `./scripts/utils/cleanup.sh`
2. Reinstall dependencies: `pnpm install`
3. Check TypeScript errors: `pnpm typecheck`
### Traefik Not Routing
**Symptoms**: 404 errors from Traefik
**Solutions**:
1. Check Traefik dashboard: http://localhost:8080
2. Verify service labels in docker-compose
3. Check routes.yml configuration
4. Review Traefik logs: `docker logs traefik-local`
## Getting Help
1. Check service logs: `./scripts/dev/logs.sh <service>`
2. Review GitHub Issues
3. Contact team lead

View File

@@ -0,0 +1,89 @@
# New Developer Guide
Welcome to the GoodGo Microservices Platform team!
## First Day Checklist
- [ ] Access to GitHub repository
- [ ] Access to development environment
- [ ] Docker installed and running
- [ ] Node.js and PNPM installed
- [ ] IDE configured (VS Code recommended)
- [ ] Read this guide
## Setup Your Development Environment
1. **Clone the repository**
```bash
git clone <repository-url>
cd Base
```
2. **Run initialization script**
```bash
./scripts/setup/init-project.sh
```
3. **Start local infrastructure**
```bash
cd deployments/local
docker-compose up -d
```
4. **Verify setup**
- Check Traefik: http://localhost:8080
- Check API: http://localhost/api/v1/health
## Development Tools
### Recommended VS Code Extensions
- ESLint
- Prettier
- Prisma
- Docker
- GitLens
### Useful Commands
```bash
# Start all services
./scripts/dev/start-all.sh
# Start specific service
./scripts/dev/start-service.sh auth-service
# View logs
./scripts/dev/logs.sh auth-service
# Run migrations
./scripts/db/migrate.sh auth-service dev
# Run tests
pnpm test
```
## Code Standards
- **TypeScript**: Strict mode enabled
- **Linting**: ESLint with shared config
- **Formatting**: Prettier
- **Commits**: Conventional Commits format
- **Tests**: Minimum 80% coverage
## Getting Help
- Check [Documentation](../guides/)
- Ask in team Slack channel
- Review existing code examples
- Pair with senior developer
## Next Steps
1. Pick a small task from backlog
2. Create feature branch
3. Implement and test
4. Create pull request
5. Get code review
Good luck! 🚀

View File

@@ -0,0 +1,65 @@
# Incident Response Runbook
## Severity Levels
- **P0 - Critical**: Service completely down, data loss
- **P1 - High**: Major functionality broken, affecting many users
- **P2 - Medium**: Minor functionality broken, workaround available
- **P3 - Low**: Cosmetic issues, no user impact
## Response Process
### 1. Acknowledge Incident
- Identify severity level
- Notify team via Slack/email
- Create incident ticket
### 2. Investigate
- Check service health endpoints
- Review logs: `./scripts/dev/logs.sh <service>`
- Check monitoring dashboards (Grafana)
- Review recent deployments
### 3. Mitigate
- Apply quick fixes if available
- Rollback if recent deployment caused issue
- Scale up if resource constraint
### 4. Resolve
- Implement permanent fix
- Verify resolution
- Update documentation
### 5. Post-Mortem
- Document incident
- Identify root cause
- Create action items
- Update runbooks
## Common Scenarios
### Service Down
1. Check Kubernetes pods: `kubectl get pods -n <namespace>`
2. Check pod logs: `kubectl logs <pod-name> -n <namespace>`
3. Restart service: `kubectl rollout restart deployment/<service> -n <namespace>`
4. If persistent, rollback: `kubectl rollout undo deployment/<service> -n <namespace>`
### Database Issues
1. Check database connectivity
2. Review slow queries
3. Check connection pool
4. Scale database if needed
### High Error Rate
1. Check error logs
2. Review recent changes
3. Check external dependencies
4. Implement circuit breaker if needed

View File

@@ -0,0 +1,71 @@
# Rollback Procedure
## When to Rollback
- Service is down or unstable
- Critical bugs introduced
- Performance degradation
- Data corruption risk
## Rollback Steps
### Kubernetes Rollback
1. **Identify current version**
```bash
kubectl get deployment auth-service -n production -o jsonpath='{.spec.template.spec.containers[0].image}'
```
2. **Rollback to previous version**
```bash
kubectl rollout undo deployment/auth-service -n production
```
3. **Verify rollback**
```bash
kubectl rollout status deployment/auth-service -n production
```
4. **Check service health**
```bash
curl https://api.goodgo.vn/health
```
### Database Migration Rollback
**Note**: Prisma doesn't support automatic rollback. Create a new migration to reverse changes.
1. Create reverse migration:
```bash
cd services/auth-service
pnpm prisma migrate dev --name rollback_previous_change
```
2. Apply reverse migration:
```bash
pnpm prisma migrate deploy
```
### Docker Compose Rollback
1. Stop current containers:
```bash
docker-compose down
```
2. Checkout previous version:
```bash
git checkout <previous-commit>
```
3. Rebuild and start:
```bash
docker-compose up -d --build
```
## Post-Rollback
1. Verify functionality
2. Monitor metrics
3. Document rollback reason
4. Plan fix for next deployment

View File

@@ -0,0 +1,102 @@
openapi: 3.0.0
info:
title: Auth Service API
version: 1.0.0
description: Authentication and Authorization Service API
servers:
- url: http://localhost/api/v1
description: Local development
- url: https://api.goodgo.vn/api/v1
description: Production
paths:
/auth/register:
post:
summary: Register new user
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
format: email
password:
type: string
minLength: 6
confirmPassword:
type: string
responses:
'201':
description: User registered successfully
'400':
description: Validation error
/auth/login:
post:
summary: Login user
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
format: email
password:
type: string
responses:
'200':
description: Login successful
'401':
description: Invalid credentials
/auth/logout:
post:
summary: Logout user
security:
- bearerAuth: []
responses:
'200':
description: Logout successful
/auth/refresh:
post:
summary: Refresh access token
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
refreshToken:
type: string
responses:
'200':
description: Token refreshed
'401':
description: Invalid refresh token
/users/me:
get:
summary: Get current user
security:
- bearerAuth: []
responses:
'200':
description: User information
'401':
description: Unauthorized
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@@ -0,0 +1,58 @@
# Giao Tiếp Giữa Các Service
## Các Mẫu Giao Tiếp
### Giao Tiếp Đồng Bộ (HTTP/REST)
Các service giao tiếp đồng bộ qua HTTP REST APIs thông qua Traefik API Gateway.
**Ví dụ:**
```typescript
// Web App -> Auth Service
const response = await fetch('http://api.goodgo.vn/api/v1/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
```
### Giao Tiếp Service-to-Service
Các service có thể giao tiếp trực tiếp qua mạng nội bộ:
```typescript
// Auth Service -> Notification Service (tương lai)
const response = await fetch('http://notification-service:5003/api/v1/notifications', {
method: 'POST',
headers: { 'X-Service-Auth': process.env.INTERNAL_API_KEY },
body: JSON.stringify({ userId, message }),
});
```
## API Gateway Routing
Traefik định tuyến requests dựa trên:
- Host header (`api.goodgo.vn`)
- Path prefix (`/api/v1/auth`)
## Xử Lý Lỗi
Tất cả services tuân theo định dạng error response nhất quán:
```json
{
"success": false,
"error": {
"code": "AUTH_001",
"message": "Invalid credentials",
"details": {}
},
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
## Retry và Circuit Breaker
Triển khai trong tương lai:
- Exponential backoff cho retries
- Circuit breaker pattern cho fault tolerance
- Fallback mechanisms

View File

@@ -0,0 +1,81 @@
# Thiết Kế Hệ Thống
## Tổng Quan
GoodGo Microservices Platform được xây dựng sử dụng kiến trúc microservices với các nguyên tắc sau:
- **Độc Lập Service**: Mỗi service có database riêng và có thể deploy độc lập
- **API Gateway**: Traefik xử lý routing, load balancing, và các concerns xuyên suốt
- **Shared Libraries**: Chức năng chung được trích xuất vào shared packages
- **Infrastructure as Code**: Tất cả cấu hình infrastructure đều được version
- **Observability**: Đầy đủ khả năng monitoring, logging, và tracing
## Sơ Đồ Kiến Trúc
```
┌─────────────┐ ┌─────────────┐
│ Web App │ │ Mobile App │
│ (Next.js) │ │ (React Native)
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬────────┘
┌────────▼────────┐
│ Traefik │
│ (API Gateway) │
└────────┬─────────┘
┌─────────────┼─────────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌───▼────┐
│ Auth │ │ Future │ │ Future │
│Service │ │Service │ │Service │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└────────────┼────────────┘
┌────────────┼────────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌───▼────┐
│Postgres│ │ Redis │ │Prometheus│
└────────┘ └────────┘ └─────────┘
```
## Các Thành Phần
### Frontend Layer
- **Web App**: Ứng dụng Next.js với App Router
- **Mobile App**: Ứng dụng React Native
### API Gateway
- **Traefik**: Reverse proxy, load balancer, SSL termination
### Services Layer
- **Auth Service**: Xác thực và phân quyền
- **Future Services**: Payment, Order, Notification, v.v.
### Infrastructure Layer
- **PostgreSQL**: Database chính
- **Redis**: Caching và session storage
- **Prometheus**: Thu thập metrics
- **Grafana**: Hiển thị metrics
- **Loki**: Tập hợp logs
## Các Mẫu Giao Tiếp
- **Đồng Bộ**: HTTP/REST cho các mẫu request-response
- **Bất Đồng Bộ**: Message queues (triển khai trong tương lai)
- **Service Discovery**: Docker networking và Kubernetes DNS
## Quản Lý Dữ Liệu
- **Database per Service**: Mỗi service sở hữu dữ liệu của mình
- **API Composition**: Services expose APIs để truy cập dữ liệu
- **Event Sourcing**: Xem xét trong tương lai cho audit trails
## Bảo Mật
- **Authentication**: JWT tokens với refresh token rotation
- **Authorization**: Role-based access control (RBAC)
- **Network Security**: TLS/SSL, rate limiting, CORS
- **Secrets Management**: Environment variables, Kubernetes secrets

View File

@@ -0,0 +1,106 @@
# Hướng Dẫn Deployment
## Thiết Lập Database (Neon)
Tất cả môi trường sử dụng **Neon PostgreSQL**. Thiết lập một lần trước khi deploy:
1. Tạo Neon project tại https://neon.tech
2. Tạo branches: `main` (dev), `staging`, `production`
3. Lấy connection strings cho mỗi branch
4. Cấu hình trong environment variables (xem bên dưới)
Xem [Hướng Dẫn Thiết Lập Neon](../../infra/databases/neon/README.md) để biết chi tiết.
## Local Deployment
```bash
# Setup Neon database URL
cp deployments/local/env.local.example deployments/local/.env.local
# Chỉnh sửa .env.local và thêm Neon DATABASE_URL của bạn
# Khởi động services (không cần PostgreSQL container)
cd deployments/local
docker-compose up -d
```
## Staging Deployment
### Yêu Cầu
- Quyền truy cập Kubernetes cluster
- kubectl đã cấu hình
- KUBECONFIG đã set
- Neon staging branch đã tạo
- GitHub Secrets đã cấu hình:
- `NEON_DATABASE_URL_STAGING`
- `KUBECONFIG_STAGING`
### Thiết Lập Secrets
```bash
# Tạo Kubernetes secret
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
--from-literal=jwt-secret='your-staging-jwt-secret' \
--from-literal=jwt-refresh-secret='your-staging-refresh-secret' \
-n staging
```
### Deploy
```bash
./scripts/deploy/deploy-staging.sh
```
Hoặc thủ công:
```bash
kubectl apply -f deployments/staging/kubernetes/
```
**Lưu ý**: Migrations chạy tự động trong CI/CD trước khi deployment.
## Production Deployment
### Yêu Cầu
- Production Kubernetes cluster
- kubectl đã cấu hình với production context
- Neon production branch đã tạo
- GitHub Secrets đã cấu hình:
- `NEON_DATABASE_URL_PRODUCTION`
- `KUBECONFIG_PRODUCTION`
### Thiết Lập Secrets
```bash
# Tạo Kubernetes secret
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true' \
--from-literal=jwt-secret='your-production-jwt-secret' \
--from-literal=jwt-refresh-secret='your-production-refresh-secret' \
-n production
```
### Deploy
```bash
./scripts/deploy/deploy-prod.sh
```
**Lưu ý**: Migrations chạy tự động trong CI/CD trước khi deployment (cần approval).
### Rollback
```bash
kubectl rollout undo deployment/auth-service -n production
```
## Health Checks
- Liveness: `GET /health/live`
- Readiness: `GET /health/ready`
- Health: `GET /health`
## Monitoring
- Prometheus: http://prometheus:9090
- Grafana: http://grafana:3000
- Traefik Dashboard: http://traefik:8080

View File

@@ -0,0 +1,87 @@
# Hướng Dẫn Development
## Cấu Trúc Dự Án
```
├── apps/ # Frontend applications
├── services/ # Backend microservices
├── packages/ # Shared libraries
├── infra/ # Infrastructure configs
├── deployments/ # Deployment configs
├── scripts/ # Automation scripts
└── docs/ # Documentation
```
## Quy Trình Development
### 1. Tạo Feature Branch
```bash
git checkout -b feature/my-feature
```
### 2. Thực Hiện Thay Đổi
- Viết code tuân theo TypeScript strict mode
- Thêm tests cho chức năng mới
- Cập nhật tài liệu nếu cần
### 3. Chạy Tests Locally
```bash
# Tất cả tests
pnpm test
# Service cụ thể
pnpm --filter @goodgo/auth-service test
```
### 4. Lint và Format
```bash
pnpm lint
pnpm format
```
### 5. Tạo Pull Request
- Push branch của bạn
- Tạo PR target `develop`
- CI/CD sẽ chạy tự động
## Thêm Service Mới
1. Sử dụng template:
```bash
./scripts/utils/create-service.sh my-new-service
```
2. Cập nhật cấu hình service
3. Implement business logic
4. Thêm tests
5. Cập nhật tài liệu
## Thêm Package Mới
1. Tạo package trong `packages/new-package`
2. Thêm vào workspace trong `pnpm-workspace.yaml`
3. Export từ `index.ts`
4. Thêm tests
5. Ghi lại cách sử dụng
## Database Migrations
```bash
# Tạo migration
cd services/auth-service
pnpm prisma migrate dev --name add_new_field
# Áp dụng migrations (production)
pnpm prisma migrate deploy
```
## Debugging
- Sử dụng logger từ `@goodgo/logger`
- Kiểm tra Traefik logs: `docker logs traefik-local`
- Kiểm tra service logs: `./scripts/dev/logs.sh auth-service`

View File

@@ -0,0 +1,81 @@
# Bắt Đầu
## Yêu Cầu
- Node.js >= 20.0.0
- PNPM >= 8.0.0
- Docker & Docker Compose
- Git
- Tài khoản Neon (https://neon.tech) - cho database
## Thiết Lập Ban Đầu
1. **Clone repository**
```bash
git clone <repository-url>
cd Base
```
2. **Thiết Lập Neon Database**
```bash
# Chạy script setup
./scripts/db/setup-neon.sh
# Hoặc thủ công:
# 1. Tạo Neon project tại https://neon.tech
# 2. Tạo branches: main (dev), staging, production
# 3. Lấy connection strings
# 4. Cập nhật deployments/local/.env.local
```
Xem [Hướng Dẫn Thiết Lập Neon](../../infra/databases/neon/README.md) để biết chi tiết.
3. **Khởi tạo project**
```bash
./scripts/setup/init-project.sh
```
4. **Khởi động infrastructure** (Redis, Traefik - không cần PostgreSQL)
```bash
cd deployments/local
docker-compose up -d
cd ../..
```
5. **Chạy database migrations**
```bash
./scripts/db/migrate.sh auth-service dev
```
6. **Seed database**
```bash
./scripts/db/seed.sh auth-service
```
7. **Khởi động tất cả services**
```bash
./scripts/dev/start-all.sh
```
## Điểm Truy Cập
- **API Gateway**: http://localhost/api/v1
- **Auth Service**: http://localhost:5001
- **Web Admin**: http://admin.localhost hoặc http://localhost:3000
- **Web Client**: http://localhost hoặc http://localhost:3001
- **Traefik Dashboard**: http://localhost:8080
## Database
Project này sử dụng **Neon PostgreSQL** cho tất cả môi trường:
- **Development**: Neon main branch
- **Staging**: Neon staging branch
- **Production**: Neon production branch
Không cần PostgreSQL local! Xem [Thiết Lập Neon](../../infra/databases/neon/README.md) để biết chi tiết.
## Các Bước Tiếp Theo
- Đọc [Hướng Dẫn Development](development.md)
- Xem [Tài Liệu API](../api/openapi/)
- Xem lại [Tổng Quan Kiến Trúc](../architecture/system-design.md)

View File

@@ -0,0 +1,741 @@
# Hướng Dẫn Development Local
Hướng dẫn chi tiết cách chạy và phát triển dự án trên máy local với hot reload real-time.
## Yêu Cầu Hệ Thống
- **Node.js**: >= 20.0.0
- **PNPM**: >= 8.0.0
- **Docker & Docker Compose**: Phiên bản mới nhất
- **Git**: Để clone repository
- **Tài khoản Neon**: https://neon.tech (cho database)
## Thiết Lập Ban Đầu
### 1. Clone Repository
```bash
git clone <repository-url>
cd Base
```
### 2. Cài Đặt Dependencies
```bash
pnpm install
```
### 3. Thiết Lập Environment Variables
Dự án sử dụng **Hybrid Environment Configuration**:
- **Shared configs** (JWT secrets, Redis): `deployments/local/.env.local`
- **Service-specific configs** (DATABASE_URL, PORT): `services/<service>/.env.local`
#### 3.1. Tạo Shared Environment File
```bash
cp deployments/local/env.local.example deployments/local/.env.local
```
File này chứa configs dùng chung cho tất cả services:
```bash
# JWT Secrets - MUST be same across all services
JWT_SECRET=dev-jwt-secret-change-in-production-min-32-chars
JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production-min-32-chars
# Redis (Docker)
REDIS_HOST=redis
REDIS_PORT=6379
# Common configs
NODE_ENV=development
LOG_LEVEL=debug
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
```
#### 3.2. Tạo Service-Specific Environment File
Mỗi service cần file `.env.local` riêng cho DATABASE_URL và configs cụ thể:
```bash
# Ví dụ: Auth Service
cp services/auth-service/env.local.example services/auth-service/.env.local
```
Chỉnh sửa `services/auth-service/.env.local`:
```bash
# Database riêng cho auth-service
DATABASE_URL=postgresql://user:password@ep-xxx.region.neon.tech/goodgo_auth_dev?sslmode=require&pgbouncer=true
# Service configs
PORT=5001
SERVICE_NAME=auth-service
# Redis override (native dev - Redis in Docker)
REDIS_HOST=localhost
```
**Lưu ý**:
- Mỗi service có **database riêng** (microservices pattern)
- JWT secrets **phải giống nhau** để services verify tokens của nhau
- Xem [Hướng Dẫn Neon Database](neon-database.md) để tạo databases
### 4. Chạy Database Migrations
```bash
./scripts/db/migrate.sh auth-service dev
```
### 5. Seed Database (Tùy chọn)
```bash
./scripts/db/seed.sh auth-service
```
## Các Cách Chạy Dự Án
### Cách 1: Chạy Tất Cả Services (Khuyến nghị)
Cách này phù hợp khi bạn làm full-stack hoặc cần test toàn bộ hệ thống:
```bash
./scripts/dev/start-all.sh
```
Script này sẽ:
1. Kiểm tra Docker đang chạy
2. Kiểm tra DATABASE_URL đã được cấu hình
3. Khởi động infrastructure (Redis, Traefik)
4. Khởi động tất cả services với hot reload
**Hoặc chạy thủ công:**
```bash
# Bước 1: Khởi động infrastructure
cd deployments/local
docker-compose up -d
cd ../..
# Bước 2: Khởi động tất cả services
pnpm dev
```
### Cách 2: Chạy Service Cụ Thể
Cách này phù hợp khi bạn chỉ làm việc với 1 service:
```bash
# Sử dụng script
./scripts/dev/start-service.sh auth-service
# Hoặc chạy trực tiếp
cd services/auth-service
pnpm dev
```
### Cách 3: Hybrid - Native + Docker (Linh hoạt nhất)
Cách này kết hợp tốt nhất của cả hai thế giới: Infrastructure chạy Docker, services đang dev chạy native với hot reload nhanh.
**Phù hợp khi:**
- Làm việc với 1-2 services cụ thể
- Cần services khác chạy background
- Muốn hot reload nhanh cho service đang dev
- Tiết kiệm tài nguyên máy
**Setup:**
```bash
# Bước 1: Khởi động infrastructure (Redis, Traefik)
cd deployments/local
docker-compose up -d redis traefik
cd ../..
# Bước 2: Chạy service đang dev với native (hot reload nhanh)
pnpm --filter @goodgo/auth-service dev
# Bước 3: (Tùy chọn) Chạy services khác trong Docker nếu cần
docker-compose -f deployments/local/docker-compose.yml up -d user-service payment-service
```
**Ví dụ workflow thực tế:**
```bash
# Scenario 1: Chỉ dev auth-service
cd deployments/local && docker-compose up -d redis traefik && cd ../..
pnpm --filter @goodgo/auth-service dev
# Scenario 2: Dev auth-service + cần web-admin để test
cd deployments/local && docker-compose up -d redis traefik && cd ../..
pnpm --filter @goodgo/auth-service dev &
pnpm --filter @goodgo/web-admin dev
# Scenario 3: Dev frontend, backend chạy Docker
cd deployments/local && docker-compose up -d redis traefik auth-service && cd ../..
pnpm --filter @goodgo/web-admin dev
```
**Lợi ích:**
- ⚡ Hot reload cực nhanh (1-2s) cho service đang dev
- 💻 Tiết kiệm RAM - chỉ chạy Docker cho services cần thiết
- 🐛 Debug dễ dàng - attach debugger trực tiếp
- 🎯 Linh hoạt - chọn service nào chạy native, service nào chạy Docker
### Cách 4: Chạy Nhóm Services
```bash
# Chỉ chạy backend services
pnpm --filter "./services/*" dev
# Chỉ chạy frontend apps
pnpm --filter "./apps/*" dev
# Chạy service cụ thể với dependencies
pnpm --filter @goodgo/auth-service... dev
```
### Cách 5: Chạy Với Docker Compose (Full Stack)
```bash
cd deployments/local
docker-compose up -d
# Xem logs
docker-compose logs -f
# Dừng services
docker-compose down
```
**Lưu ý:** Dockerfile hiện tại là production build, không có hot reload. Phù hợp để test môi trường giống production.
## So Sánh Các Phương Án
| Tiêu chí | Cách 1: All Native | Cách 3: Hybrid | Cách 5: Full Docker |
|----------|-------------------|----------------|---------------------|
| **Hot Reload** | ⚡ Cực nhanh (1-2s) | ⚡ Nhanh cho service native | ❌ Không có (production build) |
| **RAM Usage** | 💚 Thấp (~1-2GB) | 💛 Trung bình (~2-3GB) | 🔴 Cao (~3-5GB) |
| **Debug** | ✅ Dễ nhất | ✅ Dễ (native services) | ⚠️ Khó hơn (qua container) |
| **Setup** | 🟢 Đơn giản | 🟡 Trung bình | 🟢 Đơn giản |
| **Giống Production** | ⚠️ Khác biệt | 🟡 Một phần | ✅ Gần giống nhất |
| **Khi nào dùng** | Dev hàng ngày | Dev 1-2 services | Test integration/deployment |
**Khuyến nghị:**
- 🎯 **90% thời gian**: Dùng **Cách 1** (All Native) - nhanh nhất, tiện nhất
- 🔧 **Khi cần linh hoạt**: Dùng **Cách 3** (Hybrid) - chọn service nào chạy native
- 🐳 **Test production-like**: Dùng **Cách 5** (Full Docker) - test networking, deployment
## Điểm Truy Cập
Khi các services đang chạy, bạn có thể truy cập:
| Service | URL | Mô tả |
|---------|-----|-------|
| **API Gateway** | http://localhost/api/v1 | Điểm truy cập chính qua Traefik |
| **Auth Service** | http://localhost:5001 | Truy cập trực tiếp auth service |
| **Auth API** | http://localhost/api/v1/auth | Auth API qua gateway |
| **Web Admin** | http://admin.localhost hoặc http://localhost:3000 | Admin dashboard |
| **Web Client** | http://localhost hoặc http://localhost:3001 | Client web app |
| **Traefik Dashboard** | http://localhost:8080 | Xem routing và services |
## Hot Reload & Live Development
### Backend Services (TypeScript)
Backend services sử dụng `tsx watch` hoặc `nodemon` để tự động restart khi code thay đổi:
```bash
# Trong services/auth-service/package.json
"scripts": {
"dev": "tsx watch src/index.ts"
}
```
**Khi bạn thay đổi:**
- `.ts` files → Service tự động restart (1-2 giây)
- `.env` files → Cần restart thủ công
- `prisma/schema.prisma` → Cần chạy migration
### Frontend Apps (Next.js)
Frontend apps sử dụng Next.js Fast Refresh:
```bash
# Trong apps/web-admin/package.json
"scripts": {
"dev": "next dev"
}
```
**Khi bạn thay đổi:**
- React components → Cập nhật ngay lập tức (không reload page)
- CSS/Tailwind → Cập nhật ngay lập tức
- `next.config.js` → Cần restart
### Shared Packages
Khi thay đổi shared packages (trong `packages/`):
```bash
# Packages tự động rebuild với Turbo watch mode
pnpm --filter @goodgo/logger dev
```
## Workflow Development Thực Tế
### Setup 3 Terminals (Khuyến nghị)
#### Option A: Development Hàng Ngày (All Native)
**Terminal 1: Chạy Services**
```bash
./scripts/dev/start-all.sh
# Hoặc: pnpm dev
```
**Terminal 2: Watch Tests**
```bash
# Auto-run tests khi code thay đổi
pnpm --filter @goodgo/auth-service test --watch
```
**Terminal 3: Development Tasks**
```bash
# Prisma Studio
pnpm --filter @goodgo/auth-service prisma studio
# Xem logs
./scripts/dev/logs.sh auth-service
# Migrations
./scripts/db/migrate.sh auth-service dev
```
#### Option B: Hybrid Development (Selective Services)
**Terminal 1: Infrastructure + Service đang dev**
```bash
# Start infrastructure
cd deployments/local && docker-compose up -d redis traefik && cd ../..
# Dev service cụ thể với hot reload
pnpm --filter @goodgo/auth-service dev
```
**Terminal 2: Frontend (nếu cần)**
```bash
pnpm --filter @goodgo/web-admin dev
```
**Terminal 3: Tools & Logs**
```bash
# Watch tests
pnpm --filter @goodgo/auth-service test --watch
# Xem Docker logs
docker logs -f redis-cache-local
# Quick commands
pnpm format
```
### Workflow Theo Use Case
#### Use Case 1: Dev Backend Service
```bash
# Terminal 1
cd deployments/local && docker-compose up -d redis traefik && cd ../..
pnpm --filter @goodgo/auth-service dev
# Terminal 2
pnpm --filter @goodgo/auth-service test --watch
# Terminal 3
pnpm --filter @goodgo/auth-service prisma studio
```
#### Use Case 2: Dev Frontend + Backend
```bash
# Terminal 1: Backend
pnpm --filter @goodgo/auth-service dev
# Terminal 2: Frontend
pnpm --filter @goodgo/web-admin dev
# Terminal 3: Infrastructure
cd deployments/local && docker-compose up -d redis traefik
```
#### Use Case 3: Full Stack Development
```bash
# Terminal 1: All services
./scripts/dev/start-all.sh
# Terminal 2: Watch tests
pnpm test --watch
# Terminal 3: Tools
# Prisma Studio, logs, migrations, etc.
```
## Kiểm Tra Health
### Health Endpoints
```bash
# Kiểm tra API Gateway
curl http://localhost/api/v1/health
# Kiểm tra Auth Service trực tiếp
curl http://localhost:5001/health
# Kiểm tra Redis
docker exec redis-cache-local redis-cli ping
```
### Traefik Dashboard
Truy cập http://localhost:8080 để xem:
- Tất cả routes đang hoạt động
- Services đã đăng ký
- Health status của services
## Database Development
### Thay Đổi Schema
```bash
# 1. Chỉnh sửa prisma/schema.prisma
# 2. Tạo và áp dụng migration
cd services/auth-service
pnpm prisma migrate dev --name add_new_field
# 3. Prisma Client sẽ tự động regenerate
```
### Reset Database (Development Only!)
```bash
cd services/auth-service
pnpm prisma migrate reset
```
### Xem Database
```bash
# Mở Prisma Studio
cd services/auth-service
pnpm prisma studio
# Truy cập: http://localhost:5555
```
## Debugging
### VS Code Debugging
Tạo file `.vscode/launch.json`:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Auth Service",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@goodgo/auth-service", "dev"],
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal"
}
]
}
```
### Xem Logs Chi Tiết
```bash
# Service logs
./scripts/dev/logs.sh auth-service
# Docker logs
docker logs -f auth-service-local
docker logs -f redis-cache-local
docker logs -f traefik-local
# Tất cả logs
docker-compose -f deployments/local/docker-compose.yml logs -f
```
## Troubleshooting
### Port Đã Được Sử Dụng
```bash
# Tìm process đang dùng port
lsof -i :5001 # Auth service
lsof -i :3000 # Web admin
lsof -i :6379 # Redis
lsof -i :80 # Traefik
# Kill process
kill -9 <PID>
```
### Docker Không Chạy
```bash
# Kiểm tra Docker
docker info
# Khởi động Docker Desktop (macOS)
open -a Docker
# Restart Docker services
docker-compose -f deployments/local/docker-compose.yml restart
```
### Database Connection Failed
```bash
# Kiểm tra DATABASE_URL trong service-specific env
cat services/auth-service/.env.local | grep DATABASE_URL
# Nếu chưa có file .env.local, tạo từ example
cp services/auth-service/env.local.example services/auth-service/.env.local
# Chỉnh sửa DATABASE_URL với connection string từ Neon
# DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/goodgo_auth_dev?sslmode=require&pgbouncer=true
# Test connection
cd services/auth-service
pnpm prisma db pull
```
**Lưu ý**: Mỗi service cần file `.env.local` riêng với DATABASE_URL của service đó.
### Module Not Found
```bash
# Cleanup và reinstall
./scripts/utils/cleanup.sh
pnpm install
# Hoặc chỉ cleanup node_modules
rm -rf node_modules
rm -rf services/*/node_modules
rm -rf apps/*/node_modules
rm -rf packages/*/node_modules
pnpm install
```
### Hot Reload Không Hoạt Động
```bash
# Restart service
# Ctrl+C để dừng, sau đó:
pnpm dev
# Hoặc restart Docker container
docker-compose -f deployments/local/docker-compose.yml restart auth-service
```
## Tips & Best Practices
### 1. Sử Dụng Turbo Cache
Turbo cache giúp build nhanh hơn:
```bash
# Lần đầu chạy sẽ chậm
pnpm dev
# Các lần sau sẽ nhanh hơn nhờ cache
# Cache được lưu trong node_modules/.cache/turbo
```
### 2. Dev Selective Services
Không cần chạy tất cả nếu chỉ làm 1 service:
```bash
# Chỉ chạy auth-service
pnpm --filter @goodgo/auth-service dev
# Chạy auth-service và dependencies
pnpm --filter @goodgo/auth-service... dev
```
### 3. Watch Tests
```bash
# Chạy tests tự động khi code thay đổi
pnpm --filter @goodgo/auth-service test --watch
```
### 4. Format Code Tự Động
Cài đặt Prettier extension trong VS Code và bật format on save.
### 5. Sử Dụng Git Hooks
```bash
# Pre-commit hook sẽ tự động format và lint
git commit -m "feat: add new feature"
```
### 6. Hybrid Development (Best of Both Worlds)
Kết hợp Docker và Native để tối ưu workflow:
```bash
# Infrastructure luôn chạy Docker
cd deployments/local && docker-compose up -d redis traefik
# Service đang dev chạy native (hot reload nhanh)
pnpm --filter @goodgo/auth-service dev
# Services khác có thể chạy Docker nếu cần
docker-compose up -d user-service payment-service
```
**Lợi ích:**
- ⚡ Hot reload nhanh nhất cho service đang làm
- 💻 Tiết kiệm RAM - không chạy tất cả containers
- 🐛 Debug dễ dàng - breakpoints, logs trực tiếp
- 🎯 Linh hoạt - chọn service nào chạy native
### 7. Quản Lý Multiple Services
```bash
# Chạy selective services với pnpm workspace
pnpm --filter "@goodgo/auth-service" --filter "@goodgo/user-service" dev
# Hoặc dùng pattern
pnpm --filter "./services/{auth,user}-service" dev
```
## Environment Variables
Dự án sử dụng **Hybrid Environment Configuration** với 2 levels:
### Level 1: Shared Environment (`deployments/local/.env.local`)
Configs dùng chung cho tất cả services:
```bash
# JWT Secrets - MUST be same across all services
JWT_SECRET=dev-jwt-secret-change-in-production-min-32-chars
JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production-min-32-chars
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# Redis (Docker hostname)
REDIS_HOST=redis
REDIS_PORT=6379
# Common configs
NODE_ENV=development
LOG_LEVEL=debug
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
# Monitoring (optional)
TRACING_ENABLED=false
```
**Tạo file:**
```bash
cp deployments/local/env.local.example deployments/local/.env.local
```
### Level 2: Service-Specific Environment (`services/<service>/.env.local`)
Configs riêng cho từng service:
```bash
# services/auth-service/.env.local
# Database riêng cho service này
DATABASE_URL=postgresql://user:pass@ep-xxx.neon.tech/goodgo_auth_dev?sslmode=require&pgbouncer=true
# Service configs
PORT=5001
SERVICE_NAME=auth-service
API_VERSION=v1
# Redis override (native dev - Redis in Docker)
REDIS_HOST=localhost
# Service-specific configs
EMAIL_SERVICE_URL=http://notification-service:5003
PROMETHEUS_PORT=9090
```
**Tạo file:**
```bash
cp services/auth-service/env.local.example services/auth-service/.env.local
```
### Cách Hoạt Động
Services load env theo thứ tự:
1. **Shared env** (`deployments/local/.env.local`) - JWT, Redis, common configs
2. **Service env** (`.env.local`) - DATABASE_URL, PORT, overrides
```bash
# Trong package.json
"dev": "dotenv -e ../../deployments/local/.env.local -e .env.local -- tsx watch src/main.ts"
```
**Lợi ích:**
- ✅ JWT secrets giống nhau → services verify tokens của nhau
- ✅ Mỗi service có database riêng → microservices pattern
- ✅ Override configs dễ dàng → REDIS_HOST=localhost cho native dev
- ✅ Không duplicate configs → maintain dễ hơn
### Important Notes
1. **JWT Secrets**: MUST be identical across all services
2. **Database**: Each service has its own database (e.g., `goodgo_auth_dev`, `goodgo_user_dev`)
3. **Redis Host**:
- `redis` (Docker hostname) in shared env
- `localhost` (override) in service env for native dev
4. **Never commit**: `.env.local` files are gitignored
## Các Lệnh Hữu Ích
```bash
# Development
pnpm dev # Chạy tất cả services
pnpm build # Build tất cả
pnpm test # Test tất cả
pnpm lint # Lint tất cả
pnpm format # Format code
# Cleanup
pnpm clean # Xóa build artifacts
./scripts/utils/cleanup.sh # Cleanup toàn bộ
# Database
./scripts/db/migrate.sh auth-service dev # Migration
./scripts/db/seed.sh auth-service # Seed data
./scripts/db/backup.sh auth-service # Backup
# Docker
docker-compose -f deployments/local/docker-compose.yml up -d # Start
docker-compose -f deployments/local/docker-compose.yml down # Stop
docker-compose -f deployments/local/docker-compose.yml logs -f # Logs
docker-compose -f deployments/local/docker-compose.yml restart # Restart
```
## Tài Nguyên Thêm
- [Getting Started](getting-started.md) - Thiết lập ban đầu
- [Development Guide](development.md) - Quy trình development
- [Neon Database Guide](neon-database.md) - Hướng dẫn database
- [Troubleshooting](troubleshooting.md) - Xử lý sự cố

View File

@@ -0,0 +1,215 @@
# Hướng Dẫn Neon Database
Project này sử dụng [Neon PostgreSQL](https://neon.tech) cho tất cả môi trường.
## Tại Sao Chọn Neon?
-**Serverless**: Không cần quản lý infrastructure
-**Branching**: Database riêng cho dev/staging/prod
-**Auto-scaling**: Tự động xử lý traffic spikes
-**Point-in-time restore**: Dễ dàng khôi phục từ lỗi
-**Free tier**: Hoàn hảo cho development
-**Connection pooling**: Hỗ trợ PgBouncer tích hợp
## Bắt Đầu Nhanh
### 1. Tạo Tài Khoản Neon
1. Đăng ký tại https://neon.tech
2. Tạo project mới: `goodgo-platform`
### 2. Tạo Branches
Trong Neon Console, tạo branches:
- `main` (development) - đã tồn tại
- `staging` - tạo từ main
- `production` - tạo từ main
### 3. Lấy Connection Strings
Cho mỗi branch, copy connection string:
- Định dạng: `postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require`
- Thêm `?pgbouncer=true` cho connection pooling (khuyến nghị)
### 4. Cấu Hình Local Development
```bash
# Tạo .env.local
cp deployments/local/env.local.example deployments/local/.env.local
# Chỉnh sửa .env.local và thêm:
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
### 5. Chạy Migrations
```bash
./scripts/db/migrate.sh auth-service dev
```
## Định Dạng Connection String
```
postgresql://[user]:[password]@[endpoint]/[dbname]?sslmode=require&pgbouncer=true
```
**Tham số**:
- `sslmode=require` - Bắt buộc cho Neon
- `pgbouncer=true` - Bật connection pooling (khuyến nghị)
## Cấu Hình Môi Trường
### Local Development
File: `deployments/local/.env.local`
```bash
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
### Staging
Lưu trong GitHub Secrets: `NEON_DATABASE_URL_STAGING`
Hoặc trong Kubernetes:
```bash
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://...' \
-n staging
```
### Production
Lưu trong GitHub Secrets: `NEON_DATABASE_URL_PRODUCTION`
Hoặc trong Kubernetes:
```bash
kubectl create secret generic auth-service-secrets \
--from-literal=database-url='postgresql://...' \
-n production
```
## Migrations
### Development
```bash
# Tạo migration mới
./scripts/db/migrate.sh auth-service dev
# Điều này sẽ:
# 1. Tạo migration file
# 2. Áp dụng vào database
# 3. Cập nhật Prisma Client
```
### Staging/Production
Migrations chạy tự động trong CI/CD:
- Trước khi deploy lên staging
- Trước khi deploy lên production (cần approval)
Migration thủ công:
```bash
./scripts/db/migrate.sh auth-service deploy
```
## Backup & Restore
### Backup Tự Động
Neon cung cấp backup tự động. Truy cập qua Neon Console:
- Point-in-time restore
- Branch restore
- Export data
### Backup Thủ Công
```bash
./scripts/db/backup.sh auth-service
```
Điều này tạo file SQL dump trong thư mục `backups/`.
### Restore
```bash
# Từ Neon Console (khuyến nghị)
# Hoặc sử dụng psql:
psql $DATABASE_URL < backup.sql
```
## Monitoring
Theo dõi databases qua Neon Console:
- Connection metrics
- Query performance
- Storage usage
- Branch status
## Xử Lý Sự Cố
### Vấn Đề Kết Nối
1. **Kiểm tra định dạng connection string**
- Phải bao gồm `?sslmode=require`
- Xác minh credentials
2. **Kiểm tra IP allowlist**
- Neon có thể giới hạn IPs
- Thêm IP của bạn trong Neon Console
3. **Kiểm tra branch status**
- Đảm bảo branch đang active
- Kiểm tra maintenance
### Vấn Đề Migration
1. **DATABASE_URL chưa set**
```bash
export DATABASE_URL="your-neon-url"
```
2. **Schema không khớp**
```bash
# Reset và migrate lại (chỉ dev!)
pnpm prisma migrate reset
```
3. **Connection timeout**
- Thêm `?pgbouncer=true` cho pooling
- Kiểm tra Neon console cho limits
### Vấn Đề Hiệu Suất
1. **Bật connection pooling**
- Thêm `?pgbouncer=true` vào connection string
2. **Kiểm tra query performance**
- Sử dụng Neon Console query analyzer
- Xem lại slow queries
3. **Tối ưu indexes**
- Xem lại Prisma schema
- Thêm indexes cho các query thường dùng
## Tối Ưu Chi Phí
- **Free tier**: 0.5 GB storage, đủ cho dev
- **Staging**: Sử dụng free tier hoặc plan trả phí tối thiểu
- **Production**: Scale dựa trên usage
- **Branching**: Free branches cho testing
## Best Practices
1. **Luôn sử dụng connection pooling**: `?pgbouncer=true`
2. **Sử dụng SSL**: `?sslmode=require`
3. **Tách branches**: Một branch cho mỗi môi trường
4. **Backup thường xuyên**: Sử dụng automatic backups của Neon
5. **Theo dõi usage**: Kiểm tra Neon Console thường xuyên
## Tài Nguyên
- [Neon Documentation](https://neon.tech/docs)
- [Neon Console](https://console.neon.tech)
- [Prisma + Neon Guide](https://neon.tech/docs/guides/prisma)

View File

@@ -0,0 +1,57 @@
# Hướng Dẫn Xử Lý Sự Cố
## Các Vấn Đề Thường Gặp
### Kết Nối Database Thất Bại
**Triệu chứng**: Service không thể kết nối database
**Giải pháp**:
1. Kiểm tra PostgreSQL có đang chạy: `docker ps`
2. Xác minh DATABASE_URL trong .env
3. Kiểm tra kết nối mạng: `docker network ls`
4. Xem logs: `docker logs postgres-auth-local`
### Port Đã Được Sử Dụng
**Triệu chứng**: Service không khởi động với lỗi port
**Giải pháp**:
1. Tìm process đang dùng port: `lsof -i :5001`
2. Kill process hoặc thay đổi PORT trong .env
3. Kiểm tra docker-compose cho port conflicts
### Prisma Client Chưa Được Generate
**Triệu chứng**: Lỗi import cho Prisma Client
**Giải pháp**:
```bash
cd services/auth-service
pnpm prisma generate
```
### Build Failures
**Triệu chứng**: Lỗi TypeScript hoặc build
**Giải pháp**:
1. Xóa build artifacts: `./scripts/utils/cleanup.sh`
2. Cài đặt lại dependencies: `pnpm install`
3. Kiểm tra lỗi TypeScript: `pnpm typecheck`
### Traefik Không Routing
**Triệu chứng**: Lỗi 404 từ Traefik
**Giải pháp**:
1. Kiểm tra Traefik dashboard: http://localhost:8080
2. Xác minh service labels trong docker-compose
3. Kiểm tra cấu hình routes.yml
4. Xem Traefik logs: `docker logs traefik-local`
## Tìm Kiếm Trợ Giúp
1. Kiểm tra service logs: `./scripts/dev/logs.sh <service>`
2. Xem lại GitHub Issues
3. Liên hệ team lead

View File

@@ -0,0 +1,89 @@
# Hướng Dẫn Cho Developer Mới
Chào mừng đến với team GoodGo Microservices Platform!
## Checklist Ngày Đầu Tiên
- [ ] Quyền truy cập GitHub repository
- [ ] Quyền truy cập môi trường development
- [ ] Docker đã cài đặt và đang chạy
- [ ] Node.js và PNPM đã cài đặt
- [ ] IDE đã cấu hình (khuyến nghị VS Code)
- [ ] Đã đọc hướng dẫn này
## Thiết Lập Môi Trường Development
1. **Clone repository**
```bash
git clone <repository-url>
cd Base
```
2. **Chạy script khởi tạo**
```bash
./scripts/setup/init-project.sh
```
3. **Khởi động infrastructure local**
```bash
cd deployments/local
docker-compose up -d
```
4. **Xác minh setup**
- Kiểm tra Traefik: http://localhost:8080
- Kiểm tra API: http://localhost/api/v1/health
## Công Cụ Development
### VS Code Extensions Khuyến Nghị
- ESLint
- Prettier
- Prisma
- Docker
- GitLens
### Các Lệnh Hữu Ích
```bash
# Khởi động tất cả services
./scripts/dev/start-all.sh
# Khởi động service cụ thể
./scripts/dev/start-service.sh auth-service
# Xem logs
./scripts/dev/logs.sh auth-service
# Chạy migrations
./scripts/db/migrate.sh auth-service dev
# Chạy tests
pnpm test
```
## Tiêu Chuẩn Code
- **TypeScript**: Bật strict mode
- **Linting**: ESLint với shared config
- **Formatting**: Prettier
- **Commits**: Định dạng Conventional Commits
- **Tests**: Tối thiểu 80% coverage
## Tìm Kiếm Trợ Giúp
- Xem [Tài Liệu](../guides/)
- Hỏi trong Slack channel của team
- Xem lại các ví dụ code hiện có
- Pair với senior developer
## Các Bước Tiếp Theo
1. Chọn một task nhỏ từ backlog
2. Tạo feature branch
3. Implement và test
4. Tạo pull request
5. Nhận code review
Chúc may mắn! 🚀

View File

@@ -0,0 +1,65 @@
# Runbook Phản Ứng Sự Cố
## Mức Độ Nghiêm Trọng
- **P0 - Nghiêm Trọng**: Dịch vụ hoàn toàn ngừng hoạt động, mất dữ liệu
- **P1 - Cao**: Chức năng chính bị lỗi, ảnh hưởng đến nhiều người dùng
- **P2 - Trung Bình**: Chức năng phụ bị lỗi, có giải pháp thay thế
- **P3 - Thấp**: Lỗi giao diện, không ảnh hưởng người dùng
## Quy Trình Phản Ứng
### 1. Xác Nhận Sự Cố
- Xác định mức độ nghiêm trọng
- Thông báo cho team qua Slack/email
- Tạo ticket sự cố
### 2. Điều Tra
- Kiểm tra các endpoint health của service
- Xem logs: `./scripts/dev/logs.sh <service>`
- Kiểm tra monitoring dashboards (Grafana)
- Xem lại các deployment gần đây
### 3. Giảm Thiểu
- Áp dụng các sửa lỗi nhanh nếu có
- Rollback nếu deployment gần đây gây ra vấn đề
- Scale up nếu thiếu tài nguyên
### 4. Giải Quyết
- Triển khai sửa lỗi vĩnh viễn
- Xác minh giải pháp
- Cập nhật tài liệu
### 5. Hậu Phân Tích
- Ghi lại sự cố
- Xác định nguyên nhân gốc rễ
- Tạo các hành động cần thực hiện
- Cập nhật runbooks
## Các Tình Huống Thường Gặp
### Service Ngừng Hoạt Động
1. Kiểm tra Kubernetes pods: `kubectl get pods -n <namespace>`
2. Kiểm tra pod logs: `kubectl logs <pod-name> -n <namespace>`
3. Khởi động lại service: `kubectl rollout restart deployment/<service> -n <namespace>`
4. Nếu vẫn lỗi, rollback: `kubectl rollout undo deployment/<service> -n <namespace>`
### Vấn Đề Database
1. Kiểm tra kết nối database
2. Xem lại các query chậm
3. Kiểm tra connection pool
4. Scale database nếu cần
### Tỷ Lệ Lỗi Cao
1. Kiểm tra error logs
2. Xem lại các thay đổi gần đây
3. Kiểm tra các dependencies bên ngoài
4. Triển khai circuit breaker nếu cần

View File

@@ -0,0 +1,71 @@
# Quy Trình Rollback
## Khi Nào Cần Rollback
- Service ngừng hoạt động hoặc không ổn định
- Lỗi nghiêm trọng được phát hiện
- Hiệu suất suy giảm
- Rủi ro hỏng dữ liệu
## Các Bước Rollback
### Rollback Kubernetes
1. **Xác định phiên bản hiện tại**
```bash
kubectl get deployment auth-service -n production -o jsonpath='{.spec.template.spec.containers[0].image}'
```
2. **Rollback về phiên bản trước**
```bash
kubectl rollout undo deployment/auth-service -n production
```
3. **Xác minh rollback**
```bash
kubectl rollout status deployment/auth-service -n production
```
4. **Kiểm tra health của service**
```bash
curl https://api.goodgo.vn/health
```
### Rollback Database Migration
**Lưu ý**: Prisma không hỗ trợ rollback tự động. Tạo migration mới để đảo ngược thay đổi.
1. Tạo migration đảo ngược:
```bash
cd services/auth-service
pnpm prisma migrate dev --name rollback_previous_change
```
2. Áp dụng migration đảo ngược:
```bash
pnpm prisma migrate deploy
```
### Rollback Docker Compose
1. Dừng các container hiện tại:
```bash
docker-compose down
```
2. Checkout phiên bản trước:
```bash
git checkout <previous-commit>
```
3. Rebuild và khởi động:
```bash
docker-compose up -d --build
```
## Sau Khi Rollback
1. Xác minh chức năng
2. Theo dõi metrics
3. Ghi lại lý do rollback
4. Lập kế hoạch sửa lỗi cho deployment tiếp theo

View File

@@ -0,0 +1,48 @@
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: postgres-auth
environment:
POSTGRES_USER: ${POSTGRES_USER:-devuser}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-devpass}
POSTGRES_DB: ${POSTGRES_DB:-auth_db}
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
- ./postgres/postgres.conf:/etc/postgresql/postgresql.conf
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-devuser}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- microservices-network
redis:
image: redis:7-alpine
container_name: redis-cache
command: redis-server /etc/redis/redis.conf
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
- ./redis/redis.conf:/etc/redis/redis.conf
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- microservices-network
volumes:
postgres_data:
redis_data:
networks:
microservices-network:
external: true

View File

@@ -0,0 +1,125 @@
# Neon Database Setup
This project uses [Neon PostgreSQL](https://neon.tech) for all environments (dev, staging, production).
## Why Neon?
- ✅ Serverless PostgreSQL with auto-scaling
- ✅ Branching support (dev, staging, production branches)
- ✅ Point-in-time restore
- ✅ Free tier for development
- ✅ Built-in connection pooling
- ✅ No infrastructure management needed
## Setup Instructions
### 1. Create Neon Project
1. Sign up at https://neon.tech
2. Create a new project: `goodgo-platform`
3. Note your connection string from the main branch
### 2. Create Branches
Create branches for each environment:
```bash
# Using Neon Console or CLI
# Main branch = development
# Create: staging branch
# Create: production branch
```
### 3. Get Connection Strings
For each branch, get the connection string:
- Format: `postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require`
- Add `?pgbouncer=true` for connection pooling (recommended)
### 4. Configure Environment Variables
#### Local Development
Create `deployments/local/.env.local`:
```bash
DATABASE_URL=postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
#### Staging/Production
Store in GitHub Secrets:
- `NEON_DATABASE_URL_STAGING`
- `NEON_DATABASE_URL_PRODUCTION`
Or in Kubernetes Secrets (see `deployments/staging/kubernetes/secrets.yaml.example`)
## Connection Pooling
Neon provides built-in connection pooling. Add `?pgbouncer=true` to your connection string:
```
postgresql://user:pass@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true
```
## Migration
### Development
```bash
cd services/auth-service
pnpm prisma migrate dev
```
### Staging/Production
Migrations run automatically in CI/CD workflows before deployment.
## Backup & Restore
Neon provides automatic backups and point-in-time restore via their console.
For manual backup:
```bash
# Using pg_dump
pg_dump $DATABASE_URL > backup.sql
# Restore
psql $DATABASE_URL < backup.sql
```
## Monitoring
Monitor your databases via Neon Console:
- Connection metrics
- Query performance
- Storage usage
- Branch status
## Cost Optimization
- **Free tier**: Sufficient for development
- **Staging**: Use free tier or minimal paid plan
- **Production**: Scale based on usage
## Troubleshooting
### Connection Issues
1. Check connection string format
2. Verify SSL mode: `?sslmode=require`
3. Check IP allowlist in Neon console (if enabled)
4. Verify credentials
### Migration Issues
1. Ensure DATABASE_URL is set correctly
2. Check Prisma schema matches database
3. Review migration files
### Performance Issues
1. Enable connection pooling: `?pgbouncer=true`
2. Check query performance in Neon console
3. Review indexes in Prisma schema

64
infra/databases/neon/setup.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
set -e
echo "🚀 Neon Database Setup Script"
echo ""
# Check if Neon CLI is installed
if ! command -v neonctl &> /dev/null; then
echo "⚠️ Neon CLI not found. Installing..."
echo "Visit: https://neon.tech/docs/cli/install"
echo ""
read -p "Do you want to continue with manual setup? (y/n): " continue
if [ "$continue" != "y" ]; then
exit 1
fi
fi
echo "This script will help you set up Neon database branches."
echo ""
echo "Prerequisites:"
echo "1. Neon account created at https://neon.tech"
echo "2. Neon project created"
echo "3. Neon CLI installed (optional)"
echo ""
read -p "Enter your Neon project name (default: goodgo-platform): " project_name
project_name=${project_name:-goodgo-platform}
echo ""
echo "📋 Setup Steps:"
echo ""
echo "1. Go to https://console.neon.tech"
echo "2. Select your project: $project_name"
echo "3. Create branches:"
echo " - staging (from main)"
echo " - production (from main)"
echo ""
echo "4. Get connection strings for each branch:"
echo " - Development (main branch)"
echo " - Staging branch"
echo " - Production branch"
echo ""
echo "5. Update environment files:"
echo " - deployments/local/.env.local"
echo " - GitHub Secrets (for CI/CD)"
echo " - Kubernetes Secrets (for deployments)"
echo ""
read -p "Enter development DATABASE_URL (or press Enter to skip): " dev_url
if [ -n "$dev_url" ]; then
echo "DATABASE_URL=$dev_url" >> deployments/local/.env.local
echo "✅ Added to deployments/local/.env.local"
fi
echo ""
echo "📝 Next steps:"
echo ""
echo "1. Add staging URL to GitHub Secrets: NEON_DATABASE_URL_STAGING"
echo "2. Add production URL to GitHub Secrets: NEON_DATABASE_URL_PRODUCTION"
echo "3. Create Kubernetes secrets for staging/production"
echo "4. Run migrations: ./scripts/db/migrate.sh auth-service dev"
echo ""
echo "✅ Setup complete! See infra/databases/neon/README.md for details."

View File

@@ -0,0 +1,8 @@
-- Initial database setup script
-- This script runs when PostgreSQL container starts for the first time
-- Create extensions if needed
-- CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Note: Prisma migrations handle schema creation
-- This file is for any additional setup needed

View File

@@ -0,0 +1,15 @@
# PostgreSQL configuration
# Customize as needed for your environment
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB
maintenance_work_mem = 64MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 4MB
min_wal_size = 1GB
max_wal_size = 4GB

View File

@@ -0,0 +1,21 @@
# Redis configuration
# Network
bind 0.0.0.0
protected-mode yes
port 6379
# Memory
maxmemory 256mb
maxmemory-policy allkeys-lru
# Persistence
save 900 1
save 300 10
save 60 10000
# Logging
loglevel notice
# Security
# requirepass your-redis-password-here

Some files were not shown because too many files have changed in this diff Show More