feat: Semaine 8
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# Contexte
|
||||
|
||||
Deux choses :
|
||||
1 - J'ai fait la base avec le contenu de LivrExpress / ShopFlow
|
||||
2 - Je connaissais un peu d'HTML
|
||||
3 - ... mais putain ce que ça me gonfle comme langage (le balisage en général)
|
||||
|
||||
Alors j'ai codé le minimum syndical pour que ça tourne et après j'ai fait un planificateur Claude Opus 4.6 et un agent Claude Sonnet 4.6 pour générer un template propre.
|
||||
|
||||
C'est pas une instance Vault mais c'est déjà bien.
|
||||
|
||||
J'assume pleinement mon usage de l'IA pour les parties où j'ai des lacunes mais où je sais contrôler la qualité du résultat. C'est aussi la réalité des juniors. Et des seniors aussi d'ailleurs.
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}SecuVault{% endblock %}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<nav class="navbar navbar-dark bg-dark mb-4">
|
||||
<div class="container">
|
||||
<a class="navbar-brand fw-bold" href="/secrets">🔏 SecuVault</a>
|
||||
{% if user %}
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<span class="text-light">{{ user.username }}</span>
|
||||
<a href="/logout" class="btn btn-outline-light btn-sm">Déconnexion</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container pb-5">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - Erreur {{ status_code }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 text-center py-5">
|
||||
<div class="display-1 text-muted fw-bold">{{ status_code }}</div>
|
||||
<p class="lead mt-3">{{ message }}</p>
|
||||
<a href="/secrets" class="btn btn-outline-primary mt-2">⬅️ Retour aux secrets</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - Connexion{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<h1 class="h4 mb-4">Connexion</h1>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error }}</div>
|
||||
{% endif %}
|
||||
<form method="post" action="/login">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="username">👱 Nom d'utilisateur</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required autofocus>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="password">🔒 Mot de passe</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">➡️ Se connecter</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - Nouveau secret{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<h1 class="h4 mb-4">Créer un secret</h1>
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error }}</div>
|
||||
{% endif %}
|
||||
<form method="post" action="/secrets">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="name">Nom du secret</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required autofocus>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="value">Valeur <span class="text-muted small">(sera chiffrée)</span></label>
|
||||
<textarea class="form-control font-monospace" id="value" name="value" rows="4" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="team_id">Équipe</label>
|
||||
<select class="form-select" id="team_id" name="team_id" required>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-success">✅ Créer</button>
|
||||
<a href="/secrets" class="btn btn-outline-secondary">⛔ Annuler</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - {{ secret.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-7">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<strong>{{ secret.name }}</strong>
|
||||
<span class="badge bg-light text-dark border">v{{ secret.version }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label class="form-label fw-bold">Valeur du secret:</label>
|
||||
<div class="p-3 bg-warning-subtle border border-warning rounded font-monospace user-select-all">{{ plaintext }}</div>
|
||||
<p class="text-muted small mt-2">Mis à jour le {{ secret.updated_at }}</p>
|
||||
</div>
|
||||
<div class="card-footer d-flex gap-2">
|
||||
<a href="/secrets" class="btn btn-outline-secondary btn-sm">Retour</a>
|
||||
<a href="/secrets/{{ secret.id }}/rotate" class="btn btn-warning btn-sm">Rotation</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - Rotation : {{ secret.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<h1 class="h4 mb-1">Rotation du secret</h1>
|
||||
<p class="text-muted mb-4">
|
||||
{{ secret.name }}
|
||||
<span class="badge bg-light text-dark border ms-1">v{{ secret.version }}</span>
|
||||
</p>
|
||||
{% if error %}
|
||||
<div class="alert alert-warning">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post" action="/secrets/{{ secret.id }}/rotate">
|
||||
{# The current version is embedded as a hidden field for optimistic-locking. #}
|
||||
<input type="hidden" name="expected_version" value="{{ secret.version }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="new_value">Nouvelle valeur</label>
|
||||
<textarea class="form-control font-monospace" id="new_value" name="new_value" rows="4" required autofocus></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-warning">Appliquer la rotation</button>
|
||||
<a href="/secrets" class="btn btn-outline-secondary">Annuler</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}SecuVault - Mes secrets{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Mes secrets</h1>
|
||||
<a href="/secrets/new" class="btn btn-success">+ Nouveau secret</a>
|
||||
</div>
|
||||
|
||||
{% if not secrets_by_team %}
|
||||
<div class="alert alert-info">Vous n'appartenez à aucune équipe pour l'instant.</div>
|
||||
{% endif %}
|
||||
|
||||
{% for team, secrets in secrets_by_team.items() %}
|
||||
<div class="card mb-4 shadow-sm">
|
||||
<div class="card-header bg-secondary text-white d-flex align-items-center gap-2">
|
||||
<strong>{{ team.name }}</strong>
|
||||
{% if team.description %}
|
||||
<span class="opacity-75 small">- {{ team.description }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if secrets %}
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Dernière mise à jour</th>
|
||||
<th>Version</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for secret in secrets %}
|
||||
<tr>
|
||||
<td class="align-middle">{{ secret.name }}</td>
|
||||
<td class="align-middle text-muted small">{{ secret.updated_at }}</td>
|
||||
<td class="align-middle"><span class="badge bg-light text-dark border">v{{ secret.version }}</span></td>
|
||||
<td class="text-end">
|
||||
<a href="/secrets/{{ secret.id }}" class="btn btn-sm btn-outline-primary me-1">Révéler</a>
|
||||
<a href="/secrets/{{ secret.id }}/rotate" class="btn btn-sm btn-outline-warning">Rotation</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted p-3 mb-0">Aucun secret dans cette équipe.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user