chore(scripts): add deno task webhooks for register and delete-all
Wraps the createSession + Authorization: Bearer flow so callers don't need to assemble curl by hand. Reads BSKY_IDENTIFIER / BSKY_PASSWORD / BSKY_PDS / REMANSO_API from env (or matching flags). Defaults to bsky.social so non-bsky-hosted accounts must set BSKY_PDS explicitly, e.g. BSKY_PDS=https://eurosky.social.
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
"jetstream": "deno run --watch --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi jetstream.ts",
|
||||
"server": "deno run --watch --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi server.ts",
|
||||
"server:prod": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi server.ts",
|
||||
"migrate": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi src/migrations/init.ts"
|
||||
"migrate": "deno run --allow-net --allow-read --allow-write --allow-env --allow-ffi --unstable-ffi src/migrations/init.ts",
|
||||
"webhooks": "deno run --allow-net --allow-env scripts/manage-webhooks.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@db/sqlite": "jsr:@db/sqlite@^0.13.0",
|
||||
|
||||
143
scripts/manage-webhooks.ts
Normal file
143
scripts/manage-webhooks.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// Manage webhook subscriptions on the Remanso API using a Bluesky session
|
||||
// token from your own PDS. Usage:
|
||||
//
|
||||
// deno task webhooks register --url https://your-receiver --verb bulk-create
|
||||
// deno task webhooks delete-all
|
||||
//
|
||||
// Required env vars (or matching --flags):
|
||||
// BSKY_IDENTIFIER your handle (e.g. alice.eurosky.social) or email
|
||||
// BSKY_PASSWORD app password (NOT your main password)
|
||||
// BSKY_PDS your PDS base URL (default: https://bsky.social)
|
||||
// REMANSO_API Remanso API base URL (default: https://api.remanso.space)
|
||||
//
|
||||
// Example for an eurosky-hosted account:
|
||||
//
|
||||
// BSKY_IDENTIFIER=alice.eurosky.social \
|
||||
// BSKY_PASSWORD='xxxx-xxxx-xxxx-xxxx' \
|
||||
// BSKY_PDS=https://eurosky.social \
|
||||
// deno task webhooks register --url https://example.com/hook --verb bulk-create
|
||||
|
||||
const HELP = `
|
||||
Usage:
|
||||
deno task webhooks register \\
|
||||
--url <receiver-url> \\
|
||||
[--verb create|delete|bulk-create] \\
|
||||
[--method POST] \\
|
||||
[--token <outbound-bearer>]
|
||||
|
||||
deno task webhooks delete-all
|
||||
|
||||
Auth (env or flag, env preferred):
|
||||
BSKY_IDENTIFIER / --identifier
|
||||
BSKY_PASSWORD / --password
|
||||
BSKY_PDS / --pds (default: https://bsky.social)
|
||||
REMANSO_API / --api (default: https://api.remanso.space)
|
||||
`;
|
||||
|
||||
type CreateSessionResponse = {
|
||||
did: string;
|
||||
accessJwt: string;
|
||||
};
|
||||
|
||||
const parseArgs = (args: string[]): Record<string, string> => {
|
||||
const out: Record<string, string> = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (!arg.startsWith("--")) continue;
|
||||
const key = arg.slice(2);
|
||||
const next = args[i + 1];
|
||||
if (next === undefined || next.startsWith("--")) {
|
||||
out[key] = "true";
|
||||
} else {
|
||||
out[key] = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const die = (msg: string): never => {
|
||||
console.error(`error: ${msg}`);
|
||||
console.error(HELP);
|
||||
Deno.exit(1);
|
||||
};
|
||||
|
||||
const createSession = async (
|
||||
pds: string,
|
||||
identifier: string,
|
||||
password: string,
|
||||
): Promise<CreateSessionResponse> => {
|
||||
const res = await fetch(`${pds}/xrpc/com.atproto.server.createSession`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ identifier, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`createSession failed (${res.status}): ${await res.text()}`,
|
||||
);
|
||||
}
|
||||
return await res.json();
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const [command, ...rest] = Deno.args;
|
||||
if (!command || command === "--help" || command === "-h") {
|
||||
console.log(HELP);
|
||||
Deno.exit(command ? 0 : 1);
|
||||
}
|
||||
|
||||
const flags = parseArgs(rest);
|
||||
const identifier = flags.identifier ?? Deno.env.get("BSKY_IDENTIFIER");
|
||||
const password = flags.password ?? Deno.env.get("BSKY_PASSWORD");
|
||||
const pds = flags.pds ?? Deno.env.get("BSKY_PDS") ?? "https://bsky.social";
|
||||
const api = flags.api ?? Deno.env.get("REMANSO_API") ??
|
||||
"https://api.remanso.space";
|
||||
|
||||
if (!identifier) die("BSKY_IDENTIFIER (or --identifier) is required");
|
||||
if (!password) die("BSKY_PASSWORD (or --password) is required");
|
||||
|
||||
const session = await createSession(pds, identifier, password);
|
||||
console.log(`[auth] verified DID: ${session.did} via ${pds}`);
|
||||
|
||||
if (command === "register") {
|
||||
const url = flags.url ?? die("--url is required for register");
|
||||
const body: Record<string, unknown> = {
|
||||
method: flags.method ?? "POST",
|
||||
url,
|
||||
};
|
||||
if (flags.verb) body.verb = flags.verb;
|
||||
if (flags.token) body.token = flags.token;
|
||||
const res = await fetch(`${api}/${session.did}/webhooks`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${session.accessJwt}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(`register failed (${res.status}): ${await res.text()}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
console.log(JSON.stringify(await res.json(), null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "delete-all") {
|
||||
const res = await fetch(`${api}/${session.did}/webhooks`, {
|
||||
method: "DELETE",
|
||||
headers: { "Authorization": `Bearer ${session.accessJwt}` },
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(`delete failed (${res.status}): ${await res.text()}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
console.log(`[done] all webhooks for ${session.did} deleted`);
|
||||
return;
|
||||
}
|
||||
|
||||
die(`unknown command: ${command}`);
|
||||
};
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user