Docfile commited on
Commit
3c7eec5
·
verified ·
1 Parent(s): 5242158

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +497 -123
app.py CHANGED
@@ -1,163 +1,537 @@
1
- from flask import Flask, request, jsonify, redirect, url_for
2
  import requests
3
  from bs4 import BeautifulSoup
4
  import random
5
  import string
6
  import json
7
  import threading
 
 
 
8
 
9
- app = Flask(__name__)
10
 
11
- # Fichier JSON pour sauvegarder la progression
12
- JSON_FILE = "accounts.json"
13
 
14
- # Variable globale pour suivre la progression
15
- progress_data = {
16
- "accounts": [],
17
- "total": 0,
18
- "created": 0,
19
- "in_progress": False
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
- # Fonctions de génération aléatoire
23
-
24
- def generate_random_username(length=80):
25
- """Génère un nom d'utilisateur aléatoire avec une chaîne continue d'au moins 80 caractères."""
26
  return ''.join(random.choice(string.ascii_lowercase) for _ in range(length))
27
 
 
28
  def generate_random_email():
29
- """Génère une adresse email aléatoire avec un nom court."""
30
- username = ''.join(random.choice(string.ascii_lowercase) for _ in range(8))
31
  domains = ["gmail.com", "yahoo.com", "outlook.com", "example.com"]
32
  return f"{username}@{random.choice(domains)}"
33
 
34
- def generate_random_password(length=10):
35
- """Génère un mot de passe aléatoire."""
36
- chars = string.ascii_letters + string.digits + string.punctuation
 
37
  return ''.join(random.choice(chars) for _ in range(length))
38
 
39
- # URL du site à enregistrer
40
- BASE_URL = "https://ivoire-startup-tracker-edithbrou.replit.app"
41
- REGISTER_URL = f"{BASE_URL}/register"
42
-
43
  def create_account(is_startup_rep=False):
44
- """Crée un compte sur le site et renvoie les informations du compte en cas de succès."""
45
- session = requests.Session()
46
- response = session.get(REGISTER_URL)
47
-
48
- if response.status_code != 200:
49
- print(f"Erreur lors de l'accès à la page d'inscription: {response.status_code}")
50
- return False
51
-
52
- soup = BeautifulSoup(response.text, 'html.parser')
53
- csrf_input = soup.find('input', {'id': 'csrf_token'})
54
- if not csrf_input:
55
- print("Impossible de trouver le token CSRF")
56
- return False
57
- csrf_token = csrf_input.get('value')
58
-
59
- # Utilisation d'un nom d'utilisateur d'au moins 80 caractères
60
- username = generate_random_username(80)
61
- email = generate_random_email()
62
- password = generate_random_password()
63
-
64
- form_data = {
65
- 'csrf_token': csrf_token,
66
- 'username': username,
67
- 'email': email,
68
- 'password': password,
69
- 'confirm_password': password,
70
- 'submit': 'Register'
71
- }
72
- if is_startup_rep:
73
- form_data['is_startup_rep'] = 'y'
74
 
75
- headers = {
76
- 'Referer': REGISTER_URL,
77
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
78
- }
79
-
80
- response = session.post(REGISTER_URL, data=form_data, headers=headers)
81
- if response.status_code in [200, 302]:
82
- print(f"Compte créé avec succès: {username}")
83
- account = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  'username': username,
85
  'email': email,
86
  'password': password,
87
- 'is_startup_rep': is_startup_rep
 
 
88
  }
89
- return account
90
- else:
91
- print(f"Échec de la création du compte: {response.status_code}")
92
- return False
 
93
 
94
- def create_multiple_accounts(num_accounts, startup_ratio=0.3):
95
- """Crée plusieurs comptes en tâche de fond et met à jour le fichier JSON de progression."""
96
- global progress_data
97
- progress_data["in_progress"] = True
98
- progress_data["total"] = num_accounts
99
- progress_data["created"] = 0
100
- progress_data["accounts"] = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  for i in range(num_accounts):
 
 
 
103
  is_startup = random.random() < startup_ratio
104
- print(f"\nCréation du compte {i+1}/{num_accounts}...")
105
- account = create_account(is_startup_rep=is_startup)
106
- if account:
107
- progress_data["accounts"].append(account)
108
- progress_data["created"] += 1
109
- # Écriture dans le fichier JSON après chaque création
110
- with open(JSON_FILE, "w") as f:
111
- json.dump(progress_data, f, indent=2)
112
-
113
- progress_data["in_progress"] = False
114
- # Mise à jour finale du fichier JSON
115
- with open(JSON_FILE, "w") as f:
116
- json.dump(progress_data, f, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  # Routes Flask
119
-
120
- @app.route("/")
121
  def index():
122
- return '''
123
- <h1>Gestion de création de comptes</h1>
124
- <form action="/start" method="post">
125
- <label for="num">Nombre de comptes à créer:</label>
126
- <input type="number" id="num" name="num" value="5" min="1"><br><br>
127
- <label for="startup_ratio">Ratio de représentants startup (0-1):</label>
128
- <input type="text" id="startup_ratio" name="startup_ratio" value="0.3"><br><br>
129
- <button type="submit">Start</button>
130
- </form>
131
- <br>
132
- <a href="/progress">Voir la progression</a>
133
- '''
134
 
135
- @app.route("/start", methods=["POST"])
136
  def start():
137
- try:
138
- num = int(request.form.get("num", 5))
139
- except ValueError:
140
- num = 5
141
- try:
142
- startup_ratio = float(request.form.get("startup_ratio", 0.3))
143
- except ValueError:
144
- startup_ratio = 0.3
145
 
146
- # Démarrer la création en tâche de fond
147
- thread = threading.Thread(target=create_multiple_accounts, args=(num, startup_ratio))
 
148
  thread.start()
149
 
150
- return redirect(url_for("index"))
151
 
152
- @app.route("/progress")
 
 
 
 
 
153
  def progress():
154
- """Retourne le fichier JSON de progression ou les données globales en cas d'absence du fichier."""
155
- try:
156
- with open(JSON_FILE, "r") as f:
157
- data = json.load(f)
158
- except Exception as e:
159
- data = progress_data
160
- return jsonify(data)
161
 
162
- if __name__ == "__main__":
163
- app.run(debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify, redirect, url_for
2
  import requests
3
  from bs4 import BeautifulSoup
4
  import random
5
  import string
6
  import json
7
  import threading
8
+ import os
9
+ import time
10
+ from datetime import datetime
11
 
 
12
 
13
+ app = Flask(__name__, template_folder='templates')
 
14
 
15
+ # Configuration globale
16
+ config = {
17
+ 'base_url': "https://ivoire-startup-tracker-edithbrou.replit.app",
18
+ 'accounts_file': "accounts_data.json",
19
+ 'is_running': False,
20
+ 'progress': {
21
+ 'total': 0,
22
+ 'current': 0,
23
+ 'success': 0,
24
+ 'failed': 0,
25
+ 'last_username': '',
26
+ 'last_status': '',
27
+ 'start_time': None,
28
+ 'end_time': None
29
+ }
30
  }
31
 
32
+ # Fonction pour générer un nom d'utilisateur aléatoire (min 80 caractères)
33
+ def generate_random_username(min_length=80, max_length=100):
34
+ """Génère un nom d'utilisateur aléatoire d'au moins 80 caractères"""
35
+ length = random.randint(min_length, max_length)
36
  return ''.join(random.choice(string.ascii_lowercase) for _ in range(length))
37
 
38
+ # Fonction pour générer une adresse email aléatoire
39
  def generate_random_email():
40
+ """Génère une adresse email aléatoire"""
41
+ username = ''.join(random.choice(string.ascii_lowercase) for _ in range(12))
42
  domains = ["gmail.com", "yahoo.com", "outlook.com", "example.com"]
43
  return f"{username}@{random.choice(domains)}"
44
 
45
+ # Fonction pour générer un mot de passe aléatoire
46
+ def generate_random_password(length=12):
47
+ """Génère un mot de passe aléatoire"""
48
+ chars = string.ascii_letters + string.digits + "!@#$%^&*"
49
  return ''.join(random.choice(chars) for _ in range(length))
50
 
51
+ # Fonction pour créer un compte
 
 
 
52
  def create_account(is_startup_rep=False):
53
+ """Crée un compte sur le site web"""
54
+ register_url = f"{config['base_url']}/register"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ # Première requête pour récupérer le token CSRF
57
+ session = requests.Session()
58
+ try:
59
+ response = session.get(register_url)
60
+
61
+ if response.status_code != 200:
62
+ return {'success': False, 'error': f"Erreur lors de l'accès à la page: {response.status_code}"}
63
+
64
+ # Extraire le token CSRF
65
+ soup = BeautifulSoup(response.text, 'html.parser')
66
+ csrf_token = soup.find('input', {'id': 'csrf_token'}).get('value')
67
+
68
+ if not csrf_token:
69
+ return {'success': False, 'error': "Impossible de trouver le token CSRF"}
70
+
71
+ # Générer des informations de compte aléatoires
72
+ username = generate_random_username()
73
+ email = generate_random_email()
74
+ password = generate_random_password()
75
+
76
+ # Préparer les données du formulaire
77
+ form_data = {
78
+ 'csrf_token': csrf_token,
79
+ 'username': username,
80
+ 'email': email,
81
+ 'password': password,
82
+ 'confirm_password': password,
83
+ 'submit': 'Register'
84
+ }
85
+
86
+ # Ajouter l'option startup rep si nécessaire
87
+ if is_startup_rep:
88
+ form_data['is_startup_rep'] = 'y'
89
+
90
+ # Envoyer le formulaire
91
+ headers = {
92
+ 'Referer': register_url,
93
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
94
+ }
95
+
96
+ response = session.post(register_url, data=form_data, headers=headers)
97
+
98
+ result = {
99
+ 'success': response.status_code == 200 or response.status_code == 302,
100
  'username': username,
101
  'email': email,
102
  'password': password,
103
+ 'is_startup_rep': is_startup_rep,
104
+ 'created_at': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
105
+ 'status_code': response.status_code
106
  }
107
+
108
+ return result
109
+
110
+ except Exception as e:
111
+ return {'success': False, 'error': str(e)}
112
 
113
+ # Fonction pour créer plusieurs comptes en arrière-plan
114
+ def create_accounts_background(num_accounts, startup_ratio=0.3):
115
+ config['progress'] = {
116
+ 'total': num_accounts,
117
+ 'current': 0,
118
+ 'success': 0,
119
+ 'failed': 0,
120
+ 'last_username': '',
121
+ 'last_status': 'Démarrage...',
122
+ 'start_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
123
+ 'end_time': None
124
+ }
125
+
126
+ # Charger les comptes existants
127
+ accounts = []
128
+ if os.path.exists(config['accounts_file']):
129
+ try:
130
+ with open(config['accounts_file'], 'r') as f:
131
+ accounts = json.load(f)
132
+ except:
133
+ accounts = []
134
 
135
  for i in range(num_accounts):
136
+ if not config['is_running']:
137
+ break
138
+
139
  is_startup = random.random() < startup_ratio
140
+
141
+ config['progress']['current'] = i + 1
142
+ config['progress']['last_status'] = f"Création du compte {i+1}/{num_accounts}..."
143
+
144
+ result = create_account(is_startup_rep=is_startup)
145
+
146
+ if result.get('success', False):
147
+ config['progress']['success'] += 1
148
+ config['progress']['last_username'] = result['username']
149
+ config['progress']['last_status'] = f"Compte {i+1} créé avec succès"
150
+ accounts.append(result)
151
+ else:
152
+ config['progress']['failed'] += 1
153
+ config['progress']['last_status'] = f"Échec de la création du compte {i+1}: {result.get('error', 'Erreur inconnue')}"
154
+
155
+ # Enregistrer les données régulièrement
156
+ with open(config['accounts_file'], 'w') as f:
157
+ json.dump(accounts, f, indent=2)
158
+
159
+ # Petite pause pour éviter de surcharger le serveur
160
+ time.sleep(1)
161
+
162
+ config['is_running'] = False
163
+ config['progress']['end_time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
164
+ config['progress']['last_status'] = "Terminé"
165
+
166
+ # Enregistrement final
167
+ with open(config['accounts_file'], 'w') as f:
168
+ json.dump(accounts, f, indent=2)
169
 
170
  # Routes Flask
171
+ @app.route('/')
 
172
  def index():
173
+ return render_template('index.html', config=config)
 
 
 
 
 
 
 
 
 
 
 
174
 
175
+ @app.route('/start', methods=['POST'])
176
  def start():
177
+ if config['is_running']:
178
+ return jsonify({"status": "error", "message": "Une génération est déjà en cours"})
179
+
180
+ num_accounts = int(request.form.get('num_accounts', 10))
181
+ startup_ratio = float(request.form.get('startup_ratio', 0.3))
182
+
183
+ config['is_running'] = True
 
184
 
185
+ # Démarrer le processus en arrière-plan
186
+ thread = threading.Thread(target=create_accounts_background, args=(num_accounts, startup_ratio))
187
+ thread.daemon = True
188
  thread.start()
189
 
190
+ return jsonify({"status": "success", "message": "Génération démarrée"})
191
 
192
+ @app.route('/stop', methods=['POST'])
193
+ def stop():
194
+ config['is_running'] = False
195
+ return jsonify({"status": "success", "message": "Arrêt demandé"})
196
+
197
+ @app.route('/progress')
198
  def progress():
199
+ return jsonify(config['progress'])
 
 
 
 
 
 
200
 
201
+ @app.route('/accounts')
202
+ def view_accounts():
203
+ page = int(request.args.get('page', 1))
204
+ per_page = 20
205
+
206
+ accounts = []
207
+ if os.path.exists(config['accounts_file']):
208
+ try:
209
+ with open(config['accounts_file'], 'r') as f:
210
+ accounts = json.load(f)
211
+ except:
212
+ accounts = []
213
+
214
+ total_accounts = len(accounts)
215
+ total_pages = (total_accounts + per_page - 1) // per_page
216
+
217
+ start_idx = (page - 1) * per_page
218
+ end_idx = start_idx + per_page
219
+
220
+ current_accounts = accounts[start_idx:end_idx]
221
+
222
+ return render_template(
223
+ 'accounts.html',
224
+ accounts=current_accounts,
225
+ page=page,
226
+ total_pages=total_pages,
227
+ total_accounts=total_accounts
228
+ )
229
+
230
+ if __name__ == '__main__':
231
+ # Créer le dossier templates s'il n'existe pas
232
+ if not os.path.exists('templates'):
233
+ os.makedirs('templates')
234
+
235
+ # Créer les templates HTML
236
+ with open('templates/index.html', 'w') as f:
237
+ f.write('''
238
+ <!DOCTYPE html>
239
+ <html lang="fr">
240
+ <head>
241
+ <meta charset="UTF-8">
242
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
243
+ <title>Générateur de comptes automatique</title>
244
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
245
+ <style>
246
+ body { background-color: #f5f5f5; }
247
+ .card { box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
248
+ .progress { height: 25px; }
249
+ #status-display { height: 200px; overflow-y: auto; }
250
+ </style>
251
+ </head>
252
+ <body>
253
+ <div class="container mt-5">
254
+ <div class="row">
255
+ <div class="col-md-12 mb-4">
256
+ <div class="card">
257
+ <div class="card-header bg-primary text-white">
258
+ <h3 class="card-title mb-0">Générateur de comptes - Startup Côte d'Ivoire</h3>
259
+ </div>
260
+ <div class="card-body">
261
+ <form id="generator-form">
262
+ <div class="row mb-3">
263
+ <div class="col-md-6">
264
+ <label for="num_accounts" class="form-label">Nombre de comptes à créer</label>
265
+ <input type="number" class="form-control" id="num_accounts" name="num_accounts" min="1" max="1000" value="10">
266
+ </div>
267
+ <div class="col-md-6">
268
+ <label for="startup_ratio" class="form-label">Ratio de représentants de startup (%)</label>
269
+ <input type="number" class="form-control" id="startup_ratio" name="startup_ratio" min="0" max="100" value="30">
270
+ </div>
271
+ </div>
272
+ <div class="d-grid gap-2 d-md-flex justify-content-md-center">
273
+ <button type="submit" id="start-btn" class="btn btn-success btn-lg">
274
+ <i class="fas fa-play"></i> Démarrer
275
+ </button>
276
+ <button type="button" id="stop-btn" class="btn btn-danger btn-lg">
277
+ <i class="fas fa-stop"></i> Arrêter
278
+ </button>
279
+ <a href="/accounts" class="btn btn-primary btn-lg">
280
+ <i class="fas fa-list"></i> Voir les comptes
281
+ </a>
282
+ </div>
283
+ </form>
284
+ </div>
285
+ </div>
286
+ </div>
287
+
288
+ <div class="col-md-12">
289
+ <div class="card">
290
+ <div class="card-header bg-info text-white">
291
+ <h4 class="card-title mb-0">Progression</h4>
292
+ </div>
293
+ <div class="card-body">
294
+ <div class="progress mb-3">
295
+ <div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%">0%</div>
296
+ </div>
297
+
298
+ <div class="row text-center mb-3">
299
+ <div class="col">
300
+ <div class="card bg-light">
301
+ <div class="card-body">
302
+ <h5>Total</h5>
303
+ <h3 id="total-count">0</h3>
304
+ </div>
305
+ </div>
306
+ </div>
307
+ <div class="col">
308
+ <div class="card bg-success text-white">
309
+ <div class="card-body">
310
+ <h5>Réussis</h5>
311
+ <h3 id="success-count">0</h3>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ <div class="col">
316
+ <div class="card bg-danger text-white">
317
+ <div class="card-body">
318
+ <h5>Échecs</h5>
319
+ <h3 id="failed-count">0</h3>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ </div>
324
+
325
+ <div class="card mb-3">
326
+ <div class="card-header">
327
+ <h5>Dernier statut</h5>
328
+ </div>
329
+ <div class="card-body">
330
+ <p id="last-status">-</p>
331
+ <p>Dernier compte créé: <span id="last-username" class="text-primary fw-bold">-</span></p>
332
+ </div>
333
+ </div>
334
+
335
+ <div class="row">
336
+ <div class="col-md-6">
337
+ <p>Heure de début: <span id="start-time">-</span></p>
338
+ </div>
339
+ <div class="col-md-6 text-end">
340
+ <p>Heure de fin: <span id="end-time">-</span></p>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </div>
346
+ </div>
347
+ </div>
348
+
349
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
350
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
351
+ <script>
352
+ document.addEventListener('DOMContentLoaded', function() {
353
+ // Formulaire de génération
354
+ document.getElementById('generator-form').addEventListener('submit', function(e) {
355
+ e.preventDefault();
356
+
357
+ const numAccounts = document.getElementById('num_accounts').value;
358
+ const startupRatio = document.getElementById('startup_ratio').value / 100;
359
+
360
+ fetch('/start', {
361
+ method: 'POST',
362
+ headers: {
363
+ 'Content-Type': 'application/x-www-form-urlencoded',
364
+ },
365
+ body: `num_accounts=${numAccounts}&startup_ratio=${startupRatio}`
366
+ })
367
+ .then(response => response.json())
368
+ .then(data => {
369
+ if (data.status === 'success') {
370
+ startProgressUpdates();
371
+ } else {
372
+ alert(data.message);
373
+ }
374
+ });
375
+ });
376
+
377
+ // Bouton d'arrêt
378
+ document.getElementById('stop-btn').addEventListener('click', function() {
379
+ fetch('/stop', {
380
+ method: 'POST'
381
+ })
382
+ .then(response => response.json())
383
+ .then(data => {
384
+ alert(data.message);
385
+ });
386
+ });
387
+
388
+ // Mettre à jour la progression
389
+ function updateProgress() {
390
+ fetch('/progress')
391
+ .then(response => response.json())
392
+ .then(data => {
393
+ const progressPercent = data.total > 0 ? Math.round((data.current / data.total) * 100) : 0;
394
+
395
+ document.getElementById('progress-bar').style.width = `${progressPercent}%`;
396
+ document.getElementById('progress-bar').textContent = `${progressPercent}%`;
397
+
398
+ document.getElementById('total-count').textContent = data.total;
399
+ document.getElementById('success-count').textContent = data.success;
400
+ document.getElementById('failed-count').textContent = data.failed;
401
+
402
+ document.getElementById('last-status').textContent = data.last_status;
403
+ document.getElementById('last-username').textContent = data.last_username || '-';
404
+
405
+ document.getElementById('start-time').textContent = data.start_time || '-';
406
+ document.getElementById('end-time').textContent = data.end_time || '-';
407
+ });
408
+ }
409
+
410
+ let progressInterval;
411
+
412
+ function startProgressUpdates() {
413
+ // Arrêter l'intervalle précédent s'il existe
414
+ if (progressInterval) {
415
+ clearInterval(progressInterval);
416
+ }
417
+
418
+ // Mettre à jour immédiatement
419
+ updateProgress();
420
+
421
+ // Puis toutes les 2 secondes
422
+ progressInterval = setInterval(updateProgress, 2000);
423
+ }
424
+
425
+ // Vérifier si une génération est déjà en cours au chargement de la page
426
+ updateProgress();
427
+ });
428
+ </script>
429
+ </body>
430
+ </html>
431
+ ''')
432
+
433
+ with open('templates/accounts.html', 'w') as f:
434
+ f.write('''
435
+ <!DOCTYPE html>
436
+ <html lang="fr">
437
+ <head>
438
+ <meta charset="UTF-8">
439
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
440
+ <title>Liste des comptes générés</title>
441
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
442
+ <style>
443
+ body { background-color: #f5f5f5; }
444
+ .card { box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
445
+ .username-cell {
446
+ max-width: 200px;
447
+ overflow: hidden;
448
+ text-overflow: ellipsis;
449
+ white-space: nowrap;
450
+ }
451
+ .pagination { margin-bottom: 0; }
452
+ </style>
453
+ </head>
454
+ <body>
455
+ <div class="container mt-5">
456
+ <div class="row">
457
+ <div class="col-md-12 mb-4">
458
+ <div class="card">
459
+ <div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
460
+ <h3 class="mb-0">Liste des comptes générés</h3>
461
+ <a href="/" class="btn btn-light">Retour au générateur</a>
462
+ </div>
463
+ <div class="card-body">
464
+ <div class="alert alert-info">
465
+ Total des comptes générés: {{ total_accounts }}
466
+ </div>
467
+
468
+ <div class="table-responsive">
469
+ <table class="table table-striped table-hover">
470
+ <thead>
471
+ <tr>
472
+ <th>#</th>
473
+ <th>Nom d'utilisateur</th>
474
+ <th>Email</th>
475
+ <th>Mot de passe</th>
476
+ <th>Startup Rep</th>
477
+ <th>Date de création</th>
478
+ <th>Statut</th>
479
+ </tr>
480
+ </thead>
481
+ <tbody>
482
+ {% for account in accounts %}
483
+ <tr>
484
+ <td>{{ (page - 1) * 20 + loop.index }}</td>
485
+ <td class="username-cell" title="{{ account.username }}">{{ account.username }}</td>
486
+ <td>{{ account.email }}</td>
487
+ <td>{{ account.password }}</td>
488
+ <td>{% if account.is_startup_rep %}Oui{% else %}Non{% endif %}</td>
489
+ <td>{{ account.created_at }}</td>
490
+ <td>
491
+ {% if account.success %}
492
+ <span class="badge bg-success">Succès</span>
493
+ {% else %}
494
+ <span class="badge bg-danger">Échec</span>
495
+ {% endif %}
496
+ </td>
497
+ </tr>
498
+ {% endfor %}
499
+ </tbody>
500
+ </table>
501
+ </div>
502
+
503
+ {% if total_pages > 1 %}
504
+ <div class="d-flex justify-content-center mt-4">
505
+ <nav aria-label="Page navigation">
506
+ <ul class="pagination">
507
+ <li class="page-item {% if page == 1 %}disabled{% endif %}">
508
+ <a class="page-link" href="{{ url_for('view_accounts', page=page-1) if page > 1 else '#' }}">Précédent</a>
509
+ </li>
510
+
511
+ {% for p in range(1, total_pages + 1) %}
512
+ {% if p == page %}
513
+ <li class="page-item active"><span class="page-link">{{ p }}</span></li>
514
+ {% else %}
515
+ <li class="page-item"><a class="page-link" href="{{ url_for('view_accounts', page=p) }}">{{ p }}</a></li>
516
+ {% endif %}
517
+ {% endfor %}
518
+
519
+ <li class="page-item {% if page == total_pages %}disabled{% endif %}">
520
+ <a class="page-link" href="{{ url_for('view_accounts', page=page+1) if page < total_pages else '#' }}">Suivant</a>
521
+ </li>
522
+ </ul>
523
+ </nav>
524
+ </div>
525
+ {% endif %}
526
+ </div>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+
532
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
533
+ </body>
534
+ </html>
535
+ ''')
536
+
537
+ app.run(debug=True)