// // "Settings" section: instance connection state plus per-repository Actions // secrets and variables. Secrets and variables load only when expanded, since // both endpoints need scopes a read-only token may not carry. // const credentials = require("../gitea/credentials.js"); const nodes = require("./nodes.js"); const time = require("../util/time.js"); const { NodeProvider } = require("./provider.js"); const { node, message, errorNode } = nodes; class SettingsProvider extends NodeProvider { signature() { const store = this.store; return [ store.baseSignature(), store.instancesSignature(), `aliases:${[...store.router.aliases.keys()].join(",")}`, ...store.repos.map((repo) => store.settingsSignature(repo.key)), ].join("|"); } roots() { const store = this.store; const baseUrls = store.router.baseUrls; if (!baseUrls.length) { return [message("No Gitea instance configured. Open the extension preferences.")]; } const instances = baseUrls.map((baseUrl) => this.instanceNode(baseUrl)); if (!store.repos.length) return instances; return [ ...instances, node("reposFolder", "Repositories", { identifier: "settings-repos", description: String(store.repos.length), contextValue: "reposFolder", expanded: true, children: store.repos.map((repo) => this.repoNode(repo)), }), ]; } instanceNode(baseUrl) { const status = this.store.instanceStatus.get(baseUrl); const hasToken = credentials.hasToken(baseUrl); let glyph = "◌"; let description = "not checked"; if (status && status.ok) { glyph = "✓"; description = [status.user ? `@${status.user}` : null, `v${status.version}`] .filter(Boolean) .join(" · "); } else if (status) { glyph = hasToken ? "✗" : "○"; description = hasToken ? "error" : "signed out"; } const children = []; if (status && status.error) children.push(errorNode(status.error)); const instanceHost = baseUrl.replace(/^https?:\/\//, "").replace(/:\d+$/, ""); const aliases = [...this.store.router.aliases.entries()] .filter(([, target]) => target === instanceHost) .map(([alias]) => alias); for (const alias of aliases) { children.push( node("hostAlias", `↳ ${alias}`, { identifier: `settings-alias-${baseUrl}-${alias}`, description: "host alias", tooltip: `Git remotes on ${alias} resolve to ${baseUrl}.`, contextValue: "hostAlias", data: { baseUrl: baseUrl, alias: alias }, }), ); } if (!hasToken) { children.push( node("action", "Set Token…", { identifier: `settings-settoken-${baseUrl}`, contextValue: "message", command: "gitea.setToken", data: { baseUrl: baseUrl }, }), ); } return node("instance", `${glyph} ${baseUrl.replace(/^https?:\/\//, "")}`, { identifier: `settings-instance-${baseUrl}`, description: description, tooltip: `${baseUrl}\n${ status && status.ok ? `Connected as ${status.user || "unknown"} (Gitea ${status.version})` : status && status.error ? status.error : "Not checked yet" }`, contextValue: "instance", data: { baseUrl: baseUrl }, children: children.length ? children : null, }); } repoNode(repo) { return node("repo", repo.fullName, { identifier: `settings-repo-${repo.key}`, description: repo.baseUrl.replace(/^https?:\/\//, ""), tooltip: [repo.htmlUrl, repo.dir ? `Local: ${repo.dir}` : null] .filter(Boolean) .join("\n"), contextValue: "repo", repo: repo, data: { repoKey: repo.key }, children: [this.secretsFolder(repo), this.variablesFolder(repo)], }); } secretsFolder(repo) { const cached = this.store.secrets.get(repo.key); return node("secretsFolder", "Secrets", { identifier: `settings-secrets-${repo.key}`, description: cached && !cached.error ? String(cached.items.length) : "", contextValue: "secretsFolder", repo: repo, data: { repoKey: repo.key }, load: async () => { if (!this.store.secrets.has(repo.key)) await this.store.loadSecrets(repo); const entry = this.store.secrets.get(repo.key); if (!entry) return [message("Unavailable.")]; if (entry.error) return [errorNode(entry.error)]; if (!entry.items.length) return [message("No secrets defined.")]; return entry.items.map((secret) => node("secret", `🔒 ${secret.name}`, { identifier: `secret-${repo.key}-${secret.name}`, description: time.relative(secret.created_at), tooltip: secret.description || secret.name, contextValue: "secret", repo: repo, data: { secret: secret, repoKey: repo.key }, }), ); }, }); } variablesFolder(repo) { const cached = this.store.variables.get(repo.key); return node("variablesFolder", "Variables", { identifier: `settings-variables-${repo.key}`, description: cached && !cached.error ? String(cached.items.length) : "", contextValue: "variablesFolder", repo: repo, data: { repoKey: repo.key }, load: async () => { if (!this.store.variables.has(repo.key)) await this.store.loadVariables(repo); const entry = this.store.variables.get(repo.key); if (!entry) return [message("Unavailable.")]; if (entry.error) return [errorNode(entry.error)]; if (!entry.items.length) return [message("No variables defined.")]; return entry.items.map((variable) => node("variable", variable.name, { identifier: `variable-${repo.key}-${variable.name}`, description: variable.data || variable.value || "", tooltip: variable.description || variable.name, contextValue: "variable", repo: repo, data: { variable: variable, repoKey: repo.key }, }), ); }, }); } } exports.SettingsProvider = SettingsProvider;