GitHub Integration
GitHub OAuth, project export, repository creation, and practices.GitHub OAuth Setup
1. OAuth App
- Open GitHub Developer Settings → OAuth Apps → New OAuth App.
- Homepage URL:
https://your-domainorhttp://localhost:3000for local. - Authorization callback URL:
- Production:
https://your-domain/api/github/callback - Local:
http://localhost:3000/api/github/callback
- Production:
2. Environment variables
GITHUB_CALLBACK_URL=http://localhost:3000/api/github/callback.
If GITHUB_CALLBACK_URL is unset, the code uses${NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/api/github/callback.
OAuth Flow
1. Start authorization
GET/api/github/authorize?sessionId=...
-
Requires
sessionId(workflow session). -
Builds
statefrom{ sessionId }(base64), stores it in cookiegithub_oauth_state(HttpOnly, 10 min). -
Redirects to:
-
scope=repo— full control of private repos (create, push, etc.).
2. Callback
GET/api/github/callback?code=...&state=...
- Verifies
stateagainstgithub_oauth_statecookie. - Decodes
stateto getsessionId. - Exchanges
codeforaccess_tokenviahttps://github.com/login/oauth/access_token. - Fetches user with
https://api.github.com/user. - Sets cookies:
github_access_token— HttpOnly, 30 daysgithub_user— username- Clears
github_oauth_state
- Redirects to:
/execution?sessionId=...&githubConnected=true(or&githubError=...on failure).
3. Connection status
GET/api/github/status
- Reads
github_access_tokenandgithub_userfrom cookies. - If token present, calls
https://api.github.com/userto check it. On 4xx, clears the auth cookies and returnsconnected: false. - Response:
{ connected: true, username }or{ connected: false, error? }.
Project Export and Upload
Download (ZIP)
GET/api/projects/download?sessionId=...
- Prefer: read ZIP from
ZipFile(DB) forsessionId. If found, streamfileDataasapplication/zipwithContent-Disposition: attachment; filename="...". - Fallback: build ZIP from
projects/{sessionId}on disk (orPROJECTS_DIR/projects/{sessionId}ifPROJECTS_DIRis set). Project name for the filename comes fromrefinedRequirements.titleorsessionId.
POST /api/projects/generate) writes files under {PROJECTS_DIR||os.tmpdir()}/projects/{sessionId} and can also store the ZIP in ZipFile. For download to use the filesystem fallback, that path must exist and match what the server can read.
Upload to GitHub
POST/api/github/upload
Body: { sessionId, repoName?, repoDescription?, isPrivate? }
- Auth:
github_access_tokencookie (from OAuth). If missing → 401. - Repository:
repoNamedefault:scriptonia-project-{first 8 chars of sessionId}.repoDescriptiondefault:Project generated by Scriptonia.private:isPrivate === true.
- Source of files:
join(process.cwd(), 'projects', sessionId). The handler reads from that directory recursively (skipsnode_modules,.git,.next,., etc.). Text files useutf-8, othersbase64. - GitHub flow:
POST /user/repos— create repo,auto_init: false.- For each file:
POST /repos/{full_name}/git/blobs(content + encoding). POST /repos/{full_name}/git/treeswith the blob SHAs.POST /repos/{full_name}/git/commitswith the tree and messageInitial commit: Project generated by Scriptonia.PATCH /repos/{full_name}/git/refs/heads/main(orPOST /refsif the ref does not exist) to point at the commit.
- Response:
{ status: 'success', message, repositoryUrl }or{ status: 'error', error }.
projects/{sessionId} under the app’s process.cwd(). Ensure the generate/execution pipeline writes there, or that you configure PROJECTS_DIR and any file-writing logic so the same directory is used.
Repository Creation
- Repos are created via GitHub’s Create a repository API with:
name,description,private,auto_init: false
- First commit uses the Git Data API: blobs → tree → commit → update
main(or create the ref). No.giton disk is required.
Best Practices
1. Callback URL
- Must exactly match the OAuth App’s Authorization callback URL (including
/api/github/callback). - Use HTTPS in production. For localhost,
http://localhost:3000is fine.
2. State and cookies
stateis bound tosessionIdand checked in the callback. KeepsessionIdinstateso the redirect can return the user to the right workflow.github_oauth_stateis short-lived (10 min) and cleared after use.github_access_tokenis 30 days;/api/github/statusinvalidates it when GitHub returns 4xx.
3. Scope
repois broad (private repos, delete, etc.). If you only need to create and push to new repos, consider whether a more limited GitHub App or a different scope is better; the current implementation usesrepo.
4. Where files come from
- Upload reads from
process.cwd()/projects/{sessionId}. - Generate writes to
{PROJECTS_DIR||os.tmpdir()}/projects/{sessionId}and can also store a ZIP inZipFile. - Align these (e.g. set
PROJECTS_DIRtoprocess.cwd()and ensure generate writes there) so upload finds the project. If you only persist inZipFile, you’d need to extract toprojects/{sessionId}(or change upload) before calling upload.
5. Token storage
- Tokens are in HttpOnly cookies. For stronger security, move to a server-side session or DB keyed by user/session, and limit cookie scope/path.
6. Errors
- Upload can fail with “Project files not found” if
projects/{sessionId}is missing or empty. - “GitHub not connected” means the auth cookies are missing or cleared.
- GitHub API errors (e.g. repo name taken, 403) are returned in
error.