forked from EduCraft/curriculum-project-hub
feat: update .gitignore, remove unused API interfaces, and add local dev scripts for bootstrap and seeding connections
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Local dev seed for an existing Organization that predates the ADR-0024
|
||||
* secret-envelope plane (e.g. the legacy `org_default` from the tenant-root
|
||||
* migration). Creates ACTIVE Feishu Application + Provider (BYOK openrouter)
|
||||
* connections with encrypted envelopes, using the local dev keyring.
|
||||
*
|
||||
* Idempotent: re-running rotates a new secret version if the connection
|
||||
* already exists.
|
||||
*
|
||||
* Usage: npx tsx scripts/dev-seed-connections.ts
|
||||
*/
|
||||
import "dotenv/config";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { FeishuApplicationConnectionService } from "../src/connections/feishuApplicationConnections.js";
|
||||
import { ProviderConnectionService } from "../src/connections/providerConnections.js";
|
||||
import { loadLocalSecretKeyring, LocalSecretEnvelope } from "../src/security/secretEnvelope.js";
|
||||
|
||||
function requiredEnv(name: string): string {
|
||||
const v = process.env[name]?.trim();
|
||||
if (v === undefined || v === "") throw new Error(`missing required env: ${name}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const databaseUrl = requiredEnv("DATABASE_URL");
|
||||
const organizationId = requiredEnv("HUB_SILO_ORGANIZATION_ID");
|
||||
const keyring = await loadLocalSecretKeyring();
|
||||
const secrets = new LocalSecretEnvelope(keyring);
|
||||
const prisma = new PrismaClient({ datasources: { db: { url: databaseUrl } }, log: [] });
|
||||
|
||||
const feishuAppId = requiredEnv("FEISHU_APP_ID");
|
||||
const feishuAppSecret = requiredEnv("FEISHU_APP_SECRET");
|
||||
const feishuBotOpenId = requiredEnv("FEISHU_BOT_OPEN_ID");
|
||||
const providerAuthToken = requiredEnv("ANTHROPIC_AUTH_TOKEN");
|
||||
const providerBaseUrl = process.env["ANTHROPIC_BASE_URL"]?.trim() || "https://openrouter.ai/api";
|
||||
const anthropicApiKey = process.env["ANTHROPIC_API_KEY"]?.trim() || "";
|
||||
|
||||
// Pick an existing active OWNER/ADMIN as the actor for the audit rows.
|
||||
const actor = await prisma.organizationMembership.findFirst({
|
||||
where: { organizationId, role: { in: ["OWNER", "ADMIN"] }, revokedAt: null },
|
||||
select: { userId: true },
|
||||
});
|
||||
if (actor === null) throw new Error(`no active OWNER/ADMIN membership on ${organizationId}; seed a member first`);
|
||||
const actorUserId = actor.userId;
|
||||
console.log("actor:", actorUserId);
|
||||
|
||||
try {
|
||||
const feishuService = new FeishuApplicationConnectionService(
|
||||
prisma,
|
||||
secrets,
|
||||
async () => {}, // skip live Feishu probe in local dev
|
||||
);
|
||||
const feishuResult = await feishuService.rotateCustomerApplication({
|
||||
organizationId,
|
||||
actorUserId,
|
||||
appId: feishuAppId,
|
||||
appSecret: feishuAppSecret,
|
||||
botOpenId: feishuBotOpenId,
|
||||
});
|
||||
console.log("feishu connection:", JSON.stringify(feishuResult, null, 2));
|
||||
|
||||
const providerService = new ProviderConnectionService(
|
||||
prisma,
|
||||
secrets,
|
||||
async () => {}, // skip live OpenRouter probe in local dev
|
||||
);
|
||||
const providerResult = await providerService.rotateByok({
|
||||
organizationId,
|
||||
actorUserId,
|
||||
providerId: "openrouter",
|
||||
baseUrl: providerBaseUrl,
|
||||
authToken: providerAuthToken,
|
||||
...(anthropicApiKey !== "" ? { anthropicApiKey } : {}),
|
||||
});
|
||||
console.log("provider connection:", JSON.stringify(providerResult, null, 2));
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error: unknown) => {
|
||||
console.error("[dev-seed-connections] failed:", error instanceof Error ? error.message : String(error));
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user