feat(#1970): add POST /api/github-account dashboard endpoint for the credentials UI

This commit is contained in:
damocles 2026-07-11 10:53:12 +02:00 committed by mara
commit 1b6b307aa7
2 changed files with 43 additions and 0 deletions

View file

@ -262,6 +262,45 @@ pub(super) async fn post_matrix_account_login(Form(f): Form<MatrixLoginForm>) ->
axum::Json(MatrixLoginResult { ok: true, user_id }).into_response()
}
/// Form body for `POST /api/github-account` (urlencoded, the dashboard's
/// mutation convention). Writes the operator-supplied PAT to the agent's
/// `github-token` file. The GitHub counterpart of the matrix login form, but
/// far simpler: no account creation, no homeserver, no login modes — the
/// operator pastes a PAT for an existing account.
#[derive(Deserialize)]
pub(super) struct GithubAccountForm {
agent: String,
token: String,
}
#[derive(Serialize)]
struct GithubAccountResult {
ok: bool,
}
/// Provision (or refresh) an agent's GitHub PAT from the dashboard
/// credentials tab. Validates the agent name, then writes the PAT to
/// `<state>/github-token` (`0600`, agent-owned) via hive-priv. No account
/// creation and no daemon to kick — the agent's `gh` wrapper / git credential
/// helper read the file live, so the new token takes effect immediately.
/// Operator-authenticated (dashboard). Never echoes the token back — only
/// `{ ok: true }`.
pub(super) async fn post_github_account(Form(f): Form<GithubAccountForm>) -> Response {
let agent = f.agent.trim();
let token = f.token.trim();
if !is_plain_ident(agent) {
return error_response(&format!("github-account: invalid agent {agent:?}"));
}
if token.is_empty() {
return error_response("github-account: token is required");
}
if let Err(e) = crate::priv_client::write_agent_github_token(agent, token).await {
return error_response(&format!("github-account: write token failed: {e:#}"));
}
tracing::info!(%agent, "github-account: provisioned github PAT");
axum::Json(GithubAccountResult { ok: true }).into_response()
}
/// POST `m.login.password` to `<homeserver>/_matrix/client/v3/login`.
/// Returns `(access_token, user_id)`.
async fn matrix_password_login(

View file

@ -188,6 +188,10 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
"/api/matrix-account-login",
post(matrix_accounts::post_matrix_account_login),
)
.route(
"/api/github-account",
post(matrix_accounts::post_github_account),
)
.route(
"/api/cancel-reminder/{id}",
post(reminders::post_cancel_reminder),