fix: make startup bootstrap production-safe
This commit is contained in:
@@ -41,6 +41,7 @@ export const testCloudlyConfig: cloudly.ICloudlyConfig = {
|
|||||||
bucketName: 'cloudly_test_bucket'
|
bucketName: 'cloudly_test_bucket'
|
||||||
}),
|
}),
|
||||||
sslMode: 'none',
|
sslMode: 'none',
|
||||||
|
servezoneAdminaccount: 'testadmin:testpassword',
|
||||||
...(() => {
|
...(() => {
|
||||||
if (process.env.NPMCI_SECRET01) {
|
if (process.env.NPMCI_SECRET01) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ tap.test('DEBUG: Check existing users', async () => {
|
|||||||
console.log(` - User: ${user.data.username} (ID: ${user.id})`);
|
console.log(` - User: ${user.data.username} (ID: ${user.id})`);
|
||||||
console.log(` - Type: ${user.data.type}`);
|
console.log(` - Type: ${user.data.type}`);
|
||||||
console.log(` - Role: ${user.data.role}`);
|
console.log(` - Role: ${user.data.role}`);
|
||||||
console.log(` - Tokens: ${user.data.tokens.length}`);
|
console.log(` - Tokens: ${user.data.tokens?.length ?? 0}`);
|
||||||
for (const token of user.data.tokens) {
|
for (const token of user.data.tokens ?? []) {
|
||||||
console.log(` - Token: '${token.token}' | Roles: ${token.assignedRoles?.join(', ')}`);
|
console.log(` - Token: '${token.token}' | Roles: ${token.assignedRoles?.join(', ')}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -11,7 +11,7 @@ early.stop();
|
|||||||
* starts the cloudly instance
|
* starts the cloudly instance
|
||||||
*/
|
*/
|
||||||
const runCli = async () => {
|
const runCli = async () => {
|
||||||
logger.log('info', process.env.SERVEZONE_ENVIRONMENT);
|
logger.log('info', process.env.SERVEZONE_ENVIRONMENT || '');
|
||||||
const cloudlyInstance = new Cloudly();
|
const cloudlyInstance = new Cloudly();
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
@@ -20,8 +20,11 @@ const runCli = async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await cloudlyInstance.start();
|
await cloudlyInstance.start();
|
||||||
|
if (process.env.SERVEZONE_INSTALL_DEMO_DATA === 'true') {
|
||||||
|
logger.log('warn', 'SERVEZONE_INSTALL_DEMO_DATA=true: installing destructive demo data');
|
||||||
const demoMod = await import('./00demo/index.js');
|
const demoMod = await import('./00demo/index.js');
|
||||||
demoMod.installDemoData(cloudlyInstance);
|
await demoMod.installDemoData(cloudlyInstance);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { runCli, Cloudly };
|
export { runCli, Cloudly };
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export class CloudlyAuthManager {
|
|||||||
this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys);
|
this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.bootstrapInitialAdmin();
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.admin.IReq_Admin_LoginWithUsernameAndPassword>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.admin.IReq_Admin_LoginWithUsernameAndPassword>(
|
||||||
'adminLoginWithUsernameAndPassword',
|
'adminLoginWithUsernameAndPassword',
|
||||||
@@ -82,6 +84,42 @@ export class CloudlyAuthManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async bootstrapInitialAdmin() {
|
||||||
|
const users = await this.CUser.getInstances({});
|
||||||
|
const hasHumanUser = users.some((userArg) => userArg.data?.type === 'human');
|
||||||
|
if (hasHumanUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminAccount = this.cloudlyRef.config.data.servezoneAdminaccount;
|
||||||
|
if (!adminAccount) {
|
||||||
|
throw new Error('SERVEZONE_ADMINACCOUNT is required for first-run Cloudly bootstrap');
|
||||||
|
}
|
||||||
|
|
||||||
|
const separatorIndex = adminAccount.indexOf(':');
|
||||||
|
if (separatorIndex <= 0 || separatorIndex === adminAccount.length - 1) {
|
||||||
|
throw new Error('SERVEZONE_ADMINACCOUNT must use username:password format');
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = adminAccount.slice(0, separatorIndex).trim();
|
||||||
|
const password = adminAccount.slice(separatorIndex + 1);
|
||||||
|
if (!username || !password) {
|
||||||
|
throw new Error('SERVEZONE_ADMINACCOUNT must include a non-empty username and password');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = new this.CUser({
|
||||||
|
id: await this.CUser.getNewId(),
|
||||||
|
data: {
|
||||||
|
type: 'human',
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
role: 'admin',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await user.save();
|
||||||
|
logger.log('success', `created initial admin user ${username}`);
|
||||||
|
}
|
||||||
|
|
||||||
public async stop() {}
|
public async stop() {}
|
||||||
|
|
||||||
public validIdentityGuard = new plugins.smartguard.Guard<{
|
public validIdentityGuard = new plugins.smartguard.Guard<{
|
||||||
|
|||||||
Reference in New Issue
Block a user