2026-01-16 23:52:56 -03:00
|
|
|
import fs from 'node:fs';
|
|
|
|
|
import path from 'node:path';
|
|
|
|
|
import { pathToFileURL } from 'node:url';
|
|
|
|
|
|
|
|
|
|
function findRoot() {
|
|
|
|
|
const candidates = [];
|
|
|
|
|
if (process.env.ACTUAL_SERVER_ROOT) {
|
|
|
|
|
candidates.push(process.env.ACTUAL_SERVER_ROOT);
|
|
|
|
|
}
|
|
|
|
|
candidates.push('/app');
|
|
|
|
|
candidates.push('/usr/src/app');
|
|
|
|
|
candidates.push('/srv/app');
|
|
|
|
|
candidates.push('/opt/actual-server');
|
|
|
|
|
|
|
|
|
|
for (const base of candidates) {
|
|
|
|
|
if (!base) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const accountDb = path.join(base, 'src', 'account-db.js');
|
|
|
|
|
if (fs.existsSync(accountDb)) {
|
|
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const root = findRoot();
|
|
|
|
|
if (!root) {
|
|
|
|
|
console.error('actual server root not found');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const accountDbUrl = pathToFileURL(path.join(root, 'src', 'account-db.js')).href;
|
|
|
|
|
const loadConfigUrl = pathToFileURL(path.join(root, 'src', 'load-config.js')).href;
|
|
|
|
|
|
|
|
|
|
const accountDb = await import(accountDbUrl);
|
|
|
|
|
const { default: finalConfig } = await import(loadConfigUrl);
|
|
|
|
|
|
2026-01-17 02:43:25 -03:00
|
|
|
const openIdEnv = (() => {
|
|
|
|
|
if (
|
|
|
|
|
!process.env.ACTUAL_OPENID_DISCOVERY_URL &&
|
|
|
|
|
!process.env.ACTUAL_OPENID_AUTHORIZATION_ENDPOINT
|
|
|
|
|
) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process.env.ACTUAL_OPENID_DISCOVERY_URL) {
|
|
|
|
|
return {
|
|
|
|
|
issuer: process.env.ACTUAL_OPENID_DISCOVERY_URL,
|
|
|
|
|
client_id: process.env.ACTUAL_OPENID_CLIENT_ID,
|
|
|
|
|
client_secret: process.env.ACTUAL_OPENID_CLIENT_SECRET,
|
|
|
|
|
server_hostname: process.env.ACTUAL_OPENID_SERVER_HOSTNAME,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
issuer: {
|
|
|
|
|
name: process.env.ACTUAL_OPENID_PROVIDER_NAME,
|
|
|
|
|
authorization_endpoint: process.env.ACTUAL_OPENID_AUTHORIZATION_ENDPOINT,
|
|
|
|
|
token_endpoint: process.env.ACTUAL_OPENID_TOKEN_ENDPOINT,
|
|
|
|
|
userinfo_endpoint: process.env.ACTUAL_OPENID_USERINFO_ENDPOINT,
|
|
|
|
|
},
|
|
|
|
|
client_id: process.env.ACTUAL_OPENID_CLIENT_ID,
|
|
|
|
|
client_secret: process.env.ACTUAL_OPENID_CLIENT_SECRET,
|
|
|
|
|
server_hostname: process.env.ACTUAL_OPENID_SERVER_HOSTNAME,
|
|
|
|
|
};
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
const openId = finalConfig?.openId ?? openIdEnv;
|
2026-01-16 23:52:56 -03:00
|
|
|
if (!openId) {
|
|
|
|
|
console.error('missing openid configuration');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const active = accountDb.getActiveLoginMethod();
|
|
|
|
|
if (active === 'openid') {
|
|
|
|
|
console.log('openid already enabled');
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (accountDb.needsBootstrap()) {
|
|
|
|
|
const result = await accountDb.bootstrap({ openId });
|
|
|
|
|
if (result?.error && result.error !== 'already-bootstrapped') {
|
|
|
|
|
console.error(`bootstrap failed: ${result.error}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const result = await accountDb.enableOpenID({ openId });
|
|
|
|
|
if (result?.error) {
|
|
|
|
|
console.error(`enable openid failed: ${result.error}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('openid bootstrap complete');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('openid bootstrap error:', err);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|