PrettyGirllz Platform Integration Plan - Wiz-DevTech/prettygirllz GitHub Wiki
[Next.js Frontend Application]
|
[Frontend Gateway (API Gateway)]
|
+----------------+----------------+----------------+----------------+
| | | | |
[Identity-Access] [Product-Service] [Logistics] [Social-Commerce] [Client-Adapters]
| | | | |
+----------------+----------------+----------------+----------------+
|
[PostgreSQL Databases]
Create a Next.js application with the following structure:
prettygirllz-frontend/
├── src/
│ ├── app/
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── auth/
│ │ │ ├── login/page.tsx
│ │ │ ├── register/page.tsx
│ │ │ └── profile/page.tsx
│ │ ├── products/
│ │ │ ├── page.tsx
│ │ │ ├── [id]/page.tsx
│ │ │ ├── fashion/page.tsx
│ │ │ └── candy/page.tsx
│ │ ├── social/
│ │ │ ├── feed/page.tsx
│ │ │ ├── chat/page.tsx
│ │ │ └── messages/page.tsx
│ │ ├── orders/
│ │ │ ├── page.tsx
│ │ │ └── tracking/[id]/page.tsx
│ │ └── admin/
│ │ ├── dashboard/page.tsx
│ │ └── moderation/page.tsx
│ ├── components/
│ │ ├── layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Footer.tsx
│ │ │ └── Sidebar.tsx
│ │ ├── auth/
│ │ │ ├── LoginForm.tsx
│ │ │ └── AvatarSelector.tsx
│ │ ├── product/
│ │ │ ├── ProductCard.tsx
│ │ │ └── ProductGrid.tsx
│ │ ├── social/
│ │ │ ├── FeedPost.tsx
│ │ │ ├── ChatWindow.tsx
│ │ │ └── MessageBubble.tsx
│ │ └── common/
│ │ ├── Button.tsx
│ │ └── Modal.tsx
│ ├── lib/
│ │ ├── api/
│ │ │ ├── auth.ts
│ │ │ ├── products.ts
│ │ │ ├── social.ts
│ │ │ └── logistics.ts
│ │ └── utils/
│ │ ├── auth.ts
│ │ └── constants.ts
│ └── styles/
│ └── globals.css
├── public/
├── package.json
└── next.config.js
Update the Frontend Gateway to route requests to appropriate services:
# frontend-gateway/src/main/resources/application.yml
server:
port: 8080
services:
identity-access:
url: http://localhost:8081
prefix: /api/auth
product-service:
url: http://localhost:8082
prefix: /api/products
logistics-service:
url: http://localhost:8083
prefix: /api/logistics
social-commerce:
url: http://localhost:8084
prefix: /api/social
client-adapters:
url: http://localhost:8085
prefix: /api/client
routes:
- path: /api/auth/**
service: identity-access
strip-prefix: false
- path: /api/products/**
service: product-service
strip-prefix: false
- path: /api/logistics/**
service: logistics-service
strip-prefix: false
- path: /api/social/**
service: social-commerce
strip-prefix: false
- path: /api/client/**
service: client-adapters
strip-prefix: false
security:
jwt:
secret: ${JWT_SECRET}
exclude-paths:
- /api/auth/login
- /api/auth/register
- /api/products/**
- /health
// frontend-gateway/src/main/java/com/wizdevtech/gateway/router/ServiceRouter.java
@Component
public class ServiceRouter {
@Autowired
private RestTemplate restTemplate;
@Autowired
private JwtTokenValidator tokenValidator;
@Value("${services.identity-access.url}")
private String identityServiceUrl;
public ResponseEntity<?> routeRequest(
String path,
HttpMethod method,
Object body,
String token) {
// Validate JWT token
if (isProtectedPath(path) && !tokenValidator.validateToken(token)) {
throw new UnauthorizedException("Invalid token");
}
// Route to appropriate service
String targetUrl = resolveTargetUrl(path);
HttpHeaders headers = createHeaders(token);
HttpEntity<?> entity = new HttpEntity<>(body, headers);
return restTemplate.exchange(targetUrl, method, entity, Object.class);
}
private String resolveTargetUrl(String path) {
if (path.startsWith("/api/auth")) {
return identityServiceUrl + path;
}
// Add other service mappings
return null;
}
}
// common/security/JwtAuthenticationFilter.java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = getTokenFromRequest(request);
if (token != null && tokenProvider.validateToken(token)) {
// Add user context to request
request.setAttribute("user", tokenProvider.getUserFromToken(token));
}
filterChain.doFilter(request, response);
}
}
// frontend/src/lib/api/client.ts
import axios from 'axios';
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080';
export const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
},
});
// Add auth interceptor
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Add response interceptor for error handling
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/auth/login';
}
return Promise.reject(error);
}
);
// frontend/src/lib/api/auth.ts
import { apiClient } from './client';
export const authApi = {
login: async (email: string, password: string) => {
const response = await apiClient.post('/api/auth/login', { email, password });
return response.data;
},
register: async (userData: RegisterData) => {
const response = await apiClient.post('/api/auth/register', userData);
return response.data;
},
getProfile: async () => {
const response = await apiClient.get('/api/auth/profile');
return response.data;
},
updateAvatar: async (avatarData: AvatarData) => {
const response = await apiClient.put('/api/auth/avatar', avatarData);
return response.data;
},
};
// frontend/src/lib/api/products.ts
export const productsApi = {
getProducts: async (category?: string) => {
const response = await apiClient.get('/api/products', { params: { category } });
return response.data;
},
getProduct: async (id: string) => {
const response = await apiClient.get(`/api/products/${id}`);
return response.data;
},
searchProducts: async (query: string) => {
const response = await apiClient.get('/api/products/search', { params: { q: query } });
return response.data;
},
};
// frontend/src/lib/api/social.ts
export const socialApi = {
getFeed: async (page = 1) => {
const response = await apiClient.get('/api/social/feed', { params: { page } });
return response.data;
},
createPost: async (postData: PostData) => {
const response = await apiClient.post('/api/social/posts', postData);
return response.data;
},
getChatMessages: async (chatId: string) => {
const response = await apiClient.get(`/api/social/chat/${chatId}/messages`);
return response.data;
},
sendMessage: async (chatId: string, message: string) => {
const response = await apiClient.post(`/api/social/chat/${chatId}/messages`, { message });
return response.data;
},
};
// frontend/src/app/auth/login/page.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { authApi } from '@/lib/api/auth';
export default function LoginPage() {
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
try {
const { token, user } = await authApi.login(email, password);
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
router.push('/');
} catch (error) {
console.error('Login failed:', error);
}
};
return (
<div className="min-h-screen flex items-center justify-center">
<form onSubmit={handleLogin} className="w-full max-w-md">
<h1 className="text-3xl font-bold mb-6">Login to PrettyGirllz</h1>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
className="w-full p-2 mb-4 border rounded"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="w-full p-2 mb-4 border rounded"
/>
<button type="submit" className="w-full bg-pink-500 text-white p-2 rounded">
Login
</button>
</form>
</div>
);
}
// frontend/src/lib/websocket/client.ts
import { io, Socket } from 'socket.io-client';
class WebSocketClient {
private socket: Socket | null = null;
connect(token: string) {
this.socket = io('ws://localhost:8084', {
auth: { token },
transports: ['websocket'],
});
this.socket.on('connect', () => {
console.log('WebSocket connected');
});
this.socket.on('disconnect', () => {
console.log('WebSocket disconnected');
});
}
joinChat(chatId: string) {
this.socket?.emit('join_chat', { chatId });
}
sendMessage(chatId: string, message: string) {
this.socket?.emit('send_message', { chatId, message });
}
onMessage(callback: (message: any) => void) {
this.socket?.on('new_message', callback);
}
disconnect() {
this.socket?.disconnect();
}
}
export const wsClient = new WebSocketClient();
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
frontend-gateway:
build: ./1_frontend-gateway
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=dev
depends_on:
- postgres
identity-access:
build: ./2_identity-access
ports:
- "8081:8081"
environment:
- SPRING_PROFILES_ACTIVE=dev
- JWT_SECRET=${JWT_SECRET}
depends_on:
- postgres
product-service:
build: ./3_product-service
ports:
- "8082:8082"
environment:
- SPRING_PROFILES_ACTIVE=dev
depends_on:
- postgres
logistics-service:
build: ./4_logistics-service
ports:
- "8083:8083"
environment:
- SPRING_PROFILES_ACTIVE=dev
depends_on:
- postgres
social-commerce:
build: ./6_social-commerce-service
ports:
- "8084:8084"
environment:
- SPRING_PROFILES_ACTIVE=dev
depends_on:
- postgres
client-adapters:
build: ./5_client-adapters
ports:
- "8085:8085"
depends_on:
- postgres
frontend:
build: ./prettygirllz-frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://localhost:8080
depends_on:
- frontend-gateway
volumes:
postgres_data:
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-gateway
spec:
replicas: 2
selector:
matchLabels:
app: frontend-gateway
template:
metadata:
labels:
app: frontend-gateway
spec:
containers:
- name: frontend-gateway
image: prettygirllz/frontend-gateway:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
---
apiVersion: v1
kind: Service
metadata:
name: frontend-gateway
spec:
selector:
app: frontend-gateway
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
# .github/workflows/deploy.yml
name: Deploy PrettyGirllz Platform
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Frontend Gateway
run: |
cd 1_frontend-gateway
mvn clean package
docker build -t prettygirllz/frontend-gateway:latest .
- name: Build Identity Access
run: |
cd 2_identity-access
mvn clean package
docker build -t prettygirllz/identity-access:latest .
# Add other services...
- name: Build Frontend
run: |
cd prettygirllz-frontend
npm install
npm run build
docker build -t prettygirllz/frontend:latest .
- name: Push to Docker Hub
run: |
docker push prettygirllz/frontend-gateway:latest
docker push prettygirllz/identity-access:latest
# Push other images...
- name: Deploy to Kubernetes
run: |
kubectl apply -f k8s/
# prometheus/prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prettygirllz-services'
static_configs:
- targets:
- frontend-gateway:8080
- identity-access:8081
- product-service:8082
- logistics-service:8083
- social-commerce:8084
metrics_path: '/actuator/prometheus'
Create dashboards for:
- Service health
- Request rates
- Error rates
- Performance metrics
- Business metrics
-
Phase 1: Core Integration
- Set up Frontend Gateway routing
- Implement JWT propagation
- Create basic frontend structure
-
Phase 2: Frontend Development
- Build authentication pages
- Create product browsing
- Implement basic social features
-
Phase 3: Advanced Features
- Add real-time chat
- Implement feed recommendations
- Add moderation dashboard
-
Phase 4: Production Readiness
- Set up monitoring
- Configure CI/CD
- Implement caching
- Add rate limiting
-
Phase 5: Deployment
- Deploy to staging
- Perform testing
- Deploy to production
- Monitor and optimize
This integration plan provides a complete path from your current microservices to a fully integrated PrettyGirllz platform.