UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

227 lines (225 loc) • 9.72 kB
const require_runtime = require('../_virtual/_rolldown/runtime.cjs'); const require_network = require('../utils/network.cjs'); const require_theme = require('../utils/theme.cjs'); const require_contract = require('../contract.cjs'); const require_services_resolution_session = require('../services/resolution/session.cjs'); const require_site_client = require('../services/auth/site-client.cjs'); const require_device_login = require('../services/auth/device-login.cjs'); const require_outlayer = require('../services/auth/outlayer.cjs'); const require_publish_key = require('../services/auth/publish-key.cjs'); const require_session_store = require('../services/auth/session-store.cjs'); let effect = require("effect"); let node_process = require("node:process"); node_process = require_runtime.__toESM(node_process, 1); let near_kit = require("near-kit"); //#region src/handlers/keys.ts function registerKeys(builder) { return { login: builder.login.effect(function* (_opts, input) { const session = yield* require_services_resolution_session.ResolutionSessionService; const deviceLogin = yield* require_device_login.DeviceLoginService; const sessions = yield* require_session_store.SessionStore; const staging = input.env === "staging"; const config = session?.config; const account = staging ? config?.staging?.account ?? config?.account ?? "" : config?.account ?? ""; const gateway = staging ? config?.staging?.domain ?? config?.domain ?? "" : config?.domain ?? ""; const expiresIn = input.expiresIn ?? (input.key ? 31536e3 : 7776e3); const siteUrl = resolveLoginSiteUrl(input.site, session, staging); let deployKey; if (input.key) { if (!account || !gateway) return { status: "error", siteUrl, error: "Deploy keys scope to the authored config's account and domain — set both in bos.config.json (or drop --key for a plain login)" }; deployKey = { account, gateway }; } const login = yield* deviceLogin.start({ siteUrl, device: input.device, account: account || void 0, custody: input.custody ? { network: require_network.getNetworkIdForAccount(account) } : void 0, expiresIn, deployKey }).pipe(effect.Effect.catchIf(() => true, (error) => effect.Effect.succeed({ startFailed: error.message }))); if ("startFailed" in login) return { status: "error", siteUrl: input.site ?? "", error: login.startFailed }; console.log(); console.log(` One-time code: ${require_theme.colors.cyan(login.userCode)}`); yield* effect.Effect.catchIf(effect.Effect.tryPromise(() => require_device_login.openInBrowser(login.verificationUrl)), () => true, (error) => effect.Effect.sync(() => console.log(require_theme.colors.yellow(` ⚠ ${error.message}`)))); console.log(); console.log(` Waiting for approval at ${require_theme.colors.dim(login.verificationUrl)}…`); return yield* effect.Effect.tryPromise({ try: async () => { const approval = await login.waitForApproval(); if (!approval.apiKey) return { status: "error", siteUrl, loginUrl: login.verificationUrl, error: "Login approved but no CLI credential was created" }; const expiresAt = new Date(Date.now() + expiresIn * 1e3).toISOString(); const configDir = session?.root ?? node_process.default.cwd(); const existingSession = sessions.read(configDir); sessions.write(configDir, { version: 1, credential: { kind: "session", apiKey: approval.apiKey.key, apiKeyId: approval.apiKey.id, accountId: approval.accountId, label: input.device ?? siteUrl, siteUrl, createdAt: (/* @__PURE__ */ new Date()).toISOString(), expiresAt }, publishKey: null, delegateKey: null, custodyWallet: approval.custodyWallet ?? existingSession?.custodyWallet ?? null }); let warning = null; if (input.custody && !approval.custodyWallet) warning = "Custody wallet credential could not be fetched after approval. Re-run `bos login --custody` — the wallet is kept server-side."; if (account && approval.accountId && approval.accountId !== account) warning = warning ? `${warning} Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.` : `Logged in as ${approval.accountId}, but the authored config account is ${account}. Publishes will use the configured account.`; let publishKey = null; if (input.key) try { publishKey = await exportPublishKey(sessions, account, input.registry); } catch (error) { warning = `Session stored, but publish-key export failed: ${error instanceof Error ? error.message : "unknown error"}`; } let outlayerCli = null; const custodyWallet = approval.custodyWallet; if (input.custody && custodyWallet) { outlayerCli = await require_outlayer.loginOutlayerCli(custodyWallet); if (outlayerCli.status === "error") { const failure = `OutLayer CLI login failed: ${outlayerCli.detail}`; warning = warning ? `${warning} ${failure}` : failure; } } return { status: "logged-in", siteUrl, accountId: approval.accountId, expiresAt, loginUrl: login.verificationUrl, publishKey, deployKey: deployKey && approval.apiKey ? { key: approval.apiKey.key, account: deployKey.account, gateway: deployKey.gateway } : null, custodyWallet: approval.custodyWallet ? { walletId: approval.custodyWallet.walletId, nearAccountId: approval.custodyWallet.nearAccountId, network: approval.custodyWallet.network, handoffUrl: approval.custodyWallet.handoffUrl } : null, outlayerCli, warning }; }, catch: (error) => ({ status: "error", siteUrl: input.site ?? "", error: error instanceof Error ? error.message : "Unknown error" }) }); }), logout: builder.logout.effect(function* (_opts, input) { const session = yield* require_services_resolution_session.ResolutionSessionService; const siteClient = yield* require_site_client.SiteClient; const sessions = yield* require_session_store.SessionStore; const configDir = input.configDir ?? session?.root ?? node_process.default.cwd(); return yield* effect.Effect.tryPromise({ try: () => logoutBody({ siteClient, sessions, session }, configDir), catch: (error) => ({ status: "error", revokedApiKey: false, removedPublishKey: false, error: error instanceof Error ? error.message : "Unknown error" }) }); }) }; } async function logoutBody(deps, configDir) { const { siteClient, sessions, session } = deps; const stored = sessions.read(configDir); let revokedApiKey = false; let removedPublishKey = false; let warning = null; const credential = stored?.credential; if (credential) { revokedApiKey = await revokeApiKey(siteClient, credential).catch(() => false); if (!revokedApiKey) warning = "Could not revoke the API key remotely — revoke it manually under Settings → API keys."; } if (stored?.publishKey) { const account = stored.credential?.accountId ?? session?.config?.account ?? ""; sessions.removePublishedKeyFile(stored.publishKey.network, account, stored.publishKey.publicKey); removedPublishKey = true; warning = warning ? `${warning} Re-run ${require_theme.colors.cyan("bos login --key")} to re-export a publish key.` : "Removed the exported publish key. Re-run bos login --key to re-export one."; } sessions.delete(configDir); return { status: "logged-out", revokedApiKey, removedPublishKey, warning }; } function resolveLoginSiteUrl(siteInput, session, staging) { if (siteInput) return siteInput.replace(/\/+$/, ""); if (staging) { const stagingDomain = session?.config?.staging?.domain ?? session?.config?.domain; return stagingDomain ? `https://${stagingDomain}` : session?.runtime?.ui?.url ?? ""; } const devUiUrl = session?.runtime?.ui?.url; if (devUiUrl && /^https?:\/\/(localhost|127\.0\.0\.1)/.test(devUiUrl)) return devUiUrl.replace(/\/+$/, ""); const domain = session?.config?.domain; return domain ? `https://${domain}` : ""; } /** `login --key`'s export: the shared mint, then the near-cli-rs keystore * handoff (~/.near-credentials) with a 0600 permission fix-up. */ async function exportPublishKey(sessions, account, registry) { const minted = await require_publish_key.mintPublishKey({ account, registry }); const { FileKeyStore } = await import("near-kit/keys/file"); await new FileKeyStore("~/.near-credentials", minted.network).add(account, parseNearPrivateKey(minted.keyPair.privateKey)); try { const { statSync, chmodSync } = await import("node:fs"); const credentialPath = sessions.nearCredentialsPath(minted.network, account); if (statSync(credentialPath).mode & 63) chmodSync(credentialPath, 384); } catch {} return { publicKey: minted.keyPair.publicKey, network: minted.network, contract: minted.contract, exportedTo: sessions.nearCredentialsPath(minted.network, account) }; } function parseNearPrivateKey(privateKey) { return (0, near_kit.parseKey)(privateKey); } async function revokeApiKey(siteClient, credential) { if (!credential.apiKey || !credential.siteUrl) return false; try { const response = await siteClient.authPost(credential.siteUrl, "/api-key/delete", { keyId: credential.apiKeyId }, void 0, { "x-api-key": credential.apiKey }, AbortSignal.timeout(1e4)); return response.status >= 200 && response.status < 300; } catch { return false; } } //#endregion exports.registerKeys = registerKeys; //# sourceMappingURL=keys.cjs.map