mirror of
https://github.com/ajnart/homarr.git
synced 2025-10-26 00:56:12 +02:00
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
/cli/node_modules/
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
@@ -49,6 +50,7 @@ data/configs
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
/cli/.yarn/
|
||||
|
||||
#envfiles
|
||||
.env
|
||||
|
||||
@@ -21,6 +21,7 @@ COPY ./drizzle ./drizzle
|
||||
|
||||
COPY ./drizzle/migrate ./migrate
|
||||
COPY ./tsconfig.json ./migrate/tsconfig.json
|
||||
COPY ./cli ./cli
|
||||
|
||||
RUN mkdir /data
|
||||
|
||||
@@ -43,6 +44,10 @@ RUN mv node_modules ./migrate/node_modules
|
||||
# Copy temp node_modules of app to app folder
|
||||
RUN mv _node_modules node_modules
|
||||
|
||||
RUN echo '#!/bin/bash\nnode /app/cli/cli.js "$@"' > /usr/bin/homarr
|
||||
RUN chmod +x /usr/bin/homarr
|
||||
RUN cd /app/cli && yarn --immutable
|
||||
|
||||
# Expose the default application port
|
||||
EXPOSE $PORT
|
||||
ENV PORT=${PORT}
|
||||
|
||||
30
cli/cli.js
Normal file
30
cli/cli.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import yargs from 'yargs';
|
||||
|
||||
import { resetPasswordForOwner } from './commands/reset-owner-password.js';
|
||||
import { resetPasswordForUsername } from './commands/reset-password.js';
|
||||
|
||||
yargs(process.argv.slice(2))
|
||||
.scriptName('homarr')
|
||||
.usage('$0 <cmd> [args]')
|
||||
.command('reset-owner-password', 'Resets the current owner password without UI access', async () => {
|
||||
await resetPasswordForOwner();
|
||||
})
|
||||
.command(
|
||||
'reset-password',
|
||||
'Reset the password of a specific user without UI access',
|
||||
(yargs) => {
|
||||
yargs.option('username', {
|
||||
type: 'string',
|
||||
describe: 'Username of user',
|
||||
demandOption: true
|
||||
});
|
||||
},
|
||||
async (argv) => {
|
||||
await resetPasswordForUsername(argv.username);
|
||||
}
|
||||
)
|
||||
.version(false)
|
||||
.showHelpOnFail(true)
|
||||
.alias('h', 'help')
|
||||
.demandCommand()
|
||||
.help().argv;
|
||||
55
cli/commands/reset-owner-password.js
Normal file
55
cli/commands/reset-owner-password.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
import boxen from 'boxen';
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Consola from 'consola';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||
|
||||
export async function resetPasswordForOwner() {
|
||||
if (!process.env.DATABASE_URL) {
|
||||
Consola.error('Unable to connect to database due to missing database URL environment variable');
|
||||
return;
|
||||
}
|
||||
|
||||
Consola.info('Connecting to the database...');
|
||||
const sqlite = new Database(process.env.DATABASE_URL.replace('file:', ''));
|
||||
const db = drizzle(sqlite);
|
||||
|
||||
Consola.info('Connected to the database ' + chalk.green('✓'));
|
||||
Consola.info('Generating new random password...');
|
||||
|
||||
const newPassword = crypto.randomUUID();
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
const hashedPassword = bcrypt.hashSync(newPassword, salt);
|
||||
|
||||
try {
|
||||
await db.transaction((tx) => {
|
||||
tx.run(
|
||||
sql`DELETE FROM session WHERE userId = (SELECT id FROM user WHERE is_owner = 1 LIMIT 1)`
|
||||
);
|
||||
tx.run(sql`UPDATE user SET password = ${hashedPassword} WHERE is_owner = 1 LIMIT 1;`);
|
||||
});
|
||||
console.log(
|
||||
boxen(`New owner password is '${chalk.red(newPassword)}'. You can now log in with this password.\nExising sessions have been destroyed and need to login again with the new passowrd.`, {
|
||||
dimBorder: true,
|
||||
borderStyle: 'round',
|
||||
padding: {
|
||||
left: 1,
|
||||
right: 1
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
Consola.error('Failed to update password', err);
|
||||
} finally {
|
||||
Consola.info('Command has completed');
|
||||
}
|
||||
}
|
||||
56
cli/commands/reset-password.js
Normal file
56
cli/commands/reset-password.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
import Consola from 'consola';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
import boxen from 'boxen';
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||
|
||||
export async function resetPasswordForUsername(username) {
|
||||
if (!process.env.DATABASE_URL) {
|
||||
Consola.error('Unable to connect to database due to missing database URL environment variable');
|
||||
return;
|
||||
}
|
||||
|
||||
Consola.info('Connecting to the database...');
|
||||
const sqlite = new Database(process.env.DATABASE_URL.replace('file:', ''));
|
||||
const db = drizzle(sqlite);
|
||||
|
||||
Consola.info('Generating new random password...');
|
||||
|
||||
const newPassword = crypto.randomUUID();
|
||||
const salt = bcrypt.genSaltSync(10);
|
||||
const hashedPassword = bcrypt.hashSync(newPassword, salt);
|
||||
|
||||
Consola.info(`Updating password for user '${username}'`);
|
||||
|
||||
try {
|
||||
await db.transaction((tx) => {
|
||||
tx.run(
|
||||
sql`DELETE FROM session WHERE userId = (SELECT id FROM user WHERE name = ${username} LIMIT 1)`
|
||||
);
|
||||
tx.run(sql`UPDATE user SET password = ${hashedPassword} WHERE id = (SELECT id FROM user WHERE name = ${username} LIMIT 1) LIMIT 1`);
|
||||
});
|
||||
console.log(
|
||||
boxen(`New password for '${username}' is '${chalk.red(newPassword)}'. You can now log in with this password.\nExising sessions have been destroyed and need to login again with the new passowrd.`, {
|
||||
dimBorder: true,
|
||||
borderStyle: 'round',
|
||||
padding: {
|
||||
left: 1,
|
||||
right: 1
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
Consola.error('Failed to update password', err);
|
||||
} finally {
|
||||
Consola.info('Command has completed');
|
||||
}
|
||||
}
|
||||
12
cli/package.json
Normal file
12
cli/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-sqlite3": "^8.6.0",
|
||||
"boxen": "^7.1.1",
|
||||
"chalk": "^5.3.0",
|
||||
"consola": "^3.0.0",
|
||||
"drizzle-orm": "^0.28.6",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
1371
cli/yarn.lock
Normal file
1371
cli/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user