import { Command } from "jsr:@cliffy/command@1.0.0-rc.7"; import { Secret } from "jsr:@cliffy/prompt@1.0.0-rc.7"; import { homedir } from "node:os"; import { TokenApi } from "./api/token-api.ts"; import { OAuth2Api } from "./api/oauth2-api.ts"; import { CredentialManager } from "./util/credential-manager.ts"; import { prettyOauth2Application, saveSecretKeys } from "./util/helpers.ts"; // Constants that were previously in types.ts const URL_BASE = "https://git.monoid.top"; const TOKEN_NAME = "oauth2cli"; const CREDENTIALS_FILE = `${homedir()}/.oauth2cli-forgejo`; async function main() { const tokenApi = new TokenApi(URL_BASE, TOKEN_NAME); const credentialManager = new CredentialManager(CREDENTIALS_FILE, TOKEN_NAME); try { await new Command() .name("forgejo-oauth2cli") .version("0.1.0") .description("Interactive client credentials generator for Forgejo.") .command("login", "Authenticate with Forgejo interactively") .action(async() => { const username = await prompt("Enter your username: "); if (!username) { console.error("Username is required."); return; } const secret = await Secret.prompt("Enter your secret: "); if (!secret) { console.error("Secret is required."); return; } // Check for existing tokens const tokens = await tokenApi.listTokens(username, secret); if (!tokens) { return; } // Delete existing token if found const existingToken = tokens.find(t => t.name === TOKEN_NAME); if (existingToken) { console.log("Existing token found, replacing it..."); await tokenApi.deleteToken(username, secret, existingToken.id); } // Create new token console.log("Creating new token..."); const token = await tokenApi.createToken(username, secret); if (!token) { return; } // Store credentials await credentialManager.storeCredentials(username, token.sha1); console.log("Login successful! Credentials stored securely."); }) .command("logout", "Delete token and credentials") .action(async () => { const username = await prompt("Enter your username: "); if (!username) { console.error("Username is required."); return; } const secret = await Secret.prompt("Enter your secret: "); if (!secret) { console.error("Secret is required."); return; } // Find and delete token const tokens = await tokenApi.listTokens(username, secret); if (!tokens) { return; } const existingToken = tokens.find(t => t.name === TOKEN_NAME); if (existingToken) { await tokenApi.deleteToken(username, secret, existingToken.id); console.log("Token deleted from Forgejo."); } // Clear stored credentials await credentialManager.clearCredentials(username); console.log("Logout successful! Credentials removed."); }) .command("list, ls", "List all OAuth2 applications") .option("--pretty, -p", "Pretty print the output") .action(async (opt) => { const credentials = await credentialManager.getCredentials(); if (!credentials) return; // Create OAuth2Api with token const oauth2Api = new OAuth2Api(URL_BASE, credentials.token); const apps = await oauth2Api.getOauth2Applications(); if (!apps) return; if (apps.length === 0) { console.log("No OAuth2 applications found."); return; } if (opt.pretty) { console.log("OAuth2 applications:"); console.log(apps.map(prettyOauth2Application).join("\n\n")); return; } else { const maxNameLength = Math.max(...apps.map(app => app.name.length), 6); console.log(apps.map(app => [ app.id.toString().padEnd(5), app.confidential_client ? "c" : "o", app.name.padStart(maxNameLength), app.client_id, app.redirect_uris.join(", "), ].join(" ").trim()).join("\n")); } }) .command("create", "Create new OAuth2 application") .action(async () => { const credentials = await credentialManager.getCredentials(); if (!credentials) { console.error("Credentials not found. Please login first."); return; } // Create OAuth2Api with token const oauth2Api = new OAuth2Api(URL_BASE, credentials.token); const name = await prompt("Enter the name of the application: "); if (!name) { console.error("Name is required."); return; } const redirectUris = await prompt("Enter the redirect URIs (comma separated): "); if (!redirectUris) { console.error("Redirect URIs are required."); return; } const app = await oauth2Api.createOauth2Application({ name, redirect_uris: redirectUris.split(",").map(uri => uri.trim()), }); if (!app) return; console.log("OAuth2 application created successfully!"); console.log(prettyOauth2Application(app)); // Save secret keys const path = await prompt("Enter the path to save the OAuth2 secret keys: "); if (path) { await saveSecretKeys(path, app.client_id, app.client_secret); } }) .command("remove, rm", "Remove OAuth2 application") .arguments("") .action(async (_opt, appId) => { const credentials = await credentialManager.getCredentials(); if (!credentials) { console.error("Credentials not found. Please login first."); return; } // Create OAuth2Api with token const oauth2Api = new OAuth2Api(URL_BASE, credentials.token); await oauth2Api.deleteOauth2Application(appId); console.log("OAuth2 application deleted successfully!"); }) .command("update", "Update OAuth2 application") .action(async () => { const credentials = await credentialManager.getCredentials(); if (!credentials) { console.error("Credentials not found. Please login first."); return; } // Create OAuth2Api with token const oauth2Api = new OAuth2Api(URL_BASE, credentials.token); const id = await prompt("Enter the ID of the application to update: "); if (!id) { console.error("ID is required."); return; } const appId = parseInt(id); if (isNaN(appId)) { console.error("ID must be a number."); return; } const name = await prompt("Enter the new name of the application: "); if (!name) { console.error("Name is required."); return; } const redirectUris = await prompt("Enter the new redirect URIs (comma separated): "); if (!redirectUris) { console.error("Redirect URIs are required."); return; } const app = await oauth2Api.updateOauth2Application(appId, { name, redirect_uris: redirectUris.split(",").map(uri => uri.trim()), }); if (!app) return; console.log("OAuth2 application updated successfully!"); console.log(prettyOauth2Application(app)); // Save secret keys const path = await prompt("Enter the path to save the OAuth2 secret keys: "); if (path) { await saveSecretKeys(path, app.client_id, app.client_secret); } }) .parse(Deno.args); } catch (error) { if (error instanceof Error) { console.error("An error occurred:", error.message); } else { console.error("An unexpected error occurred:", error); } } } if (import.meta.main) { await main(); }