├── install-docker.bat ├── frontend ├── public │ ├── favicon.ico │ ├── favicon.png │ ├── cropped-docker-logo-favicon-32x32.png │ ├── cropped-docker-logo-favicon-180x180.png │ ├── cropped-docker-logo-favicon-192x192.png │ ├── cropped-docker-logo-favicon-270x270.png │ ├── install-docker-wsl2.sh │ ├── install-wsl2.bat │ └── install-wsl2.ps1 ├── src │ ├── stores │ │ └── index.ts │ ├── assets │ │ └── avatar.jpg │ ├── components │ │ ├── docker │ │ │ ├── DockerNoResults.vue │ │ │ ├── DockerCommandIntro.vue │ │ │ ├── DockerCommandHeader.vue │ │ │ ├── LogsModal.vue │ │ │ ├── ConfirmationModal.vue │ │ │ ├── DockerConfirmationModal.vue │ │ │ ├── GenericModal.vue │ │ │ ├── ContainerRow.vue │ │ │ ├── ComposeModal.vue │ │ │ ├── ContainerGroup.vue │ │ │ ├── DockerCommandCard.vue │ │ │ ├── ContainerTable.vue │ │ │ ├── DockerExecutionModal.vue │ │ │ └── RunContainerModal.vue │ │ └── Sidebar.vue │ ├── main.ts │ ├── App.vue │ ├── views │ │ ├── SettingsView.vue │ │ └── DashboardView.vue │ ├── router │ │ └── routes.ts │ └── utils │ │ └── environment.ts ├── Dockerfile.dev ├── index.html ├── Dockerfile ├── vite.config.ts ├── README.md ├── tsconfig.json ├── package.json ├── nginx.conf ├── .gitignore ├── DARK_MODE_FIXES.md ├── COMPOSE_INSPECT_FIXES.md ├── THEME_TOGGLE.md ├── ENVIRONMENT_GUIDE.md └── COMPREHENSIVE_DARK_THEME.md ├── package.json ├── start-backend.bat ├── start-frontend.bat ├── backend ├── nodemon.json ├── Dockerfile.dev ├── .env.example ├── package.json ├── Dockerfile ├── src │ ├── routes │ │ ├── wsl.js │ │ ├── images.js │ │ ├── networks.js │ │ ├── volumes.js │ │ ├── services.js │ │ └── compose.js │ ├── swagger.js │ ├── swagger.autogen.js │ └── server.js ├── debug-ports.js └── .gitignore ├── start.bat ├── start.sh ├── docker-compose.dev.yml ├── test-backend.js ├── .gitignore ├── docker-compose.yml ├── public ├── install-docker-wsl2.sh ├── install-wsl2.bat └── install-wsl2.ps1 ├── DOCKER-SOURCE-TESTING.md ├── WSL2-DOCKER-CONFIG.md ├── deploy.bat ├── deploy.sh ├── SOURCE_SELECTION_COMPLETE.md ├── DEVELOPMENT.md ├── .gitignore.template └── DOCKER-DEPLOYMENT.md /install-docker.bat: -------------------------------------------------------------------------------- 1 | docker-compose down 2 | docker-compose up --build -d 3 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/src/stores/index.ts: -------------------------------------------------------------------------------- 1 | // Export all stores from this index file 2 | export { useDockerStore } from './docker' -------------------------------------------------------------------------------- /frontend/src/assets/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/src/assets/avatar.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "nodemon": "^3.1.11", 4 | "swagger-autogen": "^2.23.7" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /start-backend.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo Starting Backend Server... 4 | start "Backend Server" cmd /c "cd backend && npm install && npm start" 5 | 6 | -------------------------------------------------------------------------------- /frontend/public/cropped-docker-logo-favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/cropped-docker-logo-favicon-32x32.png -------------------------------------------------------------------------------- /start-frontend.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo Starting Frontend Development Server... 4 | start "Frontend Server" cmd /c "cd frontend && npm install && npm run dev" 5 | 6 | -------------------------------------------------------------------------------- /frontend/public/cropped-docker-logo-favicon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/cropped-docker-logo-favicon-180x180.png -------------------------------------------------------------------------------- /frontend/public/cropped-docker-logo-favicon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/cropped-docker-logo-favicon-192x192.png -------------------------------------------------------------------------------- /frontend/public/cropped-docker-logo-favicon-270x270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devremoto/docker-web-desktop/HEAD/frontend/public/cropped-docker-logo-favicon-270x270.png -------------------------------------------------------------------------------- /backend/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "src" 4 | ], 5 | "ext": "js,json", 6 | "ignore": [ 7 | "src/**/*.test.js", 8 | "src/**/*.spec.js" 9 | ], 10 | "exec": "node src/server.js" 11 | } -------------------------------------------------------------------------------- /frontend/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Development Dockerfile for Frontend 2 | FROM node:20-alpine 3 | 4 | # Set the working directory 5 | WORKDIR /app 6 | 7 | # Copy package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install all dependencies 11 | RUN npm ci 12 | 13 | # Copy the rest of the application code 14 | COPY . . 15 | 16 | # Expose port 5173 (Vite default) 17 | EXPOSE 5173 18 | 19 | # Command to run the application in development mode 20 | CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerNoResults.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /backend/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Development Dockerfile for Backend 2 | FROM node:20-alpine 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Copy package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install all dependencies (including devDependencies) 11 | RUN npm ci 12 | 13 | # Copy the rest of the application code 14 | COPY . . 15 | 16 | # Expose the port the app runs on 17 | EXPOSE 3000 18 | 19 | # Command to run the application in development mode 20 | CMD ["npm", "run", "dev"] -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop Backend Configuration 2 | 3 | # Server Configuration 4 | PORT=3334 5 | 6 | # Docker Configuration 7 | # WSL2 Docker Daemon Connection 8 | # Option 1: TCP connection (requires Docker daemon to expose TCP socket) 9 | WSL2_DOCKER_HOST=localhost 10 | WSL2_DOCKER_PORT=2375 11 | 12 | # Option 2: Named pipe or socket path 13 | # WSL2_DOCKER_SOCKET=//./pipe/docker_engine_wsl 14 | # Or access WSL2 socket via UNC path (if enabled): 15 | # WSL2_DOCKER_SOCKET=\\wsl$\Ubuntu\var\run\docker.sock 16 | 17 | # CORS Configuration 18 | CORS_ORIGIN=http://localhost:5173 19 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Starting Docker Web Desktop Application... 3 | echo. 4 | 5 | echo Starting Backend Server... 6 | start "Backend Server" cmd /c "cd backend && npm install && npm start" 7 | 8 | 9 | echo Waiting for backend to start... 10 | 11 | 12 | echo Starting Frontend Development Server... 13 | start "Frontend Server" cmd /c "cd frontend && npm install && npm run dev" 14 | 15 | 16 | echo. 17 | echo Both servers are starting... 18 | echo Backend: http://localhost:3000 19 | echo Frontend: Will be available shortly at the URL shown in the frontend terminal 20 | echo. 21 | echo Press any key to exit... 22 | 23 | rem pause > nul -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | # Start Docker Web Desktop Application 2 | 3 | echo "Starting Docker Web Desktop Application..." 4 | echo "" 5 | 6 | echo "Starting Backend Server..." 7 | cd backend 8 | node src/server.js & 9 | BACKEND_PID=$! 10 | cd .. 11 | 12 | echo "Waiting for backend to start..." 13 | sleep 3 14 | 15 | echo "Starting Frontend Development Server..." 16 | cd frontend 17 | npm run dev & 18 | FRONTEND_PID=$! 19 | cd .. 20 | 21 | echo "" 22 | echo "Both servers are starting..." 23 | echo "Backend: http://localhost:3000" 24 | echo "Frontend: Will be available shortly at the URL shown above" 25 | echo "" 26 | echo "Press Ctrl+C to stop both servers" 27 | 28 | # Wait for user to stop 29 | wait $BACKEND_PID $FRONTEND_PID -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 11 | 13 | 15 | 16 | Docker Web Desktop 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Multi-stage build for Vue.js frontend 2 | 3 | # Build stage 4 | FROM node:20-alpine as build-stage 5 | 6 | # Set the working directory 7 | WORKDIR /app 8 | 9 | # Copy package.json and package-lock.json 10 | COPY package*.json ./ 11 | 12 | # Install dependencies 13 | RUN npm ci 14 | 15 | # Copy the rest of the application code 16 | COPY . . 17 | 18 | # Build the application for production 19 | RUN npm run build:docker 20 | 21 | # Production stage 22 | FROM nginx:alpine as production-stage 23 | 24 | # Copy the built application from the build stage 25 | COPY --from=build-stage /app/dist /usr/share/nginx/html 26 | 27 | # Copy custom nginx configuration 28 | COPY nginx.conf /etc/nginx/nginx.conf 29 | 30 | # Expose port 80 31 | EXPOSE 80 32 | 33 | # Start nginx 34 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-web-desktop-backend", 3 | "version": "1.0.0", 4 | "description": "Backend API for Docker Web Desktop", 5 | "main": "src/server.js", 6 | "scripts": { 7 | "start": "node src/server.js", 8 | "dev": "nodemon src/server.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [ 12 | "docker", 13 | "api", 14 | "backend", 15 | "express" 16 | ], 17 | "author": "", 18 | "license": "MIT", 19 | "dependencies": { 20 | "axios": "^1.13.2", 21 | "cors": "^2.8.5", 22 | "dockerode": "^4.0.9", 23 | "dotenv": "^16.3.1", 24 | "express": "^4.18.2", 25 | "socket.io": "^4.7.2", 26 | "swagger-jsdoc": "^6.2.8", 27 | "swagger-ui-express": "^5.0.1" 28 | }, 29 | "devDependencies": { 30 | "nodemon": "^3.0.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Node.js runtime as a parent image 2 | FROM node:20-alpine 3 | 4 | # Ensure we are explicitly running as root for system setup commands 5 | USER root 6 | 7 | # Install the Docker CLI package using Alpine's package manager 8 | RUN apk update && apk add --no-cache docker-cli && rm -rf /var/cache/apk/* 9 | 10 | # --- User Management (Requires root privileges) --- 11 | 12 | 13 | # Set the working directory 14 | WORKDIR /app 15 | 16 | # Copy package.json and package-lock.json 17 | COPY package*.json ./ 18 | 19 | # Install dependencies 20 | RUN npm ci --only=production 21 | 22 | # Copy the rest of the application code 23 | COPY . . 24 | 25 | # Create a non-root user to run the application 26 | RUN addgroup -g 1001 -S nodejs 27 | RUN adduser -S nodejs -u 1001 28 | 29 | # Change ownership of the app directory to nodejs user 30 | RUN chown -R nodejs:nodejs /app 31 | USER nodejs 32 | 33 | # Expose the port the app runs on 34 | EXPOSE 3000 35 | 36 | # Define environment variable 37 | ENV NODE_ENV=production 38 | 39 | # Command to run the application 40 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig, loadEnv } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | import vueDevTools from 'vite-plugin-vue-devtools' 6 | 7 | // https://vite.dev/config/ 8 | export default defineConfig(({ command, mode }) => { 9 | // Load environment variables based on mode 10 | const env = loadEnv(mode, process.cwd(), '') 11 | 12 | return { 13 | plugins: [ 14 | vue(), 15 | vueDevTools(), 16 | ], 17 | resolve: { 18 | alias: { 19 | '@': fileURLToPath(new URL('./src', import.meta.url)) 20 | }, 21 | }, 22 | server: { 23 | port: 5173, 24 | proxy: { 25 | '/api': { 26 | target: env.VITE_API_BASE_URL || 'http://localhost:3000', 27 | changeOrigin: true, 28 | secure: false 29 | } 30 | } 31 | }, 32 | build: { 33 | sourcemap: true 34 | }, 35 | css: { 36 | devSourcemap: true 37 | }, 38 | define: { 39 | __APP_VERSION__: JSON.stringify(env.VITE_APP_VERSION || '1.0.0'), 40 | } 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # . 2 | 3 | This template should help get you started developing with Vue 3 in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). 8 | 9 | ## Recommended Browser Setup 10 | 11 | - Chromium-based browsers (Chrome, Edge, Brave, etc.): 12 | - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) 13 | - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) 14 | - Firefox: 15 | - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) 16 | - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) 17 | 18 | ## Customize configuration 19 | 20 | See [Vite Configuration Reference](https://vite.dev/config/). 21 | 22 | ## Project Setup 23 | 24 | ```sh 25 | npm install 26 | ``` 27 | 28 | ### Compile and Hot-Reload for Development 29 | 30 | ```sh 31 | npm run dev 32 | ``` 33 | 34 | ### Compile and Minify for Production 35 | 36 | ```sh 37 | npm run build 38 | ``` 39 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createRouter, createWebHistory } from 'vue-router' 3 | import { createPinia } from 'pinia' 4 | import App from './App.vue' 5 | 6 | // Import Bootstrap CSS and JS 7 | import 'bootstrap/dist/css/bootstrap.min.css' 8 | import 'bootstrap-icons/font/bootstrap-icons.css' 9 | import 'bootstrap/dist/js/bootstrap.bundle.min.js' 10 | 11 | // Import custom dark theme CSS 12 | import './assets/dark-theme.css' 13 | 14 | // Import routes 15 | import routes from './router/routes' 16 | 17 | // Import stores 18 | import './stores' 19 | 20 | const router = createRouter({ 21 | history: createWebHistory(), 22 | routes 23 | }) 24 | 25 | const pinia = createPinia() 26 | 27 | // Initialize theme on page load 28 | const initTheme = () => { 29 | const savedTheme = localStorage.getItem('theme') 30 | const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches 31 | const theme = savedTheme || (systemPrefersDark ? 'dark' : 'light') 32 | document.documentElement.setAttribute('data-bs-theme', theme) 33 | } 34 | 35 | initTheme() 36 | 37 | createApp(App) 38 | .use(pinia) 39 | .use(router) 40 | .mount('#app') 41 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": [ 6 | "ES2020", 7 | "DOM", 8 | "DOM.Iterable" 9 | ], 10 | "module": "ESNext", 11 | "skipLibCheck": true, 12 | /* Bundler mode */ 13 | "moduleResolution": "bundler", 14 | "allowImportingTsExtensions": true, 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "preserve", 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "forceConsistentCasingInFileNames": true, 25 | /* Vue specific */ 26 | "baseUrl": ".", 27 | "paths": { 28 | "@/*": [ 29 | "./src/*" 30 | ] 31 | } 32 | }, 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "src/**/*.vue", 37 | "src/**/*.js" 38 | ], 39 | "exclude": [ 40 | "node_modules", 41 | "dist" 42 | ] 43 | } -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-web-desktop-frontend", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "engines": { 7 | "node": "^20.19.0 || >=22.12.0" 8 | }, 9 | "scripts": { 10 | "start": "vite --open --mode development", 11 | "dev": "vite --open --mode development", 12 | "dev:docker": "vite --mode docker --open", 13 | "build": "vue-tsc && vite build", 14 | "build:production": "vue-tsc && vite build --mode production", 15 | "build:docker": "vue-tsc && vite build --mode docker", 16 | "preview": "vite preview", 17 | "preview:production": "vite preview --mode production", 18 | "preview:docker": "vite preview --mode docker", 19 | "type-check": "vue-tsc --noEmit" 20 | }, 21 | "dependencies": { 22 | "axios": "^1.13.2", 23 | "bootstrap": "^5.3.8", 24 | "bootstrap-icons": "^1.13.1", 25 | "pinia": "^3.0.4", 26 | "socket.io-client": "^4.8.1", 27 | "vue": "^3.5.22", 28 | "vue-router": "^4.6.3" 29 | }, 30 | "devDependencies": { 31 | "@types/node": "^22.0.0", 32 | "@vitejs/plugin-vue": "^6.0.1", 33 | "@vue/tsconfig": "^0.5.1", 34 | "typescript": "^5.4.0", 35 | "vite": "^7.1.11", 36 | "vite-plugin-vue-devtools": "^8.0.3", 37 | "vue-tsc": "^2.0.0" 38 | } 39 | } -------------------------------------------------------------------------------- /backend/src/routes/wsl.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { DockerService } = require('../services/dockerService'); 3 | const { exec } = require('child_process'); 4 | const router = express.Router(); 5 | 6 | router.get('/', async (req, res) => { 7 | /* #swagger.tags = ['WSL'] */ 8 | /* #swagger.path = '/wsl/' */ 9 | try { 10 | const distros = await DockerService.listWslDistros(); 11 | console.log('Available WSL Distros:', distros); 12 | res.json(distros); 13 | } catch (error) { 14 | res.status(500).json({ error: error.message }); 15 | } 16 | }); 17 | 18 | router.get('/:id', async (req, res) => { 19 | /* #swagger.tags = ['WSL'] */ 20 | /* #swagger.path = '/wsl/{id}' */ 21 | try { 22 | const id = req.params.id; 23 | const distros = await DockerService.listWslDistros(); 24 | // distros is an array of strings, so just match by string equality 25 | const distro = distros.find(d => d === id); 26 | if (distro) { 27 | res.send(distro); 28 | } else { 29 | res.status(404).json({ error: 'Distro not found' }); 30 | } 31 | } catch (error) { 32 | res.status(500).json({ error: error.message }); 33 | } 34 | 35 | }); 36 | 37 | 38 | 39 | module.exports = router; -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerCommandIntro.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /backend/src/swagger.js: -------------------------------------------------------------------------------- 1 | 2 | import swaggerAutogen from 'swagger-autogen'; 3 | const doc = { 4 | info: { 5 | title: 'Docker Web Desktop API', 6 | description: 'API documentation for Docker Web Desktop (auto-generated)' 7 | }, 8 | host: 'localhost:3000', 9 | schemes: ['http'], 10 | basePath: '/api', 11 | tags: [ 12 | { name: 'Containers', description: 'Container operations' }, 13 | { name: 'Images', description: 'Image operations' }, 14 | { name: 'Volumes', description: 'Volume operations' }, 15 | { name: 'Networks', description: 'Network operations' }, 16 | { name: 'Services', description: 'Service operations' }, 17 | { name: 'Commands', description: 'Command operations' }, 18 | { name: 'Compose', description: 'Compose operations' }, 19 | { name: 'WSL', description: 'WSL operations' }, 20 | ] 21 | }; 22 | 23 | const outputFile = './src/swagger_output.json'; 24 | const endpointsFiles = [ 25 | './src/server.js', 26 | './src/routes/containers.js', 27 | './src/routes/images.js', 28 | './src/routes/volumes.js', 29 | './src/routes/networks.js', 30 | './src/routes/services.js', 31 | './src/routes/commands.js', 32 | './src/routes/compose.js', 33 | './src/routes/wsl.js', 34 | ]; 35 | 36 | swaggerAutogen(outputFile, endpointsFiles, doc); -------------------------------------------------------------------------------- /backend/src/swagger.autogen.js: -------------------------------------------------------------------------------- 1 | const swaggerAutogen = require('swagger-autogen')(); 2 | 3 | const doc = { 4 | info: { 5 | title: 'Docker Web Desktop API', 6 | description: 'API documentation for Docker Web Desktop (auto-generated)' 7 | }, 8 | host: 'localhost:3000', 9 | schemes: ['http'], 10 | basePath: '/api', 11 | tags: [ 12 | { name: 'Containers', description: 'Container operations' }, 13 | { name: 'Images', description: 'Image operations' }, 14 | { name: 'Volumes', description: 'Volume operations' }, 15 | { name: 'Networks', description: 'Network operations' }, 16 | { name: 'Services', description: 'Service operations' }, 17 | { name: 'Commands', description: 'Command operations' }, 18 | { name: 'Compose', description: 'Compose operations' }, 19 | { name: 'WSL', description: 'WSL operations' }, 20 | ] 21 | }; 22 | 23 | const outputFile = './src/swagger_output.json'; 24 | const endpointsFiles = [ 25 | './src/server.js', 26 | './src/routes/containers.js', 27 | './src/routes/images.js', 28 | './src/routes/volumes.js', 29 | './src/routes/networks.js', 30 | './src/routes/services.js', 31 | './src/routes/commands.js', 32 | './src/routes/compose.js', 33 | './src/routes/wsl.js', 34 | ]; 35 | 36 | swaggerAutogen(outputFile, endpointsFiles, doc); 37 | -------------------------------------------------------------------------------- /docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | # Backend Service (Development) 5 | backend-dev: 6 | build: 7 | context: ./backend 8 | dockerfile: Dockerfile.dev 9 | container_name: docker-web-desktop-backend-dev 10 | restart: unless-stopped 11 | ports: 12 | - "3000:3000" 13 | environment: 14 | - NODE_ENV=development 15 | - PORT=3000 16 | volumes: 17 | # Mount source code for hot reloading 18 | - ./backend:/app 19 | - /app/node_modules 20 | # Mount Docker socket to allow backend to communicate with Docker daemon 21 | - /var/run/docker.sock:/var/run/docker.sock 22 | networks: 23 | - app-network 24 | command: npm run dev 25 | 26 | # Frontend Service (Development) 27 | frontend-dev: 28 | build: 29 | context: ./frontend 30 | dockerfile: Dockerfile.dev 31 | container_name: docker-web-desktop-frontend-dev 32 | restart: unless-stopped 33 | ports: 34 | - "5173:5173" 35 | environment: 36 | - NODE_ENV=development 37 | volumes: 38 | # Mount source code for hot reloading 39 | - ./frontend:/app 40 | - /app/node_modules 41 | networks: 42 | - app-network 43 | command: npm run dev -- --host 0.0.0.0 44 | 45 | networks: 46 | app-network: 47 | driver: bridge 48 | name: docker-web-desktop-network-dev 49 | -------------------------------------------------------------------------------- /backend/src/routes/images.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const express = require('express'); 4 | const router = express.Router(); 5 | const dockerService = require('../services/dockerService'); 6 | const { DockerService } = require('../services/dockerService'); 7 | 8 | // Get all images 9 | router.get('/', async (req, res) => { 10 | /* #swagger.tags = ['Images'] */ 11 | /* #swagger.path = '/images/' */ 12 | try { 13 | const source = req.query.source || 'local'; 14 | const wslDistro = req.query.wslDistro || undefined; 15 | const serviceInstance = new DockerService({ source, wslDistro }); 16 | const images = await serviceInstance.getImages(); 17 | res.json(images); 18 | } catch (error) { 19 | res.status(500).json({ error: error.message }); 20 | } 21 | }); 22 | 23 | // Remove image 24 | router.delete('/:id', async (req, res) => { 25 | /* #swagger.tags = ['Images'] */ 26 | /* #swagger.path = '/images/{id}' */ 27 | try { 28 | const force = req.query.force === 'true'; 29 | const source = req.query.source || 'local'; 30 | const wslDistro = req.query.wslDistro || undefined; 31 | const serviceInstance = new DockerService({ source, wslDistro }); 32 | const result = await serviceInstance.removeImage(req.params.id, force); 33 | 34 | // Emit real-time update 35 | const io = req.app.get('io'); 36 | io.emit('imageRemoved', { id: req.params.id }); 37 | 38 | res.json(result); 39 | } catch (error) { 40 | res.status(500).json({ error: error.message }); 41 | } 42 | }); 43 | 44 | module.exports = router; -------------------------------------------------------------------------------- /test-backend.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | // Test backend health endpoint 4 | const options = { 5 | hostname: 'localhost', 6 | port: 3000, 7 | path: '/api/health', 8 | method: 'GET' 9 | }; 10 | 11 | const req = http.request(options, (res) => { 12 | console.log(`Status: ${res.statusCode}`); 13 | console.log(`Headers: ${JSON.stringify(res.headers)}`); 14 | 15 | res.setEncoding('utf8'); 16 | res.on('data', (chunk) => { 17 | console.log(`Body: ${chunk}`); 18 | }); 19 | 20 | res.on('end', () => { 21 | console.log('Request completed'); 22 | testLogs(); 23 | }); 24 | }); 25 | 26 | req.on('error', (e) => { 27 | console.error(`Problem with request: ${e.message}`); 28 | }); 29 | 30 | req.end(); 31 | 32 | function testLogs() { 33 | // Test logs endpoint 34 | const logOptions = { 35 | hostname: 'localhost', 36 | port: 3000, 37 | path: '/api/containers/1c64f3db3e109469e1a88b04e0aefddfe126ba3725a2e36fa1793704deba010d/logs?tail=5', 38 | method: 'GET' 39 | }; 40 | 41 | const logReq = http.request(logOptions, (res) => { 42 | console.log(`\nLogs Status: ${res.statusCode}`); 43 | 44 | res.setEncoding('utf8'); 45 | let data = ''; 46 | res.on('data', (chunk) => { 47 | data += chunk; 48 | }); 49 | 50 | res.on('end', () => { 51 | console.log('Logs Response:', data); 52 | }); 53 | }); 54 | 55 | logReq.on('error', (e) => { 56 | console.error(`Problem with logs request: ${e.message}`); 57 | }); 58 | 59 | logReq.end(); 60 | } -------------------------------------------------------------------------------- /backend/src/routes/networks.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const dockerService = require('../services/dockerService'); 4 | const { DockerService } = require('../services/dockerService'); 5 | 6 | 7 | // Get all networks 8 | router.get('/', async (req, res) => { 9 | /* #swagger.tags = ['Networks'] */ 10 | /* #swagger.path = '/networks/' */ 11 | try { 12 | const source = req.query.source || 'local'; 13 | const wslDistro = req.query.wslDistro || undefined; 14 | const serviceInstance = new DockerService({ source, wslDistro }); 15 | const networks = await serviceInstance.getNetworks(); 16 | res.json(networks); 17 | } catch (error) { 18 | res.status(500).json({ error: error.message }); 19 | } 20 | }); 21 | 22 | 23 | // Remove network 24 | router.delete('/:id', async (req, res) => { 25 | /* #swagger.tags = ['Networks'] */ 26 | /* #swagger.path = '/networks/{id}' */ 27 | try { 28 | const force = req.query.force === 'true'; 29 | const source = req.query.source || 'local'; 30 | const wslDistro = req.query.wslDistro || undefined; 31 | const serviceInstance = new DockerService({ source, wslDistro }); 32 | const result = await serviceInstance.removeNetwork(req.params.id, force); 33 | 34 | // Emit real-time update 35 | const io = req.app.get('io'); 36 | io.emit('networkRemoved', { id: req.params.id }); 37 | 38 | res.json(result); 39 | } catch (error) { 40 | res.status(500).json({ error: error.message }); 41 | } 42 | }); 43 | 44 | module.exports = router; -------------------------------------------------------------------------------- /backend/src/routes/volumes.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const dockerService = require('../services/dockerService'); 5 | const { DockerService } = require('../services/dockerService'); 6 | 7 | 8 | 9 | // Get all volumes 10 | router.get('/', async (req, res) => { 11 | /* #swagger.tags = ['Volumes'] */ 12 | /* #swagger.path = '/volumes/' */ 13 | try { 14 | const source = req.query.source || 'local'; 15 | const wslDistro = req.query.wslDistro || undefined; 16 | const serviceInstance = new DockerService({ source, wslDistro }); 17 | const volumes = await serviceInstance.getVolumes(); 18 | res.json(volumes); 19 | } catch (error) { 20 | res.status(500).json({ error: error.message }); 21 | } 22 | }); 23 | 24 | 25 | // Remove volume 26 | router.delete('/:name', async (req, res) => { 27 | /* #swagger.tags = ['Volumes'] */ 28 | /* #swagger.path = '/volumes/{name}' */ 29 | try { 30 | const force = req.query.force === 'true'; 31 | const source = req.query.source || 'local'; 32 | const wslDistro = req.query.wslDistro || undefined; 33 | const serviceInstance = new DockerService({ source, wslDistro }); 34 | const result = await serviceInstance.removeVolume(req.params.name, force); 35 | 36 | // Emit real-time update 37 | const io = req.app.get('io'); 38 | io.emit('volumeRemoved', { name: req.params.name }); 39 | 40 | res.json(result); 41 | } catch (error) { 42 | res.status(500).json({ error: error.message }); 43 | } 44 | }); 45 | 46 | module.exports = router; -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 62 | 63 | 77 | -------------------------------------------------------------------------------- /frontend/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | http { 6 | include /etc/nginx/mime.types; 7 | default_type application/octet-stream; 8 | 9 | sendfile on; 10 | keepalive_timeout 65; 11 | 12 | # Enable gzip compression 13 | gzip on; 14 | gzip_vary on; 15 | gzip_min_length 10240; 16 | gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; 17 | gzip_types 18 | text/plain 19 | text/css 20 | text/xml 21 | text/javascript 22 | application/x-javascript 23 | application/xml+rss 24 | application/javascript 25 | application/json; 26 | 27 | server { 28 | listen 80; 29 | server_name localhost; 30 | root /usr/share/nginx/html; 31 | index index.html; 32 | 33 | # Handle client-side routing (Vue Router) 34 | location / { 35 | try_files $uri $uri/ /index.html; 36 | } 37 | 38 | # Cache static assets 39 | location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { 40 | expires 1y; 41 | add_header Cache-Control "public, immutable"; 42 | } 43 | 44 | # Security headers 45 | add_header X-Frame-Options "SAMEORIGIN" always; 46 | add_header X-Content-Type-Options "nosniff" always; 47 | add_header X-XSS-Protection "1; mode=block" always; 48 | 49 | # API proxy to backend (optional, for development) 50 | location /api { 51 | proxy_pass http://backend:3000; 52 | proxy_http_version 1.1; 53 | proxy_set_header Upgrade $http_upgrade; 54 | proxy_set_header Connection 'upgrade'; 55 | proxy_set_header Host $host; 56 | proxy_set_header X-Real-IP $remote_addr; 57 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 58 | proxy_set_header X-Forwarded-Proto $scheme; 59 | proxy_cache_bypass $http_upgrade; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop - Root .gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | # Dependency directories 13 | node_modules/ 14 | */node_modules/ 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage/ 24 | */coverage/ 25 | 26 | # Build outputs 27 | dist/ 28 | build/ 29 | */dist/ 30 | */build/ 31 | 32 | # Environment variables 33 | .env 34 | .env.local 35 | .env.development.local 36 | .env.test.local 37 | .env.production.local 38 | *.env 39 | 40 | # IDE and Editor files 41 | .vscode/ 42 | .idea/ 43 | *.swp 44 | *.swo 45 | *~ 46 | .DS_Store 47 | Thumbs.db 48 | 49 | # OS generated files 50 | .DS_Store 51 | .DS_Store? 52 | ._* 53 | .Spotlight-V100 54 | .Trashes 55 | ehthumbs.db 56 | Thumbs.db 57 | 58 | # Docker 59 | .docker/ 60 | docker-compose.override.yml 61 | *.dockerignore 62 | 63 | # Database 64 | *.sqlite 65 | *.sqlite3 66 | *.db 67 | 68 | # Temporary files 69 | *.tmp 70 | *.temp 71 | .cache/ 72 | .temp/ 73 | 74 | # Package-lock files (uncomment if you want to ignore them) 75 | # package-lock.json 76 | # */package-lock.json 77 | 78 | # Yarn lock files (uncomment if you want to ignore them) 79 | # yarn.lock 80 | # */yarn.lock 81 | 82 | # Local configuration 83 | config/local.json 84 | config/development.json 85 | config/production.json 86 | 87 | # Security 88 | *.pem 89 | *.key 90 | *.crt 91 | *.csr 92 | 93 | # Application specific 94 | uploads/ 95 | public/uploads/ 96 | storage/ 97 | 98 | # Error logs 99 | error.log 100 | access.log 101 | 102 | # PM2 103 | .pm2/ 104 | 105 | # Application cache 106 | .cache/ 107 | *.cache 108 | 109 | # Deployment 110 | deploy/ 111 | deployment/ 112 | 113 | # Documentation build 114 | docs/_build/ 115 | docs/.doctrees/ 116 | 117 | # Backup files 118 | *.bak 119 | *.backup 120 | *.old 121 | 122 | edit.bat -------------------------------------------------------------------------------- /backend/debug-ports.js: -------------------------------------------------------------------------------- 1 | const Docker = require('dockerode'); 2 | 3 | async function debugPorts() { 4 | try { 5 | const docker = new Docker(); 6 | const containers = await docker.listContainers({ all: true }); 7 | 8 | if (containers.length > 0) { 9 | // Find the container with 8885 port mappings that user reported 10 | const container = containers.find(c => c.Ports && c.Ports.some(p => p.PublicPort === 8885)) || 11 | containers.find(c => c.Ports && c.Ports.some(p => p.PublicPort)) || 12 | containers[0]; 13 | console.log('=== Container from listContainers ==='); 14 | console.log('ID:', container.Id); 15 | console.log('Ports from listContainers:'); 16 | console.log(JSON.stringify(container.Ports, null, 2)); 17 | 18 | console.log('\n=== Container from inspect ==='); 19 | const containerObj = docker.getContainer(container.Id); 20 | const inspectData = await containerObj.inspect(); 21 | 22 | console.log('Ports from inspect (NetworkSettings.Ports):'); 23 | console.log(JSON.stringify(inspectData.NetworkSettings.Ports, null, 2)); 24 | 25 | console.log('\n=== Current backend merge result ==='); 26 | const merged = { 27 | ...container, 28 | Labels: inspectData.Config.Labels || {}, 29 | NetworkSettings: inspectData.NetworkSettings || {} 30 | }; 31 | console.log('Final Ports after merge:'); 32 | console.log(JSON.stringify(merged.Ports, null, 2)); 33 | 34 | console.log('\n=== Checking if NetworkSettings has different Ports ==='); 35 | console.log('NetworkSettings.Ports:'); 36 | console.log(JSON.stringify(merged.NetworkSettings.Ports, null, 2)); 37 | } else { 38 | console.log('No containers found'); 39 | } 40 | } catch (error) { 41 | console.error('Error:', error.message); 42 | } 43 | } 44 | 45 | debugPorts(); -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerCommandHeader.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/views/SettingsView.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 56 | 57 | -------------------------------------------------------------------------------- /frontend/src/router/routes.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router' 2 | import ContainersView from '../views/ContainersView.vue' 3 | import ContainerDetailsView from '../views/ContainerDetailsView.vue' 4 | import ImagesView from '../views/ImagesView.vue' 5 | import VolumesView from '../views/VolumesView.vue' 6 | import NetworksView from '../views/NetworksView.vue' 7 | import DashboardView from '../views/DashboardView.vue' 8 | import DockerCommandsView from '../views/DockerCommandsView.vue' 9 | import ResourcesView from '../views/ResourcesView.vue' 10 | import InstallationView from '../views/InstallationView.vue' 11 | import AboutView from '../views/AboutView.vue' 12 | import SettingsView from '../views/SettingsView.vue' 13 | 14 | const routes: RouteRecordRaw[] = [ 15 | { 16 | path: '/', 17 | name: 'Dashboard', 18 | component: DashboardView 19 | }, 20 | { 21 | path: '/containers', 22 | name: 'Containers', 23 | component: ContainersView 24 | }, 25 | { 26 | path: '/containers/:id', 27 | name: 'ContainerDetails', 28 | component: ContainerDetailsView, 29 | props: true 30 | }, 31 | { 32 | path: '/images', 33 | name: 'Images', 34 | component: ImagesView 35 | }, 36 | { 37 | path: '/volumes', 38 | name: 'Volumes', 39 | component: VolumesView 40 | }, 41 | { 42 | path: '/networks', 43 | name: 'Networks', 44 | component: NetworksView 45 | }, 46 | { 47 | path: '/commands', 48 | name: 'DockerCommands', 49 | component: DockerCommandsView 50 | }, 51 | { 52 | path: '/resources', 53 | name: 'Resources', 54 | component: ResourcesView 55 | }, 56 | { 57 | path: '/installation', 58 | name: 'Installation', 59 | component: InstallationView 60 | }, 61 | { 62 | path: '/about', 63 | name: 'About', 64 | component: AboutView 65 | }, 66 | { 67 | path: '/settings', 68 | name: 'Settings', 69 | component: SettingsView 70 | } 71 | ] 72 | 73 | export default routes -------------------------------------------------------------------------------- /frontend/src/components/docker/LogsModal.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /backend/src/routes/services.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { DockerService } = require('../services/dockerService'); 3 | 4 | const router = express.Router(); 5 | 6 | // Get all services 7 | router.get('/', async (req, res) => { 8 | /* #swagger.tags = ['Services'] */ 9 | /* #swagger.path = '/services/' */ 10 | try { 11 | const source = req.query.source || 'local'; 12 | const wslDistro = req.query.wslDistro || undefined; 13 | const serviceInstance = new DockerService({ source, wslDistro }); 14 | const services = await serviceInstance.docker.listServices(); 15 | res.json(services); 16 | } catch (error) { 17 | console.error('Error listing services:', error); 18 | res.status(500).json({ 19 | error: 'Failed to list services', 20 | message: error.message 21 | }); 22 | } 23 | }); 24 | 25 | // Get specific service by ID or name 26 | router.get('/:id', async (req, res) => { 27 | /* #swagger.tags = ['Services'] */ 28 | /* #swagger.path = '/services/{id}' */ 29 | try { 30 | const source = req.query.source || 'local'; 31 | const wslDistro = req.query.wslDistro || undefined; 32 | const serviceInstance = new DockerService({ source, wslDistro }); 33 | const service = serviceInstance.docker.getService(req.params.id); 34 | const serviceInfo = await service.inspect(); 35 | res.json(serviceInfo); 36 | } catch (error) { 37 | console.error('Error getting service:', error); 38 | res.status(500).json({ 39 | error: 'Failed to get service information', 40 | message: error.message 41 | }); 42 | } 43 | }); 44 | 45 | // Remove service 46 | router.delete('/:id', async (req, res) => { 47 | /* #swagger.tags = ['Services'] */ 48 | /* #swagger.path = '/services/{id}' */ 49 | try { 50 | const source = req.query.source || 'local'; 51 | const wslDistro = req.query.wslDistro || undefined; 52 | const serviceInstance = new DockerService({ source, wslDistro }); 53 | const result = await serviceInstance.removeService(req.params.id); 54 | 55 | // Emit real-time update 56 | const io = req.app.get('io'); 57 | io.emit('serviceRemoved', { id: req.params.id }); 58 | 59 | res.json(result); 60 | } catch (error) { 61 | res.status(500).json({ error: error.message }); 62 | } 63 | }); 64 | 65 | module.exports = router; -------------------------------------------------------------------------------- /frontend/src/components/docker/ConfirmationModal.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | -------------------------------------------------------------------------------- /frontend/src/utils/environment.ts: -------------------------------------------------------------------------------- 1 | // Environment configuration utility 2 | interface EnvironmentConfig { 3 | apiBaseUrl: string 4 | appTitle: string 5 | appVersion: string 6 | debug: boolean 7 | socketUrl: string 8 | logLevel: 'debug' | 'info' | 'warn' | 'error' 9 | } 10 | 11 | class Environment { 12 | private static _config: EnvironmentConfig | null = null 13 | 14 | static get config(): EnvironmentConfig { 15 | if (!this._config) { 16 | this._config = { 17 | apiBaseUrl: import.meta.env.VITE_API_BASE_URL || process.env.VITE_API_BASE_URL || 'http://localhost:3334', 18 | appTitle: import.meta.env.VITE_APP_TITLE || 'Docker Web Desktop', 19 | appVersion: import.meta.env.VITE_APP_VERSION || '1.0.0', 20 | debug: import.meta.env.VITE_DEBUG === 'true', 21 | socketUrl: import.meta.env.VITE_SOCKET_URL || import.meta.env.VITE_API_BASE_URL || process.env.VITE_API_BASE_URL || 'http://localhost:3334', 22 | logLevel: (import.meta.env.VITE_LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error') || 'info' 23 | } 24 | } 25 | return this._config 26 | } 27 | 28 | static get isDevelopment(): boolean { 29 | return import.meta.env.DEV 30 | } 31 | 32 | static get isProduction(): boolean { 33 | return import.meta.env.PROD 34 | } 35 | 36 | static get mode(): string { 37 | return import.meta.env.MODE 38 | } 39 | 40 | static log(level: 'debug' | 'info' | 'warn' | 'error', message: string, ...args: any[]): void { 41 | const levels = { debug: 0, info: 1, warn: 2, error: 3 } 42 | const configLevel = levels[this.config.logLevel] 43 | const messageLevel = levels[level] 44 | 45 | if (messageLevel >= configLevel) { 46 | console[level](`[${this.config.appTitle}] ${message}`, ...args) 47 | } 48 | } 49 | 50 | static debug(message: string, ...args: any[]): void { 51 | this.log('debug', message, ...args) 52 | } 53 | 54 | static info(message: string, ...args: any[]): void { 55 | this.log('info', message, ...args) 56 | } 57 | 58 | static warn(message: string, ...args: any[]): void { 59 | this.log('warn', message, ...args) 60 | } 61 | 62 | static error(message: string, ...args: any[]): void { 63 | this.log('error', message, ...args) 64 | } 65 | } 66 | 67 | export default Environment 68 | export type { EnvironmentConfig } -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop Frontend - .gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | # Dependency directories 13 | node_modules/ 14 | jspm_packages/ 15 | 16 | # Build outputs 17 | dist/ 18 | dist-ssr/ 19 | build/ 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage/ 23 | .nyc_output/ 24 | 25 | # Environment variables 26 | .env 27 | .env.* 28 | *.local 29 | edit.bat 30 | 31 | # Cache directories 32 | .cache/ 33 | .parcel-cache/ 34 | .vite/ 35 | 36 | # Editor directories and files 37 | .vscode/* 38 | !.vscode/extensions.json 39 | .idea/ 40 | *.suo 41 | *.ntvs* 42 | *.njsproj 43 | *.sln 44 | *.sw? 45 | *~ 46 | 47 | # OS generated files 48 | .DS_Store 49 | .DS_Store? 50 | ._* 51 | .Spotlight-V100 52 | .Trashes 53 | ehthumbs.db 54 | Thumbs.db 55 | 56 | # TypeScript 57 | *.tsbuildinfo 58 | *.d.ts 59 | 60 | # ESLint cache 61 | .eslintcache 62 | 63 | # Prettier cache 64 | .prettierache 65 | 66 | # Stylelint cache 67 | .stylelintcache 68 | 69 | # Testing 70 | /cypress/videos/ 71 | /cypress/screenshots/ 72 | __screenshots__/ 73 | test-results/ 74 | playwright-report/ 75 | 76 | # Vitest 77 | coverage/ 78 | 79 | # Storybook build outputs 80 | .out/ 81 | .storybook-out/ 82 | storybook-static/ 83 | 84 | # Temporary folders 85 | tmp/ 86 | temp/ 87 | 88 | # Hot reload 89 | .hot-update.* 90 | 91 | # Webpack 92 | .webpack/ 93 | 94 | # Rollup 95 | .rollup.cache/ 96 | 97 | # Vite 98 | .vite/ 99 | 100 | # SvelteKit 101 | .svelte-kit/ 102 | 103 | # Next.js (if applicable) 104 | .next/ 105 | out/ 106 | 107 | # Nuxt.js (if applicable) 108 | .nuxt/ 109 | 110 | # PWA files 111 | sw.js 112 | workbox-*.js 113 | 114 | # Firebase (if applicable) 115 | .firebase/ 116 | firebase-debug.log 117 | firestore-debug.log 118 | 119 | # Netlify (if applicable) 120 | .netlify/ 121 | 122 | # Vercel (if applicable) 123 | .vercel/ 124 | 125 | # Local server 126 | .serve/ 127 | 128 | # Runtime files 129 | *.pid 130 | *.seed 131 | *.pid.lock 132 | 133 | # Optional npm cache directory 134 | .npm/ 135 | 136 | # Optional eslint cache 137 | .eslintcache 138 | 139 | # Yarn Integrity file 140 | .yarn-integrity 141 | 142 | # Bundle analyzer 143 | bundle-analyzer-report.html 144 | 145 | # Application specific 146 | public/uploads/ 147 | src/assets/uploads/ 148 | 149 | # Backup files 150 | *.bak 151 | *.backup 152 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | # This is the Docker Daemon (the 'host' of your inner containers) 3 | dind-daemon: 4 | image: docker:24-dind 5 | container_name: dind-daemon 6 | privileged: true # DinD requires privileged mode to function correctly 7 | volumes: 8 | - dind-data:/var/lib/docker # Persist Docker data/images 9 | # Expose the daemon on a TCP port for the client container to use 10 | environment: 11 | DOCKER_TLS_CERTDIR: "" # Disable TLS for simpler local setup (use TLS in prod) 12 | DOCKER_HOST: "tcp://0.0.0.0:2375" 13 | networks : 14 | - app-network 15 | 16 | # Backend Service 17 | backend: 18 | build: 19 | context: ./backend 20 | dockerfile: Dockerfile 21 | container_name: docker-web-desktop-backend 22 | restart: unless-stopped 23 | ports: 24 | - "3333:3000" 25 | depends_on: 26 | - dind-daemon 27 | environment: 28 | - NODE_ENV=production 29 | - PORT=3000 30 | - DOCKER_HOST=tcp://dind-daemon:2375 31 | volumes: 32 | # Mount Docker socket to allow backend to communicate with Docker daemon 33 | - /var/run/docker.sock:/var/run/docker.sock 34 | networks: 35 | - app-network 36 | healthcheck: 37 | test: 38 | [ 39 | "CMD", 40 | "wget", 41 | "--no-verbose", 42 | "--tries=1", 43 | "--spider", 44 | "http://localhost:3000/api/health", 45 | ] 46 | interval: 30s 47 | timeout: 10s 48 | retries: 3 49 | start_period: 40s 50 | 51 | # Frontend Service 52 | frontend: 53 | build: 54 | context: ./frontend 55 | dockerfile: Dockerfile 56 | container_name: docker-web-desktop-frontend 57 | restart: unless-stopped 58 | ports: 59 | - "5174:80" 60 | depends_on: 61 | backend: 62 | condition: service_healthy 63 | environment: 64 | - NODE_ENV=docker 65 | - VITE_API_BASE_URL=http://backend:3000 66 | - API_BASE_URL=http://backend:3000 67 | networks: 68 | - app-network 69 | links: 70 | - backend 71 | healthcheck: 72 | test: 73 | [ 74 | "CMD", 75 | "wget", 76 | "--no-verbose", 77 | "--tries=1", 78 | "--spider", 79 | "http://localhost:5174", 80 | ] 81 | interval: 30s 82 | timeout: 10s 83 | retries: 3 84 | 85 | networks: 86 | app-network: 87 | driver: bridge 88 | name: docker-web-desktop-network 89 | 90 | volumes: 91 | # Optional: if you need to persist any data 92 | app-data: 93 | driver: local 94 | dind-data: 95 | -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerConfirmationModal.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | -------------------------------------------------------------------------------- /public/install-docker-wsl2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Docker Installation Script for WSL2 4 | # This script automates the installation of Docker Engine on WSL2 (Ubuntu) 5 | 6 | set -e 7 | 8 | echo "================================================" 9 | echo "Docker Installation Script for WSL2" 10 | echo "================================================" 11 | echo "" 12 | 13 | # Colors for output 14 | GREEN='\033[0;32m' 15 | BLUE='\033[0;34m' 16 | RED='\033[0;31m' 17 | NC='\033[0m' # No Color 18 | 19 | # Check if running on WSL2 20 | if ! grep -q "microsoft" /proc/version; then 21 | echo -e "${RED}Error: This script must be run inside WSL2${NC}" 22 | exit 1 23 | fi 24 | 25 | echo -e "${BLUE}Step 1: Updating package repository...${NC}" 26 | sudo apt update && sudo apt upgrade -y 27 | 28 | echo -e "${BLUE}Step 2: Installing prerequisites...${NC}" 29 | sudo apt install -y \ 30 | apt-transport-https \ 31 | ca-certificates \ 32 | curl \ 33 | gnupg \ 34 | lsb-release 35 | 36 | echo -e "${BLUE}Step 3: Adding Docker's official GPG key...${NC}" 37 | sudo mkdir -p /etc/apt/keyrings 38 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 39 | 40 | echo -e "${BLUE}Step 4: Setting up Docker repository...${NC}" 41 | echo \ 42 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ 43 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 44 | 45 | echo -e "${BLUE}Step 5: Installing Docker Engine...${NC}" 46 | sudo apt update 47 | sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 48 | 49 | echo -e "${BLUE}Step 6: Adding user to docker group...${NC}" 50 | sudo usermod -aG docker $USER 51 | 52 | echo -e "${BLUE}Step 7: Starting Docker service...${NC}" 53 | sudo service docker start 54 | 55 | echo "" 56 | echo -e "${GREEN}================================================${NC}" 57 | echo -e "${GREEN}Docker installation completed successfully!${NC}" 58 | echo -e "${GREEN}================================================${NC}" 59 | echo "" 60 | echo "Testing Docker installation..." 61 | docker --version 62 | 63 | echo "" 64 | echo "Running hello-world container..." 65 | docker run hello-world 66 | 67 | echo "" 68 | echo -e "${BLUE}Next steps:${NC}" 69 | echo "1. Close this terminal and open a new one to apply group changes" 70 | echo "2. Run 'docker --version' to verify installation" 71 | echo "3. Use Docker Desktop Web UI and select 'WSL2' as container source" 72 | echo "" 73 | echo -e "${BLUE}To start Docker manually:${NC}" 74 | echo " sudo service docker start" 75 | echo "" 76 | echo -e "${BLUE}To auto-start Docker on WSL launch, add to ~/.bashrc:${NC}" 77 | echo " echo 'if ! service docker status > /dev/null 2>&1; then' >> ~/.bashrc" 78 | echo " echo ' sudo service docker start > /dev/null 2>&1' >> ~/.bashrc" 79 | echo " echo 'fi' >> ~/.bashrc" 80 | echo "" 81 | -------------------------------------------------------------------------------- /frontend/public/install-docker-wsl2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Docker Installation Script for WSL2 4 | # This script automates the installation of Docker Engine on WSL2 (Ubuntu) 5 | 6 | set -e 7 | 8 | echo "================================================" 9 | echo "Docker Installation Script for WSL2" 10 | echo "================================================" 11 | echo "" 12 | 13 | # Colors for output 14 | GREEN='\033[0;32m' 15 | BLUE='\033[0;34m' 16 | RED='\033[0;31m' 17 | NC='\033[0m' # No Color 18 | 19 | # Check if running on WSL2 20 | if ! grep -q "microsoft" /proc/version; then 21 | echo -e "${RED}Error: This script must be run inside WSL2${NC}" 22 | exit 1 23 | fi 24 | 25 | echo -e "${BLUE}Step 1: Updating package repository...${NC}" 26 | sudo apt update && sudo apt upgrade -y 27 | 28 | echo -e "${BLUE}Step 2: Installing prerequisites...${NC}" 29 | sudo apt install -y \ 30 | apt-transport-https \ 31 | ca-certificates \ 32 | curl \ 33 | gnupg \ 34 | lsb-release 35 | 36 | echo -e "${BLUE}Step 3: Adding Docker's official GPG key...${NC}" 37 | sudo mkdir -p /etc/apt/keyrings 38 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 39 | 40 | echo -e "${BLUE}Step 4: Setting up Docker repository...${NC}" 41 | echo \ 42 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ 43 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 44 | 45 | echo -e "${BLUE}Step 5: Installing Docker Engine...${NC}" 46 | sudo apt update 47 | sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 48 | 49 | echo -e "${BLUE}Step 6: Adding user to docker group...${NC}" 50 | sudo usermod -aG docker $USER 51 | 52 | echo -e "${BLUE}Step 7: Starting Docker service...${NC}" 53 | sudo service docker start 54 | 55 | echo "" 56 | echo -e "${GREEN}================================================${NC}" 57 | echo -e "${GREEN}Docker installation completed successfully!${NC}" 58 | echo -e "${GREEN}================================================${NC}" 59 | echo "" 60 | echo "Testing Docker installation..." 61 | docker --version 62 | 63 | echo "" 64 | echo "Running hello-world container..." 65 | docker run hello-world 66 | 67 | echo "" 68 | echo -e "${BLUE}Next steps:${NC}" 69 | echo "1. Close this terminal and open a new one to apply group changes" 70 | echo "2. Run 'docker --version' to verify installation" 71 | echo "3. Use Docker Desktop Web UI and select 'WSL2' as container source" 72 | echo "" 73 | echo -e "${BLUE}To start Docker manually:${NC}" 74 | echo " sudo service docker start" 75 | echo "" 76 | echo -e "${BLUE}To auto-start Docker on WSL launch, add to ~/.bashrc:${NC}" 77 | echo " echo 'if ! service docker status > /dev/null 2>&1; then' >> ~/.bashrc" 78 | echo " echo ' sudo service docker start > /dev/null 2>&1' >> ~/.bashrc" 79 | echo " echo 'fi' >> ~/.bashrc" 80 | echo "" 81 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop Backend - .gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | # Dependency directories 13 | node_modules/ 14 | jspm_packages/ 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage/ 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # TypeScript cache 46 | *.tsbuildinfo 47 | 48 | # Optional npm cache directory 49 | .npm 50 | 51 | # Optional eslint cache 52 | .eslintcache 53 | 54 | # Microbundle cache 55 | .rpt2_cache/ 56 | .rts2_cache_cjs/ 57 | .rts2_cache_es/ 58 | .rts2_cache_umd/ 59 | 60 | # Optional REPL history 61 | .node_repl_history 62 | 63 | # Output of 'npm pack' 64 | *.tgz 65 | 66 | # Yarn Integrity file 67 | .yarn-integrity 68 | 69 | # dotenv environment variables file 70 | .env 71 | .env.test 72 | .env.local 73 | .env.development.local 74 | .env.test.local 75 | .env.production.local 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | public 91 | 92 | # Storybook build outputs 93 | .out 94 | .storybook-out 95 | 96 | # Temporary folders 97 | tmp/ 98 | temp/ 99 | 100 | # Editor directories and files 101 | .vscode/* 102 | !.vscode/extensions.json 103 | .idea 104 | *.suo 105 | *.ntvs* 106 | *.njsproj 107 | *.sln 108 | *.sw? 109 | 110 | # OS generated files 111 | .DS_Store 112 | .DS_Store? 113 | ._* 114 | .Spotlight-V100 115 | .Trashes 116 | ehthumbs.db 117 | Thumbs.db 118 | 119 | # Database 120 | *.sqlite 121 | *.sqlite3 122 | *.db 123 | database.json 124 | 125 | # Docker 126 | Dockerfile.local 127 | docker-compose.local.yml 128 | 129 | # PM2 130 | .pm2/ 131 | ecosystem.config.js 132 | 133 | # Application uploads 134 | uploads/ 135 | files/ 136 | storage/ 137 | 138 | # Session store 139 | sessions/ 140 | 141 | # SSL certificates 142 | *.pem 143 | *.key 144 | *.crt 145 | *.csr 146 | ssl/ 147 | 148 | # Application specific logs 149 | access.log 150 | error.log 151 | app.log 152 | 153 | # Testing 154 | test-results/ 155 | coverage/ 156 | 157 | # Build 158 | build/ 159 | dist/ 160 | 161 | # Config files that might contain sensitive data 162 | config/local.json 163 | config/production.json 164 | config/development.json 165 | 166 | # Backup files 167 | *.bak 168 | *.backup -------------------------------------------------------------------------------- /frontend/DARK_MODE_FIXES.md: -------------------------------------------------------------------------------- 1 | # Dark Mode Container Grid Fixes 2 | 3 | ## 🐛 Issues Fixed 4 | 5 | ### 1. Container Grid Dark Mode Issues 6 | - **Problem**: Container rows had poor contrast on hover in dark mode 7 | - **Solution**: Added specific dark mode hover states with high contrast white text (`#ffffff`) 8 | 9 | ### 2. Badge Styling Issues 10 | - **Problem**: `bg-light text-dark` badges were unreadable in dark mode 11 | - **Solution**: Override badges to use dark theme colors with proper borders 12 | 13 | ### 3. Console/Code Display Issues 14 | - **Problem**: Code blocks and console outputs didn't have proper black background with white text 15 | - **Solution**: Force all code/console elements to use `#000000` background with `#ffffff` text 16 | 17 | ## 🎨 CSS Fixes Applied 18 | 19 | ### Container Grid Styling 20 | ```css 21 | /* Container grid specific fixes for dark mode */ 22 | [data-bs-theme="dark"] .cursor-pointer:hover { 23 | background-color: var(--bs-tertiary-bg) !important; 24 | color: #ffffff !important; 25 | } 26 | 27 | [data-bs-theme="dark"] .group-header:hover { 28 | background-color: var(--bs-tertiary-bg) !important; 29 | color: #ffffff !important; 30 | } 31 | 32 | /* [data-bs-theme="dark"] .group-container:hover { 33 | background-color: var(--bs-tertiary-bg) !important; 34 | color: #ffffff !important; 35 | } */ 36 | ``` 37 | 38 | ### Badge Fixes 39 | ```css 40 | /* Badge fixes for dark mode */ 41 | [data-bs-theme="dark"] .badge.bg-light { 42 | background-color: var(--bs-secondary-bg) !important; 43 | color: var(--bs-body-color) !important; 44 | border: 1px solid var(--bs-border-color); 45 | } 46 | ``` 47 | 48 | ### Console/Code Fixes 49 | ```css 50 | /* Console/Logs containers - black background with white text */ 51 | [data-bs-theme="dark"] .logs-container { 52 | background-color: #000000 !important; 53 | color: #ffffff !important; 54 | border: 1px solid var(--bs-border-color); 55 | } 56 | 57 | [data-bs-theme="dark"] code { 58 | background-color: #000000; 59 | color: #ffffff; 60 | border: 1px solid var(--bs-border-color); 61 | } 62 | 63 | [data-bs-theme="dark"] pre { 64 | background-color: #000000; 65 | color: #ffffff; 66 | border: 1px solid var(--bs-border-color); 67 | } 68 | ``` 69 | 70 | ## ✅ Results 71 | 72 | 1. **Container Grid**: All table rows now have readable white text on hover in dark mode 73 | 2. **Image Badges**: Docker image badges now display with proper dark theme colors 74 | 3. **Console Output**: All code blocks and log containers now have black background with white text 75 | 4. **Consistency**: All fixes maintain the overall dark theme aesthetic 76 | 77 | ## 🧪 Components Affected 78 | 79 | - **ContainersView.vue**: Main container grid with group headers and individual rows 80 | - **Log Modals**: Container log viewing modal with console output 81 | - **Code Blocks**: Any `` or `
` elements throughout the application
82 | - **Dynamic Modals**: Docker compose file viewer and other dynamically created content
83 | 
84 | The dark mode now provides excellent readability and contrast across all container management interfaces! 🌙✨


--------------------------------------------------------------------------------
/DOCKER-SOURCE-TESTING.md:
--------------------------------------------------------------------------------
 1 | # Docker Source Selection - Testing Guide
 2 | 
 3 | ## What Changed
 4 | 
 5 | 1. **Docker Source Selector** moved to the **top navbar** (no longer on dashboard)
 6 | 2. The selector is now persistent across all pages
 7 | 3. Changing the source will fetch containers from the selected daemon
 8 | 
 9 | ## How to Test
10 | 
11 | ### 1. Check if the selector is visible
12 | - Look at the top navbar - you should see a dropdown with "Local" and "WSL2" options
13 | 
14 | ### 2. Test Local Containers
15 | - Make sure the dropdown shows "Local"
16 | - Click "Refresh" on the dashboard
17 | - You should see containers from your Docker Desktop
18 | 
19 | ### 3. Test WSL2 Containers (if configured)
20 | - First, ensure your WSL2 Docker daemon is properly configured
21 |   - See `WSL2-DOCKER-CONFIG.md` for setup instructions
22 | - Select "WSL2" from the navbar dropdown
23 | - Click "Refresh" on the dashboard
24 | - You should see containers from WSL2 (if different from local)
25 | 
26 | ### 4. Test the debug endpoint
27 | Open your browser and navigate to:
28 | ```
29 | http://localhost:3334/api/containers/test/source?source=local
30 | http://localhost:3334/api/containers/test/source?source=wsl2
31 | ```
32 | 
33 | You should see JSON responses showing:
34 | - The source being tested
35 | - Whether connection was successful
36 | - Docker information (container count, server version)
37 | 
38 | Example response:
39 | ```json
40 | {
41 |   "source": "local",
42 |   "connected": true,
43 |   "dockerInfo": {
44 |     "Containers": 5,
45 |     "ContainersRunning": 2,
46 |     "ServerVersion": "26.0.0"
47 |   }
48 | }
49 | ```
50 | 
51 | ## Common Issues
52 | 
53 | ### "Still seeing same containers for Local and WSL2"
54 | This is **normal** if you're using Docker Desktop with WSL2 backend. Both point to the same daemon.
55 | To see different containers, you need to:
56 | 1. Have Docker running separately in WSL2 (not Docker Desktop)
57 | 2. Configure it to expose the daemon on TCP port 2375 (see WSL2-DOCKER-CONFIG.md)
58 | 
59 | ### "Source selector not appearing in navbar"
60 | - Clear browser cache and hard refresh (Ctrl+Shift+R)
61 | - Check browser console for errors
62 | - Verify the app properly reloaded
63 | 
64 | ### "Changes to dropdown don't work"
65 | - Check browser console for errors
66 | - Verify the debug endpoints show different sources are being sent
67 | - Make sure backend is running and not cached
68 | 
69 | ## Debug Steps
70 | 
71 | 1. **Check frontend/backend communication:**
72 |    ```
73 |    http://localhost:3334/api/containers/test/source?source=local
74 |    http://localhost:3334/api/containers/test/source?source=wsl2
75 |    ```
76 | 
77 | 2. **Check if containers endpoint gets source parameter:**
78 |    Open DevTools → Network tab → select "local" in dropdown → Refresh
79 |    - Look for request to `/api/containers?all=true&source=local`
80 | 
81 | 3. **Check backend logs:**
82 |    - Look at backend server output for any errors when fetching containers
83 | 
84 | 4. **Check if WSL2 is properly configured:**
85 |    If WSL2 Docker daemon isn't accessible, you'll see connection errors in the debug endpoint
86 | 


--------------------------------------------------------------------------------
/WSL2-DOCKER-CONFIG.md:
--------------------------------------------------------------------------------
 1 | # WSL2 Docker Configuration Guide
 2 | 
 3 | This guide explains how to configure the Docker Web Desktop to access containers from both Windows Docker Desktop and WSL2 Docker daemon.
 4 | 
 5 | ## Prerequisites
 6 | 
 7 | - Windows with WSL2 installed
 8 | - Docker Desktop for Windows OR Docker installed in WSL2
 9 | - Docker Web Desktop application
10 | 
11 | ## Configuration Methods
12 | 
13 | ### Method 1: TCP Connection (Recommended for separate WSL2 Docker)
14 | 
15 | If you have Docker running inside WSL2 (not Docker Desktop), you need to expose the Docker daemon on TCP:
16 | 
17 | 1. **In your WSL2 terminal**, edit the Docker daemon configuration:
18 |    ```bash
19 |    sudo nano /etc/docker/daemon.json
20 |    ```
21 | 
22 | 2. Add TCP socket configuration:
23 |    ```json
24 |    {
25 |      "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
26 |    }
27 |    ```
28 | 
29 | 3. Restart Docker:
30 |    ```bash
31 |    sudo systemctl restart docker
32 |    ```
33 | 
34 | 4. **In Windows**, create/edit `.env` file in the backend directory:
35 |    ```env
36 |    WSL2_DOCKER_HOST=localhost
37 |    WSL2_DOCKER_PORT=2375
38 |    ```
39 | 
40 | 5. Restart the backend server
41 | 
42 | ### Method 2: Socket Path (For Docker Desktop with WSL2 integration)
43 | 
44 | If using Docker Desktop with WSL2 backend, both "Local" and "WSL2" typically point to the same daemon. Docker Desktop automatically handles this integration.
45 | 
46 | No additional configuration needed - just select "Local" in the dashboard dropdown.
47 | 
48 | ### Method 3: Custom Socket Path
49 | 
50 | If you want to access WSL2 Docker via socket path:
51 | 
52 | 1. Enable WSL2 network sharing
53 | 2. In `.env` file:
54 |    ```env
55 |    WSL2_DOCKER_SOCKET=\\wsl$\Ubuntu\var\run\docker.sock
56 |    ```
57 |    (Replace `Ubuntu` with your WSL2 distribution name)
58 | 
59 | ## Usage
60 | 
61 | 1. Start the backend server
62 | 2. Open the dashboard
63 | 3. Use the dropdown at the top to switch between "Local" and "WSL2"
64 | 4. The dashboard will show containers from the selected source
65 | 
66 | ## Troubleshooting
67 | 
68 | ### "Failed to get containers" error
69 | 
70 | - Check if Docker daemon is running in the selected source
71 | - For WSL2 TCP: Ensure port 2375 is accessible: `curl http://localhost:2375/version`
72 | - Check Windows Firewall settings
73 | 
74 | ### Same containers showing for both Local and WSL2
75 | 
76 | - This is normal if you're using Docker Desktop with WSL2 backend
77 | - Both point to the same Docker daemon managed by Docker Desktop
78 | - To use separate Docker instances, install Docker directly in WSL2 (not Docker Desktop)
79 | 
80 | ### Connection timeout
81 | 
82 | - Verify the WSL2_DOCKER_PORT and WSL2_DOCKER_HOST values in .env
83 | - Check if Docker is listening on the configured port: 
84 |   ```bash
85 |   # In WSL2
86 |   sudo netstat -tlnp | grep 2375
87 |   ```
88 | 
89 | ## Security Note
90 | 
91 | ⚠️ **Warning**: Exposing Docker daemon on TCP without TLS is insecure and should only be used in development environments. For production, always use TLS certificates and authentication.
92 | 
93 | See [Docker daemon socket security](https://docs.docker.com/engine/security/protect-access/) for more information.
94 | 


--------------------------------------------------------------------------------
/backend/src/server.js:
--------------------------------------------------------------------------------
 1 | const express = require('express');
 2 | const cors = require('cors');
 3 | const http = require('http');
 4 | const socketIo = require('socket.io');
 5 | require('dotenv').config();
 6 | 
 7 | const containerRoutes = require('./routes/containers');
 8 | const imageRoutes = require('./routes/images');
 9 | const volumeRoutes = require('./routes/volumes');
10 | const networkRoutes = require('./routes/networks');
11 | const serviceRoutes = require('./routes/services');
12 | const commandRoutes = require('./routes/commands');
13 | const composeRoutes = require('./routes/compose');
14 | const wslRoutes = require('./routes/wsl');
15 | 
16 | const app = express();
17 | const server = http.createServer(app);
18 | const io = socketIo(server, {
19 |     cors: {
20 |         origin: ["http://localhost:8080", "http://localhost:5173", "http://localhost:5174"],
21 |         methods: ["GET", "POST"]
22 |     }
23 | });
24 | 
25 | const PORT = process.env.PORT || 3000;
26 | 
27 | // Middleware
28 | app.use(cors({
29 |     origin: [
30 |         "http://localhost:8080",
31 |         "http://localhost:5173",
32 |         "http://localhost:5174",
33 |         "http://localhost:5175"
34 |     ],
35 |     methods: ["GET", "POST", "PUT", "DELETE"],
36 |     credentials: true
37 | }));
38 | app.use(express.json());
39 | 
40 | // Swagger UI (development only)
41 | if (process.env.NODE_ENV === 'development') {
42 |     try {
43 |         const swaggerUi = require('swagger-ui-express');
44 |         const swaggerDocument = require('./swagger_output.json');
45 |         app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
46 |             explorer: true,
47 |             swaggerOptions: {
48 |                 docExpansion: 'none', // Collapse all by default
49 |                 tagsSorter: 'alpha', // Sort tags alphabetically
50 |                 operationsSorter: 'alpha',
51 |             }
52 |         }));
53 |         console.log('Swagger UI available at /api-docs (development, swagger-autogen)');
54 |     } catch (err) {
55 |         console.warn('Swagger UI not available. Install swagger-ui-express and swagger-autogen to enable it.');
56 |     }
57 | }
58 | 
59 | // Routes
60 | app.use('/api/containers', containerRoutes);
61 | app.use('/api/images', imageRoutes);
62 | app.use('/api/volumes', volumeRoutes);
63 | app.use('/api/networks', networkRoutes);
64 | app.use('/api/services', serviceRoutes);
65 | app.use('/api/commands', commandRoutes);
66 | app.use('/api/compose', composeRoutes);
67 | app.use('/api/wsl', wslRoutes);
68 | 
69 | // Health check endpoint
70 | app.get('/api/health', (req, res) => {
71 |     res.json({ status: 'OK', message: 'Docker Web Desktop API is running' });
72 | });
73 | 
74 | // Socket.IO for real-time updates
75 | io.on('connection', (socket) => {
76 |     console.log('Client connected:', socket.id);
77 | 
78 |     socket.on('disconnect', () => {
79 |         console.log('Client disconnected:', socket.id);
80 |     });
81 | });
82 | 
83 | // Make io available to routes
84 | app.set('io', io);
85 | 
86 | server.listen(PORT, () => {
87 |     console.log('Environment:', process.env.NODE_ENV || 'development');
88 |     console.log(`Server is running on port ${PORT}`);
89 |     //put the app link and swagger link here
90 |     console.log(`API Endpoint: http://localhost:${PORT}/api`);
91 |     console.log(`Swagger UI: http://localhost:${PORT}/api-docs`);
92 |     console.log('server is running at: http://localhost:' + PORT);
93 | });
94 | 
95 | module.exports = { app, io };


--------------------------------------------------------------------------------
/deploy.bat:
--------------------------------------------------------------------------------
  1 | @echo off
  2 | setlocal enabledelayedexpansion
  3 | 
  4 | :: Docker Web Desktop - Deployment Script for Windows
  5 | 
  6 | echo.
  7 | echo 🐳 Docker Web Desktop - Deployment Script
  8 | echo ===========================================
  9 | 
 10 | :: Check if Docker is running
 11 | docker info >nul 2>&1
 12 | if errorlevel 1 (
 13 |     echo ❌ Docker is not running. Please start Docker and try again.
 14 |     exit /b 1
 15 | )
 16 | 
 17 | :: Check if Docker Compose is available
 18 | docker-compose --version >nul 2>&1
 19 | if errorlevel 1 (
 20 |     echo ❌ Docker Compose is not installed. Please install Docker Compose and try again.
 21 |     exit /b 1
 22 | )
 23 | 
 24 | :: Parse command
 25 | set COMMAND=%1
 26 | if "%COMMAND%"=="" set COMMAND=start
 27 | 
 28 | if "%COMMAND%"=="start" goto start
 29 | if "%COMMAND%"=="dev" goto dev
 30 | if "%COMMAND%"=="stop" goto stop
 31 | if "%COMMAND%"=="restart" goto restart
 32 | if "%COMMAND%"=="logs" goto logs
 33 | if "%COMMAND%"=="clean" goto clean
 34 | if "%COMMAND%"=="build" goto build
 35 | if "%COMMAND%"=="status" goto status
 36 | if "%COMMAND%"=="help" goto help
 37 | if "%COMMAND%"=="-h" goto help
 38 | if "%COMMAND%"=="--help" goto help
 39 | 
 40 | echo ❌ Unknown command: %COMMAND%
 41 | echo.
 42 | goto help
 43 | 
 44 | :start
 45 | echo 🚀 Starting Docker Web Desktop in production mode...
 46 | docker-compose up -d --build
 47 | if errorlevel 0 (
 48 |     echo ✅ Application started successfully!
 49 |     echo 🌐 Frontend: http://localhost
 50 |     echo 🔧 Backend API: http://localhost:3000
 51 | )
 52 | goto end
 53 | 
 54 | :dev
 55 | echo 🚀 Starting Docker Web Desktop in development mode...
 56 | docker-compose -f docker-compose.dev.yml up --build
 57 | goto end
 58 | 
 59 | :stop
 60 | echo 🛑 Stopping Docker Web Desktop...
 61 | docker-compose down
 62 | docker-compose -f docker-compose.dev.yml down >nul 2>&1
 63 | echo ✅ Application stopped successfully!
 64 | goto end
 65 | 
 66 | :restart
 67 | echo 🔄 Restarting Docker Web Desktop...
 68 | docker-compose down
 69 | docker-compose up -d --build
 70 | if errorlevel 0 (
 71 |     echo ✅ Application restarted successfully!
 72 | )
 73 | goto end
 74 | 
 75 | :logs
 76 | echo 📋 Showing application logs...
 77 | docker-compose logs -f
 78 | goto end
 79 | 
 80 | :clean
 81 | echo 🧹 Cleaning up Docker Web Desktop...
 82 | docker-compose down -v --rmi all --remove-orphans
 83 | docker-compose -f docker-compose.dev.yml down -v --rmi all --remove-orphans >nul 2>&1
 84 | echo ✅ Cleanup completed!
 85 | goto end
 86 | 
 87 | :build
 88 | echo 🔨 Building Docker images...
 89 | docker-compose build --no-cache
 90 | if errorlevel 0 (
 91 |     echo ✅ Images built successfully!
 92 | )
 93 | goto end
 94 | 
 95 | :status
 96 | echo 📊 Container status:
 97 | docker-compose ps
 98 | echo.
 99 | echo 📊 Development container status:
100 | docker-compose -f docker-compose.dev.yml ps >nul 2>&1 || echo No development containers running
101 | goto end
102 | 
103 | :help
104 | echo Usage: %0 [COMMAND]
105 | echo.
106 | echo Commands:
107 | echo   start     Start the application in production mode
108 | echo   dev       Start the application in development mode
109 | echo   stop      Stop the application
110 | echo   restart   Restart the application
111 | echo   logs      Show application logs
112 | echo   clean     Stop and remove all containers, networks, and volumes
113 | echo   build     Build all Docker images
114 | echo   status    Show status of all containers
115 | echo.
116 | echo Examples:
117 | echo   %0 start
118 | echo   %0 dev
119 | echo   %0 logs
120 | goto end
121 | 
122 | :end
123 | echo.


--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
  1 | #!/bin/bash
  2 | 
  3 | # Docker Web Desktop - Deployment Script
  4 | 
  5 | set -e
  6 | 
  7 | echo "🐳 Docker Web Desktop - Deployment Script"
  8 | echo "==========================================="
  9 | 
 10 | # Check if Docker is running
 11 | if ! docker info > /dev/null 2>&1; then
 12 |     echo "❌ Docker is not running. Please start Docker and try again."
 13 |     exit 1
 14 | fi
 15 | 
 16 | # Check if Docker Compose is available
 17 | if ! command -v docker-compose > /dev/null 2>&1; then
 18 |     echo "❌ Docker Compose is not installed. Please install Docker Compose and try again."
 19 |     exit 1
 20 | fi
 21 | 
 22 | # Function to show usage
 23 | show_usage() {
 24 |     echo "Usage: $0 [COMMAND]"
 25 |     echo ""
 26 |     echo "Commands:"
 27 |     echo "  start     Start the application in production mode"
 28 |     echo "  dev       Start the application in development mode"
 29 |     echo "  stop      Stop the application"
 30 |     echo "  restart   Restart the application"
 31 |     echo "  logs      Show application logs"
 32 |     echo "  clean     Stop and remove all containers, networks, and volumes"
 33 |     echo "  build     Build all Docker images"
 34 |     echo "  status    Show status of all containers"
 35 |     echo ""
 36 |     echo "Examples:"
 37 |     echo "  $0 start"
 38 |     echo "  $0 dev"
 39 |     echo "  $0 logs"
 40 | }
 41 | 
 42 | # Parse command
 43 | COMMAND=${1:-"start"}
 44 | 
 45 | case $COMMAND in
 46 |     "start")
 47 |         echo "🚀 Starting Docker Web Desktop in production mode..."
 48 |         docker-compose up -d --build
 49 |         echo "✅ Application started successfully!"
 50 |         echo "🌐 Frontend: http://localhost"
 51 |         echo "🔧 Backend API: http://localhost:3000"
 52 |         ;;
 53 |         
 54 |     "dev")
 55 |         echo "🚀 Starting Docker Web Desktop in development mode..."
 56 |         docker-compose -f docker-compose.dev.yml up --build
 57 |         ;;
 58 |         
 59 |     "stop")
 60 |         echo "🛑 Stopping Docker Web Desktop..."
 61 |         docker-compose down
 62 |         docker-compose -f docker-compose.dev.yml down 2>/dev/null || true
 63 |         echo "✅ Application stopped successfully!"
 64 |         ;;
 65 |         
 66 |     "restart")
 67 |         echo "🔄 Restarting Docker Web Desktop..."
 68 |         docker-compose down
 69 |         docker-compose up -d --build
 70 |         echo "✅ Application restarted successfully!"
 71 |         ;;
 72 |         
 73 |     "logs")
 74 |         echo "📋 Showing application logs..."
 75 |         docker-compose logs -f
 76 |         ;;
 77 |         
 78 |     "clean")
 79 |         echo "🧹 Cleaning up Docker Web Desktop..."
 80 |         docker-compose down -v --rmi all --remove-orphans
 81 |         docker-compose -f docker-compose.dev.yml down -v --rmi all --remove-orphans 2>/dev/null || true
 82 |         echo "✅ Cleanup completed!"
 83 |         ;;
 84 |         
 85 |     "build")
 86 |         echo "🔨 Building Docker images..."
 87 |         docker-compose build --no-cache
 88 |         echo "✅ Images built successfully!"
 89 |         ;;
 90 |         
 91 |     "status")
 92 |         echo "📊 Container status:"
 93 |         docker-compose ps
 94 |         echo ""
 95 |         echo "📊 Development container status:"
 96 |         docker-compose -f docker-compose.dev.yml ps 2>/dev/null || echo "No development containers running"
 97 |         ;;
 98 |         
 99 |     "help"|"-h"|"--help")
100 |         show_usage
101 |         ;;
102 |         
103 |     *)
104 |         echo "❌ Unknown command: $COMMAND"
105 |         echo ""
106 |         show_usage
107 |         exit 1
108 |         ;;
109 | esac


--------------------------------------------------------------------------------
/SOURCE_SELECTION_COMPLETE.md:
--------------------------------------------------------------------------------
 1 | # Docker Source Selection - Complete Implementation
 2 | 
 3 | All Docker resources (containers, images, volumes, networks, and services) now support switching between Local and WSL2 Docker daemons.
 4 | 
 5 | ## Backend Changes
 6 | 
 7 | ### Updated Routes
 8 | All routes now accept a `source` query parameter (default: 'local'):
 9 | 
10 | 1. **images.js** - GET /api/images?source=local|wsl2
11 |    - Uses `DockerService.forSource(source)` to get the appropriate Docker instance
12 |    
13 | 2. **volumes.js** - GET /api/volumes?source=local|wsl2
14 |    - Uses `DockerService.forSource(source)` to get the appropriate Docker instance
15 |    
16 | 3. **networks.js** - GET /api/networks?source=local|wsl2
17 |    - Uses `DockerService.forSource(source)` to get the appropriate Docker instance
18 |    
19 | 4. **services.js** - GET /api/services?source=local|wsl2
20 |    - Uses `DockerService.forSource(source)` to access Docker Swarm services
21 |    - GET /api/services/:id?source=local|wsl2 for specific service details
22 | 
23 | 5. **containers.js** (already updated) - GET /api/containers?source=local|wsl2
24 |    
25 | 6. **commands.js** (already updated) - POST /api/commands/execute with source parameter
26 |    - Wraps commands with `wsl.exe -d Ubuntu --` when source=wsl2
27 | 
28 | ## Frontend Changes
29 | 
30 | ### API Service (api.ts)
31 | Updated all resource fetching methods to accept source parameter:
32 | - `getImages(source: string = 'local')`
33 | - `getVolumes(source: string = 'local')`
34 | - `getNetworks(source: string = 'local')`
35 | - `getContainers(all, source: string = 'local')` (already updated)
36 | - `executeCommand(command, source: string = 'local')` (already updated)
37 | - `openCommand(command, source: string = 'local')` (already updated)
38 | 
39 | ### Store (docker.ts)
40 | Updated all fetch actions to use the selected source:
41 | - `fetchImages(source?: string)` - Uses `this.containerSource` by default
42 | - `fetchVolumes(source?: string)` - Uses `this.containerSource` by default
43 | - `fetchNetworks(source?: string)` - Uses `this.containerSource` by default
44 | - `fetchContainers(source?: string)` (already updated)
45 | 
46 | The `containerSource` state is:
47 | - Initialized from localStorage on app startup
48 | - Updated when user changes the dropdown in Navbar
49 | - Automatically saved to localStorage for persistence
50 | 
51 | ### Navbar (Navbar.vue) (already updated)
52 | - Dropdown selector for Local/WSL2
53 | - Saves selection to localStorage
54 | - Updates the store's `containerSource`
55 | - Triggers refresh of all Docker resources
56 | 
57 | ## How It Works
58 | 
59 | 1. **User selects source** in Navbar dropdown (Local or WSL2)
60 | 2. **Selection is saved** to localStorage for persistence
61 | 3. **Store is updated** with new source via `setContainerSource()`
62 | 4. **All resources refresh** using the new source
63 | 5. **Backend routes** receive source parameter and use `DockerService.forSource(source)` to connect to the correct Docker daemon
64 | 6. **Commands execute** in the appropriate environment (Windows cmd for local, WSL2 bash for wsl2)
65 | 
66 | ## Testing
67 | 
68 | To verify the implementation:
69 | 
70 | 1. Switch between Local and WSL2 in the navbar dropdown
71 | 2. Navigate to different views (Containers, Images, Volumes, Networks)
72 | 3. Verify that resources from the selected source are displayed
73 | 4. Try executing commands - they should run in the correct environment
74 | 5. Refresh the page - selection should persist
75 | 
76 | ## Architecture Notes
77 | 
78 | - **Backend**: `DockerService.forSource(source)` factory method creates appropriate Docker client
79 | - **Frontend**: Single source of truth in store's `containerSource` state
80 | - **Persistence**: localStorage ensures selection survives page refreshes
81 | - **Commands**: WSL2 commands wrapped with `wsl.exe -d Ubuntu --` prefix
82 | 


--------------------------------------------------------------------------------
/frontend/COMPOSE_INSPECT_FIXES.md:
--------------------------------------------------------------------------------
  1 | # View Compose & Inspect Tab Dark Mode Fixes
  2 | 
  3 | ## 🐛 Issues Fixed
  4 | 
  5 | ### 1. View Compose Modal Dark Mode Issues
  6 | - **Problem**: Hardcoded light theme colors (`white`, `#f8f9fa`, `#333`) that didn't adapt to dark theme
  7 | - **Solution**: Converted inline styles to CSS classes with proper dark theme support
  8 | 
  9 | ### 2. Inspect Tab Dark Mode Issues
 10 | - **Problem**: `.inspect-container` had hardcoded light background (`#f8f9fa`) 
 11 | - **Solution**: Added dark theme override with black background and white text
 12 | 
 13 | ## 🎨 CSS Fixes Applied
 14 | 
 15 | ### Compose Modal Dark Theme
 16 | ```css
 17 | /* Modal background and text */
 18 | [data-bs-theme="dark"] .compose-modal-content {
 19 |   background-color: var(--bs-secondary-bg) !important;
 20 |   color: var(--bs-body-color) !important;
 21 | }
 22 | 
 23 | /* Code content - black background with white text */
 24 | [data-bs-theme="dark"] .compose-code-content {
 25 |   background-color: #000000 !important;
 26 |   color: #ffffff !important;
 27 |   border: 1px solid var(--bs-border-color) !important;
 28 | }
 29 | 
 30 | /* Headers and footers with proper borders */
 31 | [data-bs-theme="dark"] .compose-modal-header {
 32 |   border-bottom: 1px solid var(--bs-border-color) !important;
 33 | }
 34 | 
 35 | [data-bs-theme="dark"] .compose-modal-footer {
 36 |   border-top: 1px solid var(--bs-border-color) !important;
 37 | }
 38 | ```
 39 | 
 40 | ### Inspect Container Dark Theme
 41 | ```css
 42 | [data-bs-theme="dark"] .inspect-container {
 43 |   background-color: #000000 !important;
 44 |   color: #ffffff !important;
 45 |   border-color: var(--bs-border-color) !important;
 46 | }
 47 | ```
 48 | 
 49 | ## 🔧 JavaScript Changes
 50 | 
 51 | ### Compose Modal Function Updates
 52 | - **Replaced**: Hardcoded inline styles with CSS classes
 53 | - **Added**: Theme-adaptive hover states using CSS classes
 54 | - **Improved**: Modal structure for better theme compatibility
 55 | 
 56 | ```javascript
 57 | // Before (hardcoded colors)
 58 | modalContent.style.cssText = `background: white !important; ...`
 59 | 
 60 | // After (CSS classes)
 61 | modalContent.className = 'compose-modal-content'
 62 | ```
 63 | 
 64 | ## ✅ Results
 65 | 
 66 | ### 🌙 **Dark Theme Support**
 67 | 1. **Compose Modal**: 
 68 |    - Dark gray modal background (`var(--bs-secondary-bg)`)
 69 |    - Black code background with white text (`#000000`/`#ffffff`)
 70 |    - Proper borders and hover states
 71 |    
 72 | 2. **Inspect Tab**:
 73 |    - Black background for JSON inspection data
 74 |    - White text for maximum readability
 75 |    - Consistent with other console elements
 76 | 
 77 | ### 🌕 **Light Theme Compatibility**
 78 | - Maintained original light theme appearance
 79 | - Smooth transitions between themes
 80 | - No visual regressions
 81 | 
 82 | ## 🧪 Components Affected
 83 | 
 84 | - **ContainersView.vue**: `showComposeModal` function updated
 85 | - **ContainerDetailsView.vue**: `.inspect-container` styling fixed
 86 | - **dark-theme.css**: Comprehensive modal and inspect styling added
 87 | 
 88 | ## 🎯 Technical Implementation
 89 | 
 90 | ### Modal Structure Enhancement
 91 | ```html
 92 | 
 93 | 
94 | 95 | 96 |
97 | ``` 98 | 99 | ### Hover State Management 100 | ```javascript 101 | // CSS class-based hover states for theme compatibility 102 | closeBtn.addEventListener('mouseenter', () => { 103 | closeBtn.classList.add('compose-close-hover') 104 | }) 105 | ``` 106 | 107 | The View Compose and Inspect functionality now provides excellent dark mode experience with: 108 | - **Perfect readability**: Black backgrounds with white text for all code 109 | - **Consistent theming**: Matches overall application dark theme 110 | - **Smooth interactions**: Proper hover states and transitions 111 | - **Accessibility**: High contrast maintained for all elements 112 | 113 | Both features are now fully dark-mode compatible! 🌙✨ -------------------------------------------------------------------------------- /frontend/src/components/docker/GenericModal.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 132 | 133 | -------------------------------------------------------------------------------- /frontend/THEME_TOGGLE.md: -------------------------------------------------------------------------------- 1 | # Theme Toggle Implementation 2 | 3 | ## 🎨 Overview 4 | 5 | This implementation adds Bootstrap's built-in theme toggle functionality with minimal code changes. Users can switch between light and dark modes, and their preference is saved and restored automatically. 6 | 7 | ## ✨ Features 8 | 9 | - **Bootstrap Native**: Uses Bootstrap's built-in `data-bs-theme` attribute 10 | - **Persistent**: Theme choice saved in `localStorage` under key `theme` 11 | - **System Aware**: Respects user's system preference (`prefers-color-scheme`) on first visit 12 | - **Minimal Code**: Only a few lines added to existing files 13 | - **Accessible**: Proper icons and labels for different themes 14 | 15 | ## 🔧 Implementation Details 16 | 17 | ### Files Modified 18 | 19 | 1. **`src/main.ts`** - Theme initialization on page load 20 | 2. **`src/components/Navbar.vue`** - Theme toggle button and logic 21 | 22 | ### Code Changes 23 | 24 | #### main.ts 25 | ```typescript 26 | // Initialize theme on page load 27 | const initTheme = () => { 28 | const savedTheme = localStorage.getItem('theme') 29 | const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches 30 | const theme = savedTheme || (systemPrefersDark ? 'dark' : 'light') 31 | document.documentElement.setAttribute('data-bs-theme', theme) 32 | } 33 | 34 | initTheme() 35 | ``` 36 | 37 | #### Navbar.vue 38 | - Added theme toggle button in the gear dropdown menu 39 | - Added reactive theme state tracking 40 | - Added theme toggle functionality with localStorage persistence 41 | 42 | ## 🎯 How It Works 43 | 44 | ### Initialization 45 | 1. On page load, `main.ts` checks for saved theme in localStorage 46 | 2. If no saved theme, uses system preference (`prefers-color-scheme`) 47 | 3. Applies theme by setting `data-bs-theme` attribute on `` element 48 | 49 | ### Theme Toggle 50 | 1. User clicks theme toggle button in gear menu 51 | 2. Theme switches between 'light' and 'dark' 52 | 3. New theme is saved to localStorage 53 | 4. Bootstrap automatically applies theme styles 54 | 55 | ### UI Updates 56 | - **Light Mode**: Shows moon icon + "Dark Mode" text 57 | - **Dark Mode**: Shows sun icon + "Light Mode" text 58 | - Button is located in the gear dropdown menu in the navbar 59 | 60 | ## 🎨 Bootstrap Theme Support 61 | 62 | Bootstrap automatically applies themes based on the `data-bs-theme` attribute: 63 | 64 | ```html 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | 72 | This affects: 73 | - Background colors 74 | - Text colors 75 | - Border colors 76 | - Component variants 77 | - And all other Bootstrap theme-aware styles 78 | 79 | ## 🔍 Usage 80 | 81 | ### For Users 82 | 1. Click the gear icon in the top navbar 83 | 2. Click "Dark Mode" or "Light Mode" to toggle 84 | 3. Theme preference is automatically saved 85 | 86 | ### For Developers 87 | The theme system is fully automatic. No additional code needed in components - they inherit Bootstrap's theme styling automatically. 88 | 89 | ## 📱 Responsive Design 90 | 91 | The theme toggle works on all screen sizes: 92 | - Desktop: Full dropdown menu with icons and text 93 | - Mobile: Collapsed menu that expands when needed 94 | - Theme applies consistently across all viewport sizes 95 | 96 | ## 🚀 Future Enhancements 97 | 98 | If needed, you could extend this system to: 99 | - Add more theme variants (e.g., high contrast) 100 | - Add animation transitions between themes 101 | - Sync theme across multiple browser tabs 102 | - Add theme-specific custom CSS properties 103 | 104 | ## 🔧 Troubleshooting 105 | 106 | ### Theme not applying 107 | - Check that `data-bs-theme` attribute is set on `` element 108 | - Ensure localStorage has read/write permissions 109 | - Verify Bootstrap CSS is loaded 110 | 111 | ### Toggle not working 112 | - Check browser console for JavaScript errors 113 | - Ensure dropdown menu is properly functioning 114 | - Verify theme state is being updated in component 115 | 116 | ## 🎉 Complete! 117 | 118 | The theme system is now fully functional with minimal code changes. Users can toggle between light and dark modes, and their preference will be remembered across sessions! -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development Guide 2 | 3 | ## Quick Start 4 | 5 | 1. **Backend Only**: 6 | ```bash 7 | cd backend 8 | npm start 9 | # or for development with auto-restart: 10 | npm run dev 11 | ``` 12 | 13 | 2. **Frontend Only**: 14 | ```bash 15 | cd frontend 16 | npm run dev 17 | ``` 18 | 19 | 3. **Both (Windows)**: 20 | ```cmd 21 | start.bat 22 | ``` 23 | 24 | 4. **Both (Linux/Mac)**: 25 | ```bash 26 | chmod +x start.sh 27 | ./start.sh 28 | ``` 29 | 30 | ## Testing API Endpoints 31 | 32 | ### Health Check 33 | ```bash 34 | curl http://localhost:3000/api/health 35 | ``` 36 | 37 | ### Containers 38 | ```bash 39 | # List all containers 40 | curl http://localhost:3000/api/containers 41 | 42 | # Get specific container 43 | curl http://localhost:3000/api/containers/{container_id} 44 | 45 | # Start container 46 | curl -X POST http://localhost:3000/api/containers/{container_id}/start 47 | 48 | # Stop container 49 | curl -X POST http://localhost:3000/api/containers/{container_id}/stop 50 | 51 | # Remove container 52 | curl -X DELETE http://localhost:3000/api/containers/{container_id} 53 | 54 | # Get container logs 55 | curl http://localhost:3000/api/containers/{container_id}/logs?tail=50 56 | ``` 57 | 58 | ### Images 59 | ```bash 60 | # List all images 61 | curl http://localhost:3000/api/images 62 | 63 | # Remove image 64 | curl -X DELETE http://localhost:3000/api/images/{image_id} 65 | ``` 66 | 67 | ### Volumes 68 | ```bash 69 | # List all volumes 70 | curl http://localhost:3000/api/volumes 71 | 72 | # Remove volume 73 | curl -X DELETE http://localhost:3000/api/volumes/{volume_name} 74 | ``` 75 | 76 | ### Networks 77 | ```bash 78 | # List all networks 79 | curl http://localhost:3000/api/networks 80 | 81 | # Remove network 82 | curl -X DELETE http://localhost:3000/api/networks/{network_id} 83 | ``` 84 | 85 | ## Development Tips 86 | 87 | ### Backend Development 88 | - Use `npm run dev` for auto-restart on file changes 89 | - Check logs for Docker API connection issues 90 | - Ensure Docker Desktop is running before starting the backend 91 | 92 | ### Frontend Development 93 | - Hot reload is enabled by default with Vite 94 | - Check browser console for any JavaScript errors 95 | - API calls to backend should work on `http://localhost:3000` 96 | 97 | ### Real-time Features 98 | - WebSocket connection is established automatically 99 | - Real-time updates work for container state changes, removals, etc. 100 | - Check browser Developer Tools → Network tab for WebSocket connection 101 | 102 | ## Common Issues 103 | 104 | ### Backend Won't Start 105 | - **Port 3000 in use**: Kill process on port 3000 or change PORT in .env 106 | - **Docker connection error**: Ensure Docker Desktop is running 107 | - **Module not found**: Run `npm install` in backend directory 108 | 109 | ### Frontend Won't Start 110 | - **Dependencies missing**: Run `npm install` in frontend directory 111 | - **Port conflict**: Vite will automatically use next available port 112 | - **API connection issues**: Ensure backend is running on port 3000 113 | 114 | ### CORS Errors 115 | - Backend CORS is configured for `http://localhost:8080` and common Vite ports 116 | - If using different port, update CORS configuration in `backend/src/server.js` 117 | 118 | ## Production Build 119 | 120 | ### Backend 121 | ```bash 122 | cd backend 123 | npm start 124 | ``` 125 | 126 | ### Frontend 127 | ```bash 128 | cd frontend 129 | npm run build 130 | npm run preview 131 | ``` 132 | 133 | ## Project Structure 134 | 135 | ``` 136 | docker-web-desktop/ 137 | ├── backend/ # Node.js Express API 138 | │ ├── src/ 139 | │ │ ├── routes/ # API endpoint definitions 140 | │ │ ├── services/ # Docker integration logic 141 | │ │ └── server.js # Main server file 142 | │ ├── package.json 143 | │ └── .env # Environment variables 144 | ├── frontend/ # Vue.js SPA 145 | │ ├── src/ 146 | │ │ ├── components/ # Reusable Vue components 147 | │ │ ├── views/ # Page components 148 | │ │ ├── stores/ # Pinia state management 149 | │ │ ├── services/ # API communication 150 | │ │ └── router/ # Vue Router setup 151 | │ └── package.json 152 | ├── start.bat # Windows start script 153 | ├── start.sh # Linux/Mac start script 154 | └── README.md # Main documentation 155 | ``` -------------------------------------------------------------------------------- /.gitignore.template: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop - .gitignore Template 2 | # 3 | # This file serves as a reference for what should typically be ignored 4 | # Copy relevant sections to your local .gitignore as needed 5 | 6 | # ============================================================================= 7 | # ENVIRONMENT & CONFIGURATION 8 | # ============================================================================= 9 | 10 | # Environment variables 11 | .env 12 | .env.local 13 | .env.development 14 | .env.production 15 | .env.test 16 | .env.*.local 17 | 18 | # Configuration files with sensitive data 19 | config/local.json 20 | config/secrets.json 21 | config/database.json 22 | 23 | # ============================================================================= 24 | # DEVELOPMENT TOOLS 25 | # ============================================================================= 26 | 27 | # Node.js 28 | node_modules/ 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | package-lock.json # Uncomment if using yarn 33 | yarn.lock # Uncomment if using npm 34 | 35 | # Build outputs 36 | dist/ 37 | build/ 38 | coverage/ 39 | 40 | # Cache directories 41 | .cache/ 42 | .npm/ 43 | .yarn/ 44 | 45 | # ============================================================================= 46 | # EDITORS & IDEs 47 | # ============================================================================= 48 | 49 | # VS Code 50 | .vscode/ 51 | !.vscode/extensions.json 52 | !.vscode/settings.json 53 | 54 | # JetBrains IDEs 55 | .idea/ 56 | 57 | # Sublime Text 58 | *.sublime-* 59 | 60 | # Vim 61 | *.swp 62 | *.swo 63 | *~ 64 | 65 | # ============================================================================= 66 | # OPERATING SYSTEMS 67 | # ============================================================================= 68 | 69 | # macOS 70 | .DS_Store 71 | .DS_Store? 72 | ._* 73 | .Spotlight-V100 74 | .Trashes 75 | 76 | # Windows 77 | Thumbs.db 78 | ehthumbs.db 79 | Desktop.ini 80 | 81 | # Linux 82 | *~ 83 | 84 | # ============================================================================= 85 | # DOCKER & DEPLOYMENT 86 | # ============================================================================= 87 | 88 | # Docker 89 | docker-compose.override.yml 90 | Dockerfile.local 91 | .dockerignore.local 92 | 93 | # Deployment 94 | deploy-config/ 95 | *.pem 96 | *.key 97 | *.crt 98 | 99 | # ============================================================================= 100 | # DATABASES & STORAGE 101 | # ============================================================================= 102 | 103 | # SQLite 104 | *.sqlite 105 | *.sqlite3 106 | *.db 107 | 108 | # Uploads 109 | uploads/ 110 | storage/ 111 | public/uploads/ 112 | 113 | # ============================================================================= 114 | # LOGS & MONITORING 115 | # ============================================================================= 116 | 117 | # Log files 118 | logs/ 119 | *.log 120 | access.log 121 | error.log 122 | 123 | # PM2 124 | .pm2/ 125 | 126 | # ============================================================================= 127 | # TESTING & QUALITY ASSURANCE 128 | # ============================================================================= 129 | 130 | # Coverage reports 131 | coverage/ 132 | .nyc_output/ 133 | 134 | # Test results 135 | test-results/ 136 | cypress/videos/ 137 | cypress/screenshots/ 138 | 139 | # ============================================================================= 140 | # SECURITY 141 | # ============================================================================= 142 | 143 | # SSL certificates 144 | *.pem 145 | *.key 146 | *.crt 147 | *.csr 148 | ssl/ 149 | 150 | # JWT secrets 151 | jwt-secret.txt 152 | 153 | # API keys 154 | api-keys.json 155 | 156 | # ============================================================================= 157 | # BACKUP & TEMPORARY FILES 158 | # ============================================================================= 159 | 160 | # Backup files 161 | *.bak 162 | *.backup 163 | *.old 164 | *.orig 165 | 166 | # Temporary files 167 | *.tmp 168 | *.temp 169 | tmp/ 170 | temp/ 171 | 172 | # ============================================================================= 173 | # COMMON IGNORE PATTERNS BY TECHNOLOGY 174 | # ============================================================================= 175 | 176 | # Vue.js specific 177 | .vite/ 178 | dist-ssr/ 179 | 180 | # React specific 181 | .next/ 182 | out/ 183 | 184 | # Angular specific 185 | .angular/ 186 | 187 | # Svelte specific 188 | .svelte-kit/ 189 | 190 | # Express.js specific 191 | sessions/ -------------------------------------------------------------------------------- /frontend/src/components/docker/ContainerRow.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 144 | 145 | -------------------------------------------------------------------------------- /frontend/src/components/docker/ComposeModal.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /frontend/ENVIRONMENT_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Environment Variables Configuration Guide 2 | 3 | ## 📋 Overview 4 | 5 | This Vue.js application supports three different environment configurations: 6 | 7 | - **Development** (`.env`) - Local development 8 | - **Production** (`.env.production`) - Production deployment 9 | - **Docker** (`.env.docker`) - Docker container deployment 10 | 11 | ## 🔧 Environment Files 12 | 13 | ### `.env` (Development) 14 | ```env 15 | VITE_API_BASE_URL=http://localhost:3000 16 | VITE_APP_TITLE=Docker Web Desktop 17 | VITE_APP_VERSION=1.0.0 18 | VITE_DEBUG=true 19 | VITE_SOCKET_URL=http://localhost:3000 20 | VITE_LOG_LEVEL=debug 21 | ``` 22 | 23 | ### `.env.production` (Production) 24 | ```env 25 | VITE_API_BASE_URL=https://your-production-api.com 26 | VITE_APP_TITLE=Docker Web Desktop 27 | VITE_APP_VERSION=1.0.0 28 | VITE_DEBUG=false 29 | VITE_SOCKET_URL=https://your-production-api.com 30 | VITE_LOG_LEVEL=error 31 | ``` 32 | 33 | ### `.env.docker` (Docker) 34 | ```env 35 | VITE_API_BASE_URL=http://backend:3000 36 | VITE_APP_TITLE=Docker Web Desktop 37 | VITE_APP_VERSION=1.0.0 38 | VITE_DEBUG=false 39 | VITE_SOCKET_URL=http://backend:3000 40 | VITE_LOG_LEVEL=warn 41 | ``` 42 | 43 | ## 🚀 Usage Commands 44 | 45 | ### Development 46 | ```bash 47 | npm run dev # Uses .env 48 | npm run build # Uses .env for development build 49 | ``` 50 | 51 | ### Production 52 | ```bash 53 | npm run build:production # Uses .env.production 54 | npm run preview:production # Preview production build 55 | ``` 56 | 57 | ### Docker 58 | ```bash 59 | npm run dev:docker # Uses .env.docker for development 60 | npm run build:docker # Uses .env.docker for build 61 | npm run preview:docker # Preview docker build 62 | ``` 63 | 64 | ## 📝 Available Environment Variables 65 | 66 | | Variable | Description | Default | 67 | |----------|-------------|---------| 68 | | `VITE_API_BASE_URL` | Backend API base URL | `http://localhost:3000` | 69 | | `VITE_APP_TITLE` | Application title | `Docker Web Desktop` | 70 | | `VITE_APP_VERSION` | Application version | `1.0.0` | 71 | | `VITE_DEBUG` | Enable debug logging | `false` | 72 | | `VITE_SOCKET_URL` | Socket.IO server URL | Same as API URL | 73 | | `VITE_LOG_LEVEL` | Logging level | `info` | 74 | 75 | ## 💻 TypeScript Support 76 | 77 | Environment variables are fully typed in `src/env.d.ts`: 78 | 79 | ```typescript 80 | interface ImportMetaEnv { 81 | readonly VITE_API_BASE_URL: string 82 | readonly VITE_APP_TITLE: string 83 | readonly VITE_APP_VERSION: string 84 | readonly VITE_DEBUG: string 85 | readonly VITE_SOCKET_URL: string 86 | readonly VITE_LOG_LEVEL: string 87 | } 88 | ``` 89 | 90 | ## 🛠️ Usage in Code 91 | 92 | ### Direct Access 93 | ```typescript 94 | const apiUrl = import.meta.env.VITE_API_BASE_URL 95 | const debug = import.meta.env.VITE_DEBUG === 'true' 96 | ``` 97 | 98 | ### Using Environment Utility 99 | ```typescript 100 | import Environment from '@/utils/environment' 101 | 102 | // Get configuration 103 | const config = Environment.config 104 | 105 | // Logging with environment-aware levels 106 | Environment.debug('Debug message') 107 | Environment.info('Info message') 108 | Environment.warn('Warning message') 109 | Environment.error('Error message') 110 | 111 | // Environment checks 112 | if (Environment.isDevelopment) { 113 | // Development-only code 114 | } 115 | 116 | if (Environment.isProduction) { 117 | // Production-only code 118 | } 119 | ``` 120 | 121 | ## 🐳 Docker Configuration 122 | 123 | When building for Docker, the application expects the backend to be accessible at `http://backend:3000`. Make sure your Docker Compose or container setup uses the correct service names. 124 | 125 | Example `docker-compose.yml`: 126 | ```yaml 127 | services: 128 | frontend: 129 | build: . 130 | ports: 131 | - "80:80" 132 | depends_on: 133 | - backend 134 | 135 | backend: 136 | # Your backend configuration 137 | ports: 138 | - "3000:3000" 139 | ``` 140 | 141 | ## 🔍 Environment Detection 142 | 143 | The application can detect its environment: 144 | 145 | ```typescript 146 | import Environment from '@/utils/environment' 147 | 148 | console.log('Current mode:', Environment.mode) 149 | console.log('Is development:', Environment.isDevelopment) 150 | console.log('Is production:', Environment.isProduction) 151 | ``` 152 | 153 | ## 🎯 Best Practices 154 | 155 | 1. **Never commit sensitive data** to environment files 156 | 2. **Use different API URLs** for different environments 157 | 3. **Enable debug only in development** 158 | 4. **Set appropriate log levels** for each environment 159 | 5. **Use TypeScript** for environment variable type safety 160 | 161 | ## 🔒 Security Notes 162 | 163 | - Environment variables starting with `VITE_` are exposed to the client 164 | - Never put secrets or sensitive data in these files 165 | - Use server-side environment variables for sensitive configuration 166 | 167 | ## 🆘 Troubleshooting 168 | 169 | ### Issue: Environment variables not loading 170 | **Solutions:** 171 | 1. Ensure variable names start with `VITE_` 172 | 2. Restart the dev server after changes 173 | 3. Check file naming (`.env.production`, not `.env-production`) 174 | 175 | ### Issue: Wrong environment being used 176 | **Solutions:** 177 | 1. Use the correct npm script for your target environment 178 | 2. Check that the `.env` file exists for the target mode 179 | 3. Verify the `--mode` flag in package.json scripts -------------------------------------------------------------------------------- /frontend/src/components/docker/ContainerGroup.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 129 | 130 | -------------------------------------------------------------------------------- /frontend/src/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 123 | 124 | 129 | 130 | -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerCommandCard.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 131 | 132 | -------------------------------------------------------------------------------- /public/install-wsl2.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM WSL2 Installation Script for Windows (Batch Version) 3 | REM This script automates the installation of WSL2 on Windows 10/11 4 | REM Must be run as Administrator 5 | 6 | setlocal enabledelayedexpansion 7 | 8 | REM Check if running as Administrator 9 | net session >nul 2>&1 10 | if %errorLevel% neq 0 ( 11 | echo. 12 | echo ====================================== 13 | echo ERROR: Administrator privileges required! 14 | echo ====================================== 15 | echo. 16 | echo This script must be run as Administrator. 17 | echo Right-click this file and select "Run as administrator" 18 | echo. 19 | pause 20 | exit /b 1 21 | ) 22 | 23 | echo ====================================== 24 | echo WSL2 Installation Script 25 | echo ====================================== 26 | echo. 27 | 28 | REM Check Windows version 29 | echo [1/6] Checking Windows version... 30 | for /f "tokens=3" %%a in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v CurrentBuild ^| findstr CurrentBuild') do set BUILD=%%a 31 | 32 | if %BUILD% LSS 18362 ( 33 | echo ERROR: WSL2 requires Windows 10 version 1903 or higher ^(Build 18362+^) 34 | echo Your version: Build %BUILD% 35 | echo. 36 | pause 37 | exit /b 1 38 | ) 39 | 40 | echo [OK] Windows version is compatible (Build %BUILD%) 41 | echo. 42 | 43 | REM Check if WSL is already installed 44 | echo [2/6] Checking current WSL installation... 45 | where wsl >nul 2>&1 46 | if %errorLevel% equ 0 ( 47 | echo WSL is already installed. Checking status... 48 | wsl --status 2>nul 49 | echo. 50 | echo This script will ensure WSL2 is properly configured. 51 | ) else ( 52 | echo WSL not detected. Will perform fresh installation. 53 | ) 54 | echo. 55 | 56 | REM Enable WSL and Virtual Machine Platform features 57 | echo [3/6] Enabling WSL and Virtual Machine Platform features... 58 | echo This may take a few minutes... 59 | echo. 60 | 61 | echo Enabling Windows Subsystem for Linux... 62 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 63 | if %errorLevel% neq 0 ( 64 | echo ERROR: Failed to enable WSL feature 65 | pause 66 | exit /b 1 67 | ) 68 | 69 | echo Enabling Virtual Machine Platform... 70 | dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 71 | if %errorLevel% neq 0 ( 72 | echo ERROR: Failed to enable Virtual Machine Platform 73 | pause 74 | exit /b 1 75 | ) 76 | 77 | echo [OK] Features enabled successfully 78 | echo. 79 | 80 | REM Download and install WSL2 kernel update 81 | echo [4/6] Downloading WSL2 Linux kernel update... 82 | set KERNEL_URL=https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi 83 | set KERNEL_FILE=%TEMP%\wsl_update_x64.msi 84 | 85 | echo Downloading from: %KERNEL_URL% 86 | powershell -Command "& {Invoke-WebRequest -Uri '%KERNEL_URL%' -OutFile '%KERNEL_FILE%' -UseBasicParsing}" >nul 2>&1 87 | 88 | if %errorLevel% equ 0 ( 89 | echo [OK] Download completed 90 | echo. 91 | echo Installing WSL2 kernel update... 92 | msiexec /i "%KERNEL_FILE%" /quiet /norestart 93 | if %errorLevel% equ 0 ( 94 | echo [OK] WSL2 kernel installed 95 | ) else ( 96 | echo WARNING: Kernel installation may have failed 97 | echo You can download it manually from: https://aka.ms/wsl2kernel 98 | ) 99 | del "%KERNEL_FILE%" >nul 2>&1 100 | ) else ( 101 | echo WARNING: Kernel download failed 102 | echo You can download it manually from: https://aka.ms/wsl2kernel 103 | ) 104 | echo. 105 | 106 | REM Set WSL2 as default version 107 | echo [5/6] Setting WSL2 as default version... 108 | wsl --set-default-version 2 >nul 2>&1 109 | if %errorLevel% equ 0 ( 110 | echo [OK] WSL2 set as default version 111 | ) else ( 112 | echo WARNING: Could not set WSL2 as default 113 | echo You may need to run: wsl --set-default-version 2 114 | ) 115 | echo. 116 | 117 | REM Install Ubuntu distribution 118 | echo [6/6] Installing Ubuntu Linux distribution... 119 | echo This will download and install Ubuntu 22.04 LTS... 120 | echo. 121 | 122 | REM Check if Ubuntu is already installed 123 | wsl --list --quiet 2>&1 | findstr /i "Ubuntu" >nul 124 | if %errorLevel% equ 0 ( 125 | echo Ubuntu is already installed: 126 | wsl --list --verbose 127 | echo. 128 | echo Skipping Ubuntu installation. 129 | ) else ( 130 | echo Installing Ubuntu 22.04... 131 | wsl --install -d Ubuntu-22.04 132 | if %errorLevel% equ 0 ( 133 | echo [OK] Ubuntu installation initiated 134 | ) else ( 135 | echo WARNING: Ubuntu installation may require manual setup 136 | echo You can install Ubuntu from Microsoft Store or run: wsl --install -d Ubuntu-22.04 137 | ) 138 | ) 139 | echo. 140 | 141 | REM Completion message 142 | echo ====================================== 143 | echo Installation Completed! 144 | echo ====================================== 145 | echo. 146 | echo IMPORTANT: A system restart is required to complete the installation. 147 | echo. 148 | echo Next Steps: 149 | echo 1. Restart your computer 150 | echo 2. After restart, launch Ubuntu from the Start Menu 151 | echo 3. Complete the Ubuntu setup (create username and password) 152 | echo 4. Verify installation by running: wsl --list --verbose 153 | echo 5. You can then proceed to install Docker Desktop or Docker Engine 154 | echo. 155 | 156 | REM Ask for restart 157 | set /p RESTART="Would you like to restart now? (Y/N): " 158 | if /i "%RESTART%"=="Y" ( 159 | echo Restarting in 5 seconds... 160 | timeout /t 5 /nobreak >nul 161 | shutdown /r /t 0 162 | ) else ( 163 | echo. 164 | echo Please remember to restart your computer manually to complete the installation. 165 | echo. 166 | pause 167 | ) 168 | -------------------------------------------------------------------------------- /frontend/public/install-wsl2.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM WSL2 Installation Script for Windows (Batch Version) 3 | REM This script automates the installation of WSL2 on Windows 10/11 4 | REM Must be run as Administrator 5 | 6 | setlocal enabledelayedexpansion 7 | 8 | REM Check if running as Administrator 9 | net session >nul 2>&1 10 | if %errorLevel% neq 0 ( 11 | echo. 12 | echo ====================================== 13 | echo ERROR: Administrator privileges required! 14 | echo ====================================== 15 | echo. 16 | echo This script must be run as Administrator. 17 | echo Right-click this file and select "Run as administrator" 18 | echo. 19 | pause 20 | exit /b 1 21 | ) 22 | 23 | echo ====================================== 24 | echo WSL2 Installation Script 25 | echo ====================================== 26 | echo. 27 | 28 | REM Check Windows version 29 | echo [1/6] Checking Windows version... 30 | for /f "tokens=3" %%a in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v CurrentBuild ^| findstr CurrentBuild') do set BUILD=%%a 31 | 32 | if %BUILD% LSS 18362 ( 33 | echo ERROR: WSL2 requires Windows 10 version 1903 or higher ^(Build 18362+^) 34 | echo Your version: Build %BUILD% 35 | echo. 36 | pause 37 | exit /b 1 38 | ) 39 | 40 | echo [OK] Windows version is compatible (Build %BUILD%) 41 | echo. 42 | 43 | REM Check if WSL is already installed 44 | echo [2/6] Checking current WSL installation... 45 | where wsl >nul 2>&1 46 | if %errorLevel% equ 0 ( 47 | echo WSL is already installed. Checking status... 48 | wsl --status 2>nul 49 | echo. 50 | echo This script will ensure WSL2 is properly configured. 51 | ) else ( 52 | echo WSL not detected. Will perform fresh installation. 53 | ) 54 | echo. 55 | 56 | REM Enable WSL and Virtual Machine Platform features 57 | echo [3/6] Enabling WSL and Virtual Machine Platform features... 58 | echo This may take a few minutes... 59 | echo. 60 | 61 | echo Enabling Windows Subsystem for Linux... 62 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 63 | if %errorLevel% neq 0 ( 64 | echo ERROR: Failed to enable WSL feature 65 | pause 66 | exit /b 1 67 | ) 68 | 69 | echo Enabling Virtual Machine Platform... 70 | dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 71 | if %errorLevel% neq 0 ( 72 | echo ERROR: Failed to enable Virtual Machine Platform 73 | pause 74 | exit /b 1 75 | ) 76 | 77 | echo [OK] Features enabled successfully 78 | echo. 79 | 80 | REM Download and install WSL2 kernel update 81 | echo [4/6] Downloading WSL2 Linux kernel update... 82 | set KERNEL_URL=https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi 83 | set KERNEL_FILE=%TEMP%\wsl_update_x64.msi 84 | 85 | echo Downloading from: %KERNEL_URL% 86 | powershell -Command "& {Invoke-WebRequest -Uri '%KERNEL_URL%' -OutFile '%KERNEL_FILE%' -UseBasicParsing}" >nul 2>&1 87 | 88 | if %errorLevel% equ 0 ( 89 | echo [OK] Download completed 90 | echo. 91 | echo Installing WSL2 kernel update... 92 | msiexec /i "%KERNEL_FILE%" /quiet /norestart 93 | if %errorLevel% equ 0 ( 94 | echo [OK] WSL2 kernel installed 95 | ) else ( 96 | echo WARNING: Kernel installation may have failed 97 | echo You can download it manually from: https://aka.ms/wsl2kernel 98 | ) 99 | del "%KERNEL_FILE%" >nul 2>&1 100 | ) else ( 101 | echo WARNING: Kernel download failed 102 | echo You can download it manually from: https://aka.ms/wsl2kernel 103 | ) 104 | echo. 105 | 106 | REM Set WSL2 as default version 107 | echo [5/6] Setting WSL2 as default version... 108 | wsl --set-default-version 2 >nul 2>&1 109 | if %errorLevel% equ 0 ( 110 | echo [OK] WSL2 set as default version 111 | ) else ( 112 | echo WARNING: Could not set WSL2 as default 113 | echo You may need to run: wsl --set-default-version 2 114 | ) 115 | echo. 116 | 117 | REM Install Ubuntu distribution 118 | echo [6/6] Installing Ubuntu Linux distribution... 119 | echo This will download and install Ubuntu 22.04 LTS... 120 | echo. 121 | 122 | REM Check if Ubuntu is already installed 123 | wsl --list --quiet 2>&1 | findstr /i "Ubuntu" >nul 124 | if %errorLevel% equ 0 ( 125 | echo Ubuntu is already installed: 126 | wsl --list --verbose 127 | echo. 128 | echo Skipping Ubuntu installation. 129 | ) else ( 130 | echo Installing Ubuntu 22.04... 131 | wsl --install -d Ubuntu-22.04 132 | if %errorLevel% equ 0 ( 133 | echo [OK] Ubuntu installation initiated 134 | ) else ( 135 | echo WARNING: Ubuntu installation may require manual setup 136 | echo You can install Ubuntu from Microsoft Store or run: wsl --install -d Ubuntu-22.04 137 | ) 138 | ) 139 | echo. 140 | 141 | REM Completion message 142 | echo ====================================== 143 | echo Installation Completed! 144 | echo ====================================== 145 | echo. 146 | echo IMPORTANT: A system restart is required to complete the installation. 147 | echo. 148 | echo Next Steps: 149 | echo 1. Restart your computer 150 | echo 2. After restart, launch Ubuntu from the Start Menu 151 | echo 3. Complete the Ubuntu setup (create username and password) 152 | echo 4. Verify installation by running: wsl --list --verbose 153 | echo 5. You can then proceed to install Docker Desktop or Docker Engine 154 | echo. 155 | 156 | REM Ask for restart 157 | set /p RESTART="Would you like to restart now? (Y/N): " 158 | if /i "%RESTART%"=="Y" ( 159 | echo Restarting in 5 seconds... 160 | timeout /t 5 /nobreak >nul 161 | shutdown /r /t 0 162 | ) else ( 163 | echo. 164 | echo Please remember to restart your computer manually to complete the installation. 165 | echo. 166 | pause 167 | ) 168 | -------------------------------------------------------------------------------- /frontend/COMPREHENSIVE_DARK_THEME.md: -------------------------------------------------------------------------------- 1 | # Comprehensive Dark Theme Implementation 2 | 3 | ## 🌙 Overview 4 | 5 | This implementation provides comprehensive dark theme support for the entire Docker Web Desktop application, extending beyond Bootstrap's basic dark mode to cover all custom components and styling. 6 | 7 | ## ✨ Features 8 | 9 | - **Full Application Coverage**: Dark theme applies to all components, not just Bootstrap defaults 10 | - **Gray-Scale Palette**: Uses proper gray shades for dark mode aesthetics 11 | - **High Contrast Buttons**: Enhanced button visibility and contrast in dark mode 12 | - **Smooth Transitions**: 0.3s smooth transitions between light and dark modes 13 | - **Custom Component Support**: Covers sidebar, tables, cards, modals, and custom styling 14 | - **CSS Custom Properties**: Uses CSS variables for maintainable theming 15 | 16 | ## 🎨 Color Scheme 17 | 18 | ### Light Theme Colors 19 | - **Body Background**: `#ffffff` (white) 20 | - **Secondary Background**: `#f8f9fa` (light gray) 21 | - **Tertiary Background**: `#e9ecef` (lighter gray) 22 | - **Text Color**: `#212529` (dark gray) 23 | - **Border Color**: `#dee2e6` (light border) 24 | 25 | ### Dark Theme Colors 26 | - **Body Background**: `#212529` (dark gray) 27 | - **Secondary Background**: `#343a40` (medium gray) 28 | - **Tertiary Background**: `#495057` (lighter gray) 29 | - **Text Color**: `#adb5bd` (light gray) 30 | - **Border Color**: `#495057` (gray border) 31 | 32 | ## 🛠️ Implementation Details 33 | 34 | ### Files Modified/Created 35 | 36 | 1. **`src/assets/dark-theme.css`** - Comprehensive dark theme CSS 37 | 2. **`src/main.ts`** - Import dark theme CSS 38 | 3. **`src/App.vue`** - Updated to use CSS custom properties 39 | 4. **`src/components/Sidebar.vue`** - Dark theme compatible styling 40 | 41 | ### CSS Custom Properties 42 | 43 | The implementation uses CSS custom properties (variables) for dynamic theming: 44 | 45 | ```css 46 | /* Light Theme */ 47 | :root, [data-bs-theme="light"] { 48 | --bs-body-bg: #ffffff; 49 | --bs-body-color: #212529; 50 | --bs-secondary-bg: #f8f9fa; 51 | --bs-tertiary-bg: #e9ecef; 52 | --bs-border-color: #dee2e6; 53 | } 54 | 55 | /* Dark Theme */ 56 | [data-bs-theme="dark"] { 57 | --bs-body-bg: #212529; 58 | --bs-body-color: #adb5bd; 59 | --bs-secondary-bg: #343a40; 60 | --bs-tertiary-bg: #495057; 61 | --bs-border-color: #495057; 62 | } 63 | ``` 64 | 65 | ## 🎯 Components Covered 66 | 67 | ### ✅ **Core Layout** 68 | - Body background and text colors 69 | - Main container areas 70 | - Sidebar with proper gray backgrounds 71 | - Navigation elements 72 | 73 | ### ✅ **Bootstrap Components** 74 | - **Cards**: Dark backgrounds with proper borders 75 | - **Tables**: Dark headers and hover states 76 | - **Buttons**: High contrast colors for all variants 77 | - **Badges**: Enhanced contrast and visibility 78 | - **Modals**: Dark backgrounds and borders 79 | - **Dropdowns**: Proper dark styling 80 | - **Forms**: Dark form controls and focus states 81 | - **Alerts**: Theme-appropriate alert colors 82 | 83 | ### ✅ **Interactive Elements** 84 | - **Hover States**: Proper gray hover backgrounds 85 | - **Active States**: Clear active state indicators 86 | - **Focus States**: Maintained accessibility focus rings 87 | - **Transitions**: Smooth 0.3s transitions for all elements 88 | 89 | ### ✅ **High Contrast Buttons** 90 | 91 | #### Primary Buttons (Dark Mode) 92 | - Background: `#0d6efd` (bright blue) 93 | - Hover: `#0b5ed7` (darker blue) 94 | - Text: `#ffffff` (white) 95 | 96 | #### Secondary Buttons (Dark Mode) 97 | - Background: `#6c757d` (gray) 98 | - Hover: `#5c636a` (darker gray) 99 | - Text: `#ffffff` (white) 100 | 101 | #### Success/Danger/Warning Buttons 102 | - Maintained high contrast colors 103 | - Enhanced visibility in dark environment 104 | - Proper hover and active states 105 | 106 | ### ✅ **Custom Scrollbars** 107 | - Dark theme compatible scrollbars 108 | - Subtle gray colors that match the theme 109 | - Proper hover states 110 | 111 | ## 🚀 Usage 112 | 113 | The dark theme automatically applies when the user toggles to dark mode via the theme toggle button. All components automatically inherit the dark theme styling. 114 | 115 | ### Automatic Application 116 | - Theme applies to all existing components without code changes 117 | - New components automatically inherit dark theme styles 118 | - CSS custom properties ensure consistency 119 | 120 | ### Manual Styling (if needed) 121 | ```css 122 | /* Use CSS custom properties in component styles */ 123 | .my-component { 124 | background-color: var(--bs-secondary-bg); 125 | color: var(--bs-body-color); 126 | border-color: var(--bs-border-color); 127 | } 128 | ``` 129 | 130 | ## 🎨 Visual Improvements 131 | 132 | ### Gray-Scale Focus 133 | - Consistent gray palette throughout the application 134 | - No harsh whites or blacks in dark mode 135 | - Proper contrast ratios for accessibility 136 | 137 | ### Smooth Transitions 138 | - All color changes animate smoothly (0.3s duration) 139 | - Reduces jarring transitions when switching themes 140 | - Professional polish to the user experience 141 | 142 | ### High Contrast Elements 143 | - Buttons maintain excellent visibility 144 | - Text remains highly readable 145 | - Interactive elements are clearly distinguishable 146 | 147 | ## 🔍 Testing Checklist 148 | 149 | ✅ **Layout Components** 150 | - Sidebar background and text colors 151 | - Main content area backgrounds 152 | - Navigation elements 153 | 154 | ✅ **Interactive Elements** 155 | - All button variants (primary, secondary, success, danger, warning, info) 156 | - Hover and active states 157 | - Form controls and inputs 158 | 159 | ✅ **Content Components** 160 | - Tables with headers and rows 161 | - Cards and containers 162 | - Modals and dropdowns 163 | - Alerts and badges 164 | 165 | ✅ **Transitions** 166 | - Smooth theme switching 167 | - No flash of unstyled content 168 | - Consistent animation timing 169 | 170 | ## 🚀 Benefits 171 | 172 | 1. **Complete Coverage**: Every part of the UI properly supports dark mode 173 | 2. **Consistent Experience**: Unified gray-scale palette throughout 174 | 3. **High Accessibility**: Maintained contrast ratios and focus indicators 175 | 4. **Professional Polish**: Smooth transitions and refined styling 176 | 5. **Maintainable**: CSS custom properties make future updates easy 177 | 6. **Performance**: Efficient CSS with minimal overhead 178 | 179 | ## 🎉 Result 180 | 181 | Users now have a comprehensive dark mode experience with: 182 | - **Full application dark theme coverage** 183 | - **Professional gray-scale color scheme** 184 | - **High contrast interactive elements** 185 | - **Smooth, polished transitions** 186 | - **Consistent Bootstrap integration** 187 | 188 | The dark theme is now production-ready and covers all aspects of the Docker Web Desktop interface! 🌙✨ -------------------------------------------------------------------------------- /frontend/src/components/docker/ContainerTable.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 166 | 167 | -------------------------------------------------------------------------------- /public/install-wsl2.ps1: -------------------------------------------------------------------------------- 1 | # WSL2 Installation Script for Windows 2 | # This script automates the installation of WSL2 on Windows 10/11 3 | # Must be run as Administrator 4 | 5 | # Color codes for output 6 | $Green = "Green" 7 | $Red = "Red" 8 | $Yellow = "Yellow" 9 | $Cyan = "Cyan" 10 | 11 | function Write-ColorOutput($ForegroundColor) { 12 | $fc = $host.UI.RawUI.ForegroundColor 13 | $host.UI.RawUI.ForegroundColor = $ForegroundColor 14 | if ($args) { 15 | Write-Output $args 16 | } 17 | $host.UI.RawUI.ForegroundColor = $fc 18 | } 19 | 20 | function Test-Administrator { 21 | $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) 22 | return $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) 23 | } 24 | 25 | # Check if running as Administrator 26 | if (-not (Test-Administrator)) { 27 | Write-ColorOutput $Red "ERROR: This script must be run as Administrator!" 28 | Write-ColorOutput $Yellow "Please right-click PowerShell and select 'Run as Administrator'" 29 | pause 30 | exit 1 31 | } 32 | 33 | Write-ColorOutput $Cyan "======================================" 34 | Write-ColorOutput $Cyan " WSL2 Installation Script" 35 | Write-ColorOutput $Cyan "======================================" 36 | Write-Output "" 37 | 38 | # Check Windows version 39 | Write-ColorOutput $Cyan "[1/6] Checking Windows version..." 40 | $osVersion = [System.Environment]::OSVersion.Version 41 | if ($osVersion.Build -lt 18362) { 42 | Write-ColorOutput $Red "ERROR: WSL2 requires Windows 10 version 1903 or higher (Build 18362+)" 43 | Write-ColorOutput $Yellow "Your version: Build $($osVersion.Build)" 44 | pause 45 | exit 1 46 | } 47 | Write-ColorOutput $Green "✓ Windows version is compatible (Build $($osVersion.Build))" 48 | Write-Output "" 49 | 50 | # Check if WSL is already installed 51 | Write-ColorOutput $Cyan "[2/6] Checking current WSL installation..." 52 | $wslInstalled = Get-Command wsl -ErrorAction SilentlyContinue 53 | if ($wslInstalled) { 54 | Write-ColorOutput $Yellow "WSL is already installed. Checking version..." 55 | try { 56 | $wslVersion = wsl --status 2>&1 57 | Write-Output $wslVersion 58 | Write-ColorOutput $Yellow "This script will ensure WSL2 is properly configured." 59 | Write-Output "" 60 | } 61 | catch { 62 | Write-ColorOutput $Yellow "WSL command available but status check failed." 63 | } 64 | } 65 | else { 66 | Write-ColorOutput $Yellow "WSL not detected. Will perform fresh installation." 67 | } 68 | Write-Output "" 69 | 70 | # Enable WSL and Virtual Machine Platform features 71 | Write-ColorOutput $Cyan "[3/6] Enabling WSL and Virtual Machine Platform features..." 72 | Write-ColorOutput $Yellow "This may take a few minutes..." 73 | 74 | try { 75 | # Enable WSL 76 | Write-Output "Enabling Windows Subsystem for Linux..." 77 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 78 | 79 | # Enable Virtual Machine Platform 80 | Write-Output "Enabling Virtual Machine Platform..." 81 | dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 82 | 83 | Write-ColorOutput $Green "✓ Features enabled successfully" 84 | } 85 | catch { 86 | Write-ColorOutput $Red "ERROR: Failed to enable features - $_" 87 | pause 88 | exit 1 89 | } 90 | Write-Output "" 91 | 92 | # Download and install WSL2 kernel update 93 | Write-ColorOutput $Cyan "[4/6] Downloading WSL2 Linux kernel update..." 94 | $kernelUrl = "https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi" 95 | $kernelInstaller = "$env:TEMP\wsl_update_x64.msi" 96 | 97 | try { 98 | Write-Output "Downloading from: $kernelUrl" 99 | Invoke-WebRequest -Uri $kernelUrl -OutFile $kernelInstaller -UseBasicParsing 100 | Write-ColorOutput $Green "✓ Download completed" 101 | 102 | Write-Output "Installing WSL2 kernel update..." 103 | Start-Process msiexec.exe -Wait -ArgumentList "/i $kernelInstaller /quiet /norestart" 104 | Write-ColorOutput $Green "✓ WSL2 kernel installed" 105 | 106 | # Cleanup 107 | Remove-Item $kernelInstaller -ErrorAction SilentlyContinue 108 | } 109 | catch { 110 | Write-ColorOutput $Yellow "Warning: Kernel update download/install failed - $_" 111 | Write-ColorOutput $Yellow "You may need to download it manually from: https://aka.ms/wsl2kernel" 112 | } 113 | Write-Output "" 114 | 115 | # Set WSL2 as default version 116 | Write-ColorOutput $Cyan "[5/6] Setting WSL2 as default version..." 117 | try { 118 | wsl --set-default-version 2 119 | Write-ColorOutput $Green "✓ WSL2 set as default version" 120 | } 121 | catch { 122 | Write-ColorOutput $Yellow "Warning: Could not set WSL2 as default - $_" 123 | } 124 | Write-Output "" 125 | 126 | # Install Ubuntu distribution 127 | Write-ColorOutput $Cyan "[6/6] Installing Ubuntu Linux distribution..." 128 | Write-ColorOutput $Yellow "This will download and install Ubuntu 22.04 LTS..." 129 | 130 | try { 131 | # Check if Ubuntu is already installed 132 | $ubuntuInstalled = wsl --list --quiet 2>&1 | Select-String -Pattern "Ubuntu" 133 | 134 | if ($ubuntuInstalled) { 135 | Write-ColorOutput $Yellow "Ubuntu is already installed:" 136 | wsl --list --verbose 137 | Write-Output "" 138 | Write-ColorOutput $Yellow "Skipping Ubuntu installation." 139 | } 140 | else { 141 | Write-Output "Installing Ubuntu 22.04..." 142 | wsl --install -d Ubuntu-22.04 143 | Write-ColorOutput $Green "✓ Ubuntu installation initiated" 144 | } 145 | } 146 | catch { 147 | Write-ColorOutput $Yellow "Warning: Ubuntu installation may require manual setup - $_" 148 | Write-ColorOutput $Yellow "You can install Ubuntu from Microsoft Store or run: wsl --install -d Ubuntu-22.04" 149 | } 150 | Write-Output "" 151 | 152 | # Completion message 153 | Write-ColorOutput $Green "======================================" 154 | Write-ColorOutput $Green " Installation Completed!" 155 | Write-ColorOutput $Green "======================================" 156 | Write-Output "" 157 | 158 | Write-ColorOutput $Yellow "IMPORTANT: A system restart is required to complete the installation." 159 | Write-Output "" 160 | Write-ColorOutput $Cyan "Next Steps:" 161 | Write-Output "1. Restart your computer" 162 | Write-Output "2. After restart, launch Ubuntu from the Start Menu" 163 | Write-Output "3. Complete the Ubuntu setup (create username and password)" 164 | Write-Output "4. Verify installation by running: wsl --list --verbose" 165 | Write-Output "5. You can then proceed to install Docker Desktop or Docker Engine" 166 | Write-Output "" 167 | 168 | Write-ColorOutput $Yellow "Would you like to restart now? (Y/N)" 169 | $restart = Read-Host 170 | if ($restart -eq "Y" -or $restart -eq "y") { 171 | Write-ColorOutput $Green "Restarting in 5 seconds..." 172 | Start-Sleep -Seconds 5 173 | Restart-Computer -Force 174 | } 175 | else { 176 | Write-ColorOutput $Yellow "Please remember to restart your computer manually to complete the installation." 177 | Write-Output "" 178 | pause 179 | } 180 | -------------------------------------------------------------------------------- /frontend/public/install-wsl2.ps1: -------------------------------------------------------------------------------- 1 | # WSL2 Installation Script for Windows 2 | # This script automates the installation of WSL2 on Windows 10/11 3 | # Must be run as Administrator 4 | 5 | # Color codes for output 6 | $Green = "Green" 7 | $Red = "Red" 8 | $Yellow = "Yellow" 9 | $Cyan = "Cyan" 10 | 11 | function Write-ColorOutput($ForegroundColor) { 12 | $fc = $host.UI.RawUI.ForegroundColor 13 | $host.UI.RawUI.ForegroundColor = $ForegroundColor 14 | if ($args) { 15 | Write-Output $args 16 | } 17 | $host.UI.RawUI.ForegroundColor = $fc 18 | } 19 | 20 | function Test-Administrator { 21 | $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) 22 | return $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) 23 | } 24 | 25 | # Check if running as Administrator 26 | if (-not (Test-Administrator)) { 27 | Write-ColorOutput $Red "ERROR: This script must be run as Administrator!" 28 | Write-ColorOutput $Yellow "Please right-click PowerShell and select 'Run as Administrator'" 29 | pause 30 | exit 1 31 | } 32 | 33 | Write-ColorOutput $Cyan "======================================" 34 | Write-ColorOutput $Cyan " WSL2 Installation Script" 35 | Write-ColorOutput $Cyan "======================================" 36 | Write-Output "" 37 | 38 | # Check Windows version 39 | Write-ColorOutput $Cyan "[1/6] Checking Windows version..." 40 | $osVersion = [System.Environment]::OSVersion.Version 41 | if ($osVersion.Build -lt 18362) { 42 | Write-ColorOutput $Red "ERROR: WSL2 requires Windows 10 version 1903 or higher (Build 18362+)" 43 | Write-ColorOutput $Yellow "Your version: Build $($osVersion.Build)" 44 | pause 45 | exit 1 46 | } 47 | Write-ColorOutput $Green "✓ Windows version is compatible (Build $($osVersion.Build))" 48 | Write-Output "" 49 | 50 | # Check if WSL is already installed 51 | Write-ColorOutput $Cyan "[2/6] Checking current WSL installation..." 52 | $wslInstalled = Get-Command wsl -ErrorAction SilentlyContinue 53 | if ($wslInstalled) { 54 | Write-ColorOutput $Yellow "WSL is already installed. Checking version..." 55 | try { 56 | $wslVersion = wsl --status 2>&1 57 | Write-Output $wslVersion 58 | Write-ColorOutput $Yellow "This script will ensure WSL2 is properly configured." 59 | Write-Output "" 60 | } 61 | catch { 62 | Write-ColorOutput $Yellow "WSL command available but status check failed." 63 | } 64 | } 65 | else { 66 | Write-ColorOutput $Yellow "WSL not detected. Will perform fresh installation." 67 | } 68 | Write-Output "" 69 | 70 | # Enable WSL and Virtual Machine Platform features 71 | Write-ColorOutput $Cyan "[3/6] Enabling WSL and Virtual Machine Platform features..." 72 | Write-ColorOutput $Yellow "This may take a few minutes..." 73 | 74 | try { 75 | # Enable WSL 76 | Write-Output "Enabling Windows Subsystem for Linux..." 77 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 78 | 79 | # Enable Virtual Machine Platform 80 | Write-Output "Enabling Virtual Machine Platform..." 81 | dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 82 | 83 | Write-ColorOutput $Green "✓ Features enabled successfully" 84 | } 85 | catch { 86 | Write-ColorOutput $Red "ERROR: Failed to enable features - $_" 87 | pause 88 | exit 1 89 | } 90 | Write-Output "" 91 | 92 | # Download and install WSL2 kernel update 93 | Write-ColorOutput $Cyan "[4/6] Downloading WSL2 Linux kernel update..." 94 | $kernelUrl = "https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi" 95 | $kernelInstaller = "$env:TEMP\wsl_update_x64.msi" 96 | 97 | try { 98 | Write-Output "Downloading from: $kernelUrl" 99 | Invoke-WebRequest -Uri $kernelUrl -OutFile $kernelInstaller -UseBasicParsing 100 | Write-ColorOutput $Green "✓ Download completed" 101 | 102 | Write-Output "Installing WSL2 kernel update..." 103 | Start-Process msiexec.exe -Wait -ArgumentList "/i $kernelInstaller /quiet /norestart" 104 | Write-ColorOutput $Green "✓ WSL2 kernel installed" 105 | 106 | # Cleanup 107 | Remove-Item $kernelInstaller -ErrorAction SilentlyContinue 108 | } 109 | catch { 110 | Write-ColorOutput $Yellow "Warning: Kernel update download/install failed - $_" 111 | Write-ColorOutput $Yellow "You may need to download it manually from: https://aka.ms/wsl2kernel" 112 | } 113 | Write-Output "" 114 | 115 | # Set WSL2 as default version 116 | Write-ColorOutput $Cyan "[5/6] Setting WSL2 as default version..." 117 | try { 118 | wsl --set-default-version 2 119 | Write-ColorOutput $Green "✓ WSL2 set as default version" 120 | } 121 | catch { 122 | Write-ColorOutput $Yellow "Warning: Could not set WSL2 as default - $_" 123 | } 124 | Write-Output "" 125 | 126 | # Install Ubuntu distribution 127 | Write-ColorOutput $Cyan "[6/6] Installing Ubuntu Linux distribution..." 128 | Write-ColorOutput $Yellow "This will download and install Ubuntu 22.04 LTS..." 129 | 130 | try { 131 | # Check if Ubuntu is already installed 132 | $ubuntuInstalled = wsl --list --quiet 2>&1 | Select-String -Pattern "Ubuntu" 133 | 134 | if ($ubuntuInstalled) { 135 | Write-ColorOutput $Yellow "Ubuntu is already installed:" 136 | wsl --list --verbose 137 | Write-Output "" 138 | Write-ColorOutput $Yellow "Skipping Ubuntu installation." 139 | } 140 | else { 141 | Write-Output "Installing Ubuntu 22.04..." 142 | wsl --install -d Ubuntu-22.04 143 | Write-ColorOutput $Green "✓ Ubuntu installation initiated" 144 | } 145 | } 146 | catch { 147 | Write-ColorOutput $Yellow "Warning: Ubuntu installation may require manual setup - $_" 148 | Write-ColorOutput $Yellow "You can install Ubuntu from Microsoft Store or run: wsl --install -d Ubuntu-22.04" 149 | } 150 | Write-Output "" 151 | 152 | # Completion message 153 | Write-ColorOutput $Green "======================================" 154 | Write-ColorOutput $Green " Installation Completed!" 155 | Write-ColorOutput $Green "======================================" 156 | Write-Output "" 157 | 158 | Write-ColorOutput $Yellow "IMPORTANT: A system restart is required to complete the installation." 159 | Write-Output "" 160 | Write-ColorOutput $Cyan "Next Steps:" 161 | Write-Output "1. Restart your computer" 162 | Write-Output "2. After restart, launch Ubuntu from the Start Menu" 163 | Write-Output "3. Complete the Ubuntu setup (create username and password)" 164 | Write-Output "4. Verify installation by running: wsl --list --verbose" 165 | Write-Output "5. You can then proceed to install Docker Desktop or Docker Engine" 166 | Write-Output "" 167 | 168 | Write-ColorOutput $Yellow "Would you like to restart now? (Y/N)" 169 | $restart = Read-Host 170 | if ($restart -eq "Y" -or $restart -eq "y") { 171 | Write-ColorOutput $Green "Restarting in 5 seconds..." 172 | Start-Sleep -Seconds 5 173 | Restart-Computer -Force 174 | } 175 | else { 176 | Write-ColorOutput $Yellow "Please remember to restart your computer manually to complete the installation." 177 | Write-Output "" 178 | pause 179 | } 180 | -------------------------------------------------------------------------------- /frontend/src/components/docker/DockerExecutionModal.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 193 | -------------------------------------------------------------------------------- /frontend/src/views/DashboardView.vue: -------------------------------------------------------------------------------- 1 | 155 | 156 | 216 | 217 | -------------------------------------------------------------------------------- /frontend/src/components/docker/RunContainerModal.vue: -------------------------------------------------------------------------------- 1 | 101 | 102 | 177 | 178 | 183 | -------------------------------------------------------------------------------- /backend/src/routes/compose.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const fs = require('fs').promises; 4 | const { exec } = require('child_process'); 5 | const { promisify } = require('util'); 6 | const execAsync = promisify(exec); 7 | const path = require('path'); 8 | 9 | // Convert Windows path to WSL path 10 | function windowsToWslPath(windowsPath) { 11 | if (!windowsPath) return windowsPath; 12 | // Handle UNC paths and regular Windows paths 13 | const normalizedPath = windowsPath.replace(/\\/g, '/'); 14 | // Match drive letter: C: → /mnt/c/ 15 | const match = normalizedPath.match(/^([a-zA-Z]):/); 16 | if (match) { 17 | const drive = match[1].toLowerCase(); 18 | const rest = normalizedPath.substring(2); 19 | return `/mnt/${drive}${rest}`; 20 | } 21 | return normalizedPath; 22 | } 23 | 24 | // Get docker-compose file content 25 | router.post('/file', async (req, res) => { 26 | /* #swagger.tags = ['Compose'] */ 27 | /* #swagger.path = '/compose/file' */ 28 | try { 29 | const { projectName, workingDir, configFiles, source = 'local', wslDistro } = req.body; 30 | 31 | if (!projectName) { 32 | return res.status(400).json({ error: 'Project name is required' }); 33 | } 34 | 35 | let composeFilePath = null; 36 | let fileName = null; 37 | 38 | // For WSL2, convert Windows paths to WSL paths 39 | const searchDir = source === 'wsl2' && workingDir ? windowsToWslPath(workingDir) : workingDir; 40 | 41 | // First try to use the working directory and config files from labels 42 | if (searchDir && configFiles) { 43 | const configFilesList = configFiles.split(','); 44 | for (const configFile of configFilesList) { 45 | const basePath = path.isAbsolute(configFile) ? configFile : `${searchDir}/${configFile}`; 46 | const fullPath = source === 'wsl2' ? basePath : path.join(searchDir, configFile); 47 | try { 48 | // For WSL2, use wsl to check if file exists 49 | if (source === 'wsl2') { 50 | const distro = wslDistro || process.env.WSL_DISTRO || 'Ubuntu'; 51 | await execAsync(`wsl.exe -d ${distro} -- test -f "${fullPath}"`); 52 | composeFilePath = fullPath; 53 | fileName = path.basename(configFile); 54 | } else { 55 | const stat = await fs.lstat(fullPath); 56 | if (stat.isFile()) { 57 | composeFilePath = fullPath; 58 | fileName = path.basename(configFile); 59 | } 60 | } 61 | if (composeFilePath) { 62 | break; 63 | } 64 | } catch (err) { 65 | // File doesn't exist or not accessible, try next 66 | continue; 67 | } 68 | } 69 | } 70 | 71 | // If not found, try common compose file names in working directory 72 | if (!composeFilePath && searchDir) { 73 | const commonNames = [ 74 | 'docker-compose.yml', 75 | 'docker-compose.yaml', 76 | 'compose.yml', 77 | 'compose.yaml' 78 | ]; 79 | 80 | for (const name of commonNames) { 81 | const fullPath = source === 'wsl2' ? `${searchDir}/${name}` : path.join(searchDir, name); 82 | try { 83 | if (source === 'wsl2') { 84 | const distro = wslDistro || process.env.WSL_DISTRO || 'Ubuntu'; 85 | await execAsync(`wsl.exe -d ${distro} -- test -f "${fullPath}"`); 86 | composeFilePath = fullPath; 87 | fileName = name; 88 | } else { 89 | const stat = await fs.lstat(fullPath); 90 | if (stat.isFile()) { 91 | composeFilePath = fullPath; 92 | fileName = name; 93 | } 94 | } 95 | if (composeFilePath) { 96 | break; 97 | } 98 | } catch (err) { 99 | continue; 100 | } 101 | } 102 | } 103 | 104 | // If still not found, try to find in current working directory 105 | if (!composeFilePath) { 106 | const commonNames = [ 107 | 'docker-compose.yml', 108 | 'docker-compose.yaml', 109 | 'compose.yml', 110 | 'compose.yaml' 111 | ]; 112 | 113 | for (const name of commonNames) { 114 | const fullPath = path.join(process.cwd(), name); 115 | try { 116 | const stat = await fs.lstat(fullPath); 117 | if (stat.isFile()) { 118 | composeFilePath = fullPath; 119 | fileName = name; 120 | break; 121 | } 122 | } catch (err) { 123 | continue; 124 | } 125 | } 126 | } 127 | 128 | if (!composeFilePath) { 129 | return res.status(404).json({ 130 | error: 'Docker compose file not found', 131 | details: `Searched for compose files for project: ${projectName}`, 132 | searchedPaths: workingDir ? [workingDir, process.cwd()] : [process.cwd()] 133 | }); 134 | } 135 | 136 | let content; 137 | // Read the file content depending on source 138 | if (source === 'wsl2') { 139 | const distro = wslDistro || process.env.WSL_DISTRO || 'Ubuntu'; 140 | // Use WSL to cat the file (supports /mnt/* paths) 141 | const { stdout } = await execAsync(`wsl.exe -d ${distro} -- cat "${composeFilePath}"`); 142 | content = stdout; 143 | } else { 144 | content = await fs.readFile(composeFilePath, 'utf-8'); 145 | } 146 | 147 | res.json({ 148 | fileName: fileName, 149 | filePath: composeFilePath, 150 | content: content, 151 | projectName: projectName 152 | }); 153 | 154 | } catch (error) { 155 | console.error('Error reading compose file:', error); 156 | res.status(500).json({ 157 | error: 'Failed to read compose file', 158 | details: error.message 159 | }); 160 | } 161 | }); 162 | 163 | // Get all compose projects 164 | router.get('/', async (req, res) => { 165 | /* #swagger.tags = ['Compose'] */ 166 | /* #swagger.path = '/compose/' */ 167 | try { 168 | const source = req.query.source || 'local'; 169 | const wslDistro = req.query.wslDistro || undefined; 170 | const serviceInstance = new DockerService({ source, wslDistro }); 171 | const projects = await serviceInstance.getComposeProjects(); 172 | res.json(projects); 173 | } catch (error) { 174 | res.status(500).json({ error: error.message }); 175 | } 176 | }); 177 | 178 | // Start compose project 179 | router.post('/:project/start', async (req, res) => { 180 | /* #swagger.tags = ['Compose'] */ 181 | /* #swagger.path = '/compose/{project}/start' */ 182 | try { 183 | const source = req.query.source || req.body.source || 'local'; 184 | const wslDistro = req.query.wslDistro || req.body.wslDistro || undefined; 185 | const serviceInstance = new DockerService({ source, wslDistro }); 186 | const result = await serviceInstance.startComposeProject(req.params.project); 187 | res.json(result); 188 | } catch (error) { 189 | res.status(500).json({ error: error.message }); 190 | } 191 | }); 192 | 193 | module.exports = router; -------------------------------------------------------------------------------- /DOCKER-DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | # Docker Web Desktop - Docker Deployment 2 | 3 | This document provides instructions for deploying the Docker Web Desktop application using Docker and Docker Compose. 4 | 5 | ## Prerequisites 6 | 7 | - Docker Engine 20.10+ installed 8 | - Docker Compose v2.0+ installed 9 | - Git (to clone the repository) 10 | 11 | ## Project Structure 12 | 13 | ``` 14 | docker-web-desktop/ 15 | ├── backend/ 16 | │ ├── src/ 17 | │ ├── Dockerfile 18 | │ ├── Dockerfile.dev 19 | │ ├── .dockerignore 20 | │ └── package.json 21 | ├── frontend/ 22 | │ ├── src/ 23 | │ ├── Dockerfile 24 | │ ├── Dockerfile.dev 25 | │ ├── nginx.conf 26 | │ ├── .dockerignore 27 | │ └── package.json 28 | ├── docker-compose.yml 29 | ├── docker-compose.dev.yml 30 | └── DOCKER-DEPLOYMENT.md 31 | ``` 32 | 33 | ## Quick Start 34 | 35 | ### Production Deployment 36 | 37 | 1. **Clone the repository:** 38 | ```bash 39 | git clone 40 | cd docker-web-desktop 41 | ``` 42 | 43 | 2. **Build and run with Docker Compose:** 44 | ```bash 45 | docker-compose up -d --build 46 | ``` 47 | 48 | 3. **Access the application:** 49 | - Frontend: http://localhost 50 | - Backend API: http://localhost:3000 51 | 52 | 4. **Stop the application:** 53 | ```bash 54 | docker-compose down 55 | ``` 56 | 57 | ### Development Deployment 58 | 59 | For development with hot reloading: 60 | 61 | 1. **Run development environment:** 62 | ```bash 63 | docker-compose -f docker-compose.dev.yml up --build 64 | ``` 65 | 66 | 2. **Access the application:** 67 | - Frontend: http://localhost:5173 68 | - Backend API: http://localhost:3000 69 | 70 | ## Docker Images 71 | 72 | ### Backend Image 73 | - **Base Image:** `node:20-alpine` 74 | - **Exposed Port:** 3000 75 | - **Health Check:** `/api/health` 76 | - **Features:** 77 | - Multi-stage build for optimization 78 | - Non-root user for security 79 | - Health checks for container monitoring 80 | 81 | ### Frontend Image 82 | - **Base Image:** `node:20-alpine` (build stage) + `nginx:alpine` (runtime) 83 | - **Exposed Port:** 80 84 | - **Features:** 85 | - Multi-stage build for minimal size 86 | - Nginx for static file serving 87 | - Gzip compression enabled 88 | - API proxy configuration 89 | 90 | ## Environment Configuration 91 | 92 | ### Backend Environment Variables 93 | - `NODE_ENV`: production/development 94 | - `PORT`: Server port (default: 3000) 95 | 96 | ### Docker Socket Access 97 | The backend container requires access to the Docker socket to manage Docker resources: 98 | ```yaml 99 | volumes: 100 | - /var/run/docker.sock:/var/run/docker.sock 101 | ``` 102 | 103 | ## Health Checks 104 | 105 | Both services include health checks: 106 | 107 | - **Backend:** HTTP GET `/api/health` 108 | - **Frontend:** HTTP GET to root `/` 109 | 110 | Health checks ensure containers are ready before dependent services start. 111 | 112 | ## Networking 113 | 114 | - **Network Name:** `docker-web-desktop-network` 115 | - **Driver:** bridge 116 | - **Inter-service Communication:** Services communicate using service names 117 | 118 | ## Commands Reference 119 | 120 | ### Build Images 121 | ```bash 122 | # Build all images 123 | docker-compose build 124 | 125 | # Build specific service 126 | docker-compose build backend 127 | docker-compose build frontend 128 | ``` 129 | 130 | ### Run Services 131 | ```bash 132 | # Run in background 133 | docker-compose up -d 134 | 135 | # Run with logs 136 | docker-compose up 137 | 138 | # Run specific service 139 | docker-compose up backend 140 | ``` 141 | 142 | ### View Logs 143 | ```bash 144 | # All services 145 | docker-compose logs -f 146 | 147 | # Specific service 148 | docker-compose logs -f backend 149 | docker-compose logs -f frontend 150 | ``` 151 | 152 | ### Scale Services 153 | ```bash 154 | # Scale backend service 155 | docker-compose up -d --scale backend=3 156 | ``` 157 | 158 | ### Stop and Clean 159 | ```bash 160 | # Stop services 161 | docker-compose stop 162 | 163 | # Stop and remove containers 164 | docker-compose down 165 | 166 | # Remove with volumes 167 | docker-compose down -v 168 | 169 | # Remove with images 170 | docker-compose down --rmi all 171 | ``` 172 | 173 | ## Development Workflow 174 | 175 | 1. **Start development environment:** 176 | ```bash 177 | docker-compose -f docker-compose.dev.yml up 178 | ``` 179 | 180 | 2. **Make changes to source code:** 181 | - Backend changes trigger nodemon restart 182 | - Frontend changes trigger Vite hot reload 183 | 184 | 3. **View real-time logs:** 185 | ```bash 186 | docker-compose -f docker-compose.dev.yml logs -f 187 | ``` 188 | 189 | ## Production Deployment 190 | 191 | ### Using Docker Compose 192 | ```bash 193 | # Pull latest images (if using registry) 194 | docker-compose pull 195 | 196 | # Deploy with restart policy 197 | docker-compose up -d --force-recreate 198 | ``` 199 | 200 | ### Manual Docker Commands 201 | ```bash 202 | # Build backend image 203 | docker build -t docker-web-desktop-backend ./backend 204 | 205 | # Build frontend image 206 | docker build -t docker-web-desktop-frontend ./frontend 207 | 208 | # Create network 209 | docker network create docker-web-desktop-network 210 | 211 | # Run backend 212 | docker run -d \ 213 | --name docker-web-desktop-backend \ 214 | --network docker-web-desktop-network \ 215 | -p 3000:3000 \ 216 | -v /var/run/docker.sock:/var/run/docker.sock \ 217 | docker-web-desktop-backend 218 | 219 | # Run frontend 220 | docker run -d \ 221 | --name docker-web-desktop-frontend \ 222 | --network docker-web-desktop-network \ 223 | -p 80:80 \ 224 | docker-web-desktop-frontend 225 | ``` 226 | 227 | ## Security Considerations 228 | 229 | 1. **Non-root User:** Backend runs as non-root user (nodejs:1001) 230 | 2. **Docker Socket:** Limited to read-only operations where possible 231 | 3. **Network Isolation:** Services run in isolated Docker network 232 | 4. **Security Headers:** Nginx includes security headers 233 | 5. **Minimal Images:** Alpine-based images for smaller attack surface 234 | 235 | ## Troubleshooting 236 | 237 | ### Common Issues 238 | 239 | 1. **Port conflicts:** 240 | ```bash 241 | # Check port usage 242 | netstat -tulpn | grep :80 243 | netstat -tulpn | grep :3000 244 | ``` 245 | 246 | 2. **Docker socket permissions:** 247 | ```bash 248 | # Add user to docker group (Linux) 249 | sudo usermod -aG docker $USER 250 | ``` 251 | 252 | 3. **Container logs:** 253 | ```bash 254 | # Check container status 255 | docker-compose ps 256 | 257 | # View logs 258 | docker-compose logs backend 259 | docker-compose logs frontend 260 | ``` 261 | 262 | 4. **Health check failures:** 263 | ```bash 264 | # Check health status 265 | docker-compose ps 266 | 267 | # Test health endpoints manually 268 | curl http://localhost:3000/api/health 269 | curl http://localhost/ 270 | ``` 271 | 272 | ### Performance Optimization 273 | 274 | 1. **Use .dockerignore:** Exclude unnecessary files from build context 275 | 2. **Multi-stage builds:** Minimize final image size 276 | 3. **Layer caching:** Order Dockerfile commands for optimal caching 277 | 4. **Resource limits:** Set memory and CPU limits for containers 278 | 279 | ## Monitoring 280 | 281 | ### Container Stats 282 | ```bash 283 | # Real-time stats 284 | docker stats 285 | 286 | # Compose stats 287 | docker-compose top 288 | ``` 289 | 290 | ### Logs 291 | ```bash 292 | # Follow logs 293 | docker-compose logs -f --tail=100 294 | 295 | # Export logs 296 | docker-compose logs > application.log 297 | ``` 298 | 299 | ## Backup and Recovery 300 | 301 | ### Data Volumes 302 | ```bash 303 | # Backup volumes 304 | docker run --rm -v docker-web-desktop_app-data:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz -C /data . 305 | 306 | # Restore volumes 307 | docker run --rm -v docker-web-desktop_app-data:/data -v $(pwd):/backup alpine tar xzf /backup/backup.tar.gz -C /data 308 | ``` 309 | 310 | ## Support 311 | 312 | For issues or questions: 313 | 1. Check container logs: `docker-compose logs` 314 | 2. Verify health checks: `docker-compose ps` 315 | 3. Review this documentation 316 | 4. Check Docker and Docker Compose versions --------------------------------------------------------------------------------