Merge branch 'canary' into add-disable-recurse-submodules-option

This commit is contained in:
Mauricio Siu
2025-04-26 16:10:59 -06:00
10 changed files with 390 additions and 36 deletions

View File

@@ -201,7 +201,7 @@ const { handler, api } = betterAuth({
const host =
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://dokploy.com";
: "https://app.dokploy.com";
const inviteLink = `${host}/invitation?token=${data.id}`;
await sendEmail({

View File

@@ -76,7 +76,7 @@ CURRENT_USER=$USER
echo "Installing requirements for: OS: $OS_TYPE"
if [ $EUID != 0 ]; then
echo "Please run this script as root or with sudo ❌"
echo "Please run this script as root or with sudo ❌"
exit
fi
@@ -263,7 +263,7 @@ const setupMainDirectory = () => `
# Create the /etc/dokploy directory
mkdir -p /etc/dokploy
chmod 777 /etc/dokploy
echo "Directory /etc/dokploy created ✅"
fi
`;
@@ -276,16 +276,16 @@ export const setupSwarm = () => `
# Get IP address
get_ip() {
local ip=""
# Try IPv4 with multiple services
# First attempt: ifconfig.io
ip=\$(curl -4s --connect-timeout 5 https://ifconfig.io 2>/dev/null)
# Second attempt: icanhazip.com
if [ -z "\$ip" ]; then
ip=\$(curl -4s --connect-timeout 5 https://icanhazip.com 2>/dev/null)
fi
# Third attempt: ipecho.net
if [ -z "\$ip" ]; then
ip=\$(curl -4s --connect-timeout 5 https://ipecho.net/plain 2>/dev/null)
@@ -295,12 +295,12 @@ export const setupSwarm = () => `
if [ -z "\$ip" ]; then
# Try IPv6 with ifconfig.io
ip=\$(curl -6s --connect-timeout 5 https://ifconfig.io 2>/dev/null)
# Try IPv6 with icanhazip.com
if [ -z "\$ip" ]; then
ip=\$(curl -6s --connect-timeout 5 https://icanhazip.com 2>/dev/null)
fi
# Try IPv6 with ipecho.net
if [ -z "\$ip" ]; then
ip=\$(curl -6s --connect-timeout 5 https://ipecho.net/plain 2>/dev/null)
@@ -549,7 +549,7 @@ export const createTraefikInstance = () => {
sleep 8
echo "Traefik migrated to Standalone ✅"
fi
if docker inspect dokploy-traefik > /dev/null 2>&1; then
echo "Traefik already exists ✅"
else
@@ -577,7 +577,7 @@ const installNixpacks = () => `
if command_exists nixpacks; then
echo "Nixpacks already installed ✅"
else
export NIXPACKS_VERSION=1.29.1
export NIXPACKS_VERSION=1.35.0
bash -c "$(curl -fsSL https://nixpacks.com/install.sh)"
echo "Nixpacks version $NIXPACKS_VERSION installed ✅"
fi

View File

@@ -1,4 +1,4 @@
import { randomBytes } from "node:crypto";
import { randomBytes, createHmac } from "node:crypto";
import { existsSync } from "node:fs";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
@@ -24,6 +24,12 @@ export interface Template {
domains: DomainSchema[];
}
export interface GenerateJWTOptions {
length?: number;
secret?: string;
payload?: Record<string, unknown> | undefined;
}
export const generateRandomDomain = ({
serverIp,
projectName,
@@ -61,8 +67,48 @@ export function generateBase64(bytes = 32): string {
return randomBytes(bytes).toString("base64");
}
export function generateJwt(length = 256): string {
return randomBytes(length).toString("hex");
function safeBase64(str: string): string {
return str.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
function objToJWTBase64(obj: any): string {
return safeBase64(
Buffer.from(JSON.stringify(obj), "utf8").toString("base64"),
);
}
export function generateJwt(options: GenerateJWTOptions = {}): string {
let { length, secret, payload = {} } = options;
if (length) {
return randomBytes(length).toString("hex");
}
const encodedHeader = objToJWTBase64({
alg: "HS256",
typ: "JWT",
});
if (!payload.iss) {
payload.iss = "dokploy";
}
if (!payload.iat) {
payload.iat = Math.floor(Date.now() / 1000);
}
if (!payload.exp) {
payload.exp = Math.floor(new Date("2030-01-01T00:00:00Z").getTime() / 1000);
}
const encodedPayload = objToJWTBase64({
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(new Date("2030-01-01T00:00:00Z").getTime() / 1000),
...payload,
});
if (!secret) {
secret = randomBytes(32).toString("hex");
}
const signature = safeBase64(
createHmac("SHA256", secret)
.update(`${encodedHeader}.${encodedPayload}`)
.digest("base64"),
);
return `${encodedHeader}.${encodedPayload}.${signature}`;
}
/**

View File

@@ -65,7 +65,7 @@ export interface Template {
/**
* Process a string value and replace variables
*/
function processValue(
export function processValue(
value: string,
variables: Record<string, string>,
schema: Schema,
@@ -84,11 +84,11 @@ function processValue(
const length = Number.parseInt(varName.split(":")[1], 10) || 32;
return generateBase64(length);
}
if (varName.startsWith("password:")) {
const length = Number.parseInt(varName.split(":")[1], 10) || 16;
return generatePassword(length);
}
if (varName === "password") {
return generatePassword(16);
}
@@ -97,14 +97,31 @@ function processValue(
const length = Number.parseInt(varName.split(":")[1], 10) || 8;
return generateHash(length);
}
if (varName === "hash") {
return generateHash();
}
if (varName === "uuid") {
return crypto.randomUUID();
}
if (varName === "timestamp") {
if (varName === "timestamp" || varName === "timestampms") {
return Date.now().toString();
}
if (varName === "timestamps") {
return Math.round(Date.now() / 1000).toString();
}
if (varName.startsWith("timestampms:")) {
return new Date(varName.slice(12)).getTime().toString();
}
if (varName.startsWith("timestamps:")) {
return Math.round(
new Date(varName.slice(11)).getTime() / 1000,
).toString();
}
if (varName === "randomPort") {
return Math.floor(Math.random() * 65535).toString();
}
@@ -114,8 +131,34 @@ function processValue(
}
if (varName.startsWith("jwt:")) {
const length = Number.parseInt(varName.split(":")[1], 10) || 256;
return generateJwt(length);
const params: string[] = varName.split(":").slice(1);
if (params.length === 1 && params[0] && params[0].match(/^\d{1,3}$/)) {
return generateJwt({ length: Number.parseInt(params[0], 10) });
}
let [secret, payload] = params;
if (typeof payload === "string" && variables[payload]) {
payload = variables[payload];
}
if (
typeof payload === "string" &&
payload.startsWith("{") &&
payload.endsWith("}")
) {
try {
payload = JSON.parse(payload);
} catch (e) {
// If payload is not a valid JSON, invalid it
payload = undefined;
console.error("Invalid JWT payload", e);
}
}
if (typeof payload !== "object") {
payload = undefined;
}
return generateJwt({
secret: secret ? variables[secret] || secret : undefined,
payload: payload as any,
});
}
if (varName === "username") {
@@ -147,7 +190,7 @@ export function processVariables(
): Record<string, string> {
const variables: Record<string, string> = {};
// First pass: Process variables that don't depend on other variables
// First pass: Process some variables that don't depend on other variables
for (const [key, value] of Object.entries(template.variables)) {
if (typeof value !== "string") continue;
@@ -161,6 +204,8 @@ export function processVariables(
const match = value.match(/\${password:(\d+)}/);
const length = match?.[1] ? Number.parseInt(match[1], 10) : 16;
variables[key] = generatePassword(length);
} else if (value === "${hash}") {
variables[key] = generateHash();
} else if (value.startsWith("${hash:")) {
const match = value.match(/\${hash:(\d+)}/);
const length = match?.[1] ? Number.parseInt(match[1], 10) : 8;