363 lines
10 KiB
TypeScript
363 lines
10 KiB
TypeScript
|
|
import * as plugins from './plugins.js';
|
||
|
|
import { IdpCli } from './classes.idpcli.js';
|
||
|
|
|
||
|
|
export { IdpCli } from './classes.idpcli.js';
|
||
|
|
|
||
|
|
const DEFAULT_IDP_URL = 'https://idp.global';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Run the CLI
|
||
|
|
*/
|
||
|
|
export const runCli = async () => {
|
||
|
|
const smartcliInstance = new plugins.smartcli.Smartcli();
|
||
|
|
smartcliInstance.addVersion('1.0.0');
|
||
|
|
|
||
|
|
const getIdpClient = () => {
|
||
|
|
const idpUrl = process.env.IDP_URL || DEFAULT_IDP_URL;
|
||
|
|
return new IdpCli({ idpBaseUrl: idpUrl });
|
||
|
|
};
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Help
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addHelp({
|
||
|
|
helpText: `
|
||
|
|
idp - CLI for idp.global identity provider
|
||
|
|
|
||
|
|
USAGE:
|
||
|
|
idp <command> [options]
|
||
|
|
|
||
|
|
COMMANDS:
|
||
|
|
login Login with email and password
|
||
|
|
login-token Login with API token
|
||
|
|
logout Logout and clear credentials
|
||
|
|
whoami Show current user information
|
||
|
|
|
||
|
|
orgs List organizations
|
||
|
|
orgs-create Create a new organization
|
||
|
|
|
||
|
|
members List organization members
|
||
|
|
invite Invite a user to organization
|
||
|
|
|
||
|
|
sessions List active sessions
|
||
|
|
revoke Revoke a session
|
||
|
|
|
||
|
|
admin-check Check if current user is global admin
|
||
|
|
admin-apps List global apps (admin only)
|
||
|
|
admin-suspend Suspend a user (admin only)
|
||
|
|
|
||
|
|
help Show this help message
|
||
|
|
|
||
|
|
ENVIRONMENT:
|
||
|
|
IDP_URL Override IDP server URL (default: https://idp.global)
|
||
|
|
|
||
|
|
EXAMPLES:
|
||
|
|
idp login
|
||
|
|
idp whoami
|
||
|
|
idp orgs
|
||
|
|
idp members --org <org-id>
|
||
|
|
idp invite --org <org-id> --email user@example.com
|
||
|
|
`,
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Login Commands
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addCommand('login').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const interact = new plugins.smartinteract.SmartInteract();
|
||
|
|
|
||
|
|
const emailAnswer = await interact.askQuestion({
|
||
|
|
type: 'input',
|
||
|
|
name: 'email',
|
||
|
|
message: 'Email:',
|
||
|
|
default: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
const passwordAnswer = await interact.askQuestion({
|
||
|
|
type: 'password',
|
||
|
|
name: 'password',
|
||
|
|
message: 'Password:',
|
||
|
|
default: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
await client.loginWithPassword(emailAnswer.value as string, passwordAnswer.value as string);
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('login-token').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const interact = new plugins.smartinteract.SmartInteract();
|
||
|
|
|
||
|
|
const tokenAnswer = await interact.askQuestion({
|
||
|
|
type: 'password',
|
||
|
|
name: 'token',
|
||
|
|
message: 'API Token:',
|
||
|
|
default: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
await client.loginWithApiToken(tokenAnswer.value as string);
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('logout').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
await client.logout();
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// User Commands
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addCommand('whoami').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const user = await client.whoami();
|
||
|
|
|
||
|
|
if (user) {
|
||
|
|
console.log('\nUser Information:');
|
||
|
|
console.log(` ID: ${user.id}`);
|
||
|
|
console.log(` Name: ${user.data?.name || 'N/A'}`);
|
||
|
|
console.log(` Username: ${user.data?.username || 'N/A'}`);
|
||
|
|
console.log(` Email: ${user.data?.email || 'N/A'}`);
|
||
|
|
console.log(` Status: ${user.data?.status || 'N/A'}`);
|
||
|
|
console.log(` Global Admin: ${user.data?.isGlobalAdmin ? 'Yes' : 'No'}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('sessions').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const sessions = await client.getSessions();
|
||
|
|
|
||
|
|
if (sessions) {
|
||
|
|
console.log('\nActive Sessions:');
|
||
|
|
for (const session of sessions) {
|
||
|
|
console.log(` - ${session.id}`);
|
||
|
|
console.log(` Device: ${session.deviceName || 'Unknown'}`);
|
||
|
|
console.log(` Browser: ${session.browser || 'Unknown'}`);
|
||
|
|
console.log(` OS: ${session.os || 'Unknown'}`);
|
||
|
|
console.log(` Last Active: ${new Date(session.lastActive).toLocaleString()}`);
|
||
|
|
console.log(` Current: ${session.isCurrent ? 'Yes' : 'No'}`);
|
||
|
|
console.log('');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('revoke').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const sessionId = argv.session || argv.s || argv._[1];
|
||
|
|
|
||
|
|
if (!sessionId) {
|
||
|
|
console.error('Please provide a session ID: idp revoke --session <session-id>');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const success = await client.revokeSession(sessionId);
|
||
|
|
if (success) {
|
||
|
|
console.log('Session revoked successfully.');
|
||
|
|
} else {
|
||
|
|
console.error('Failed to revoke session.');
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Organization Commands
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addCommand('orgs').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const result = await client.getOrganizations();
|
||
|
|
|
||
|
|
if (result) {
|
||
|
|
console.log('\nOrganizations:');
|
||
|
|
for (const org of result.organizations) {
|
||
|
|
const role = result.roles.find((r) => r.data?.organizationId === org.id);
|
||
|
|
console.log(` - ${org.data?.name} (${org.id})`);
|
||
|
|
console.log(` Slug: ${org.data?.slug}`);
|
||
|
|
console.log(` Roles: ${role?.data?.roles?.join(', ') || 'Unknown'}`);
|
||
|
|
console.log('');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('orgs-create').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const interact = new plugins.smartinteract.SmartInteract();
|
||
|
|
|
||
|
|
const nameAnswer = await interact.askQuestion({
|
||
|
|
type: 'input',
|
||
|
|
name: 'name',
|
||
|
|
message: 'Organization Name:',
|
||
|
|
default: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
const slugAnswer = await interact.askQuestion({
|
||
|
|
type: 'input',
|
||
|
|
name: 'slug',
|
||
|
|
message: 'Organization Slug:',
|
||
|
|
default: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
// First check availability
|
||
|
|
const checkResult = await client.createOrganization(
|
||
|
|
nameAnswer.value as string,
|
||
|
|
slugAnswer.value as string,
|
||
|
|
'checkAvailability'
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!checkResult?.nameAvailable) {
|
||
|
|
console.error('Organization name or slug is not available.');
|
||
|
|
await client.disconnect();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Then create
|
||
|
|
const result = await client.createOrganization(
|
||
|
|
nameAnswer.value as string,
|
||
|
|
slugAnswer.value as string,
|
||
|
|
'manifest'
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result?.resultingOrganization) {
|
||
|
|
console.log('\nOrganization created successfully!');
|
||
|
|
console.log(` ID: ${result.resultingOrganization.id}`);
|
||
|
|
console.log(` Name: ${result.resultingOrganization.data?.name}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Member Commands
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addCommand('members').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const orgId = argv.org || argv.o || argv._[1];
|
||
|
|
|
||
|
|
if (!orgId) {
|
||
|
|
console.error('Please provide an organization ID: idp members --org <org-id>');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const members = await client.getOrgMembers(orgId);
|
||
|
|
|
||
|
|
if (members) {
|
||
|
|
console.log('\nOrganization Members:');
|
||
|
|
for (const member of members) {
|
||
|
|
console.log(` - ${member.user.data?.name || 'Unknown'}`);
|
||
|
|
console.log(` Email: ${member.user.data?.email || 'N/A'}`);
|
||
|
|
console.log(` Roles: ${member.role.data?.roles?.join(', ') || 'Unknown'}`);
|
||
|
|
console.log('');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('invite').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const orgId = argv.org || argv.o;
|
||
|
|
const email = argv.email || argv.e || argv._[1];
|
||
|
|
|
||
|
|
if (!orgId || !email) {
|
||
|
|
console.error('Please provide organization ID and email:');
|
||
|
|
console.error(' idp invite --org <org-id> --email user@example.com');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = await client.inviteMember(orgId, email);
|
||
|
|
|
||
|
|
if (result?.success) {
|
||
|
|
console.log(`Invitation sent to ${email}`);
|
||
|
|
} else {
|
||
|
|
console.error(`Failed to send invitation: ${result?.message || 'Unknown error'}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Admin Commands
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.addCommand('admin-check').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const isAdmin = await client.checkGlobalAdmin();
|
||
|
|
|
||
|
|
if (isAdmin) {
|
||
|
|
console.log('You are a global admin.');
|
||
|
|
} else {
|
||
|
|
console.log('You are not a global admin.');
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('admin-apps').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const apps = await client.getGlobalAppStats();
|
||
|
|
|
||
|
|
if (apps) {
|
||
|
|
console.log('\nGlobal Apps:');
|
||
|
|
for (const appInfo of apps) {
|
||
|
|
console.log(` - ${appInfo.app.data?.name}`);
|
||
|
|
console.log(` ID: ${appInfo.app.id}`);
|
||
|
|
console.log(` Connections: ${appInfo.connectionCount}`);
|
||
|
|
console.log('');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
smartcliInstance.addCommand('admin-suspend').subscribe(async (argv) => {
|
||
|
|
const client = getIdpClient();
|
||
|
|
const userId = argv.user || argv.u || argv._[1];
|
||
|
|
|
||
|
|
if (!userId) {
|
||
|
|
console.error('Please provide a user ID: idp admin-suspend --user <user-id>');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const interact = new plugins.smartinteract.SmartInteract();
|
||
|
|
const confirmAnswer = await interact.askQuestion({
|
||
|
|
type: 'confirm',
|
||
|
|
name: 'confirm',
|
||
|
|
message: `Are you sure you want to suspend user ${userId}?`,
|
||
|
|
default: false,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (confirmAnswer.value) {
|
||
|
|
const success = await client.suspendUser(userId);
|
||
|
|
if (success) {
|
||
|
|
console.log('User suspended successfully.');
|
||
|
|
} else {
|
||
|
|
console.error('Failed to suspend user.');
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
console.log('Operation cancelled.');
|
||
|
|
}
|
||
|
|
|
||
|
|
await client.disconnect();
|
||
|
|
});
|
||
|
|
|
||
|
|
// ============================================
|
||
|
|
// Default/Standard command
|
||
|
|
// ============================================
|
||
|
|
smartcliInstance.standardCommand().subscribe(async (argv) => {
|
||
|
|
// If no command specified, show help
|
||
|
|
smartcliInstance.triggerCommand('help', argv);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Start parsing
|
||
|
|
smartcliInstance.startParse();
|
||
|
|
};
|
||
|
|
|
||
|
|
// Auto-run if this is the main module
|
||
|
|
runCli().catch(console.error);
|