mirror of
https://github.com/louislam/dockge.git
synced 2025-08-12 09:18:24 +02:00
Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
89e1297b1c | |||
cd9f9d2427 | |||
be7a296fc7 | |||
5ce6b90546 | |||
a488518f6e | |||
8c4004f32d | |||
393bbcae79 | |||
9fbf94586b | |||
0a46a7df1a | |||
d1732af529 | |||
87a6436f28 | |||
ac75283b0f | |||
8d6160ec5b | |||
ecb16ae007 | |||
c296069a8d | |||
d76442434f | |||
54e8484efd | |||
2cd10ad16d | |||
96a4f2fd0c | |||
700a24171b | |||
6ce75a2df3 | |||
317c97650d | |||
9295583727 | |||
6dc998bedf | |||
f5552b3344 | |||
b90fd35348 | |||
3dca9e735a | |||
200ba0ca07 | |||
dd58a9cbc4 | |||
7f41cc099c | |||
4ce696181b | |||
959dbba776 | |||
ffa978eea1 | |||
9fd0c6416a | |||
e6fc623758 | |||
209dedf682 | |||
cf49a2ef2a |
@ -92,6 +92,9 @@ module.exports = {
|
||||
"one-var": [ "error", "never" ],
|
||||
"max-statements-per-line": [ "error", { "max": 1 }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [ "warn", {
|
||||
"args": "none"
|
||||
}],
|
||||
"prefer-const" : "off",
|
||||
},
|
||||
};
|
||||
|
57
.github/workflows/ci.yml
vendored
Normal file
57
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
name: Node.js CI - Dockge
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node: [20.x] # Can be changed
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- run: git config --global core.autocrlf false # Mainly for Windows
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{matrix.node}}
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
# more things can be add later like tests etc..
|
||||
|
50
README.md
50
README.md
@ -4,15 +4,19 @@
|
||||
|
||||
# Dockge
|
||||
|
||||
A fancy, easy-to-use and reactive docker `compose.yaml` stack-oriented manager.
|
||||
A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
|
||||
|
||||
      
|
||||
|
||||
<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
|
||||
|
||||
[View Video](https://youtu.be/AWAlOQeNpgU?t=48)
|
||||
View Video: https://youtu.be/AWAlOQeNpgU?t=48
|
||||
|
||||
## ⭐ Features
|
||||
|
||||
- Manage `compose.yaml`
|
||||
- Create/Edit/Start/Stop/Restart/Delete
|
||||
- Update Docker Images
|
||||
- Interactive Editor for `compose.yaml`
|
||||
- Interactive Web Terminal
|
||||
- Reactive
|
||||
@ -25,14 +29,17 @@ A fancy, easy-to-use and reactive docker `compose.yaml` stack-oriented manager.
|
||||
<img src="https://github.com/louislam/dockge/assets/1336778/cc071864-592e-4909-b73a-343a57494002" width=300 />
|
||||
|
||||
|
||||

|
||||
|
||||
## 🔧 How to Install
|
||||
|
||||
Requirements:
|
||||
- [Docker CE](https://docs.docker.com/engine/install/) 20+ is recommended
|
||||
- [Docker Compose V2](https://docs.docker.com/compose/install/linux/)
|
||||
- [Docker CE](https://docs.docker.com/engine/install/) 20+ is recommended / Podman
|
||||
- (Docker only) [Docker Compose Plugin](https://docs.docker.com/compose/install/linux/)
|
||||
- (Podman only) podman-docker (Debian: `apt install podman-docker`)
|
||||
- OS:
|
||||
- As long as you can run Docker CE, it should be fine, but:
|
||||
- Debian/Raspbian Buster or lower is not supported, please upgrade to Bullseye
|
||||
- As long as you can run Docker CE / Podman, it should be fine, but:
|
||||
- Debian/Raspbian Buster or lower is not supported, please upgrade to Bullseye or higher
|
||||
- Arch: armv7, arm64, amd64 (a.k.a x86_64)
|
||||
|
||||
### Basic
|
||||
@ -51,7 +58,7 @@ curl https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml --out
|
||||
# Start Server
|
||||
docker compose up -d
|
||||
|
||||
# If you are using docker-compose V1
|
||||
# If you are using docker-compose V1 or Podman
|
||||
# docker-compose up -d
|
||||
```
|
||||
|
||||
@ -61,8 +68,6 @@ Dockge is now running on http://localhost:5001
|
||||
|
||||
If you want to store your stacks in another directory, you can change the `DOCKGE_STACKS_DIR` environment variable and volumes.
|
||||
|
||||
For example, if you want to store your stacks in `/my-stacks`:
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
services:
|
||||
@ -79,24 +84,29 @@ services:
|
||||
# If you want to use private registries, you need to share the auth file with Dockge:
|
||||
# - /root/.docker/:/root/.docker
|
||||
|
||||
# Your stacks directory in the host
|
||||
# (The paths inside container must be the same as the host)
|
||||
- /my-stacks:/my-stacks
|
||||
# Your stacks directory in the host (The paths inside container must be the same as the host)
|
||||
# ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
|
||||
# ✔️✔️✔️✔️ CORRECT EXAMPLE: - /my-stacks:/my-stacks (Both paths match)
|
||||
# ❌❌❌❌ WRONG EXAMPLE: - /docker:/my-stacks (Both paths do not match)
|
||||
- /opt/stacks:/opt/stacks
|
||||
environment:
|
||||
# Tell Dockge where is your stacks directory
|
||||
- DOCKGE_STACKS_DIR=/my-stacks
|
||||
- DOCKGE_STACKS_DIR=/opt/stacks
|
||||
```
|
||||
|
||||
## How to Update
|
||||
|
||||
```bash
|
||||
cd /opt/stacks
|
||||
cd /opt/dockge
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
@ -112,16 +122,22 @@ docker compose up -d
|
||||
If you love this project, please consider giving this project a ⭐.
|
||||
|
||||
|
||||
## 🗣️
|
||||
|
||||
### Bug Report
|
||||
https://github.com/louislam/dockge/issues
|
||||
|
||||
### Ask for Help / Discussions
|
||||
https://github.com/louislam/dockge/discussions
|
||||
|
||||
## FAQ
|
||||
|
||||
#### "Dockge"?
|
||||
|
||||
"Dockge" is a coinage word which is created by myself. I hope it sounds like `Badge` but replacing with `Dock` - `Dock-ge`.
|
||||
"Dockge" is a coinage word which is created by myself. I hope it sounds like `Dodge`.
|
||||
|
||||
The naming idea was coming from Twitch emotes like `sadge`, `bedge` or `wokege`. They are all ending with `-ge`.
|
||||
|
||||
If you are not comfortable with the pronunciation, you can call it `Dockage`.
|
||||
|
||||
#### Can I manage a single container without `compose.yaml`?
|
||||
|
||||
The main objective of Dockge is that try to use docker `compose.yaml` for everything. If you want to manage a single container, you can just use Portainer or Docker CLI.
|
||||
|
@ -80,7 +80,7 @@ export class Database {
|
||||
* @param {boolean} noLog Should logs not be output?
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async connect(autoloadModels = true, noLog = false) {
|
||||
static async connect(autoloadModels = true) {
|
||||
const acquireConnectionTimeout = 120 * 1000;
|
||||
let dbConfig;
|
||||
try {
|
||||
|
@ -291,7 +291,7 @@ export class DockgeServer {
|
||||
}
|
||||
|
||||
// Run every 5 seconds
|
||||
const job = Cron("*/2 * * * * *", {
|
||||
Cron("*/2 * * * * *", {
|
||||
protect: true, // Enabled over-run protection.
|
||||
}, () => {
|
||||
log.debug("server", "Cron job running");
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { SocketHandler } from "../socket-handler.js";
|
||||
import { Socket } from "socket.io";
|
||||
import { DockgeServer } from "../dockge-server";
|
||||
import { log } from "../log";
|
||||
import { R } from "redbean-node";
|
||||
|
@ -24,6 +24,7 @@ export class Stack {
|
||||
protected _status: number = UNKNOWN;
|
||||
protected _composeYAML?: string;
|
||||
protected _configFilePath?: string;
|
||||
protected _composeFileName: string = "compose.yaml";
|
||||
protected server: DockgeServer;
|
||||
|
||||
protected combinedTerminal? : Terminal;
|
||||
@ -34,6 +35,15 @@ export class Stack {
|
||||
this.name = name;
|
||||
this.server = server;
|
||||
this._composeYAML = composeYAML;
|
||||
|
||||
// Check if compose file name is different from compose.yaml
|
||||
const supportedFileNames = [ "compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml" ];
|
||||
for (const filename of supportedFileNames) {
|
||||
if (fs.existsSync(path.join(this.path, filename))) {
|
||||
this._composeFileName = filename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() : object {
|
||||
@ -50,6 +60,7 @@ export class Stack {
|
||||
status: this._status,
|
||||
tags: [],
|
||||
isManagedByDockge: this.isManagedByDockge,
|
||||
composeFileName: this._composeFileName,
|
||||
};
|
||||
}
|
||||
|
||||
@ -72,9 +83,9 @@ export class Stack {
|
||||
}
|
||||
|
||||
validate() {
|
||||
// Check name, allows [a-z][A-Z][0-9] _ - only
|
||||
if (!this.name.match(/^[a-zA-Z0-9_-]+$/)) {
|
||||
throw new ValidationError("Stack name can only contain [a-z][A-Z][0-9] _ - only");
|
||||
// Check name, allows [a-z][0-9] _ - only
|
||||
if (!this.name.match(/^[a-z0-9_-]+$/)) {
|
||||
throw new ValidationError("Stack name can only contain [a-z][0-9] _ - only");
|
||||
}
|
||||
|
||||
// Check YAML format
|
||||
@ -84,7 +95,7 @@ export class Stack {
|
||||
get composeYAML() : string {
|
||||
if (this._composeYAML === undefined) {
|
||||
try {
|
||||
this._composeYAML = fs.readFileSync(path.join(this.path, "compose.yaml"), "utf-8");
|
||||
this._composeYAML = fs.readFileSync(path.join(this.path, this._composeFileName), "utf-8");
|
||||
} catch (e) {
|
||||
this._composeYAML = "";
|
||||
}
|
||||
@ -135,7 +146,7 @@ export class Stack {
|
||||
}
|
||||
|
||||
// Write or overwrite the compose.yaml
|
||||
fs.writeFileSync(path.join(dir, "compose.yaml"), this.composeYAML);
|
||||
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
|
||||
}
|
||||
|
||||
async deploy(socket? : DockgeSocket) : Promise<number> {
|
||||
@ -149,7 +160,7 @@ export class Stack {
|
||||
|
||||
async delete(socket?: DockgeSocket) : Promise<number> {
|
||||
const terminalName = getComposeTerminalName(this.name);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans", "--rmi", "all" ], this.path);
|
||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path);
|
||||
if (exitCode !== 0) {
|
||||
throw new Error("Failed to delete, please check the terminal output for more information.");
|
||||
}
|
||||
@ -177,6 +188,11 @@ export class Stack {
|
||||
|
||||
for (let filename of filenameList) {
|
||||
try {
|
||||
// Check if it is a directory
|
||||
let stat = fs.statSync(path.join(stacksDir, filename));
|
||||
if (!stat.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
let stack = this.getStack(server, filename);
|
||||
stack._status = CREATED_FILE;
|
||||
stackList.set(filename, stack);
|
||||
@ -346,7 +362,11 @@ export class Stack {
|
||||
for (let line of lines) {
|
||||
try {
|
||||
let obj = JSON.parse(line);
|
||||
statusList.set(obj.Service, obj.State);
|
||||
if (obj.Health === "") {
|
||||
statusList.set(obj.Service, obj.State);
|
||||
} else {
|
||||
statusList.set(obj.Service, obj.Health);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ export const TERMINAL_COLS = 105;
|
||||
export const TERMINAL_ROWS = 10;
|
||||
export const PROGRESS_TERMINAL_ROWS = 8;
|
||||
|
||||
export const COMBINED_TERMINAL_COLS = 56;
|
||||
export const COMBINED_TERMINAL_ROWS = 15;
|
||||
export const COMBINED_TERMINAL_COLS = 58;
|
||||
export const COMBINED_TERMINAL_ROWS = 20;
|
||||
|
||||
export const ERROR_TYPE_VALIDATION = 1;
|
||||
|
||||
|
10
compose.yaml
10
compose.yaml
@ -7,14 +7,16 @@ services:
|
||||
# Host Port : Container Port
|
||||
- 5001:5001
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data:/app/data
|
||||
|
||||
# If you want to use private registries, you need to share the auth file with Dockge:
|
||||
# - /root/.docker/:/root/.docker
|
||||
|
||||
# Docker Socket
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# Dockge Config
|
||||
- ./data:/app/data
|
||||
# Your stacks directory in the host (The paths inside container must be the same as the host)
|
||||
# ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
|
||||
# ✔️✔️✔️✔️ CORRECT: - /my-stacks:/my-stacks (Both paths match)
|
||||
# ❌❌❌❌ WRONG: - /docker:/my-stacks (Both paths do not match)
|
||||
- /opt/stacks:/opt/stacks
|
||||
environment:
|
||||
# Tell Dockge where is your stacks directory
|
||||
|
20
extra/env2arg.js
Normal file
20
extra/env2arg.js
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import childProcess from "child_process";
|
||||
|
||||
let env = process.env;
|
||||
|
||||
let cmd = process.argv[2];
|
||||
let args = process.argv.slice(3);
|
||||
let replacedArgs = [];
|
||||
|
||||
for (let arg of args) {
|
||||
for (let key in env) {
|
||||
arg = arg.replaceAll(`$${key}`, env[key]);
|
||||
}
|
||||
replacedArgs.push(arg);
|
||||
}
|
||||
|
||||
let child = childProcess.spawn(cmd, replacedArgs);
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
9
extra/test-docker.ts
Normal file
9
extra/test-docker.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Check if docker is running
|
||||
import { exec } from "child_process";
|
||||
|
||||
exec("docker ps", (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error("Docker is not running. Please start docker and try again.");
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
64
extra/update-version.ts
Normal file
64
extra/update-version.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import pkg from "../package.json";
|
||||
import childProcess from "child_process";
|
||||
import fs from "fs";
|
||||
|
||||
const newVersion = process.env.VERSION;
|
||||
|
||||
console.log("New Version: " + newVersion);
|
||||
|
||||
if (! newVersion) {
|
||||
console.error("invalid version");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const exists = tagExists(newVersion);
|
||||
|
||||
if (! exists) {
|
||||
// Process package.json
|
||||
pkg.version = newVersion;
|
||||
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
|
||||
commit(newVersion);
|
||||
tag(newVersion);
|
||||
} else {
|
||||
console.log("version exists");
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit updated files
|
||||
* @param {string} version Version to update to
|
||||
*/
|
||||
function commit(version) {
|
||||
let msg = "Update to " + version;
|
||||
|
||||
let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]);
|
||||
let stdout = res.stdout.toString().trim();
|
||||
console.log(stdout);
|
||||
|
||||
if (stdout.includes("no changes added to commit")) {
|
||||
throw new Error("commit error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tag with the specified version
|
||||
* @param {string} version Tag to create
|
||||
*/
|
||||
function tag(version) {
|
||||
let res = childProcess.spawnSync("git", [ "tag", version ]);
|
||||
console.log(res.stdout.toString().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tag exists for the specified version
|
||||
* @param {string} version Version to check
|
||||
* @returns {boolean} Does the tag already exist
|
||||
*/
|
||||
function tagExists(version) {
|
||||
if (! version) {
|
||||
throw new Error("invalid version");
|
||||
}
|
||||
|
||||
let res = childProcess.spawnSync("git", [ "tag", "-l", version ]);
|
||||
|
||||
return res.stdout.toString().trim() === version;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<li v-for="(value, index) in array" :key="index" class="list-group-item">
|
||||
<select v-model="array[index]" class="no-bg domain-input">
|
||||
<option value="">Select a network...</option>
|
||||
<option v-for="option in options" :value="option">{{ option }}</option>
|
||||
<option v-for="option in options" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
|
||||
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="remove(index)" />
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div v-if="!isEditMode">
|
||||
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
|
||||
|
||||
<a v-for="port in service.ports" :href="parsePort(port).url" target="_blank">
|
||||
<a v-for="port in service.ports" :key="port" :href="parsePort(port).url" target="_blank">
|
||||
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -179,8 +179,10 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
bgStyle() {
|
||||
if (this.status === "running") {
|
||||
if (this.status === "running" || this.status === "healthy") {
|
||||
return "bg-primary";
|
||||
} else if (this.status === "unhealthy") {
|
||||
return "bg-danger";
|
||||
} else {
|
||||
return "bg-secondary";
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
class="form-control"
|
||||
@keyup.enter="createExternelNetwork"
|
||||
/>
|
||||
<button class="btn btn-normal btn-sm me-2" type="button" @click="">
|
||||
<button class="btn btn-normal btn-sm me-2" type="button">
|
||||
{{ $t("createExternalNetwork") }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -77,8 +77,8 @@ export default {
|
||||
}
|
||||
|
||||
this.terminal = new Terminal({
|
||||
fontSize: 16,
|
||||
fontFamily: "monospace",
|
||||
fontSize: 14,
|
||||
fontFamily: "'JetBrains Mono', monospace",
|
||||
cursorBlink,
|
||||
cols: this.cols,
|
||||
rows: this.rows,
|
||||
|
@ -19,7 +19,6 @@ export default {
|
||||
|
||||
computed: {
|
||||
uptime() {
|
||||
return "0.00%";
|
||||
return this.$t("notAvailableShort");
|
||||
},
|
||||
|
||||
|
@ -68,13 +68,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HiddenInput from "../../components/HiddenInput.vue";
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import { timezoneList } from "../../util-frontend";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HiddenInput,
|
||||
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -13,6 +13,7 @@ import Toast, { POSITION, useToast } from "vue-toastification";
|
||||
import "xterm/lib/xterm.js";
|
||||
|
||||
// CSS
|
||||
import "@fontsource/jetbrains-mono";
|
||||
import "vue-toastification/dist/index.css";
|
||||
import "xterm/css/xterm.css";
|
||||
import "./styles/main.scss";
|
||||
@ -22,6 +23,9 @@ import socket from "./mixins/socket";
|
||||
import lang from "./mixins/lang";
|
||||
import theme from "./mixins/theme";
|
||||
|
||||
// Set Title
|
||||
document.title = document.title + " - " + location.host;
|
||||
|
||||
const app = createApp(rootApp());
|
||||
|
||||
app.use(Toast, {
|
||||
|
@ -68,9 +68,10 @@
|
||||
<h4 class="mb-3">{{ $t("general") }}</h4>
|
||||
<div class="shadow-box big-padding mb-3">
|
||||
<!-- Stack Name -->
|
||||
<div class="mb-3">
|
||||
<div>
|
||||
<label for="name" class="form-label">{{ $t("stackName") }}</label>
|
||||
<input id="name" v-model="stack.name" type="text" class="form-control" required>
|
||||
<input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
|
||||
<div class="form-text">Lowercase only</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -117,7 +118,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<h4 class="mb-3">compose.yaml</h4>
|
||||
<h4 class="mb-3">{{ stack.composeFileName }}</h4>
|
||||
|
||||
<!-- YAML editor -->
|
||||
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
|
||||
@ -582,6 +583,10 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
stackNameToLowercase() {
|
||||
this.stack.name = this.stack?.name?.toLowerCase();
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -592,6 +597,8 @@ export default {
|
||||
}
|
||||
|
||||
.editor-box {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 14px;
|
||||
&.edit-mode {
|
||||
background-color: #2c2f38 !important;
|
||||
}
|
||||
|
@ -227,5 +227,7 @@ table {
|
||||
.docker-run {
|
||||
background-color: $dark-bg !important;
|
||||
border: none;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
|
@ -680,6 +680,10 @@ code {
|
||||
}
|
||||
}
|
||||
|
||||
.form-text {
|
||||
color: $dark-font-color3;
|
||||
}
|
||||
|
||||
// Vue Prism Editor bug - workaround
|
||||
// https://github.com/koca/vue-prism-editor/issues/87
|
||||
/*
|
||||
|
1
frontend/src/vite-env.d.ts
vendored
1
frontend/src/vite-env.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module "*.vue" {
|
||||
|
20
package.json
20
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dockge",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
|
||||
@ -8,22 +8,23 @@
|
||||
"start": "tsx ./backend/index.ts",
|
||||
"dev:backend": "cross-env NODE_ENV=development tsx watch ./backend/index.ts",
|
||||
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
||||
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker",
|
||||
"build:frontend": "vite build --config ./frontend/vite.config.ts",
|
||||
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
|
||||
"build:docker": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:1.0.0 --target release -f ./docker/Dockerfile . --push",
|
||||
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
|
||||
"build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
|
||||
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
|
||||
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.10",
|
||||
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.11",
|
||||
"@louislam/sqlite3": "~15.1.6",
|
||||
"bcryptjs": "~2.4.3",
|
||||
"check-password-strength": "~2.0.7",
|
||||
"command-exists": "~1.2.9",
|
||||
"compare-versions": "~6.1.0",
|
||||
"composerize": "~1.4.1",
|
||||
"croner": "~7.0.4",
|
||||
"croner": "~7.0.5",
|
||||
"dayjs": "~1.11.10",
|
||||
"express": "~4.18.2",
|
||||
"express-static-gzip": "~2.1.7",
|
||||
@ -43,17 +44,18 @@
|
||||
"yaml": "~2.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fontsource/jetbrains-mono": "^5.0.17",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
||||
"@fortawesome/vue-fontawesome": "3.0.3",
|
||||
"@types/bootstrap": "~5.2.8",
|
||||
"@types/bootstrap": "~5.2.9",
|
||||
"@types/command-exists": "~1.2.3",
|
||||
"@types/express": "~4.17.21",
|
||||
"@types/jsonwebtoken": "~9.0.4",
|
||||
"@types/jsonwebtoken": "~9.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
||||
"@typescript-eslint/parser": "~6.8.0",
|
||||
"@vitejs/plugin-vue": "~4.3.4",
|
||||
"@vitejs/plugin-vue": "~4.5.0",
|
||||
"bootstrap": "5.3.2",
|
||||
"bootstrap-vue-next": "~0.14.10",
|
||||
"cross-env": "~7.0.3",
|
||||
@ -64,7 +66,7 @@
|
||||
"sass": "~1.68.0",
|
||||
"typescript": "~5.2.2",
|
||||
"unplugin-vue-components": "~0.25.2",
|
||||
"vite": "~4.5.0",
|
||||
"vite": "~5.0.0",
|
||||
"vite-plugin-compression": "~0.5.1",
|
||||
"vue": "~3.3.8",
|
||||
"vue-eslint-parser": "~9.3.2",
|
||||
@ -73,7 +75,7 @@
|
||||
"vue-qrcode": "~2.2.0",
|
||||
"vue-router": "~4.2.5",
|
||||
"vue-toastification": "2.0.0-rc.5",
|
||||
"xterm": "~5.4.0-beta.37",
|
||||
"xterm": "5.4.0-beta.37",
|
||||
"xterm-addon-web-links": "~0.9.0"
|
||||
}
|
||||
}
|
||||
|
628
pnpm-lock.yaml
generated
628
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user