use axum::{
extract::Query,
routing::{get, post},
Json, Router,
};
use serde::Deserialize;
use crate::state::AppState;
pub fn router() -> Router<AppState> {
Router::new()
.route("/", get(list_workspaces))
.route("/import", post(import_repo))
.route("/tree", get(get_tree))
.route("/file", get(get_file))
.route("/search", get(search_files))
.route("/pr-comment", post(post_pr_comment))
}
async fn list_workspaces() -> Json<serde_json::Value> {
Json(serde_json::json!({ "workspaces": [] }))
}
#[derive(Debug, Deserialize)]
struct ImportRequest {
owner: Option<String>,
repo: Option<String>,
#[serde(rename = "ref")]
git_ref: Option<String>,
url: Option<String>,
}
async fn import_repo(Json(body): Json<ImportRequest>) -> Json<serde_json::Value> {
let (owner, repo) = if let (Some(owner), Some(repo)) = (&body.owner, &body.repo) {
(owner.clone(), repo.clone())
} else if let Some(url) = &body.url {
let stripped = url
.trim_start_matches("https://github.com/")
.trim_start_matches("http://github.com/");
let parts: Vec<&str> = stripped.splitn(2, '/').collect();
if parts.len() == 2 {
(parts[0].to_string(), parts[1].to_string())
} else {
return Json(serde_json::json!({
"error": "Invalid GitHub URL. Expected: https://github.com/owner/repo or owner/repo",
"code": "BAD_REQUEST"
}));
}
} else {
return Json(serde_json::json!({
"error": "Missing 'owner' and 'repo' fields (or provide 'url')",
"code": "BAD_REQUEST"
}));
};
let git_ref = body.git_ref.as_deref().unwrap_or("HEAD");
Json(serde_json::json!({
"error": "GitHub virtual workspace import is not available in desktop mode. Use POST /api/clone to work with local repositories.",
"code": "NOT_IMPLEMENTED",
"hint": {
"owner": owner,
"repo": repo,
"ref": git_ref,
"alternative": "/api/clone"
}
}))
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct RepoQuery {
owner: Option<String>,
repo: Option<String>,
#[serde(rename = "ref")]
git_ref: Option<String>,
}
fn not_imported(owner: &str, repo: &str) -> Json<serde_json::Value> {
Json(serde_json::json!({
"error": format!(
"Workspace not imported. POST /api/github/import first for {}/{}",
owner, repo
),
"code": "NOT_FOUND"
}))
}
async fn get_tree(Query(q): Query<RepoQuery>) -> Json<serde_json::Value> {
let owner = q.owner.as_deref().unwrap_or("");
let repo = q.repo.as_deref().unwrap_or("");
if owner.is_empty() || repo.is_empty() {
return Json(serde_json::json!({
"error": "Missing 'owner' and 'repo' query parameters",
"code": "BAD_REQUEST"
}));
}
not_imported(owner, repo)
}
#[derive(Debug, Deserialize)]
struct FileQuery {
owner: Option<String>,
repo: Option<String>,
path: Option<String>,
#[serde(rename = "ref")]
_git_ref: Option<String>,
}
async fn get_file(Query(q): Query<FileQuery>) -> Json<serde_json::Value> {
let owner = q.owner.as_deref().unwrap_or("");
let repo = q.repo.as_deref().unwrap_or("");
if owner.is_empty() || repo.is_empty() || q.path.is_none() {
return Json(serde_json::json!({
"error": "Missing 'owner', 'repo', or 'path' query parameters",
"code": "BAD_REQUEST"
}));
}
not_imported(owner, repo)
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct SearchQuery {
owner: Option<String>,
repo: Option<String>,
q: Option<String>,
#[serde(rename = "ref")]
_git_ref: Option<String>,
limit: Option<usize>,
}
async fn search_files(Query(q): Query<SearchQuery>) -> Json<serde_json::Value> {
let owner = q.owner.as_deref().unwrap_or("");
let repo = q.repo.as_deref().unwrap_or("");
if owner.is_empty() || repo.is_empty() {
return Json(serde_json::json!({
"error": "Missing 'owner' and 'repo' query parameters",
"code": "BAD_REQUEST"
}));
}
Json(serde_json::json!({
"files": [],
"total": 0,
"query": q.q.as_deref().unwrap_or(""),
"note": "GitHub virtual workspaces are not available in desktop mode."
}))
}
#[derive(Debug, serde::Deserialize)]
struct PrCommentRequest {
owner: Option<String>,
repo: Option<String>,
#[serde(rename = "prNumber")]
pr_number: Option<u64>,
body: Option<String>,
}
async fn post_pr_comment(Json(body): Json<PrCommentRequest>) -> Json<serde_json::Value> {
let owner = body.owner.as_deref().unwrap_or("");
let repo = body.repo.as_deref().unwrap_or("");
let pr_number = body.pr_number.unwrap_or(0);
if owner.is_empty() || repo.is_empty() || pr_number == 0 || body.body.is_none() {
return Json(serde_json::json!({
"error": "Missing required fields: owner, repo, prNumber, body",
"code": "BAD_REQUEST"
}));
}
Json(serde_json::json!({
"error": "GitHub PR comments are not available in desktop mode. Configure GITHUB_TOKEN and use the web backend.",
"code": "NOT_IMPLEMENTED",
"hint": {
"owner": owner,
"repo": repo,
"prNumber": pr_number,
}
}))
}