mirror of
https://github.com/louislam/dockge.git
synced 2025-08-13 18:57:20 +02:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
89e1297b1c | |||
cd9f9d2427 | |||
be7a296fc7 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -53,8 +53,5 @@ jobs:
|
|||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
- name: Check Typescript
|
|
||||||
run: pnpm run check-ts
|
|
||||||
# more things can be add later like tests etc..
|
# more things can be add later like tests etc..
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import fs from "fs";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import knex from "knex";
|
import knex from "knex";
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import Dialect from "knex/lib/dialects/sqlite3/index.js";
|
import Dialect from "knex/lib/dialects/sqlite3/index.js";
|
||||||
|
|
||||||
import sqlite from "@louislam/sqlite3";
|
import sqlite from "@louislam/sqlite3";
|
||||||
@ -13,11 +12,6 @@ import { sleep } from "./util-common";
|
|||||||
|
|
||||||
interface DBConfig {
|
interface DBConfig {
|
||||||
type?: "sqlite" | "mysql";
|
type?: "sqlite" | "mysql";
|
||||||
hostname?: string;
|
|
||||||
port?: string;
|
|
||||||
database?: string;
|
|
||||||
username?: string;
|
|
||||||
password?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Database {
|
export class Database {
|
||||||
@ -25,7 +19,7 @@ export class Database {
|
|||||||
* SQLite file path (Default: ./data/dockge.db)
|
* SQLite file path (Default: ./data/dockge.db)
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
static sqlitePath : string;
|
static sqlitePath;
|
||||||
|
|
||||||
static noReject = true;
|
static noReject = true;
|
||||||
|
|
||||||
@ -57,7 +51,7 @@ export class Database {
|
|||||||
* @typedef {string|undefined} envString
|
* @typedef {string|undefined} envString
|
||||||
* @returns {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} Database config
|
* @returns {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} Database config
|
||||||
*/
|
*/
|
||||||
static readDBConfig() : DBConfig {
|
static readDBConfig() {
|
||||||
const dbConfigString = fs.readFileSync(path.join(this.server.config.dataDir, "db-config.json")).toString("utf-8");
|
const dbConfigString = fs.readFileSync(path.join(this.server.config.dataDir, "db-config.json")).toString("utf-8");
|
||||||
const dbConfig = JSON.parse(dbConfigString);
|
const dbConfig = JSON.parse(dbConfigString);
|
||||||
|
|
||||||
@ -73,10 +67,10 @@ export class Database {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {string|undefined} envString
|
* @typedef {string|undefined} envString
|
||||||
* @param dbConfig the database configuration that should be written
|
* @param {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString}} dbConfig the database configuration that should be written
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
static writeDBConfig(dbConfig : DBConfig) {
|
static writeDBConfig(dbConfig) {
|
||||||
fs.writeFileSync(path.join(this.server.config.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4));
|
fs.writeFileSync(path.join(this.server.config.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,15 +82,12 @@ export class Database {
|
|||||||
*/
|
*/
|
||||||
static async connect(autoloadModels = true) {
|
static async connect(autoloadModels = true) {
|
||||||
const acquireConnectionTimeout = 120 * 1000;
|
const acquireConnectionTimeout = 120 * 1000;
|
||||||
let dbConfig : DBConfig;
|
let dbConfig;
|
||||||
try {
|
try {
|
||||||
dbConfig = this.readDBConfig();
|
dbConfig = this.readDBConfig();
|
||||||
Database.dbConfig = dbConfig;
|
Database.dbConfig = dbConfig;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
|
||||||
log.warn("db", err.message);
|
log.warn("db", err.message);
|
||||||
}
|
|
||||||
|
|
||||||
dbConfig = {
|
dbConfig = {
|
||||||
type: "sqlite",
|
type: "sqlite",
|
||||||
};
|
};
|
||||||
@ -185,7 +176,6 @@ export class Database {
|
|||||||
directory: Database.knexMigrationsPath,
|
directory: Database.knexMigrationsPath,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
// Allow missing patch files for downgrade or testing pr.
|
// Allow missing patch files for downgrade or testing pr.
|
||||||
if (e.message.includes("the following files are missing:")) {
|
if (e.message.includes("the following files are missing:")) {
|
||||||
log.warn("db", e.message);
|
log.warn("db", e.message);
|
||||||
@ -196,7 +186,6 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special handle, because tarn.js throw a promise reject that cannot be caught
|
* Special handle, because tarn.js throw a promise reject that cannot be caught
|
||||||
|
@ -60,7 +60,7 @@ export class DockgeServer {
|
|||||||
*/
|
*/
|
||||||
needSetup = false;
|
needSetup = false;
|
||||||
|
|
||||||
jwtSecret : string = "";
|
jwtSecret? : string;
|
||||||
|
|
||||||
stacksDir : string = "";
|
stacksDir : string = "";
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ export class DockgeServer {
|
|||||||
this.config.sslKey = args.sslKey || process.env.DOCKGE_SSL_KEY || undefined;
|
this.config.sslKey = args.sslKey || process.env.DOCKGE_SSL_KEY || undefined;
|
||||||
this.config.sslCert = args.sslCert || process.env.DOCKGE_SSL_CERT || undefined;
|
this.config.sslCert = args.sslCert || process.env.DOCKGE_SSL_CERT || undefined;
|
||||||
this.config.sslKeyPassphrase = args.sslKeyPassphrase || process.env.DOCKGE_SSL_KEY_PASSPHRASE || undefined;
|
this.config.sslKeyPassphrase = args.sslKeyPassphrase || process.env.DOCKGE_SSL_KEY_PASSPHRASE || undefined;
|
||||||
this.config.port = args.port || Number(process.env.DOCKGE_PORT) || 5001;
|
this.config.port = args.port || parseInt(process.env.DOCKGE_PORT) || 5001;
|
||||||
this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
|
this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
|
||||||
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
|
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
|
||||||
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
|
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
|
||||||
@ -218,7 +218,7 @@ export class DockgeServer {
|
|||||||
log.debug("auth", "check auto login");
|
log.debug("auth", "check auto login");
|
||||||
if (await Settings.get("disableAuth")) {
|
if (await Settings.get("disableAuth")) {
|
||||||
log.info("auth", "Disabled Auth: auto login to admin");
|
log.info("auth", "Disabled Auth: auto login to admin");
|
||||||
this.afterLogin(socket as DockgeSocket, await R.findOne("user") as User);
|
this.afterLogin(socket as DockgeSocket, await R.findOne("user"));
|
||||||
socket.emit("autoLogin");
|
socket.emit("autoLogin");
|
||||||
} else {
|
} else {
|
||||||
log.debug("auth", "need auth");
|
log.debug("auth", "need auth");
|
||||||
@ -253,9 +253,7 @@ export class DockgeServer {
|
|||||||
try {
|
try {
|
||||||
await Database.init(this);
|
await Database.init(this);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.error("server", "Failed to prepare your database: " + e.message);
|
log.error("server", "Failed to prepare your database: " + e.message);
|
||||||
}
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,10 +376,8 @@ export class DockgeServer {
|
|||||||
return process.env.TZ;
|
return process.env.TZ;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.warn("timezone", e.message + " in process.env.TZ");
|
log.warn("timezone", e.message + " in process.env.TZ");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const timezone = await Settings.get("serverTimezone");
|
const timezone = await Settings.get("serverTimezone");
|
||||||
|
|
||||||
@ -393,10 +389,8 @@ export class DockgeServer {
|
|||||||
return timezone;
|
return timezone;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.warn("timezone", e.message + " in settings");
|
log.warn("timezone", e.message + " in settings");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Guess
|
// Guess
|
||||||
try {
|
try {
|
||||||
|
@ -17,7 +17,7 @@ export function generatePasswordHash(password : string) {
|
|||||||
* @param {string} hash Hash to verify against
|
* @param {string} hash Hash to verify against
|
||||||
* @returns {boolean} Does the password match the hash?
|
* @returns {boolean} Does the password match the hash?
|
||||||
*/
|
*/
|
||||||
export function verifyPassword(password : string, hash : string) {
|
export function verifyPassword(password, hash) {
|
||||||
return bcrypt.compareSync(password, hash);
|
return bcrypt.compareSync(password, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ export const SHAKE256_LENGTH = 16;
|
|||||||
* @param {number} len Output length of the hash
|
* @param {number} len Output length of the hash
|
||||||
* @returns {string} The hashed data in hex format
|
* @returns {string} The hashed data in hex format
|
||||||
*/
|
*/
|
||||||
export function shake256(data : string, len : number) {
|
export function shake256(data, len) {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,8 @@
|
|||||||
// "limit" is bugged in Typescript, use "limiter-es6-compat" instead
|
// "limit" is bugged in Typescript, use "limiter-es6-compat" instead
|
||||||
// See https://github.com/jhurliman/node-rate-limiter/issues/80
|
// See https://github.com/jhurliman/node-rate-limiter/issues/80
|
||||||
import { RateLimiter, RateLimiterOpts } from "limiter-es6-compat";
|
import { RateLimiter } from "limiter-es6-compat";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
|
|
||||||
export interface KumaRateLimiterOpts extends RateLimiterOpts {
|
|
||||||
errorMessage : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type KumaRateLimiterCallback = (err : object) => void;
|
|
||||||
|
|
||||||
class KumaRateLimiter {
|
class KumaRateLimiter {
|
||||||
|
|
||||||
errorMessage : string;
|
errorMessage : string;
|
||||||
@ -17,7 +11,7 @@ class KumaRateLimiter {
|
|||||||
/**
|
/**
|
||||||
* @param {object} config Rate limiter configuration object
|
* @param {object} config Rate limiter configuration object
|
||||||
*/
|
*/
|
||||||
constructor(config : KumaRateLimiterOpts) {
|
constructor(config) {
|
||||||
this.errorMessage = config.errorMessage;
|
this.errorMessage = config.errorMessage;
|
||||||
this.rateLimiter = new RateLimiter(config);
|
this.rateLimiter = new RateLimiter(config);
|
||||||
}
|
}
|
||||||
@ -30,11 +24,11 @@ class KumaRateLimiter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Should the request be passed through
|
* Should the request be passed through
|
||||||
* @param callback Callback function to call with decision
|
* @param {passCB} callback Callback function to call with decision
|
||||||
* @param {number} num Number of tokens to remove
|
* @param {number} num Number of tokens to remove
|
||||||
* @returns {Promise<boolean>} Should the request be allowed?
|
* @returns {Promise<boolean>} Should the request be allowed?
|
||||||
*/
|
*/
|
||||||
async pass(callback : KumaRateLimiterCallback, num = 1) {
|
async pass(callback, num = 1) {
|
||||||
const remainingRequests = await this.removeTokens(num);
|
const remainingRequests = await this.removeTokens(num);
|
||||||
log.info("rate-limit", "remaining requests: " + remainingRequests);
|
log.info("rate-limit", "remaining requests: " + remainingRequests);
|
||||||
if (remainingRequests < 0) {
|
if (remainingRequests < 0) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DockgeServer } from "../dockge-server";
|
import { DockgeServer } from "../dockgeServer";
|
||||||
import { Router } from "../router";
|
import { Router } from "../router";
|
||||||
import express, { Express, Router as ExpressRouter } from "express";
|
import express, { Express, Router as ExpressRouter } from "express";
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { R } from "redbean-node";
|
import { R } from "redbean-node";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
import { LooseObject } from "./util-common";
|
|
||||||
|
|
||||||
export class Settings {
|
export class Settings {
|
||||||
|
|
||||||
@ -16,19 +15,20 @@ export class Settings {
|
|||||||
* timestamp: 12345678
|
* timestamp: 12345678
|
||||||
* },
|
* },
|
||||||
* }
|
* }
|
||||||
|
* @type {{}}
|
||||||
*/
|
*/
|
||||||
static cacheList : LooseObject = {
|
static cacheList = {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static cacheCleaner? : NodeJS.Timeout;
|
static cacheCleaner = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve value of setting based on key
|
* Retrieve value of setting based on key
|
||||||
* @param key Key of setting to retrieve
|
* @param {string} key Key of setting to retrieve
|
||||||
* @returns Value
|
* @returns {Promise<any>} Value
|
||||||
*/
|
*/
|
||||||
static async get(key : string) {
|
static async get(key) {
|
||||||
|
|
||||||
// Start cache clear if not started yet
|
// Start cache clear if not started yet
|
||||||
if (!Settings.cacheCleaner) {
|
if (!Settings.cacheCleaner) {
|
||||||
@ -72,12 +72,12 @@ export class Settings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the specified setting to specified value
|
* Sets the specified setting to specified value
|
||||||
* @param key Key of setting to set
|
* @param {string} key Key of setting to set
|
||||||
* @param value Value to set to
|
* @param {any} value Value to set to
|
||||||
* @param {?string} type Type of setting
|
* @param {?string} type Type of setting
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async set(key : string, value : object | string | number | boolean, type : string | null = null) {
|
static async set(key, value, type = null) {
|
||||||
|
|
||||||
let bean = await R.findOne("setting", " `key` = ? ", [
|
let bean = await R.findOne("setting", " `key` = ? ", [
|
||||||
key,
|
key,
|
||||||
@ -95,15 +95,15 @@ export class Settings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get settings based on type
|
* Get settings based on type
|
||||||
* @param type The type of setting
|
* @param {string} type The type of setting
|
||||||
* @returns Settings
|
* @returns {Promise<Bean>} Settings
|
||||||
*/
|
*/
|
||||||
static async getSettings(type : string) {
|
static async getSettings(type) {
|
||||||
const list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
|
const list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
|
||||||
type,
|
type,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result : LooseObject = {};
|
const result = {};
|
||||||
|
|
||||||
for (const row of list) {
|
for (const row of list) {
|
||||||
try {
|
try {
|
||||||
@ -118,11 +118,11 @@ export class Settings {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set settings based on type
|
* Set settings based on type
|
||||||
* @param type Type of settings to set
|
* @param {string} type Type of settings to set
|
||||||
* @param data Values of settings
|
* @param {object} data Values of settings
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async setSettings(type : string, data : LooseObject) {
|
static async setSettings(type, data) {
|
||||||
const keyList = Object.keys(data);
|
const keyList = Object.keys(data);
|
||||||
|
|
||||||
const promiseList = [];
|
const promiseList = [];
|
||||||
@ -154,7 +154,7 @@ export class Settings {
|
|||||||
* @param {string[]} keyList Keys to remove
|
* @param {string[]} keyList Keys to remove
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
static deleteCache(keyList : string[]) {
|
static deleteCache(keyList) {
|
||||||
for (const key of keyList) {
|
for (const key of keyList) {
|
||||||
delete Settings.cacheList[key];
|
delete Settings.cacheList[key];
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ export class Settings {
|
|||||||
static stopCacheCleaner() {
|
static stopCacheCleaner() {
|
||||||
if (Settings.cacheCleaner) {
|
if (Settings.cacheCleaner) {
|
||||||
clearInterval(Settings.cacheCleaner);
|
clearInterval(Settings.cacheCleaner);
|
||||||
Settings.cacheCleaner = undefined;
|
Settings.cacheCleaner = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { R } from "redbean-node";
|
|||||||
import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
|
import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
|
||||||
import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
|
import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
|
||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import { checkLogin, DockgeSocket, doubleCheckPassword, JWTDecoded } from "../util-server";
|
import { checkLogin, DockgeSocket, doubleCheckPassword } from "../util-server";
|
||||||
import { passwordStrength } from "check-password-strength";
|
import { passwordStrength } from "check-password-strength";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { Settings } from "../settings";
|
import { Settings } from "../settings";
|
||||||
@ -42,13 +42,11 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: e.message,
|
msg: e.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Login by token
|
// Login by token
|
||||||
@ -58,7 +56,7 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
log.info("auth", `Login by token. IP=${clientIP}`);
|
log.info("auth", `Login by token. IP=${clientIP}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, server.jwtSecret) as JWTDecoded;
|
const decoded = jwt.verify(token, server.jwtSecret);
|
||||||
|
|
||||||
log.info("auth", "Username from JWT: " + decoded.username);
|
log.info("auth", "Username from JWT: " + decoded.username);
|
||||||
|
|
||||||
@ -92,13 +90,9 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error instanceof Error)) {
|
|
||||||
console.error("Unknown error:", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.error("auth", `Invalid token. IP=${clientIP}`);
|
log.error("auth", `Invalid token. IP=${clientIP}`);
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
log.error("auth", error.message + ` IP=${clientIP}`);
|
log.error("auth", error.message, `IP=${clientIP}`);
|
||||||
}
|
}
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
@ -154,7 +148,6 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.token) {
|
if (data.token) {
|
||||||
// @ts-ignore
|
|
||||||
const verify = notp.totp.verify(data.token, user.twofa_secret, twoFAVerifyOptions);
|
const verify = notp.totp.verify(data.token, user.twofa_secret, twoFAVerifyOptions);
|
||||||
|
|
||||||
if (user.twofa_last_token !== data.token && verify) {
|
if (user.twofa_last_token !== data.token && verify) {
|
||||||
@ -217,13 +210,11 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: e.message,
|
msg: e.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("getSettings", async (callback) => {
|
socket.on("getSettings", async (callback) => {
|
||||||
@ -237,13 +228,11 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: e.message,
|
msg: e.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("setSettings", async (data, currentPassword, callback) => {
|
socket.on("setSettings", async (data, currentPassword, callback) => {
|
||||||
@ -272,24 +261,22 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
server.sendInfo(socket);
|
server.sendInfo(socket);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: e.message,
|
msg: e.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(username : string, password : string) : Promise<User | null> {
|
async login(username : string, password : string) {
|
||||||
if (typeof username !== "string" || typeof password !== "string") {
|
if (typeof username !== "string" || typeof password !== "string") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await R.findOne("user", " username = ? AND active = 1 ", [
|
const user = await R.findOne("user", " username = ? AND active = 1 ", [
|
||||||
username,
|
username,
|
||||||
]) as User;
|
]);
|
||||||
|
|
||||||
if (user && verifyPassword(password, user.password)) {
|
if (user && verifyPassword(password, user.password)) {
|
||||||
// Upgrade the hash to bcrypt
|
// Upgrade the hash to bcrypt
|
||||||
|
@ -38,13 +38,11 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
throw new Error("Terminal not found or it is not a Interactive Terminal.");
|
throw new Error("Terminal not found or it is not a Interactive Terminal.");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
errorCallback({
|
errorCallback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: e.message,
|
msg: e.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Main Terminal
|
// Main Terminal
|
||||||
|
@ -197,11 +197,9 @@ export class Stack {
|
|||||||
stack._status = CREATED_FILE;
|
stack._status = CREATED_FILE;
|
||||||
stackList.set(filename, stack);
|
stackList.set(filename, stack);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.warn("getStackList", `Failed to get stack ${filename}, error: ${e.message}`);
|
log.warn("getStackList", `Failed to get stack ${filename}, error: ${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Cache by copying
|
// Cache by copying
|
||||||
this.managedStackList = new Map(stackList);
|
this.managedStackList = new Map(stackList);
|
||||||
|
@ -54,11 +54,9 @@ export class Terminal {
|
|||||||
try {
|
try {
|
||||||
this.ptyProcess?.resize(this.cols, this.rows);
|
this.ptyProcess?.resize(this.cols, this.rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.debug("Terminal", "Failed to resize terminal: " + e.message);
|
log.debug("Terminal", "Failed to resize terminal: " + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get cols() {
|
get cols() {
|
||||||
return this._cols;
|
return this._cols;
|
||||||
@ -69,11 +67,9 @@ export class Terminal {
|
|||||||
try {
|
try {
|
||||||
this.ptyProcess?.resize(this.cols, this.rows);
|
this.ptyProcess?.resize(this.cols, this.rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
|
||||||
log.debug("Terminal", "Failed to resize terminal: " + e.message);
|
log.debug("Terminal", "Failed to resize terminal: " + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
if (this._ptyProcess) {
|
if (this._ptyProcess) {
|
||||||
@ -89,7 +85,7 @@ export class Terminal {
|
|||||||
|
|
||||||
// On Data
|
// On Data
|
||||||
this._ptyProcess.onData((data) => {
|
this._ptyProcess.onData((data) => {
|
||||||
this.buffer.pushItem(data);
|
this.buffer.push(data);
|
||||||
if (this.server.io) {
|
if (this.server.io) {
|
||||||
this.server.io.to(this.name).emit("terminalWrite", this.name, data);
|
this.server.io.to(this.name).emit("terminalWrite", this.name, data);
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,6 @@ dayjs.extend(utc);
|
|||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
export interface LooseObject {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
|
|
||||||
let randomBytes : (numBytes: number) => Uint8Array;
|
let randomBytes : (numBytes: number) => Uint8Array;
|
||||||
initRandomBytes();
|
initRandomBytes();
|
||||||
|
|
||||||
|
@ -6,11 +6,6 @@ import { ERROR_TYPE_VALIDATION } from "./util-common";
|
|||||||
import { R } from "redbean-node";
|
import { R } from "redbean-node";
|
||||||
import { verifyPassword } from "./password-hash";
|
import { verifyPassword } from "./password-hash";
|
||||||
|
|
||||||
export interface JWTDecoded {
|
|
||||||
username : string;
|
|
||||||
h? : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DockgeSocket extends Socket {
|
export interface DockgeSocket extends Socket {
|
||||||
userID: number;
|
userID: number;
|
||||||
consoleTerminal? : Terminal;
|
consoleTerminal? : Terminal;
|
||||||
|
@ -4,14 +4,14 @@
|
|||||||
*/
|
*/
|
||||||
export class LimitQueue<T> extends Array<T> {
|
export class LimitQueue<T> extends Array<T> {
|
||||||
__limit;
|
__limit;
|
||||||
__onExceed? : (item : T | undefined) => void;
|
__onExceed = null;
|
||||||
|
|
||||||
constructor(limit: number) {
|
constructor(limit: number) {
|
||||||
super();
|
super();
|
||||||
this.__limit = limit;
|
this.__limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushItem(value : T) {
|
push(value : T) {
|
||||||
super.push(value);
|
super.push(value);
|
||||||
if (this.length > this.__limit) {
|
if (this.length > this.__limit) {
|
||||||
const item = this.shift();
|
const item = this.shift();
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<div v-if="isEditMode" class="mt-2">
|
<div v-if="isEditMode" class="mt-2">
|
||||||
<button class="btn btn-normal me-2" @click="showConfig = !showConfig">
|
<button class="btn btn-normal me-2" @click="showConfig = !showConfig">
|
||||||
<font-awesome-icon icon="edit" />
|
<font-awesome-icon icon="edit" />
|
||||||
{{ $t("Edit") }}
|
Edit
|
||||||
</button>
|
</button>
|
||||||
<button v-if="false" class="btn btn-normal me-2">Rename</button>
|
<button v-if="false" class="btn btn-normal me-2">Rename</button>
|
||||||
<button class="btn btn-danger me-2" @click="remove">
|
<button class="btn btn-danger me-2" @click="remove">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h5>{{ $t("Internal Networks") }}</h5>
|
<h5>Internal Networks</h5>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li v-for="(networkRow, index) in networkList" :key="index" class="list-group-item">
|
<li v-for="(networkRow, index) in networkList" :key="index" class="list-group-item">
|
||||||
<input v-model="networkRow.key" type="text" class="no-bg domain-input" placeholder="Network name..." />
|
<input v-model="networkRow.key" type="text" class="no-bg domain-input" placeholder="Network name..." />
|
||||||
@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
<button class="btn btn-normal btn-sm mt-3 me-2" @click="addField">{{ $t("addInternalNetwork") }}</button>
|
<button class="btn btn-normal btn-sm mt-3 me-2" @click="addField">{{ $t("addInternalNetwork") }}</button>
|
||||||
|
|
||||||
<h5 class="mt-3">{{ $t("External Networks") }}</h5>
|
<h5 class="mt-3">External Networks</h5>
|
||||||
|
|
||||||
<div v-if="externalNetworkList.length === 0">
|
<div v-if="externalNetworkList.length === 0">
|
||||||
{{ $t("No External Networks") }}
|
No External Networks
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(networkName, index) in externalNetworkList" :key="networkName" class="form-check form-switch my-3">
|
<div v-for="(networkName, index) in externalNetworkList" :key="networkName" class="form-check form-switch my-3">
|
||||||
|
@ -47,10 +47,10 @@
|
|||||||
<input
|
<input
|
||||||
v-model="settings.primaryHostname"
|
v-model="settings.primaryHostname"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="(Unset: Follow current hostname)"
|
placeholder="localhost"
|
||||||
/>
|
/>
|
||||||
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryHostname">
|
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryHostname">
|
||||||
{{ $t("autoGet") }}
|
{{ $t("Auto Get") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ import { createI18n } from "vue-i18n/dist/vue-i18n.esm-browser.prod.js";
|
|||||||
import en from "./lang/en.json";
|
import en from "./lang/en.json";
|
||||||
|
|
||||||
const languageList = {
|
const languageList = {
|
||||||
"fr": "Français",
|
|
||||||
"tr": "Türkçe",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let messages = {
|
let messages = {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
{
|
{
|
||||||
"languageName": "English",
|
"languageName": "English",
|
||||||
"Create your admin account": "Create your admin account",
|
|
||||||
"authIncorrectCreds": "Incorrect username or password.",
|
"authIncorrectCreds": "Incorrect username or password.",
|
||||||
"PasswordsDoNotMatch": "Passwords do not match.",
|
"PasswordsDoNotMatch": "Passwords do not match.",
|
||||||
"Repeat Password": "Repeat Password",
|
|
||||||
"Create": "Create",
|
|
||||||
"signedInDisp": "Signed in as {0}",
|
"signedInDisp": "Signed in as {0}",
|
||||||
"signedInDispDisabled": "Auth Disabled.",
|
"signedInDispDisabled": "Auth Disabled.",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
@ -46,49 +43,11 @@
|
|||||||
"addContainer": "Add Container",
|
"addContainer": "Add Container",
|
||||||
"addNetwork": "Add Network",
|
"addNetwork": "Add Network",
|
||||||
"disableauth.message1": "Are you sure want to <strong>disable authentication</strong>?",
|
"disableauth.message1": "Are you sure want to <strong>disable authentication</strong>?",
|
||||||
"disableauth.message2": "It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Dockge such as Cloudflare Access, Authelia or other authentication mechanisms.",
|
"disableauth.message2": "It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.",
|
||||||
"passwordNotMatchMsg": "The repeat password does not match.",
|
"passwordNotMatchMsg": "The repeat password does not match.",
|
||||||
"autoGet": "Auto Get",
|
"autoGet": "Auto Get",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"Edit": "Edit",
|
|
||||||
"applyToYAML": "Apply to YAML",
|
"applyToYAML": "Apply to YAML",
|
||||||
"createExternalNetwork": "Create",
|
"createExternalNetwork": "Create",
|
||||||
"addInternalNetwork": "Add",
|
"addInternalNetwork": "Add"
|
||||||
"Save": "Save",
|
|
||||||
"Language": "Language",
|
|
||||||
"Current User": "Current User",
|
|
||||||
"Change Password": "Change Password",
|
|
||||||
"Current Password": "Current Password",
|
|
||||||
"New Password": "New Password",
|
|
||||||
"Repeat New Password": "Repeat New Password",
|
|
||||||
"Update Password": "Update Password",
|
|
||||||
"Advanced": "Advanced",
|
|
||||||
"Please use this option carefully!": "Please use this option carefully!",
|
|
||||||
"Enable Auth": "Enable Auth",
|
|
||||||
"Disable Auth": "Disable Auth",
|
|
||||||
"I understand, please disable": "I understand, please disable",
|
|
||||||
"Leave": "Leave",
|
|
||||||
"Frontend Version": "Frontend Version",
|
|
||||||
"Check Update On GitHub": "Check Update On GitHub",
|
|
||||||
"Show update if available": "Show update if available",
|
|
||||||
"Also check beta release": "Also check beta release",
|
|
||||||
"Remember me": "Remember me",
|
|
||||||
"Login": "Login",
|
|
||||||
"Username": "Username",
|
|
||||||
"Password": "Password",
|
|
||||||
"Settings": "Settings",
|
|
||||||
"Logout": "Logout",
|
|
||||||
"Lowercase only": "Lowercase only",
|
|
||||||
"Convert to Compose": "Convert to Compose",
|
|
||||||
"Docker Run": "Docker Run",
|
|
||||||
"active": "active",
|
|
||||||
"exited": "exited",
|
|
||||||
"inactive": "inactive",
|
|
||||||
"Appearance": "Appearance",
|
|
||||||
"Security": "Security",
|
|
||||||
"About": "About",
|
|
||||||
"Allowed commands:": "Allowed commands:",
|
|
||||||
"Internal Networks": "Internal Networks",
|
|
||||||
"External Networks": "External Networks",
|
|
||||||
"No External Networks": "No External Networks"
|
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
{
|
|
||||||
"languageName": "Francais",
|
|
||||||
"Create your admin account": "Créez votre compte administrateur",
|
|
||||||
"authIncorrectCreds": "identifiant ou mot de passe incorrect.",
|
|
||||||
"Repeat Password": "Répéter le mot de passe",
|
|
||||||
"PasswordsDoNotMatch": "Les mots de passe ne correspondent pas.",
|
|
||||||
"Create": "Créer",
|
|
||||||
"signedInDisp": "Connecté en tant que {0}",
|
|
||||||
"signedInDispDisabled": "Authentification désactivée.",
|
|
||||||
"home": "Accueil",
|
|
||||||
"console": "Console",
|
|
||||||
"registry": "Registre",
|
|
||||||
"compose": "Compose",
|
|
||||||
"addFirstStackMsg": "Créez votre première pile!",
|
|
||||||
"stackName" : "Nom de la pile",
|
|
||||||
"deployStack": "Déployer",
|
|
||||||
"deleteStack": "Supprimer",
|
|
||||||
"stopStack": "Arrêter",
|
|
||||||
"restartStack": "Redémarrer",
|
|
||||||
"updateStack": "Mettre à jour",
|
|
||||||
"startStack": "Démarrer",
|
|
||||||
"editStack": "Modifier",
|
|
||||||
"discardStack": "Ignorer",
|
|
||||||
"saveStackDraft": "Sauvegarder",
|
|
||||||
"notAvailableShort" : "N/A",
|
|
||||||
"deleteStackMsg": "Êtes-vous sûr de vouloir supprimer cette pile ?",
|
|
||||||
"stackNotManagedByDockgeMsg": "Cette pile n'est pas gérée par Dockge.",
|
|
||||||
"primaryHostname": "Nom d'hôte principal",
|
|
||||||
"general": "Générale",
|
|
||||||
"container": "Conteneur | Conteneurs",
|
|
||||||
"scanFolder": "Analyser le dossier des piles",
|
|
||||||
"dockerImage": "Image",
|
|
||||||
"restartPolicyUnlessStopped": "Sauf arrêt",
|
|
||||||
"restartPolicyAlways": "Toujours",
|
|
||||||
"restartPolicyOnFailure": "En cas d'échec",
|
|
||||||
"restartPolicyNo": "Non",
|
|
||||||
"environmentVariable": "Variable d'environnement | Variables d'environnement",
|
|
||||||
"restartPolicy": "Politique de redémarrage",
|
|
||||||
"containerName": "Nom du conteneur",
|
|
||||||
"port": "Port | Ports",
|
|
||||||
"volume": "Volume | Volumes",
|
|
||||||
"network": "Réseau | Réseaux",
|
|
||||||
"dependsOn": "Dépendance du conteneur | Dépendances du conteneur",
|
|
||||||
"addListItem": "Ajouter {0}",
|
|
||||||
"deleteContainer": "Supprimer",
|
|
||||||
"addContainer": "Ajouter un conteneur",
|
|
||||||
"addNetwork": "Ajouter un réseau",
|
|
||||||
"disableauth.message1": "Voulez-vous vraiment <strong>désactiver l'authentification</strong> ?",
|
|
||||||
"disableauth.message2": "Il est conçu pour les scénarios <strong>dans lesquels vous avez l'intention d'implémenter une authentification tierce</strong> devant Dockge, comme Cloudflare Access, Authelia ou d'autres mécanismes d'authentification.",
|
|
||||||
"passwordNotMatchMsg": "Le mot de passe de confirmation ne correspond pas.",
|
|
||||||
"autoGet": "Obtention automatique",
|
|
||||||
"add": "Ajouter",
|
|
||||||
"Edit": "Modifier",
|
|
||||||
"applyToYAML": "Appliquer à YAML",
|
|
||||||
"createExternalNetwork": "Créer",
|
|
||||||
"addInternalNetwork": "Ajouter",
|
|
||||||
"Save": "Enregistrer",
|
|
||||||
"Language": "Langue",
|
|
||||||
"Current User": "Utilisateur Actuel",
|
|
||||||
"Change Password": "Changer le Mot de Passe",
|
|
||||||
"Current Password": "Mot de passe actuel",
|
|
||||||
"New Password": "Nouveau Mot de Passe",
|
|
||||||
"Repeat New Password": "Répéter le Nouveau Mot de Passe",
|
|
||||||
"Update Password": "Mettre à Jour le Mot de Passe",
|
|
||||||
"Advanced": "Avancé",
|
|
||||||
"Please use this option carefully!": "Veuillez utiliser cette option avec précaution !",
|
|
||||||
"Enable Auth": "Activer l'Authentification",
|
|
||||||
"Disable Auth": "Désactiver l'Authentification",
|
|
||||||
"I understand, please disable": "Je comprends, veuillez désactiver",
|
|
||||||
"Leave": "Quitter",
|
|
||||||
"Frontend Version": "Version Frontend",
|
|
||||||
"Check Update On GitHub": "Vérifier la Mise à Jour sur GitHub",
|
|
||||||
"Show update if available": "Afficher la mise à jour si disponible",
|
|
||||||
"Also check beta release": "Vérifier également la version bêta",
|
|
||||||
"Remember me": "Se souvenir de moi",
|
|
||||||
"Login": "Connexion",
|
|
||||||
"Username": "Nom d'utilisateur",
|
|
||||||
"Password": "Mot de Passe",
|
|
||||||
"Settings": "Paramètres",
|
|
||||||
"Logout": "Déconnexion",
|
|
||||||
"Lowercase only": "Minuscules uniquement",
|
|
||||||
"Convert to Compose": "Convertir en Compose",
|
|
||||||
"Docker Run": "Exécution Docker",
|
|
||||||
"active": "actif",
|
|
||||||
"exited": "arrêté",
|
|
||||||
"inactive": "inactif",
|
|
||||||
"Appearance": "Apparence",
|
|
||||||
"Security": "Sécurité",
|
|
||||||
"About": "À propos",
|
|
||||||
"Allowed commands:": "Commandes autorisées:",
|
|
||||||
"Internal Networks": "Réseaux Internes",
|
|
||||||
"External Networks": "Réseaux Externes",
|
|
||||||
"No External Networks": "Aucun Réseau Externe"
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"languageName": "Türkçe",
|
|
||||||
"authIncorrectCreds": "Yanlış kullanıcı adı veya parola.",
|
|
||||||
"PasswordsDoNotMatch": "Parolalar eşleşmiyor.",
|
|
||||||
"signedInDisp": "{0} olarak oturum açıldı",
|
|
||||||
"signedInDispDisabled": "Yetkilendirme Devre Dışı.",
|
|
||||||
"home": "Anasayfa",
|
|
||||||
"console": "Konsol",
|
|
||||||
"registry": "Kayıt Defteri",
|
|
||||||
"compose": "Compose",
|
|
||||||
"addFirstStackMsg": "İlk yığınınızı oluşturun!",
|
|
||||||
"stackName" : "Yığın Adı",
|
|
||||||
"deployStack": "Dağıtmak",
|
|
||||||
"deleteStack": "Sil",
|
|
||||||
"stopStack": "Dudur",
|
|
||||||
"restartStack": "Yeniden Başlat",
|
|
||||||
"updateStack": "Güncelle",
|
|
||||||
"startStack": "Başlat",
|
|
||||||
"editStack": "Düzenle",
|
|
||||||
"discardStack": "Çıkar",
|
|
||||||
"saveStackDraft": "Kaydet",
|
|
||||||
"notAvailableShort" : "N/A",
|
|
||||||
"deleteStackMsg": "Bu yığını silmek istediğinizden emin misiniz?",
|
|
||||||
"stackNotManagedByDockgeMsg": "Bu yığın Dockge tarafından yönetilmemektedir.",
|
|
||||||
"primaryHostname": "Birincil Ana Bilgisayar Adı",
|
|
||||||
"general": "Genel",
|
|
||||||
"container": "Konteyner | Konteynerler",
|
|
||||||
"scanFolder": "Yığınlar Klasörünü Tara",
|
|
||||||
"dockerImage": "Görüntü",
|
|
||||||
"restartPolicyUnlessStopped": "Durdurulana Kadar",
|
|
||||||
"restartPolicyAlways": "Her zaman",
|
|
||||||
"restartPolicyOnFailure": "Başarısızlıkta",
|
|
||||||
"restartPolicyNo": "Hayır",
|
|
||||||
"environmentVariable": "Ortam Değişkeni | Ortam Değişkenleri",
|
|
||||||
"restartPolicy": "Yeniden Başlatma Politikası",
|
|
||||||
"containerName": "Konteyner Adı",
|
|
||||||
"port": "Port | Portlar",
|
|
||||||
"volume": "Disk Bölümü | Disk Bölümleri",
|
|
||||||
"network": "Ağ | Ağlar",
|
|
||||||
"dependsOn": "Konteyner Bağımlılığı | Konteyner Bağımlılıkları",
|
|
||||||
"addListItem": "{0} Ekle",
|
|
||||||
"deleteContainer": "Sil",
|
|
||||||
"addContainer": "Konteyner Ekle",
|
|
||||||
"addNetwork": "Ağ Ekle",
|
|
||||||
"disableauth.message1": "<strong>Kimlik doğrulamayı devre dışı</strong> bırakmak istediğinizden emin misiniz?",
|
|
||||||
"disableauth.message2": "Cloudflare Access, Authelia veya diğer kimlik doğrulama mekanizmaları gibi Uptime Kuma'nın önünde <strong>üçüncü taraf kimlik doğrulaması uygulamak</strong> istediğiniz senaryolar için tasarlanmıştır.",
|
|
||||||
"passwordNotMatchMsg": "Tekrarlanan parola eşleşmiyor.",
|
|
||||||
"autoGet": "Otomatik Al",
|
|
||||||
"add": "Ekle",
|
|
||||||
"applyToYAML": "YAML'ye uygulayın",
|
|
||||||
"createExternalNetwork": "Oluştur",
|
|
||||||
"addInternalNetwork": "Ekle"
|
|
||||||
}
|
|
@ -71,7 +71,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<label for="name" class="form-label">{{ $t("stackName") }}</label>
|
<label for="name" class="form-label">{{ $t("stackName") }}</label>
|
||||||
<input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
|
<input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
|
||||||
<div class="form-text">{{ $t("Lowercase only") }}</div>
|
<div class="form-text">Lowercase only</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
{{ $t("Allowed commands:") }}
|
Allowed commands:
|
||||||
<template v-for="(command, index) in allowedCommandList" :key="command">
|
<template v-for="(command, index) in allowedCommandList" :key="command">
|
||||||
<code>{{ command }}</code>
|
<code>{{ command }}</code>
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
|
<h2 class="mb-3">Docker Run</h2>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
|
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn-normal btn" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
|
<button class="btn-normal btn" @click="convertDockerRun">Convert to Compose</button>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<router-view ref="child" />
|
<router-view ref="child" />
|
||||||
|
@ -75,7 +75,7 @@ export default {
|
|||||||
subMenus() {
|
subMenus() {
|
||||||
return {
|
return {
|
||||||
general: {
|
general: {
|
||||||
title: this.$t("general"),
|
title: this.$t("General"),
|
||||||
},
|
},
|
||||||
appearance: {
|
appearance: {
|
||||||
title: this.$t("Appearance"),
|
title: this.$t("Appearance"),
|
||||||
|
@ -10,7 +10,7 @@ import { POSITION } from "vue-toastification";
|
|||||||
*
|
*
|
||||||
* Generated by Trelent
|
* Generated by Trelent
|
||||||
*/
|
*/
|
||||||
function getTimezoneOffset(timeZone : string) {
|
function getTimezoneOffset(timeZone) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const tzString = now.toLocaleString("en-US", {
|
const tzString = now.toLocaleString("en-US", {
|
||||||
timeZone,
|
timeZone,
|
||||||
@ -124,6 +124,33 @@ export function hostNameRegexPattern(mqtt = false) {
|
|||||||
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tag color options
|
||||||
|
* Shared between components
|
||||||
|
* @param {any} self Component
|
||||||
|
* @returns {object[]} Colour options
|
||||||
|
*/
|
||||||
|
export function colorOptions(self) {
|
||||||
|
return [
|
||||||
|
{ name: self.$t("Gray"),
|
||||||
|
color: "#4B5563" },
|
||||||
|
{ name: self.$t("Red"),
|
||||||
|
color: "#DC2626" },
|
||||||
|
{ name: self.$t("Orange"),
|
||||||
|
color: "#D97706" },
|
||||||
|
{ name: self.$t("Green"),
|
||||||
|
color: "#059669" },
|
||||||
|
{ name: self.$t("Blue"),
|
||||||
|
color: "#2563EB" },
|
||||||
|
{ name: self.$t("Indigo"),
|
||||||
|
color: "#4F46E5" },
|
||||||
|
{ name: self.$t("Purple"),
|
||||||
|
color: "#7C3AED" },
|
||||||
|
{ name: self.$t("Pink"),
|
||||||
|
color: "#DB2777" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the toast timeout settings from storage.
|
* Loads the toast timeout settings from storage.
|
||||||
* @returns {object} The toast plugin options object.
|
* @returns {object} The toast plugin options object.
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "dockge",
|
"name": "dockge",
|
||||||
"version": "1.1.0",
|
"version": "1.0.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
|
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
|
||||||
"lint": "eslint \"**/*.{ts,vue}\"",
|
"lint": "eslint \"**/*.{ts,vue}\"",
|
||||||
"check-ts": "tsc --noEmit",
|
|
||||||
"start": "tsx ./backend/index.ts",
|
"start": "tsx ./backend/index.ts",
|
||||||
"dev:backend": "cross-env NODE_ENV=development tsx watch ./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",
|
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
||||||
@ -34,8 +33,8 @@
|
|||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"knex": "~2.5.1",
|
"knex": "~2.5.1",
|
||||||
"limiter-es6-compat": "~2.1.2",
|
"limiter-es6-compat": "~2.1.2",
|
||||||
"mysql2": "~3.6.3",
|
"mysql2": "^3.6.3",
|
||||||
"redbean-node": "~0.3.3",
|
"redbean-node": "0.3.2",
|
||||||
"socket.io": "~4.7.2",
|
"socket.io": "~4.7.2",
|
||||||
"socket.io-client": "~4.7.2",
|
"socket.io-client": "~4.7.2",
|
||||||
"timezones-list": "~3.0.2",
|
"timezones-list": "~3.0.2",
|
||||||
@ -50,7 +49,6 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.3",
|
"@fortawesome/vue-fontawesome": "3.0.3",
|
||||||
"@types/bcryptjs": "^2.4.6",
|
|
||||||
"@types/bootstrap": "~5.2.9",
|
"@types/bootstrap": "~5.2.9",
|
||||||
"@types/command-exists": "~1.2.3",
|
"@types/command-exists": "~1.2.3",
|
||||||
"@types/express": "~4.17.21",
|
"@types/express": "~4.17.21",
|
||||||
|
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@ -54,11 +54,11 @@ dependencies:
|
|||||||
specifier: ~2.1.2
|
specifier: ~2.1.2
|
||||||
version: 2.1.2
|
version: 2.1.2
|
||||||
mysql2:
|
mysql2:
|
||||||
specifier: ~3.6.3
|
specifier: ^3.6.3
|
||||||
version: 3.6.3
|
version: 3.6.3
|
||||||
redbean-node:
|
redbean-node:
|
||||||
specifier: ~0.3.3
|
specifier: 0.3.2
|
||||||
version: 0.3.3(mysql2@3.6.3)
|
version: 0.3.2(mysql2@3.6.3)
|
||||||
socket.io:
|
socket.io:
|
||||||
specifier: ~4.7.2
|
specifier: ~4.7.2
|
||||||
version: 4.7.2
|
version: 4.7.2
|
||||||
@ -97,9 +97,6 @@ devDependencies:
|
|||||||
'@fortawesome/vue-fontawesome':
|
'@fortawesome/vue-fontawesome':
|
||||||
specifier: 3.0.3
|
specifier: 3.0.3
|
||||||
version: 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.8)
|
version: 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.8)
|
||||||
'@types/bcryptjs':
|
|
||||||
specifier: ^2.4.6
|
|
||||||
version: 2.4.6
|
|
||||||
'@types/bootstrap':
|
'@types/bootstrap':
|
||||||
specifier: ~5.2.9
|
specifier: ~5.2.9
|
||||||
version: 5.2.9
|
version: 5.2.9
|
||||||
@ -1016,10 +1013,6 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@types/bcryptjs@2.4.6:
|
|
||||||
resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/body-parser@1.19.5:
|
/@types/body-parser@1.19.5:
|
||||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3854,8 +3847,8 @@ packages:
|
|||||||
resolve: 1.22.8
|
resolve: 1.22.8
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/redbean-node@0.3.3(mysql2@3.6.3):
|
/redbean-node@0.3.2(mysql2@3.6.3):
|
||||||
resolution: {integrity: sha512-0J59/QlShdWs1h0lsFHRfb8NwjvgIYTQKwYrvq6FykRmeX1cG2u8AgHEIRVBrm56mtKLRASVy/8ykk6fSntLdw==}
|
resolution: {integrity: sha512-39VMxPWPpPicRlU4FSJJnJuUMoxw5/4envFthHtKnLe+3qWTBje3RMrJTFZcQGLruWQ/s2LgeYzdd+d0O+p+uQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.3.3
|
'@types/node': 20.3.3
|
||||||
await-lock: 2.2.2
|
await-lock: 2.2.2
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler"
|
||||||
"skipLibCheck": true
|
}
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"backend/**/*"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user