diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8c6cc6c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,40 @@ +# Dépendances +node_modules +npm-debug.log + +# Fichiers de build +dist +.vite + +# Fichiers de développement +.env +.env.local +.env.*.local + +# Fichiers Git +.git +.gitignore + +# Fichiers Replit +.replit +replit.nix + +# Fichiers de test +*.test.js +*.test.ts +__tests__ + +# Documentation +*.md +!README.md + +# Fichiers système +.DS_Store +Thumbs.db + +# Logs +logs +*.log + +# Uploads (seront montés comme volume) +uploads/* diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5cf910a --- /dev/null +++ b/.env.example @@ -0,0 +1,29 @@ +# Configuration de la base de données PostgreSQL +PGDATABASE=socialflow +PGUSER=socialflow +PGPASSWORD=changeme +PGPORT=5432 +PGHOST=postgres + +# URL de connexion complète (générée automatiquement dans Docker) +DATABASE_URL=postgresql://socialflow:changeme@postgres:5432/socialflow + +# Configuration de l'application +PORT=5000 +NODE_ENV=production +APP_URL=http://localhost:5000 + +# Clé secrète pour les sessions (CHANGEZ CETTE VALEUR) +SESSION_SECRET=your-secret-key-change-me-to-random-string + +# Clé API OpenRouter pour la génération de texte IA +# Obtenez votre clé sur https://openrouter.ai/ +OPENROUTER_API_KEY=your-openrouter-api-key-here + +# Optionnel: Configuration Facebook API +# FACEBOOK_APP_ID= +# FACEBOOK_APP_SECRET= + +# Optionnel: Configuration Instagram API +# INSTAGRAM_APP_ID= +# INSTAGRAM_APP_SECRET= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9ba7f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.DS_Store +server/public +vite.config.ts.* +*.tar.gz \ No newline at end of file diff --git a/.replit b/.replit new file mode 100644 index 0000000..3cb59c7 --- /dev/null +++ b/.replit @@ -0,0 +1,46 @@ +modules = ["nodejs-20", "web", "postgresql-16"] +run = "npm run dev" +hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"] + +[nix] +channel = "stable-24_05" + +[deployment] +deploymentTarget = "autoscale" +build = ["npm", "run", "build"] +run = ["npm", "run", "start"] + +[[ports]] +localPort = 5000 +externalPort = 80 + +[[ports]] +localPort = 32809 +externalPort = 3000 + +[env] +PORT = "5000" + +[agent] +integrations = ["javascript_object_storage:1.0.0", "javascript_database:1.0.0"] + +[workflows] +runButton = "Project" + +[[workflows.workflow]] +name = "Project" +mode = "parallel" +author = "agent" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "Start application" + +[[workflows.workflow]] +name = "Start application" +author = "agent" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "npm run dev" +waitForPort = 5000 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..169acce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +# Étape 1: Build de l'application +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copier les fichiers de dépendances +COPY package*.json ./ + +# Installer toutes les dépendances (dev + prod) +RUN npm ci + +# Copier le reste du code source +COPY . . + +# Build du frontend +RUN npm run build + +# Étape 2: Image de production +FROM node:20-alpine + +WORKDIR /app + +# Installer uniquement les dépendances de production +COPY package*.json ./ +RUN npm ci --only=production + +# Copier les fichiers buildés et le code serveur +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/server ./server +COPY --from=builder /app/shared ./shared +COPY --from=builder /app/drizzle.config.ts ./ + +# Créer le dossier uploads +RUN mkdir -p uploads + +# Exposer le port 5000 +EXPOSE 5000 + +# Variables d'environnement par défaut +ENV NODE_ENV=production +ENV PORT=5000 + +# Démarrer l'application +CMD ["node", "dist/server.js"] diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..24ee8e2 --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,161 @@ +# Installation de Social Flow sur un serveur privé + +## Prérequis + +- Docker et Docker Compose installés sur votre serveur +- Clé API OpenRouter (https://openrouter.ai/) +- (Optionnel) Tokens d'accès Facebook et Instagram + +## Installation rapide + +### 1. Cloner ou télécharger l'application + +```bash +git clone socialflow +cd socialflow +``` + +### 2. Configurer les variables d'environnement + +Copiez le fichier d'exemple et modifiez les valeurs : + +```bash +cp .env.example .env +nano .env # ou utilisez votre éditeur préféré +``` + +**Variables importantes à modifier :** + +- `PGPASSWORD` : Changez le mot de passe de la base de données +- `SESSION_SECRET` : Générez une clé secrète aléatoire +- `OPENROUTER_API_KEY` : Votre clé API OpenRouter +- `APP_URL` : L'URL publique de votre application + +### 3. Démarrer l'application + +```bash +docker-compose up -d +``` + +L'application sera accessible sur `http://localhost:5000` (ou le port configuré). + +### 4. Vérifier le statut + +```bash +# Voir les logs +docker-compose logs -f app + +# Vérifier que les containers sont démarrés +docker-compose ps +``` + +## Commandes utiles + +### Arrêter l'application +```bash +docker-compose down +``` + +### Redémarrer l'application +```bash +docker-compose restart +``` + +### Voir les logs +```bash +# Logs de l'application +docker-compose logs -f app + +# Logs de la base de données +docker-compose logs -f postgres +``` + +### Mettre à jour l'application +```bash +# Arrêter l'application +docker-compose down + +# Récupérer les dernières modifications +git pull + +# Reconstruire et redémarrer +docker-compose up -d --build +``` + +### Sauvegarder la base de données +```bash +docker exec socialflow-db pg_dump -U socialflow socialflow > backup.sql +``` + +### Restaurer la base de données +```bash +docker exec -i socialflow-db psql -U socialflow socialflow < backup.sql +``` + +## Configuration avancée + +### Changer le port d'exposition + +Modifiez le fichier `docker-compose.yml` : + +```yaml +services: + app: + ports: + - "8080:5000" # Exposer sur le port 8080 +``` + +### Utiliser un domaine personnalisé + +1. Configurez votre reverse proxy (Nginx, Caddy, etc.) +2. Pointez vers `http://localhost:5000` +3. Mettez à jour `APP_URL` dans `.env` + +### Exemple de configuration Nginx + +```nginx +server { + listen 80; + server_name socialflow.votredomaine.com; + + location / { + proxy_pass http://localhost:5000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} +``` + +## Résolution de problèmes + +### L'application ne démarre pas + +1. Vérifiez les logs : `docker-compose logs -f` +2. Assurez-vous que les ports ne sont pas déjà utilisés +3. Vérifiez que Docker a suffisamment de ressources + +### Erreurs de base de données + +1. Vérifiez que PostgreSQL est démarré : `docker-compose ps` +2. Vérifiez les credentials dans `.env` +3. Recréez la base si nécessaire : `docker-compose down -v && docker-compose up -d` + +### Problèmes de performance + +1. Augmentez les ressources Docker +2. Configurez un reverse proxy avec cache +3. Utilisez un stockage SSD pour les volumes + +## Sécurité + +- Changez toujours `PGPASSWORD` et `SESSION_SECRET` +- Utilisez HTTPS en production (avec Let's Encrypt par exemple) +- Configurez un firewall pour limiter l'accès aux ports +- Mettez à jour régulièrement les images Docker + +## Support + +Pour plus d'informations ou en cas de problème, consultez la documentation ou ouvrez une issue. diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..f15fa88 --- /dev/null +++ b/client/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..40f7f31 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,31 @@ +import { Switch, Route } from "wouter"; +import { queryClient } from "./lib/queryClient"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { Toaster } from "@/components/ui/toaster"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import NotFound from "@/pages/not-found"; +import Dashboard from "@/pages/dashboard"; + +function Router() { + return ( + + + + + ); +} + +function App() { + return ( + + +
+ + +
+
+
+ ); +} + +export default App; diff --git a/client/src/components/ai-chat.tsx b/client/src/components/ai-chat.tsx new file mode 100644 index 0000000..48427de --- /dev/null +++ b/client/src/components/ai-chat.tsx @@ -0,0 +1,192 @@ +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { apiRequest, queryClient } from "@/lib/queryClient"; +import { Bot, User, Lightbulb, History, Zap } from "lucide-react"; +import { useToast } from "@/hooks/use-toast"; + +interface Message { + role: "user" | "assistant"; + content: string; + variants?: Array<{ variant: string; text: string; characterCount: number }>; +} + +export default function AiChat() { + const [messages, setMessages] = useState([ + { + role: "assistant", + content: "Bonjour ! Je suis votre assistant IA. Fournissez-moi les informations sur votre produit (nom, description, caractéristiques, prix) et je générerai automatiquement du contenu optimisé pour vos publications Facebook et Instagram.", + }, + ]); + const [input, setInput] = useState(""); + const { toast } = useToast(); + + const generateMutation = useMutation({ + mutationFn: async (productInfo: any) => { + const response = await apiRequest("POST", "/api/ai/generate", productInfo); + return response.json(); + }, + onSuccess: (data) => { + setMessages((prev) => [ + ...prev, + { + role: "assistant", + content: "Parfait ! J'ai généré 3 variantes de texte pour votre produit. Choisissez celle qui vous convient :", + variants: data.variants, + }, + ]); + queryClient.invalidateQueries({ queryKey: ["/api/ai/generations"] }); + }, + onError: () => { + toast({ + title: "Erreur", + description: "Impossible de générer le texte. Vérifiez votre clé API OpenRouter.", + variant: "destructive", + }); + }, + }); + + const handleSubmit = () => { + if (!input.trim()) return; + + // Add user message + const userMessage = input; + setMessages((prev) => [...prev, { role: "user", content: userMessage }]); + + // Parse product info from input (simple parsing) + const productInfo = { + name: userMessage.match(/Produit:\s*(.+?)(?:\n|$)/i)?.[1] || userMessage, + price: userMessage.match(/Prix:\s*(.+?)(?:\n|$)/i)?.[1] || "", + description: userMessage.match(/Description:\s*(.+?)(?:\n|$)/i)?.[1] || "", + features: userMessage.match(/Caractéristiques:\s*(.+?)(?:\n|$)/i)?.[1]?.split(",") || [], + }; + + generateMutation.mutate(productInfo); + setInput(""); + }; + + return ( +
+
+
+
+ +
+
+

Assistant IA - Génération de contenu

+

Décrivez votre produit pour générer du texte

+
+
+ +
+ +
+ {messages.map((message, index) => ( +
+ {message.role === "assistant" && ( +
+ +
+ )} + +
+
+

{message.content}

+ + {message.variants && ( +
+ {message.variants.map((variant, vIndex) => ( +
+
+ + {variant.variant} + + {variant.characterCount} caractères +
+

{variant.text}

+
+ ))} +
+ )} +
+ + {message.role === "user" ? "Maintenant" : "Il y a quelques secondes"} + +
+ + {message.role === "user" && ( +
+ +
+ )} +
+ ))} +
+ +
+
+