mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
add delete client/device cli command
This commit is contained in:
123
cli/commands/deleteClient.ts
Normal file
123
cli/commands/deleteClient.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { CommandModule } from "yargs";
|
||||||
|
import { db, clients, olms, currentFingerprint, userClients, approvals } from "@server/db";
|
||||||
|
import { eq, and, inArray } from "drizzle-orm";
|
||||||
|
|
||||||
|
type DeleteClientArgs = {
|
||||||
|
orgId: string;
|
||||||
|
niceId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteClient: CommandModule<{}, DeleteClientArgs> = {
|
||||||
|
command: "delete-client",
|
||||||
|
describe:
|
||||||
|
"Delete a client and all associated data (OLMs, current fingerprint, userClients, approvals). Snapshots are preserved.",
|
||||||
|
builder: (yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("orgId", {
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
describe: "The organization ID"
|
||||||
|
})
|
||||||
|
.option("niceId", {
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
describe: "The client niceId (identifier)"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (argv: { orgId: string; niceId: string }) => {
|
||||||
|
try {
|
||||||
|
const { orgId, niceId } = argv;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Deleting client with orgId: ${orgId}, niceId: ${niceId}...`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find the client
|
||||||
|
const [client] = await db
|
||||||
|
.select()
|
||||||
|
.from(clients)
|
||||||
|
.where(and(eq(clients.orgId, orgId), eq(clients.niceId, niceId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
console.error(
|
||||||
|
`Error: Client with orgId "${orgId}" and niceId "${niceId}" not found.`
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientId = client.clientId;
|
||||||
|
console.log(`Found client with clientId: ${clientId}`);
|
||||||
|
|
||||||
|
// Find all OLMs associated with this client
|
||||||
|
const associatedOlms = await db
|
||||||
|
.select()
|
||||||
|
.from(olms)
|
||||||
|
.where(eq(olms.clientId, clientId));
|
||||||
|
|
||||||
|
console.log(`Found ${associatedOlms.length} OLM(s) associated with this client`);
|
||||||
|
|
||||||
|
// Delete in a transaction to ensure atomicity
|
||||||
|
await db.transaction(async (trx) => {
|
||||||
|
// Delete currentFingerprint entries for the associated OLMs
|
||||||
|
// Note: We delete these explicitly before deleting OLMs to ensure
|
||||||
|
// we have control, even though cascade would handle it
|
||||||
|
let fingerprintCount = 0;
|
||||||
|
if (associatedOlms.length > 0) {
|
||||||
|
const olmIds = associatedOlms.map((olm) => olm.olmId);
|
||||||
|
const deletedFingerprints = await trx
|
||||||
|
.delete(currentFingerprint)
|
||||||
|
.where(inArray(currentFingerprint.olmId, olmIds))
|
||||||
|
.returning();
|
||||||
|
fingerprintCount = deletedFingerprints.length;
|
||||||
|
}
|
||||||
|
console.log(`Deleted ${fingerprintCount} current fingerprint(s)`);
|
||||||
|
|
||||||
|
// Delete OLMs
|
||||||
|
// Note: OLMs have onDelete: "set null" for clientId, so we need to delete them explicitly
|
||||||
|
const deletedOlms = await trx
|
||||||
|
.delete(olms)
|
||||||
|
.where(eq(olms.clientId, clientId))
|
||||||
|
.returning();
|
||||||
|
console.log(`Deleted ${deletedOlms.length} OLM(s)`);
|
||||||
|
|
||||||
|
// Delete approvals
|
||||||
|
// Note: Approvals have onDelete: "cascade" but we delete explicitly for clarity
|
||||||
|
const deletedApprovals = await trx
|
||||||
|
.delete(approvals)
|
||||||
|
.where(eq(approvals.clientId, clientId))
|
||||||
|
.returning();
|
||||||
|
console.log(`Deleted ${deletedApprovals.length} approval(s)`);
|
||||||
|
|
||||||
|
// Delete userClients
|
||||||
|
// Note: userClients have onDelete: "cascade" but we delete explicitly for clarity
|
||||||
|
const deletedUserClients = await trx
|
||||||
|
.delete(userClients)
|
||||||
|
.where(eq(userClients.clientId, clientId))
|
||||||
|
.returning();
|
||||||
|
console.log(`Deleted ${deletedUserClients.length} userClient association(s)`);
|
||||||
|
|
||||||
|
// Finally, delete the client itself
|
||||||
|
const deletedClients = await trx
|
||||||
|
.delete(clients)
|
||||||
|
.where(eq(clients.clientId, clientId))
|
||||||
|
.returning();
|
||||||
|
console.log(`Deleted client: ${deletedClients[0]?.name || niceId}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("\nClient deletion completed successfully!");
|
||||||
|
console.log("\nSummary:");
|
||||||
|
console.log(` - Client: ${niceId} (clientId: ${clientId})`);
|
||||||
|
console.log(` - Olm(s): ${associatedOlms.length}`);
|
||||||
|
console.log(` - Current fingerprints: deleted`);
|
||||||
|
console.log(` - Approvals: deleted`);
|
||||||
|
console.log(` - UserClients: deleted`);
|
||||||
|
console.log(` - Snapshots: preserved (not deleted)`);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting client:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -7,6 +7,7 @@ import { resetUserSecurityKeys } from "@cli/commands/resetUserSecurityKeys";
|
|||||||
import { clearExitNodes } from "./commands/clearExitNodes";
|
import { clearExitNodes } from "./commands/clearExitNodes";
|
||||||
import { rotateServerSecret } from "./commands/rotateServerSecret";
|
import { rotateServerSecret } from "./commands/rotateServerSecret";
|
||||||
import { clearLicenseKeys } from "./commands/clearLicenseKeys";
|
import { clearLicenseKeys } from "./commands/clearLicenseKeys";
|
||||||
|
import { deleteClient } from "./commands/deleteClient";
|
||||||
|
|
||||||
yargs(hideBin(process.argv))
|
yargs(hideBin(process.argv))
|
||||||
.scriptName("pangctl")
|
.scriptName("pangctl")
|
||||||
@@ -15,5 +16,6 @@ yargs(hideBin(process.argv))
|
|||||||
.command(clearExitNodes)
|
.command(clearExitNodes)
|
||||||
.command(rotateServerSecret)
|
.command(rotateServerSecret)
|
||||||
.command(clearLicenseKeys)
|
.command(clearLicenseKeys)
|
||||||
|
.command(deleteClient)
|
||||||
.demandCommand()
|
.demandCommand()
|
||||||
.help().argv;
|
.help().argv;
|
||||||
|
|||||||
Reference in New Issue
Block a user