PrettyGirllz Plateform Integration Plan - Wiz-DevTech/prettygirllz GitHub Wiki

Looking at your complete architecture, you have all the backend services ready. Now you need to integrate them and create a frontend application. Here's a comprehensive integration plan for your PrettyGirllz platform:

I've created a comprehensive integration plan for your PrettyGirllz platform. Here are the key steps to integrate all your services:

1. Create Frontend Application

First, create a Next.js frontend:

cd C:\Development Projects\prettygirllz
npx create-next-app@latest prettygirllz-frontend --typescript --tailwind --app

2. Configure Frontend Gateway

Update your Frontend Gateway to act as an API Gateway for all services:

package com.wizdevtech.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@RestController
@RequestMapping("/api")
public class ApiController {

    @Autowired
    private RestTemplate restTemplate;

    private static final String IDENTITY_SERVICE = "http://localhost:8081";
    private static final String PRODUCT_SERVICE = "http://localhost:8082";
    private static final String LOGISTICS_SERVICE = "http://localhost:8083";
    private static final String SOCIAL_COMMERCE_SERVICE = "http://localhost:8084";
    private static final String CLIENT_ADAPTER_SERVICE = "http://localhost:8085";

    @RequestMapping(value = "/auth/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public ResponseEntity<?> proxyAuthRequests(HttpServletRequest request, 
                                             @RequestBody(required = false) Object body) {
        return proxyRequest(request, body, IDENTITY_SERVICE);
    }

    @RequestMapping(value = "/products/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public ResponseEntity<?> proxyProductRequests(HttpServletRequest request, 
                                                @RequestBody(required = false) Object body) {
        return proxyRequest(request, body, PRODUCT_SERVICE);
    }

    @RequestMapping(value = "/logistics/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public ResponseEntity<?> proxyLogisticsRequests(HttpServletRequest request, 
                                                  @RequestBody(required = false) Object body) {
        return proxyRequest(request, body, LOGISTICS_SERVICE);
    }

    @RequestMapping(value = "/social/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public ResponseEntity<?> proxySocialRequests(HttpServletRequest request, 
                                               @RequestBody(required = false) Object body) {
        return proxyRequest(request, body, SOCIAL_COMMERCE_SERVICE);
    }

    @RequestMapping(value = "/client/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
    public ResponseEntity<?> proxyClientRequests(HttpServletRequest request, 
                                               @RequestBody(required = false) Object body) {
        return proxyRequest(request, body, CLIENT_ADAPTER_SERVICE);
    }

    private ResponseEntity<?> proxyRequest(HttpServletRequest request, Object body, String targetService) {
        String path = request.getRequestURI();
        String queryString = request.getQueryString();
        String url = targetService + path + (queryString != null ? "?" + queryString : "");

        HttpHeaders headers = new HttpHeaders();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headers.add(headerName, request.getHeader(headerName));
        }

        HttpEntity<Object> entity = new HttpEntity<>(body, headers);
        
        try {
            return restTemplate.exchange(
                url,
                HttpMethod.valueOf(request.getMethod()),
                entity,
                Object.class
            );
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error proxying request: " + e.getMessage());
        }
    }
}

3. Create Frontend Pages

Here's an example of the main product page:

'use client';

import { useState, useEffect } from 'react';
import Link from 'next/link';

interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  category: string;
  imageUrl: string;
}

export default function ProductsPage() {
  const [products, setProducts] = useState<Product[]>([]);
  const [loading, setLoading] = useState(true);
  const [category, setCategory] = useState('all');

  useEffect(() => {
    fetchProducts();
  }, [category]);

  const fetchProducts = async () => {
    try {
      const response = await fetch(
        `http://localhost:8080/api/products${category !== 'all' ? `?category=${category}` : ''}`
      );
      const data = await response.json();
      setProducts(data);
    } catch (error) {
      console.error('Failed to fetch products:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">PrettyGirllz Products</h1>
      
      <div className="flex gap-4 mb-8">
        <button
          onClick={() => setCategory('all')}
          className={`px-4 py-2 rounded ${category === 'all' ? 'bg-pink-500 text-white' : 'bg-gray-200'}`}
        >
          All
        </button>
        <button
          onClick={() => setCategory('fashion')}
          className={`px-4 py-2 rounded ${category === 'fashion' ? 'bg-pink-500 text-white' : 'bg-gray-200'}`}
        >
          Fashion
        </button>
        <button
          onClick={() => setCategory('candy')}
          className={`px-4 py-2 rounded ${category === 'candy' ? 'bg-pink-500 text-white' : 'bg-gray-200'}`}
        >
          Candy
        </button>
      </div>

      {loading ? (
        <div className="text-center">Loading...</div>
      ) : (
        <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-6">
          {products.map((product) => (
            <Link href={`/products/${product.id}`} key={product.id}>
              <div className="border rounded-lg overflow-hidden hover:shadow-lg transition-shadow">
                <img
                  src={product.imageUrl || '/placeholder.jpg'}
                  alt={product.name}
                  className="w-full h-48 object-cover"
                />
                <div className="p-4">
                  <h3 className="font-semibold mb-2">{product.name}</h3>
                  <p className="text-gray-600 text-sm mb-2">{product.description}</p>
                  <p className="text-lg font-bold text-pink-500">${product.price}</p>
                </div>
              </div>
            </Link>
          ))}
        </div>
      )}
    </div>
  );
}

4. Social Feed Integration

'use client';

import { useState, useEffect } from 'react';
import { useAuth } from '@/hooks/useAuth';

interface FeedItem {
  id: string;
  userId: string;
  userName: string;
  userAvatar: string;
  content: string;
  imageUrl?: string;
  likes: number;
  comments: number;
  createdAt: string;
}

export default function SocialFeedPage() {
  const [feedItems, setFeedItems] = useState<FeedItem[]>([]);
  const [newPost, setNewPost] = useState('');
  const { user, token } = useAuth();

  useEffect(() => {
    fetchFeed();
  }, []);

  const fetchFeed = async () => {
    try {
      const response = await fetch('http://localhost:8080/api/social/feed', {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      const data = await response.json();
      setFeedItems(data);
    } catch (error) {
      console.error('Failed to fetch feed:', error);
    }
  };

  const createPost = async () => {
    if (!newPost.trim()) return;

    try {
      const response = await fetch('http://localhost:8080/api/social/posts', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({ content: newPost }),
      });
      
      if (response.ok) {
        setNewPost('');
        fetchFeed();
      }
    } catch (error) {
      console.error('Failed to create post:', error);
    }
  };

  const likePost = async (postId: string) => {
    try {
      await fetch(`http://localhost:8080/api/social/posts/${postId}/like`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      fetchFeed();
    } catch (error) {
      console.error('Failed to like post:', error);
    }
  };

  return (
    <div className="max-w-2xl mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">Social Feed</h1>
      
      {user && (
        <div className="mb-8">
          <textarea
            value={newPost}
            onChange={(e) => setNewPost(e.target.value)}
            placeholder="What's on your mind?"
            className="w-full p-4 border rounded-lg resize-none"
            rows={3}
          />
          <button
            onClick={createPost}
            className="mt-2 px-6 py-2 bg-pink-500 text-white rounded-lg hover:bg-pink-600"
          >
            Post
          </button>
        </div>
      )}

      <div className="space-y-6">
        {feedItems.map((item) => (
          <div key={item.id} className="bg-white rounded-lg shadow p-6">
            <div className="flex items-center mb-4">
              <img
                src={item.userAvatar || '/default-avatar.png'}
                alt={item.userName}
                className="w-12 h-12 rounded-full mr-4"
              />
              <div>
                <h3 className="font-semibold">{item.userName}</h3>
                <p className="text-gray-500 text-sm">
                  {new Date(item.createdAt).toLocaleDateString()}
                </p>
              </div>
            </div>
            
            <p className="mb-4">{item.content}</p>
            
            {item.imageUrl && (
              <img
                src={item.imageUrl}
                alt="Post content"
                className="w-full rounded-lg mb-4"
              />
            )}
            
            <div className="flex items-center gap-6 text-gray-500">
              <button
                onClick={() => likePost(item.id)}
                className="flex items-center gap-2 hover:text-pink-500"
              >
                <span>❤️</span>
                <span>{item.likes}</span>
              </button>
              <div className="flex items-center gap-2">
                <span>💬</span>
                <span>{item.comments}</span>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

5. Real-time Chat Implementation

'use client';

import { useState, useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';
import { useAuth } from '@/hooks/useAuth';

interface Message {
  id: string;
  senderId: string;
  senderName: string;
  content: string;
  timestamp: string;
}

export default function ChatComponent({ chatId }: { chatId: string }) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [newMessage, setNewMessage] = useState('');
  const [socket, setSocket] = useState<Socket | null>(null);
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const { user, token } = useAuth();

  useEffect(() => {
    // Fetch existing messages
    fetchMessages();

    // Connect to WebSocket
    const newSocket = io('ws://localhost:8084', {
      auth: { token },
      transports: ['websocket'],
    });

    newSocket.on('connect', () => {
      console.log('Connected to chat');
      newSocket.emit('join_chat', { chatId });
    });

    newSocket.on('new_message', (message: Message) => {
      setMessages((prev) => [...prev, message]);
    });

    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, [chatId, token]);

  useEffect(() => {
    scrollToBottom();
  }, [messages]);

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  };

  const fetchMessages = async () => {
    try {
      const response = await fetch(`http://localhost:8080/api/social/chat/${chatId}/messages`, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      });
      const data = await response.json();
      setMessages(data);
    } catch (error) {
      console.error('Failed to fetch messages:', error);
    }
  };

  const sendMessage = async () => {
    if (!newMessage.trim() || !socket) return;

    const message = {
      chatId,
      content: newMessage,
      senderId: user.id,
    };

    // Send via WebSocket
    socket.emit('send_message', message);
    
    // Clear input
    setNewMessage('');
  };

  return (
    <div className="flex flex-col h-[600px] bg-white rounded-lg shadow">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((message) => (
          <div
            key={message.id}
            className={`flex ${
              message.senderId === user?.id ? 'justify-end' : 'justify-start'
            }`}
          >
            <div
              className={`max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
                message.senderId === user?.id
                  ? 'bg-pink-500 text-white'
                  : 'bg-gray-200 text-gray-800'
              }`}
            >
              <p className="font-semibold text-sm">{message.senderName}</p>
              <p>{message.content}</p>
              <p className="text-xs mt-1 opacity-70">
                {new Date(message.timestamp).toLocaleTimeString()}
              </p>
            </div>
          </div>
        ))}
        <div ref={messagesEndRef} />
      </div>
      
      <div className="p-4 border-t">
        <div className="flex gap-2">
          <input
            type="text"
            value={newMessage}
            onChange={(e) => setNewMessage(e.target.value)}
            onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
            placeholder="Type a message..."
            className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-500"
          />
          <button
            onClick={sendMessage}
            className="px-6 py-2 bg-pink-500 text-white rounded-lg hover:bg-pink-600"
          >
            Send
          </button>
        </div>
      </div>
    </div>
  );
}

6. Docker Compose Setup

Create a docker-compose.yml in your root directory:

version: '3.8'

services:
  # PostgreSQL Database
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: prettygirllz
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./db-init:/docker-entrypoint-initdb.d

  # Frontend Gateway
  frontend-gateway:
    build: ./1_frontend-gateway
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/prettygirllz
    depends_on:
      - postgres

  # Identity Access Service
  identity-access:
    build: ./2_identity-access
    ports:
      - "8081:8081"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/identityaccess
      - JWT_SECRET=ESJgV3asQTucPstTLyg6bqCfPhVQTxxE9zqr+2doXxCvKTiAwVmloZTFwdxCqvyPmzk+UsYLHIXbaxgc3olo7w==
      - JWT_EXPIRATION_MS=86400000
      - SERVER_PORT=8081
    depends_on:
      - postgres

  # Product Service
  product-service:
    build: ./3_product-service
    ports:
      - "8082:8082"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/productdb
      - SERVER_PORT=8082
    depends_on:
      - postgres

  # Logistics Service
  logistics-service:
    build: ./4_logistics-service
    ports:
      - "8083:8083"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/logisticsdb
      - SERVER_PORT=8083
    depends_on:
      - postgres

  # Social Commerce Service
  social-commerce:
    build: ./6_social-commerce-service
    ports:
      - "8084:8084"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/social_commerce_db
      - JWT_SECRET=ESJgV3asQTucPstTLyg6bqCfPhVQTxxE9zqr+2doXxCvKTiAwVmloZTFwdxCqvyPmzk+UsYLHIXbaxgc3olo7w==
      - SERVER_PORT=8084
    depends_on:
      - postgres

  # Client Adapters (Node.js)
  client-adapters:
    build: ./5_client-adapters
    ports:
      - "8085:8085"
    environment:
      - NODE_ENV=development
      - PORT=8085
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/prettygirllz
    depends_on:
      - postgres

  # Frontend Application (Next.js)
  frontend:
    build: ./prettygirllz-frontend
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_API_URL=http://localhost:8080
    depends_on:
      - frontend-gateway

  # Redis for caching and sessions
  redis:
    image: redis:7
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  # RabbitMQ for messaging
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "5672:5672"
      - "15672:15672"
    environment:
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS=admin

volumes:
  postgres_data:
  redis_data:

7. Create Frontend Application Structure

# Create the frontend application
cd C:\Development Projects\prettygirllz
npx create-next-app@latest prettygirllz-frontend --typescript --tailwind --app

# Create the necessary directories
cd prettygirllz-frontend
mkdir -p src/app/auth/login
mkdir -p src/app/auth/register
mkdir -p src/app/products
mkdir -p src/app/social/feed
mkdir -p src/app/social/chat
mkdir -p src/lib/api
mkdir -p src/components/layout
mkdir -p src/hooks

8. Service Port Configuration

Update each service's application.yml to ensure they're running on the correct ports:

# 2_identity-access/src/main/resources/application.yml
server:
  port: 8081

---

# 3_product-service/src/main/resources/application.yml
server:
  port: 8082

---

# 4_logistics-service/src/main/resources/application.yml
server:
  port: 8083

---

# 6_social-commerce-service/src/main/resources/application.yml
server:
  port: 8084

---

# 5_client-adapters/server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8085;

app.listen(PORT, () => {
  console.log(`Client adapters running on port ${PORT}`);
});

9. Start the Platform

  1. Start all services using Docker Compose:

    cd C:\Development Projects\prettygirllz
    docker-compose up --build
  2. Or start services individually for development:

    # Terminal 1: Frontend Gateway
    cd 1_frontend-gateway
    mvn spring-boot:run
    
    # Terminal 2: Identity Access
    cd 2_identity-access
    mvn spring-boot:run
    
    # Terminal 3: Product Service
    cd 3_product-service
    mvn spring-boot:run
    
    # Terminal 4: Logistics Service
    cd 4_logistics-service
    mvn spring-boot:run
    
    # Terminal 5: Social Commerce
    cd 6_social-commerce-service
    mvn spring-boot:run
    
    # Terminal 6: Client Adapters
    cd 5_client-adapters
    npm start
    
    # Terminal 7: Frontend
    cd prettygirllz-frontend
    npm run dev

10. Access Your Platform

Once everything is running:

Next Steps

  1. Create authentication flow in the frontend
  2. Implement product browsing with categories
  3. Add social features (feed, chat, interactions)
  4. Set up order processing with logistics
  5. Configure monitoring with Prometheus/Grafana
  6. Add caching with Redis
  7. Set up CI/CD pipeline
  8. Deploy to production (AWS/GCP/Azure)

This integration brings all your microservices together into a cohesive platform. Each service maintains its independence while the Frontend Gateway provides a unified API for the frontend application to consume.

⚠️ **GitHub.com Fallback** ⚠️