@@ -32,7 +32,6 @@ import {
3232 deleteProjectAnnouncement as dbDeleteProjectAnnouncement ,
3333 updateProjectGithubRepo as dbUpdateProjectGithubRepo ,
3434 getUserGithubOAuthToken as dbGetUserGithubOAuthToken ,
35- // getUserGithubInstallation, // Not used directly, part of GitHub client
3635} from '@/lib/db' ;
3736import { z } from 'zod' ;
3837import { auth } from '@/lib/authEdge' ;
@@ -565,8 +564,8 @@ async function updateReadmeOnGithub(octokit: Octokit, owner: string, repo: strin
565564 message : 'Update README.md from FlowUp' ,
566565 content : Buffer . from ( content ) . toString ( 'base64' ) ,
567566 committer : {
568- name : 'FlowUp Bot' , // Or use the user's GitHub name if available
569- email : 'bot@flowup.app' , // Or user's email if allowed and consented
567+ name : 'FlowUp Bot' ,
568+ email : 'bot@flowup.app' ,
570569 } ,
571570 } ;
572571 if ( existingSha ) {
@@ -614,7 +613,7 @@ export async function saveProjectReadmeAction(prevState: SaveProjectReadmeFormSt
614613 if ( ! userRole || ! [ 'owner' , 'co-owner' , 'editor' ] . includes ( userRole ) ) {
615614 return { error : "You do not have permission to edit the README for this project." } ;
616615 }
617-
616+
618617 const updatedProject = await dbUpdateProjectReadme ( projectUuid , readmeContent ) ;
619618 if ( ! updatedProject ) {
620619 return { error : "Failed to save README in FlowUp." } ;
@@ -624,7 +623,7 @@ export async function saveProjectReadmeAction(prevState: SaveProjectReadmeFormSt
624623 const oauthToken = await dbGetUserGithubOAuthToken ( session . user . uuid ) ;
625624 if ( oauthToken ?. accessToken ) {
626625 const octokit = new Octokit ( { auth : oauthToken . accessToken } ) ;
627-
626+
628627 const repoParts = updatedProject . githubRepoName . split ( '/' ) ;
629628 if ( repoParts . length !== 2 ) {
630629 console . error ( `[saveProjectReadmeAction] Invalid githubRepoName format: ${ updatedProject . githubRepoName } ` ) ;
@@ -635,23 +634,23 @@ export async function saveProjectReadmeAction(prevState: SaveProjectReadmeFormSt
635634 console . error ( `[saveProjectReadmeAction] Invalid owner or repo after splitting githubRepoName: ${ updatedProject . githubRepoName } ` ) ;
636635 return { message : "README saved in FlowUp, but GitHub owner or repo name is invalid for GitHub update." , project : updatedProject } ;
637636 }
638-
637+
639638 let existingSha : string | null = null ;
640639 try {
641640 const { data : readmeData } = await octokit . rest . repos . getContent ( {
642641 owner,
643642 repo,
644643 path : 'README.md' ,
645644 } ) ;
646- if ( 'sha' in readmeData && readmeData . type === 'file' ) {
645+ if ( 'sha' in readmeData && readmeData . type === 'file' ) {
647646 existingSha = readmeData . sha ;
648647 }
649648 } catch ( error : any ) {
650- if ( error . status !== 404 ) {
649+ if ( error . status !== 404 ) {
651650 console . warn ( `[saveProjectReadmeAction] Could not fetch existing README SHA for ${ owner } /${ repo } :` , error . message ) ;
652651 }
653652 }
654-
653+
655654 await updateReadmeOnGithub ( octokit , owner , repo , readmeContent , existingSha ) ;
656655 return { message : "README saved in FlowUp and updated on GitHub." , project : updatedProject } ;
657656 } else {
@@ -733,7 +732,7 @@ export async function toggleProjectVisibilityAction(prevState: ToggleProjectVisi
733732 if ( userRole !== 'owner' && globalUser ?. role !== 'admin' ) {
734733 return { error : `Only project owners or admins can change project visibility. Your project role: ${ userRole || 'not a member' } , global role: ${ globalUser ?. role || 'unknown' } . UUID: ${ session . user . uuid } ` } ;
735734 }
736-
735+
737736 const updatedProjectInDb = await dbUpdateProjectVisibility ( projectUuid , isPrivate ) ;
738737 if ( ! updatedProjectInDb ) {
739738 return { error : "Failed to update project visibility in FlowUp." } ;
@@ -1105,7 +1104,6 @@ export async function deleteProjectAnnouncementAction(
11051104}
11061105
11071106// GitHub OAuth and Repo Actions
1108-
11091107export async function fetchUserGithubOAuthTokenAction ( ) : Promise < UserGithubOAuthToken | null > {
11101108 const session = await auth ( ) ;
11111109 if ( ! session ?. user ?. uuid ) {
@@ -1122,7 +1120,7 @@ export async function fetchUserGithubOAuthTokenAction(): Promise<UserGithubOAuth
11221120}
11231121
11241122function sanitizeRepoName ( name : string | null | undefined ) : string {
1125- if ( ! name ) {
1123+ if ( ! name ) {
11261124 return '' ;
11271125 }
11281126 const trimmedName = name . trim ( ) ;
@@ -1131,13 +1129,13 @@ function sanitizeRepoName(name: string | null | undefined): string {
11311129 }
11321130
11331131 let sanitized = trimmedName
1134- . replace ( / \s + / g, '-' ) // Replace spaces with hyphens
1135- . replace ( / [ ^ a - z A - Z 0 - 9 _ . - ] / g, '' ) // Remove invalid characters
1136- . replace ( / - + / g, '-' ) // Replace multiple hyphens with a single one
1137- . replace ( / ^ \. + | \. $ / g, '' ) // Remove leading/trailing dots
1138- . replace ( / ^ - + | - + $ / g, '' ) ; // Remove leading/trailing hyphens
1139-
1140- return sanitized . substring ( 0 , 100 ) ; // GitHub repo name max length is 100
1132+ . replace ( / \s + / g, '-' )
1133+ . replace ( / [ ^ a - z A - Z 0 - 9 _ . - ] / g, '' )
1134+ . replace ( / - + / g, '-' )
1135+ . replace ( / ^ \. + | \. $ / g, '' )
1136+ . replace ( / ^ - + | - + $ / g, '' ) ;
1137+
1138+ return sanitized . substring ( 0 , 100 ) ;
11411139}
11421140
11431141
@@ -1165,7 +1163,7 @@ export async function linkProjectToGithubAction(
11651163
11661164 if ( useDefaultRepoName ) {
11671165 if ( typeof flowUpProjectNameValue === 'string' && flowUpProjectNameValue . trim ( ) !== '' ) {
1168- nameForRepoCreation = `FlowUp- ${ flowUpProjectNameValue } ` ;
1166+ nameForRepoCreation = `FlowUp - ${ flowUpProjectNameValue } ` ;
11691167 } else {
11701168 return { error : "FlowUp Project Name is required to generate the default repository name." } ;
11711169 }
@@ -1176,7 +1174,7 @@ export async function linkProjectToGithubAction(
11761174 return { error : "Custom repository name cannot be empty." } ;
11771175 }
11781176 }
1179-
1177+
11801178 const repoSlug = sanitizeRepoName ( nameForRepoCreation ) ;
11811179 if ( ! repoSlug ) {
11821180 return { error : "Resulting repository name is invalid after sanitization. Please provide a valid name." } ;
@@ -1210,24 +1208,23 @@ export async function linkProjectToGithubAction(
12101208 name : repoSlug ,
12111209 private : repoIsPrivate ,
12121210 description : `Repository for FlowUp project: ${ projectFromDb . name } ` ,
1213- auto_init : true ,
1211+ auto_init : true ,
12141212 } ) ;
12151213 console . log ( `Successfully created repository: ${ createdRepo . data . html_url } ` ) ;
12161214
12171215 if ( projectFromDb . readmeContent && projectFromDb . readmeContent . trim ( ) !== '' ) {
1218- // Fetch existing README's SHA to avoid errors if it was created by auto_init
12191216 let existingReadmeSha : string | undefined = undefined ;
12201217 try {
12211218 const { data : readmeData } = await octokit . rest . repos . getContent ( {
12221219 owner : createdRepo . data . owner . login ,
12231220 repo : createdRepo . data . name ,
12241221 path : 'README.md' ,
12251222 } ) ;
1226- if ( 'sha' in readmeData && readmeData . type === 'file' ) { // Check if readmeData is a file object
1223+ if ( 'sha' in readmeData && readmeData . type === 'file' ) {
12271224 existingReadmeSha = readmeData . sha ;
12281225 }
12291226 } catch ( getContentError : any ) {
1230- if ( getContentError . status !== 404 ) { // Log error if not a "file not found"
1227+ if ( getContentError . status !== 404 ) {
12311228 console . warn ( `[linkProjectToGithubAction] Could not fetch existing README SHA for ${ createdRepo . data . owner . login } /${ createdRepo . data . name } :` , getContentError . message ) ;
12321229 }
12331230 }
@@ -1237,14 +1234,17 @@ export async function linkProjectToGithubAction(
12371234 } catch ( apiError : any ) {
12381235 console . error ( `GitHub API error creating repository: ${ apiError . status } ${ apiError . message } ` , apiError . response ?. data ) ;
12391236 const errorMessage = apiError . response ?. data ?. message || apiError . message || 'Unknown GitHub API error.' ;
1240- if ( apiError . status === 422 ) { // Unprocessable Entity (e.g., repo already exists)
1237+ if ( apiError . status === 403 ) {
1238+ return { error : `GitHub Permission Denied: ${ errorMessage } . Ensure your GitHub account has permissions to create repositories and the FlowUp OAuth App has the 'repo' scope.` } ;
1239+ }
1240+ if ( apiError . status === 422 ) {
12411241 return { error : `Failed to create GitHub repository '${ repoSlug } '. It might already exist or there's a naming conflict. GitHub's message: ${ errorMessage } ` } ;
12421242 }
12431243 return { error : `GitHub API Error (${ apiError . status || 'unknown' } ): ${ errorMessage } - ${ apiError . documentation_url || '' } ` } ;
12441244 }
12451245
12461246 const repoUrl = createdRepo . data . html_url ;
1247- const actualRepoName = createdRepo . data . full_name ;
1247+ const actualRepoName = createdRepo . data . full_name ;
12481248
12491249 const updatedProject = await dbUpdateProjectGithubRepo ( projectUuid , repoUrl , actualRepoName ) ;
12501250
@@ -1260,13 +1260,9 @@ export async function linkProjectToGithubAction(
12601260 }
12611261}
12621262
1263- /*
1264- // This function might be needed if we switch back to GitHub App installation flow for repo creation
1265- // For now, with OAuth flow, it's not directly used for creating repos but good for fetching installation details
1266- // if we need to interact with the app installation itself.
1267- export async function fetchUserGithubAccessDetailsAction(userUuid: string): Promise<{
1263+ async function fetchUserGithubAccessDetailsAction ( userUuid : string ) : Promise < {
12681264 oauthToken : UserGithubOAuthToken | null ;
1269- installation: UserGithubInstallation | null; // Placeholder, not fully implemented for OAuth flow
1265+ installation : UserGithubInstallation | null ;
12701266} > {
12711267 const session = await auth ( ) ;
12721268 if ( ! session ?. user ?. uuid || session . user . uuid !== userUuid ) {
@@ -1275,13 +1271,11 @@ export async function fetchUserGithubAccessDetailsAction(userUuid: string): Prom
12751271 }
12761272 try {
12771273 const oauthToken = await dbGetUserGithubOAuthToken ( userUuid ) ;
1278- // const installation = await getUserGithubInstallation(userUuid); // This is for GitHub App installs
1279- return { oauthToken, installation: null }; // Return null for installation in pure OAuth flow
1274+ return { oauthToken, installation : null } ;
12801275 } catch ( error ) {
12811276 console . error ( "[fetchUserGithubAccessDetailsAction] Error fetching GitHub access details:" , error ) ;
12821277 return { oauthToken : null , installation : null } ;
12831278 }
12841279}
1285- */
12861280
1287- `` `
1281+
0 commit comments