feat(cli): add command to show qr code (#2518)

* refactor cli, add commands

* add docs

* improve

* fix ec mode order
This commit is contained in:
Bernd Storath
2026-03-05 11:53:27 +01:00
committed by GitHub
parent 47f81dd66a
commit 5228734c98
10 changed files with 292 additions and 94 deletions
+26 -74
View File
@@ -1,84 +1,38 @@
#!/usr/bin/env node
// ! Auto Imports are not supported in this file
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
import type { Resolvable, SubCommandsDef } from 'citty';
import { defineCommand, runMain } from 'citty';
import { consola } from 'consola';
import { eq } from 'drizzle-orm';
import packageJson from '../package.json';
import * as schema from '../server/database/schema';
import { hashPassword } from '../server/utils/password';
const client = createClient({ url: 'file:/etc/wireguard/wg-easy.db' });
const db = drizzle({ client, schema });
// Commands
import dbAdminReset from './admin/reset';
import clientsList from './clients/list';
import clientsQr from './clients/qr';
const subCommands = [dbAdminReset, clientsList, clientsQr] as const;
const dbAdminReset = defineCommand({
meta: {
name: 'db:admin:reset',
description: 'Reset the admin user password and TOTP settings',
},
args: {
password: {
type: 'string',
description: 'New password for the admin user',
required: false,
},
},
async run(ctx) {
let password = ctx.args.password || undefined;
if (!password) {
password = await consola.prompt('Please enter a new password:', {
type: 'text',
});
// from citty
function resolveValue<T>(input: Resolvable<T>): T | Promise<T> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return typeof input === 'function' ? (input as any)() : input;
}
async function generateSubCommands(): Promise<SubCommandsDef> {
const subCommandsMap: Record<string, SubCommandsDef[string]> = {};
for (const cmd of subCommands) {
const cmdMeta = await resolveValue(cmd.meta || {});
if (!cmdMeta.name) {
console.warn('Skipping command without name:', cmd);
continue;
}
if (!password) {
consola.error('Password is required');
return;
}
if (password.length < 12) {
consola.error('Password must be at least 12 characters long');
return;
}
console.info('Setting new password for admin user...');
const hash = await hashPassword(password);
subCommandsMap[cmdMeta.name] = cmd;
}
const user = await db.transaction(async (tx) => {
const user = await tx
.select()
.from(schema.user)
.where(eq(schema.user.id, 1))
.get();
return subCommandsMap;
}
if (!user) {
consola.error('Admin user not found');
return;
}
await tx
.update(schema.user)
.set({
password: hash,
totpVerified: false,
totpKey: null,
})
.where(eq(schema.user.id, 1));
return user;
});
if (!user) {
consola.error('Failed to update admin user');
return;
}
consola.success(
`Successfully updated admin user ${user.id} (${user.username})`
);
},
});
const subCommandsMap = await generateSubCommands();
const main = defineCommand({
meta: {
@@ -86,9 +40,7 @@ const main = defineCommand({
version: packageJson.version,
description: 'Command Line Interface',
},
subCommands: {
'db:admin:reset': dbAdminReset,
},
subCommands: subCommandsMap,
});
runMain(main);