2026-08-16 11:03:25 +02:00
|
|
|
import crypto from "node:crypto";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import { password as promptPassword } from "@inquirer/prompts";
|
|
|
|
|
import { stringify } from "yaml";
|
|
|
|
|
import { hashPassword } from "../../lib/auth/password";
|
|
|
|
|
import { upsertEnvVar } from "../lib/env-file";
|
|
|
|
|
import { resolveConfigPath } from "../lib/paths";
|
|
|
|
|
import { slug } from "../lib/slug";
|
|
|
|
|
|
|
|
|
|
export interface UsersOptions {
|
|
|
|
|
config?: string;
|
|
|
|
|
inline?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function printSnippet(heading: string, entry: Record<string, unknown>): void {
|
|
|
|
|
console.log(`\n${heading}\n`);
|
|
|
|
|
console.log(stringify([entry]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function promptNewPassword(): Promise<string> {
|
|
|
|
|
for (;;) {
|
|
|
|
|
const first = await promptPassword({ message: "Password", mask: true });
|
2026-08-16 13:55:25 +02:00
|
|
|
const second = await promptPassword({
|
|
|
|
|
message: "Confirm password",
|
|
|
|
|
mask: true,
|
|
|
|
|
});
|
2026-08-16 11:03:25 +02:00
|
|
|
if (first === second) return first;
|
|
|
|
|
console.error("Passwords did not match, try again.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-16 13:55:25 +02:00
|
|
|
export async function usersAddCommand(
|
|
|
|
|
username: string,
|
|
|
|
|
opts: UsersOptions,
|
|
|
|
|
): Promise<void> {
|
2026-08-16 11:03:25 +02:00
|
|
|
const password = await promptNewPassword();
|
|
|
|
|
const passwordHash = await hashPassword(password);
|
|
|
|
|
|
|
|
|
|
if (opts.inline) {
|
2026-08-16 13:55:25 +02:00
|
|
|
printSnippet("Add this under `auth.users:` in your config file:", {
|
|
|
|
|
username,
|
|
|
|
|
passwordHash,
|
|
|
|
|
});
|
2026-08-16 11:03:25 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const configPath = resolveConfigPath(opts.config);
|
|
|
|
|
const envPath = path.join(path.dirname(configPath), ".env");
|
|
|
|
|
const varName = `TRIGGERSHELL_USER_${slug(username)}_PASSWORD_HASH`;
|
|
|
|
|
upsertEnvVar(envPath, varName, passwordHash);
|
|
|
|
|
|
|
|
|
|
console.log(`Stored ${varName} in ${envPath}`);
|
|
|
|
|
printSnippet("Add this under `auth.users:` in your config file:", {
|
|
|
|
|
username,
|
|
|
|
|
passwordHash: `\${${varName}}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-16 13:55:25 +02:00
|
|
|
export async function usersAddTokenCommand(
|
|
|
|
|
name: string,
|
|
|
|
|
opts: UsersOptions,
|
|
|
|
|
): Promise<void> {
|
2026-08-16 11:03:25 +02:00
|
|
|
const token = crypto.randomBytes(32).toString("hex");
|
|
|
|
|
const tokenHash = `sha256:${crypto.createHash("sha256").update(token).digest("hex")}`;
|
|
|
|
|
|
|
|
|
|
console.log("\nSave this token now - it will not be shown again:");
|
|
|
|
|
console.log(` ${token}`);
|
|
|
|
|
console.log(`Use it as: Authorization: Bearer ${token}`);
|
|
|
|
|
|
|
|
|
|
if (opts.inline) {
|
2026-08-16 13:55:25 +02:00
|
|
|
printSnippet("Add this under `auth.tokens:` in your config file:", {
|
|
|
|
|
name,
|
|
|
|
|
tokenHash,
|
|
|
|
|
});
|
2026-08-16 11:03:25 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const configPath = resolveConfigPath(opts.config);
|
|
|
|
|
const envPath = path.join(path.dirname(configPath), ".env");
|
|
|
|
|
const varName = `TRIGGERSHELL_TOKEN_${slug(name)}_HASH`;
|
|
|
|
|
upsertEnvVar(envPath, varName, tokenHash);
|
|
|
|
|
|
|
|
|
|
console.log(`Stored ${varName} in ${envPath}`);
|
|
|
|
|
printSnippet("Add this under `auth.tokens:` in your config file:", {
|
|
|
|
|
name,
|
|
|
|
|
tokenHash: `\${${varName}}`,
|
|
|
|
|
});
|
|
|
|
|
}
|