Skip to content

Commit 66cb5fc

Browse files
committed
#23
1 parent aa8aae1 commit 66cb5fc

27 files changed

Lines changed: 1997 additions & 77 deletions

File tree

.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ DOCKER_SOCKET=/var/run/docker.sock
2828
# Host filesystem mount point inside the container
2929
HOST_FS_MOUNT=/host_system
3030

31+
# Remote host access for terminal/files (optional)
32+
# Set SSH_ENABLED=true to use SSH/SFTP backend instead of local host mount.
33+
SSH_ENABLED=false
34+
SSH_HOST=127.0.0.1
35+
SSH_PORT=22
36+
SSH_USERNAME=root
37+
SSH_PASSWORD=REPLACE_WITH_SSH_PASSWORD
38+
SSH_SFTP_ROOT=/
39+
3140
# Session expiry in seconds (default 8 hours)
3241
SESSION_MAX_AGE=28800
3342

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
| Feature | Description |
1515
|---|---|
1616
| **Docker Management** | Full container lifecycle via `/var/run/docker.sock` using `dockerode` |
17+
| **Container Factory** | Create and duplicate containers with image/env/cmd/ports/volumes/networks |
18+
| **Container Port Forwarding** | Portainer-style managed TCP forwards per container (host port -> container port) |
1719
| **Granular RBAC** | Per-container, per-path, per-feature permissions — mapped to individual users |
18-
| **File Explorer** | Touch-optimized SFTP-like explorer for the host filesystem |
19-
| **Multi-session Terminal** | xterm.js with `node-pty` — full shell access with optional read-only mode |
20+
| **File Explorer** | Touch-optimized explorer via host mount or remote SFTP backend |
21+
| **Multi-session Terminal** | xterm.js with local PTY or remote SSH shell backend |
2022
| **User Management** | Admin UI to create users and assign surgical permissions |
2123
| **Audit Log** | Every action is recorded with user, IP, resource, and outcome |
2224
| **Dark Mode** | Enterprise-grade UI built with Tailwind CSS + Shadcn/UI |
@@ -70,10 +72,11 @@ Then run setup again:
7072

7173
`setup.sh` will:
7274
1. Prompt for an **admin username and password**
73-
2. Generate a cryptographically secure `.env` (unique secrets per install)
74-
3. Build the Docker image
75-
4. Run `docker compose up -d`
76-
5. Wait for the health check and print the URL
75+
2. Optionally prompt for **SSH/SFTP host credentials** (username/password)
76+
3. Generate a cryptographically secure `.env` (unique secrets per install)
77+
4. Build the Docker image
78+
5. Run `docker compose up -d`
79+
6. Wait for the health check and print the URL
7780

7881
### Full reset to fresh clone state
7982

@@ -177,6 +180,12 @@ All configuration lives in `.env` (generated by `setup.sh`):
177180
| `SESSION_MAX_AGE` | Session duration in seconds (default 8h) |
178181
| `DOCKER_SOCKET` | Docker socket path (default `/var/run/docker.sock`) |
179182
| `HOST_FS_MOUNT` | Host filesystem mount point inside container |
183+
| `SSH_ENABLED` | Enable remote SSH/SFTP backend for Terminal + Files (`true`/`false`) |
184+
| `SSH_HOST` | Remote SSH host/IP |
185+
| `SSH_PORT` | Remote SSH port |
186+
| `SSH_USERNAME` | Remote SSH username |
187+
| `SSH_PASSWORD` | Remote SSH password |
188+
| `SSH_SFTP_ROOT` | Root directory used for SFTP operations |
180189

181190
---
182191

next.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const nextConfig = {
22
output: "standalone",
33
experimental: {
4-
serverComponentsExternalPackages: ["dockerode", "node-pty", "@prisma/client"],
4+
serverComponentsExternalPackages: ["dockerode", "node-pty", "@prisma/client", "ssh2", "ssh2-sftp-client"],
55
},
66
webpack: (config, { isServer }) => {
77
if (isServer) {
8-
config.externals = [...(config.externals || []), "node-pty"];
8+
config.externals = [...(config.externals || []), "node-pty", "ssh2", "ssh2-sftp-client"];
99
}
1010
return config;
1111
},

package-lock.json

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
"@radix-ui/react-tabs": "^1.1.0",
3030
"@radix-ui/react-toast": "^1.2.1",
3131
"@radix-ui/react-tooltip": "^1.1.2",
32-
"@xterm/xterm": "^5.5.0",
3332
"@xterm/addon-fit": "^0.10.0",
3433
"@xterm/addon-web-links": "^0.11.0",
34+
"@xterm/xterm": "^5.5.0",
3535
"bcryptjs": "^2.4.3",
3636
"class-variance-authority": "^0.7.0",
3737
"clsx": "^2.1.1",
@@ -45,6 +45,8 @@
4545
"react-dom": "^18.3.1",
4646
"socket.io": "^4.7.5",
4747
"socket.io-client": "^4.7.5",
48+
"ssh2": "^1.16.0",
49+
"ssh2-sftp-client": "^11.0.0",
4850
"tailwind-merge": "^2.3.0",
4951
"tailwindcss-animate": "^1.0.7",
5052
"ws": "^8.18.0",
@@ -56,6 +58,7 @@
5658
"@types/node": "^20.14.2",
5759
"@types/react": "^18.3.3",
5860
"@types/react-dom": "^18.3.0",
61+
"@types/ssh2-sftp-client": "^9.0.6",
5962
"@types/ws": "^8.5.10",
6063
"autoprefixer": "^10.4.19",
6164
"eslint": "^8.57.0",
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
-- CreateTable
2+
CREATE TABLE "User" (
3+
"id" TEXT NOT NULL PRIMARY KEY,
4+
"username" TEXT NOT NULL,
5+
"passwordHash" TEXT NOT NULL,
6+
"displayName" TEXT,
7+
"role" TEXT NOT NULL DEFAULT 'USER',
8+
"isActive" BOOLEAN NOT NULL DEFAULT true,
9+
"mustChangePassword" BOOLEAN NOT NULL DEFAULT false,
10+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
11+
"updatedAt" DATETIME NOT NULL
12+
);
13+
14+
-- CreateTable
15+
CREATE TABLE "Session" (
16+
"id" TEXT NOT NULL PRIMARY KEY,
17+
"userId" TEXT NOT NULL,
18+
"token" TEXT NOT NULL,
19+
"expiresAt" DATETIME NOT NULL,
20+
"userAgent" TEXT,
21+
"ipAddress" TEXT,
22+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
23+
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
24+
);
25+
26+
-- CreateTable
27+
CREATE TABLE "UserPermission" (
28+
"id" TEXT NOT NULL PRIMARY KEY,
29+
"userId" TEXT NOT NULL,
30+
"dockerAccess" BOOLEAN NOT NULL DEFAULT false,
31+
"dockerViewAll" BOOLEAN NOT NULL DEFAULT false,
32+
"dockerImages" BOOLEAN NOT NULL DEFAULT false,
33+
"dockerVolumes" BOOLEAN NOT NULL DEFAULT false,
34+
"dockerNetworks" BOOLEAN NOT NULL DEFAULT false,
35+
"dockerCreate" BOOLEAN NOT NULL DEFAULT false,
36+
"dockerDelete" BOOLEAN NOT NULL DEFAULT false,
37+
"fsAccess" BOOLEAN NOT NULL DEFAULT false,
38+
"terminalAccess" BOOLEAN NOT NULL DEFAULT false,
39+
"terminalReadOnly" BOOLEAN NOT NULL DEFAULT true,
40+
"terminalMaxSessions" INTEGER NOT NULL DEFAULT 1,
41+
CONSTRAINT "UserPermission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
42+
);
43+
44+
-- CreateTable
45+
CREATE TABLE "ContainerPermission" (
46+
"id" TEXT NOT NULL PRIMARY KEY,
47+
"permissionId" TEXT NOT NULL,
48+
"containerId" TEXT NOT NULL,
49+
"containerName" TEXT NOT NULL,
50+
"canView" BOOLEAN NOT NULL DEFAULT true,
51+
"canStart" BOOLEAN NOT NULL DEFAULT false,
52+
"canStop" BOOLEAN NOT NULL DEFAULT false,
53+
"canRestart" BOOLEAN NOT NULL DEFAULT false,
54+
"canDelete" BOOLEAN NOT NULL DEFAULT false,
55+
"canLogs" BOOLEAN NOT NULL DEFAULT false,
56+
"canExec" BOOLEAN NOT NULL DEFAULT false,
57+
"canInspect" BOOLEAN NOT NULL DEFAULT false,
58+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
59+
"updatedAt" DATETIME NOT NULL,
60+
CONSTRAINT "ContainerPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "UserPermission" ("id") ON DELETE CASCADE ON UPDATE CASCADE
61+
);
62+
63+
-- CreateTable
64+
CREATE TABLE "FsPathPermission" (
65+
"id" TEXT NOT NULL PRIMARY KEY,
66+
"permissionId" TEXT NOT NULL,
67+
"path" TEXT NOT NULL,
68+
"readOnly" BOOLEAN NOT NULL DEFAULT true,
69+
"canCreate" BOOLEAN NOT NULL DEFAULT false,
70+
"canDelete" BOOLEAN NOT NULL DEFAULT false,
71+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
72+
CONSTRAINT "FsPathPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "UserPermission" ("id") ON DELETE CASCADE ON UPDATE CASCADE
73+
);
74+
75+
-- CreateTable
76+
CREATE TABLE "AuditLog" (
77+
"id" TEXT NOT NULL PRIMARY KEY,
78+
"userId" TEXT,
79+
"username" TEXT NOT NULL,
80+
"action" TEXT NOT NULL,
81+
"resource" TEXT NOT NULL,
82+
"detail" TEXT,
83+
"ipAddress" TEXT,
84+
"success" BOOLEAN NOT NULL DEFAULT true,
85+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
86+
CONSTRAINT "AuditLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
87+
);
88+
89+
-- CreateIndex
90+
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
91+
92+
-- CreateIndex
93+
CREATE UNIQUE INDEX "Session_token_key" ON "Session"("token");
94+
95+
-- CreateIndex
96+
CREATE INDEX "Session_token_idx" ON "Session"("token");
97+
98+
-- CreateIndex
99+
CREATE INDEX "Session_userId_idx" ON "Session"("userId");
100+
101+
-- CreateIndex
102+
CREATE UNIQUE INDEX "UserPermission_userId_key" ON "UserPermission"("userId");
103+
104+
-- CreateIndex
105+
CREATE INDEX "ContainerPermission_permissionId_idx" ON "ContainerPermission"("permissionId");
106+
107+
-- CreateIndex
108+
CREATE INDEX "ContainerPermission_containerId_idx" ON "ContainerPermission"("containerId");
109+
110+
-- CreateIndex
111+
CREATE UNIQUE INDEX "ContainerPermission_permissionId_containerId_key" ON "ContainerPermission"("permissionId", "containerId");
112+
113+
-- CreateIndex
114+
CREATE INDEX "FsPathPermission_permissionId_idx" ON "FsPathPermission"("permissionId");
115+
116+
-- CreateIndex
117+
CREATE UNIQUE INDEX "FsPathPermission_permissionId_path_key" ON "FsPathPermission"("permissionId", "path");
118+
119+
-- CreateIndex
120+
CREATE INDEX "AuditLog_userId_idx" ON "AuditLog"("userId");
121+
122+
-- CreateIndex
123+
CREATE INDEX "AuditLog_createdAt_idx" ON "AuditLog"("createdAt");
124+
125+
-- CreateIndex
126+
CREATE INDEX "AuditLog_resource_idx" ON "AuditLog"("resource");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (i.e. Git)
3+
provider = "sqlite"

0 commit comments

Comments
 (0)