Files
kyle f6faf97cdf
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Successful in 10s
CI/CD Pipeline / i18n Catalog Check (push) Successful in 43s
CI/CD Pipeline / Build and Push App Docker Image (push) Successful in 2m14s
CI/CD Pipeline / Deploy to Kubernetes (push) Successful in 8m29s
develpoer role
2026-08-30 12:36:28 -05:00

91 lines
2.8 KiB
TypeScript

// Grant or revoke a role for a user by phone number (US +1 assumed).
// Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]
import "dotenv/config";
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import Passwordless from "supertokens-node/recipe/passwordless";
import UserRoles from "supertokens-node/recipe/userroles";
SuperTokens.init({
framework: "custom",
supertokens: {
connectionURI: process.env.SUPERTOKENS_URI || "http://localhost:3567",
apiKey: process.env.SUPERTOKENS_API_KEY || undefined,
},
appInfo: {
appName: "FLXN",
apiDomain: "http://localhost:3000",
websiteDomain: "http://localhost:3000",
apiBasePath: "/api/auth",
websiteBasePath: "/auth",
},
recipeList: [
Passwordless.init({ contactMethod: "PHONE", flowType: "USER_INPUT_CODE" }),
Session.init(),
UserRoles.init(),
],
});
const VALID_ROLES = ["Admin", "Developer"] as const;
const raw = process.argv[2];
const revoke = process.argv.includes("--revoke");
const roleFlagIndex = process.argv.indexOf("--role");
const role = roleFlagIndex === -1 ? "Admin" : process.argv[roleFlagIndex + 1];
if (!raw) {
console.error("Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]");
process.exit(1);
}
if (!VALID_ROLES.includes(role as (typeof VALID_ROLES)[number])) {
console.error(`Invalid role "${role}". Valid roles: ${VALID_ROLES.join(", ")}`);
process.exit(1);
}
const digits = raw.replace(/[^\d]/g, "");
const candidates = Array.from(
new Set([
raw.startsWith("+") ? raw : null,
digits.length === 10 ? `+1${digits}` : null,
`+${digits}`,
digits,
].filter(Boolean) as string[])
);
let user: { id: string } | undefined;
let matched = "";
for (const phoneNumber of candidates) {
const users = await SuperTokens.listUsersByAccountInfo("public", { phoneNumber });
if (users.length) {
user = users[0];
matched = phoneNumber;
break;
}
}
if (!user) {
console.error(`No SuperTokens user found for phone (tried: ${candidates.join(", ")}).`);
console.error("The user must have logged in at least once so their account exists.");
process.exit(1);
}
await UserRoles.createNewRoleOrAddPermissions(role, []);
if (revoke) {
const res = await UserRoles.removeUserRole("public", user.id, role);
console.log(`Removed ${role} from ${matched} (user ${user.id}):`, res.status);
} else {
const res = await UserRoles.addRoleToUser("public", user.id, role);
console.log(
`Granted ${role} to ${matched} (user ${user.id}):`,
res.status === "OK"
? res.didUserAlreadyHaveRole
? "already had it"
: "added"
: res.status
);
}
console.log("Note: sign out and back in (or refresh the session) for the role to take effect.");
process.exit(0);