fix(deploy): construire le dashboard depuis les sources versionnées
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
services:
|
services:
|
||||||
dashboard:
|
dashboard:
|
||||||
build:
|
build:
|
||||||
context: .
|
# Les sources actives sont versionnées sous src/. Le contexte racine
|
||||||
|
# pouvait réutiliser d’anciens dossiers backend/frontend restés sur l’hôte.
|
||||||
|
context: ./src
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: manus-dashboard
|
container_name: manus-dashboard
|
||||||
# Le dashboard doit repartir seul après un arrêt anormal du processus.
|
# Le dashboard doit repartir seul après un arrêt anormal du processus.
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
User,
|
User,
|
||||||
Code,
|
Code,
|
||||||
|
CheckCircle2,
|
||||||
|
XCircle,
|
||||||
|
Loader2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { getGiteaRepos, getGiteaCommits } from '../utils/api';
|
import { getGiteaRepos, getGiteaCommits, getGiteaWorkflowRuns } from '../utils/api';
|
||||||
|
|
||||||
function formatDate(dateStr) {
|
function formatDate(dateStr) {
|
||||||
if (!dateStr) return 'N/A';
|
if (!dateStr) return 'N/A';
|
||||||
@@ -27,11 +30,25 @@ function formatSize(kb) {
|
|||||||
return `${(kb / 1024).toFixed(1)} MB`;
|
return `${(kb / 1024).toFixed(1)} MB`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDuration(seconds) {
|
||||||
|
if (!Number.isFinite(seconds)) return 'En attente';
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
const remainingSeconds = seconds % 60;
|
||||||
|
return minutes > 0 ? `${minutes} min ${remainingSeconds.toString().padStart(2, '0')} s` : `${remainingSeconds} s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CiStatus({ run }) {
|
||||||
|
if (run.status !== 'completed') return <span className="text-amber-300 flex items-center gap-1"><Loader2 className="w-3 h-3 animate-spin" /> En cours</span>;
|
||||||
|
if (run.conclusion === 'success') return <span className="text-emerald-300 flex items-center gap-1"><CheckCircle2 className="w-3 h-3" /> Réussi</span>;
|
||||||
|
return <span className="text-red-300 flex items-center gap-1"><XCircle className="w-3 h-3" /> {run.conclusion || 'Échec'}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
export default function GiteaPage() {
|
export default function GiteaPage() {
|
||||||
const [repos, setRepos] = useState([]);
|
const [repos, setRepos] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [selectedRepo, setSelectedRepo] = useState(null);
|
const [selectedRepo, setSelectedRepo] = useState(null);
|
||||||
const [commits, setCommits] = useState([]);
|
const [commits, setCommits] = useState([]);
|
||||||
|
const [workflowRuns, setWorkflowRuns] = useState([]);
|
||||||
const [loadingCommits, setLoadingCommits] = useState(false);
|
const [loadingCommits, setLoadingCommits] = useState(false);
|
||||||
|
|
||||||
const fetchRepos = async () => {
|
const fetchRepos = async () => {
|
||||||
@@ -51,11 +68,16 @@ export default function GiteaPage() {
|
|||||||
setSelectedRepo(fullName);
|
setSelectedRepo(fullName);
|
||||||
try {
|
try {
|
||||||
const [owner, repo] = fullName.split('/');
|
const [owner, repo] = fullName.split('/');
|
||||||
const res = await getGiteaCommits(owner, repo);
|
const [commitsResponse, runsResponse] = await Promise.all([
|
||||||
setCommits(res.data);
|
getGiteaCommits(owner, repo),
|
||||||
|
getGiteaWorkflowRuns(owner, repo),
|
||||||
|
]);
|
||||||
|
setCommits(commitsResponse.data);
|
||||||
|
setWorkflowRuns(runsResponse.data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Erreur chargement commits:', err);
|
console.error('Erreur chargement commits:', err);
|
||||||
setCommits([]);
|
setCommits([]);
|
||||||
|
setWorkflowRuns([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingCommits(false);
|
setLoadingCommits(false);
|
||||||
}
|
}
|
||||||
@@ -191,6 +213,27 @@ export default function GiteaPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
<div className="card p-3">
|
||||||
|
<p className="text-xs font-medium text-gray-400 uppercase tracking-wider mb-2">Dernières validations CI</p>
|
||||||
|
{workflowRuns.length === 0 ? (
|
||||||
|
<p className="text-sm text-gray-500">Aucune validation CI trouvée pour ce dépôt.</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{workflowRuns.slice(0, 5).map((run) => (
|
||||||
|
<div key={run.id} className="flex items-center justify-between gap-3 text-xs">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="text-gray-200 truncate">{run.name}</p>
|
||||||
|
<p className="text-gray-500">{run.commit?.slice(0, 7) || 'Commit inconnu'} · {formatDate(run.createdAt)}</p>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 text-right">
|
||||||
|
<CiStatus run={run} />
|
||||||
|
<span className="text-gray-500 flex items-center justify-end gap-1 mt-1"><Clock className="w-3 h-3" />{formatDuration(run.durationSeconds)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{commits.map((commit, idx) => (
|
{commits.map((commit, idx) => (
|
||||||
<div key={commit.sha || idx} className="card p-3">
|
<div key={commit.sha || idx} className="card p-3">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ export const getDeployments = (appId) =>
|
|||||||
export const getGiteaRepos = () => api.get('/gitea/repos');
|
export const getGiteaRepos = () => api.get('/gitea/repos');
|
||||||
export const getGiteaCommits = (owner, repo) =>
|
export const getGiteaCommits = (owner, repo) =>
|
||||||
api.get(`/gitea/repos/${owner}/${repo}/commits`);
|
api.get(`/gitea/repos/${owner}/${repo}/commits`);
|
||||||
|
export const getGiteaWorkflowRuns = (owner, repo) =>
|
||||||
|
api.get(`/gitea/repos/${owner}/${repo}/actions/runs`);
|
||||||
|
|
||||||
// Docker
|
// Docker
|
||||||
export const getContainers = () => api.get('/docker/containers');
|
export const getContainers = () => api.get('/docker/containers');
|
||||||
|
|||||||
Reference in New Issue
Block a user