github repo creation flow

This commit is contained in:
Will Chen
2025-04-14 21:55:51 -07:00
parent 7ad83a2bdc
commit 05a97d31c4
12 changed files with 288 additions and 10 deletions

View File

@@ -5,7 +5,8 @@ import {
IpcMainInvokeEvent,
} from "electron";
import fetch from "node-fetch"; // Use node-fetch for making HTTP requests in main process
import { writeSettings } from "../../main/settings";
import { writeSettings, readSettings } from "../../main/settings";
import { updateAppGithubRepo } from "../../db/index";
// --- GitHub Device Flow Constants ---
// TODO: Fetch this securely, e.g., from environment variables or a config file
@@ -275,8 +276,97 @@ function handleStartGithubFlow(
// event.sender.send('github:flow-cancelled', { message: 'GitHub flow cancelled.' });
// }
// --- GitHub Repo Availability Handler ---
async function handleIsRepoAvailable(
event: IpcMainInvokeEvent,
{ org, repo }: { org: string; repo: string }
) {
try {
// Get access token from settings
const settings = readSettings();
const accessToken = settings.githubSettings?.secrets?.accessToken;
if (!accessToken) {
return { available: false, error: "Not authenticated with GitHub." };
}
// If org is empty, use the authenticated user
const owner =
org ||
(await fetch("https://api.github.com/user", {
headers: { Authorization: `Bearer ${accessToken}` },
})
.then((r) => r.json())
.then((u) => u.login));
// Check if repo exists
const url = `https://api.github.com/repos/${owner}/${repo}`;
const res = await fetch(url, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (res.status === 404) {
return { available: true };
} else if (res.ok) {
return { available: false, error: "Repository already exists." };
} else {
const data = await res.json();
return { available: false, error: data.message || "Unknown error" };
}
} catch (err: any) {
return { available: false, error: err.message || "Unknown error" };
}
}
// --- GitHub Create Repo Handler ---
async function handleCreateRepo(
event: IpcMainInvokeEvent,
{ org, repo, appId }: { org: string; repo: string; appId: number }
) {
try {
// Get access token from settings
const settings = readSettings();
const accessToken = settings.githubSettings?.secrets?.accessToken;
if (!accessToken) {
return { success: false, error: "Not authenticated with GitHub." };
}
// If org is empty, create for the authenticated user
let owner = org;
if (!owner) {
const userRes = await fetch("https://api.github.com/user", {
headers: { Authorization: `Bearer ${accessToken}` },
});
const user = await userRes.json();
owner = user.login;
}
// Create repo
const createUrl = org
? `https://api.github.com/orgs/${owner}/repos`
: `https://api.github.com/user/repos`;
const res = await fetch(createUrl, {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
Accept: "application/vnd.github+json",
},
body: JSON.stringify({
name: repo,
private: true,
}),
});
if (!res.ok) {
const data = await res.json();
return { success: false, error: data.message || "Failed to create repo" };
}
// Store org and repo in the app's DB row (apps table)
await updateAppGithubRepo(appId, owner, repo);
return { success: true };
} catch (err: any) {
return { success: false, error: err.message || "Unknown error" };
}
}
// --- Registration ---
export function registerGithubHandlers() {
ipcMain.handle("github:start-flow", handleStartGithubFlow);
// ipcMain.on('github:cancel-flow', handleCancelGithubFlow); // Uncomment if you add cancellation
ipcMain.handle("github:is-repo-available", handleIsRepoAvailable);
ipcMain.handle("github:create-repo", handleCreateRepo);
}

View File

@@ -571,6 +571,40 @@ export class IpcClient {
// }
// --- End GitHub Device Flow ---
// --- GitHub Repo Management ---
public async checkGithubRepoAvailable(
org: string,
repo: string
): Promise<{ available: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:is-repo-available", {
org,
repo,
});
return result;
} catch (error: any) {
return { available: false, error: error.message || "Unknown error" };
}
}
public async createGithubRepo(
org: string,
repo: string,
appId: number
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:create-repo", {
org,
repo,
appId,
});
return result;
} catch (error: any) {
return { success: false, error: error.message || "Unknown error" };
}
}
// --- End GitHub Repo Management ---
// Example methods for listening to events (if needed)
// public on(channel: string, func: (...args: any[]) => void): void {
}

View File

@@ -50,6 +50,8 @@ export interface App {
files: string[];
createdAt: Date;
updatedAt: Date;
githubOrg: string | null;
githubRepo: string | null;
}
export interface Version {