Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions backend/app/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ def create_environment_task(self, environment_id):
# works if capabilities=[["gpu"]]. For simplicity with 'count', we assume generic allocation.
# If we want specific indices, device_requests are constructed as above.

# 3. Configure Volumes
volumes = {}
if env.mount_config:
for mount in env.mount_config:
# mount is expected to be a dict from JSONB
host_path = mount.get('host_path')
container_path = mount.get('container_path')
mode = mount.get('mode', 'rw')

if host_path and container_path:
volumes[host_path] = {'bind': container_path, 'mode': mode}

if volumes:
container_config['volumes'] = volumes

client.containers.run(**container_config)

env.status = "running"
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@
backdrop-filter: blur(10px);
border: 1px solid rgba(63, 63, 70, 0.5);
}

.scrollbar-hide {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.scrollbar-hide::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
}
75 changes: 73 additions & 2 deletions frontend/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import axios from 'axios';
import { RefreshCw, Trash2 } from 'lucide-react';
import { HardDrive, RefreshCw, Trash2, X } from 'lucide-react';
import { useEffect, useState } from 'react';
import Modal from '../components/Modal';

interface MountConfig {
host_path: string;
container_path: string;
mode: string;
}

interface Environment {
id: string;
name: string;
Expand All @@ -11,12 +17,14 @@ interface Environment {
ssh_port: number;
jupyter_port: number;
created_at: string;
mount_config: MountConfig[];
}

export default function Dashboard() {
const [environments, setEnvironments] = useState<Environment[]>([]);
const [loading, setLoading] = useState(true);
const [deleteId, setDeleteId] = useState<string | null>(null);
const [selectedVolEnv, setSelectedVolEnv] = useState<Environment | null>(null);

const fetchEnvironments = async () => {
try {
Expand Down Expand Up @@ -59,6 +67,53 @@ export default function Dashboard() {
isDestructive={true}
/>

{/* Volume Details Modal */}
{selectedVolEnv && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
<div className="bg-[#18181b] rounded-xl border border-[#3f3f46] shadow-2xl w-full max-w-lg overflow-hidden animate-in fade-in zoom-in duration-200">
<div className="p-6 border-b border-[#3f3f46] flex justify-between items-center">
<h3 className="text-xl font-bold text-white flex items-center gap-2">
<HardDrive size={20} className="text-blue-400" />
Volume Mounts
</h3>
<button onClick={() => setSelectedVolEnv(null)} className="text-gray-400 hover:text-white transition-colors">
<X size={20} />
</button>
</div>
<div className="p-6">
<p className="text-gray-400 text-sm mb-4">
Mounted volumes for <span className="text-white font-medium">{selectedVolEnv.name}</span>
</p>
<div className="space-y-3">
{selectedVolEnv.mount_config.map((mount, idx) => (
<div key={idx} className="bg-[#27272a] p-3 rounded-lg border border-[#3f3f46] text-sm">
<div className="flex items-center gap-2 mb-1">
<span className="text-xs font-bold text-blue-400 uppercase w-10 shrink-0">Host</span>
<span className="text-gray-300 font-mono overflow-x-auto whitespace-nowrap flex-1 scrollbar-hide">{mount.host_path}</span>
</div>
<div className="flex items-center gap-2">
<span className="text-xs font-bold text-green-400 uppercase w-10 shrink-0">Dest</span>
<span className="text-gray-300 font-mono overflow-x-auto whitespace-nowrap flex-1 scrollbar-hide">{mount.container_path}</span>
<span className="ml-auto text-[10px] bg-[#3f3f46] px-1.5 py-0.5 rounded text-gray-400 uppercase">
{mount.mode}
</span>
</div>
</div>
))}
</div>
</div>
<div className="p-4 border-t border-[#3f3f46] bg-[#27272a]/50 flex justify-end">
<button
onClick={() => setSelectedVolEnv(null)}
className="px-4 py-2 rounded-lg text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20 transition-all"
>
Close
</button>
</div>
</div>
</div>
)}

<div className="flex justify-between items-center">
<div>
<h2 className="text-3xl font-bold text-white">Dashboard</h2>
Expand All @@ -78,7 +133,7 @@ export default function Dashboard() {
{loading && environments.length === 0 ? (
<div className="p-8 text-center text-gray-500">Loading environments...</div>
) : environments.length === 0 ? (
<div className="p-8 text-center text-gray-500">No environments found. Create one to get started.</div>
<div className="p-8 text-center text-gray-500">No environments found.</div>
) : (
<table className="w-full">
<thead className="bg-[#18181b] text-gray-400 text-sm uppercase">
Expand Down Expand Up @@ -110,6 +165,22 @@ export default function Dashboard() {
{env.gpu_indices.length > 0 ? env.gpu_indices.join(', ') : "-"}
</td>
<td className="px-6 py-4 text-right space-x-2">
<button
onClick={() => {
if (env.mount_config && env.mount_config.length > 0) {
setSelectedVolEnv(env);
}
}}
disabled={!env.mount_config || env.mount_config.length === 0}
className={`p-2 rounded-lg transition-colors ${
env.mount_config && env.mount_config.length > 0
? "text-gray-400 hover:text-blue-400 hover:bg-blue-500/10"
: "text-gray-600 cursor-not-allowed opacity-30"
}`}
title={env.mount_config && env.mount_config.length > 0 ? "View Volumes" : "No Volumes"}
>
<HardDrive size={18} />
</button>
<button
onClick={() => setDeleteId(env.id)}
className="p-2 hover:bg-gray-700 rounded-lg text-gray-400 hover:text-red-400 transition-colors"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Provisioning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export default function Provisioning() {
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
<span className="w-1 h-5 bg-green-500 rounded-full"></span>
Storage Mounts
Volume Mounts
</h3>
<button onClick={handleAddMount} className="p-1 hover:bg-[#27272a] rounded text-gray-400 hover:text-white transition-colors">
<Plus size={18} />
Expand Down