Skip to content

Commit b9a9433

Browse files
Build Error
Module not found: Can't resolve '../../actions' ./src/app/
1 parent 9046499 commit b9a9433

3 files changed

Lines changed: 224 additions & 103 deletions

File tree

src/app/(app)/projects/[id]/actions.ts

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ import {
3131
deleteProjectAnnouncement as dbDeleteProjectAnnouncement,
3232
updateProjectGithubRepo as dbUpdateProjectGithubRepo,
3333
getUserGithubOAuthToken as dbGetUserGithubOAuthToken,
34+
getTaskByUuid as dbGetTaskByUuid,
3435
} from '@/lib/db';
3536
import { z } from 'zod';
3637
import { auth } from '@/lib/authEdge';
3738
import { Octokit } from 'octokit';
3839
import { Buffer } from 'buffer';
39-
import { getAppAuthOctokit, getInstallationOctokit } from '@/lib/githubAppClient';
4040

4141

4242
export async function fetchProjectAction(uuid: string | undefined): Promise<Project | null> {
@@ -328,6 +328,18 @@ export interface UpdateTaskFormState {
328328
updatedTask?: Task;
329329
}
330330

331+
const UpdateTaskSchema = z.object({
332+
taskUuid: z.string().uuid("Invalid task UUID."),
333+
projectUuid: z.string().uuid("Invalid project UUID."),
334+
title: z.string().min(1, "Title is required.").max(255).optional(),
335+
description: z.string().optional(),
336+
todoListMarkdown: z.string().optional().default(''),
337+
status: z.enum(['To Do', 'In Progress', 'Done', 'Archived'] as [TaskStatus, ...TaskStatus[]]).optional(),
338+
assigneeUuid: z.string().uuid("Invalid Assignee UUID format.").optional().or(z.literal('')),
339+
tagsString: z.string().optional(),
340+
});
341+
342+
331343
export async function updateTaskAction(prevState: UpdateTaskFormState, formData: FormData): Promise<UpdateTaskFormState> {
332344
const session = await auth();
333345
if (!session?.user?.uuid) {
@@ -336,18 +348,6 @@ export async function updateTaskAction(prevState: UpdateTaskFormState, formData:
336348
}
337349
console.log("[updateTaskAction] Authenticated user for permission check:", session.user.uuid);
338350

339-
const UpdateTaskSchema = z.object({
340-
taskUuid: z.string().uuid("Invalid task UUID."),
341-
projectUuid: z.string().uuid("Invalid project UUID."),
342-
title: z.string().min(1, "Title is required.").max(255).optional(),
343-
description: z.string().optional(),
344-
todoListMarkdown: z.string().optional().default(''),
345-
status: z.enum(['To Do', 'In Progress', 'Done', 'Archived'] as [TaskStatus, ...TaskStatus[]]).optional(),
346-
assigneeUuid: z.string().uuid("Invalid Assignee UUID format.").optional().or(z.literal('')),
347-
tagsString: z.string().optional(),
348-
});
349-
350-
351351
const validatedFields = UpdateTaskSchema.safeParse({
352352
taskUuid: formData.get('taskUuid'),
353353
projectUuid: formData.get('projectUuid'),
@@ -400,7 +400,8 @@ export async function updateTaskAction(prevState: UpdateTaskFormState, formData:
400400

401401

402402
if (Object.keys(dataToUpdate).length === 0) {
403-
return { message: "No changes to update.", updatedTask: await dbGetTaskByUuid(taskUuid) || undefined };
403+
const currentTaskData = await dbGetTaskByUuid(taskUuid);
404+
return { message: "No changes to update.", updatedTask: currentTaskData || undefined };
404405
}
405406

406407
const updatedTask = await dbUpdateTask(taskUuid, dataToUpdate);
@@ -530,8 +531,8 @@ async function updateReadmeOnGithub(octokit: Octokit, owner: string, repo: strin
530531
message: 'Update README.md from FlowUp',
531532
content: Buffer.from(content).toString('base64'),
532533
committer: {
533-
name: 'FlowUp Bot', // Consider making this configurable or using the user's GitHub name
534-
email: 'bot@flowup.app', // Or a no-reply / user's email
534+
name: 'FlowUp Bot',
535+
email: 'bot@flowup.app',
535536
},
536537
};
537538
if (existingSha) {
@@ -544,8 +545,8 @@ async function updateReadmeOnGithub(octokit: Octokit, owner: string, repo: strin
544545
} catch (error: any) {
545546
if (error.status === 404 && !existingSha) {
546547
console.log(`[updateReadmeOnGithub] README.md not found for ${owner}/${repo}, creating it.`);
547-
const paramsForCreation = { ...paramsForUpdate };
548-
delete paramsForCreation.sha;
548+
const paramsForCreation = { ...paramsForUpdate }; // Re-create params for creation
549+
delete paramsForCreation.sha; // Ensure SHA is not in creation params
549550
try {
550551
await octokit.rest.repos.createOrUpdateFileContents(paramsForCreation);
551552
console.log(`[updateReadmeOnGithub] README.md created successfully on GitHub for ${owner}/${repo} after initial 404.`);
@@ -711,15 +712,11 @@ export async function toggleProjectVisibilityAction(prevState: ToggleProjectVisi
711712
if (oauthToken?.accessToken) {
712713
const octokit = new Octokit({ auth: oauthToken.accessToken });
713714
const repoParts = updatedProjectInDb.githubRepoName.split('/');
714-
if (repoParts.length !== 2) {
715+
if (repoParts.length !== 2 || !repoParts[0] || !repoParts[1]) { // Added check for empty parts
715716
console.error(`[toggleProjectVisibilityAction] Invalid githubRepoName format: ${updatedProjectInDb.githubRepoName}`);
716717
return { error: `Project visibility updated in FlowUp, but GitHub repo name format is invalid.`, project: updatedProjectInDb };
717718
}
718719
const [owner, repo] = repoParts;
719-
if (!owner || !repo) {
720-
console.error(`[toggleProjectVisibilityAction] Invalid owner or repo after splitting githubRepoName: ${updatedProjectInDb.githubRepoName}`);
721-
return { error: `Project visibility updated in FlowUp, but GitHub owner or repo name is invalid.`, project: updatedProjectInDb };
722-
}
723720

724721
try {
725722
await octokit.rest.repos.update({
@@ -1139,7 +1136,8 @@ export async function linkProjectToGithubAction(
11391136
if (githubRepoNameValue && githubRepoNameValue.trim() !== '') {
11401137
nameForRepoCreation = githubRepoNameValue;
11411138
} else {
1142-
return { error: "Custom repository name cannot be empty." };
1139+
// This case should ideally be caught by client-side validation, but good to have server-side too.
1140+
return { error: "Custom repository name cannot be empty when not using the default." };
11431141
}
11441142
}
11451143

@@ -1176,11 +1174,10 @@ export async function linkProjectToGithubAction(
11761174
name: repoSlug,
11771175
private: repoIsPrivate,
11781176
description: `Repository for FlowUp project: ${projectFromDb.name}`,
1179-
auto_init: true, // Initialize with a README to avoid empty repo issues
1177+
auto_init: true,
11801178
});
11811179
console.log(`Successfully created repository: ${createdRepo.data.html_url}`);
11821180

1183-
// Sync FlowUp README to GitHub if content exists
11841181
if (projectFromDb.readmeContent && projectFromDb.readmeContent.trim() !== '') {
11851182
let existingReadmeSha: string | undefined = undefined;
11861183
try {
@@ -1206,7 +1203,7 @@ export async function linkProjectToGithubAction(
12061203
console.error(`GitHub API error creating repository: ${apiError.status} ${apiError.message}`, apiError.response?.data);
12071204
const errorMessage = apiError.response?.data?.message || apiError.message || 'Unknown GitHub API error.';
12081205
if (apiError.status === 403) {
1209-
return { error: `GitHub Permission Denied: ${errorMessage}. Ensure your GitHub account has permissions to create repositories and the FlowUp OAuth App has the 'repo' scope.`};
1206+
return { error: `GitHub Permission Denied: ${errorMessage}. Ensure your GitHub OAuth App has the 'repo' scope and necessary permissions.`};
12101207
}
12111208
if (apiError.status === 422) {
12121209
return { error: `Failed to create GitHub repository '${repoSlug}'. It might already exist or there's a naming conflict. GitHub's message: ${errorMessage}` };
@@ -1215,7 +1212,7 @@ export async function linkProjectToGithubAction(
12151212
}
12161213

12171214
const repoUrl = createdRepo.data.html_url;
1218-
const actualRepoName = createdRepo.data.full_name; // e.g., "username/repo-name"
1215+
const actualRepoName = createdRepo.data.full_name;
12191216

12201217
const updatedProject = await dbUpdateProjectGithubRepo(projectUuid, repoUrl, actualRepoName);
12211218

@@ -1243,11 +1240,11 @@ export async function getRepoContentsAction(projectUuid: string, path: string =
12431240

12441241
const oauthToken = await dbGetUserGithubOAuthToken(session.user.uuid);
12451242
if (!oauthToken || !oauthToken.accessToken) {
1246-
return { error: "GitHub account not linked or token missing." };
1243+
return { error: "GitHub account not linked or token missing. Please connect your GitHub account from the CodeSpace tab." };
12471244
}
12481245

12491246
const [owner, repo] = project.githubRepoName.split('/');
1250-
if (!owner || !repo) return { error: "Invalid GitHub repository name format." };
1247+
if (!owner || !repo) return { error: "Invalid GitHub repository name format in FlowUp project data." };
12511248

12521249
const octokit = new Octokit({ auth: oauthToken.accessToken });
12531250

@@ -1260,15 +1257,26 @@ export async function getRepoContentsAction(projectUuid: string, path: string =
12601257
if (Array.isArray(data)) {
12611258
return data as GithubRepoContentItem[];
12621259
}
1263-
// If data is a single file object (when path points to a file)
1260+
// If data is a single file object (when path points to a file, though this endpoint usually returns array for dirs)
12641261
return [data as GithubRepoContentItem];
12651262
} catch (error: any) {
1266-
console.error(`[getRepoContentsAction] Error fetching content for ${owner}/${repo}/${path}:`, error);
1267-
return { error: error.message || "Failed to fetch repository contents." };
1263+
console.error(`[getRepoContentsAction] Error fetching content for ${owner}/${repo}/${path}:`, error.status, error.message, error.response?.data);
1264+
let userMessage = "Failed to fetch repository contents.";
1265+
if (error.status === 404) {
1266+
userMessage = `Path '${path}' not found in repository '${project.githubRepoName}'.`;
1267+
} else if (error.status === 403) {
1268+
userMessage = `Access denied to repository '${project.githubRepoName}'. Check your GitHub token permissions.`;
1269+
} else if (error.message) {
1270+
userMessage = error.message;
1271+
}
1272+
return { error: userMessage };
12681273
}
12691274
}
12701275

1271-
export async function getFileContentAction(projectUuid: string, filePath: string): Promise<{ content: string; sha: string } | { error: string }> {
1276+
export async function getFileContentAction(
1277+
projectUuid: string,
1278+
filePath: string
1279+
): Promise<{ content: string; sha: string; name: string; path: string; html_url?: string | null; download_url?: string | null; encoding?: string; size: number } | { error: string }> {
12721280
const session = await auth();
12731281
if (!session?.user?.uuid) return { error: "Authentication required." };
12741282

@@ -1279,7 +1287,7 @@ export async function getFileContentAction(projectUuid: string, filePath: string
12791287

12801288
const oauthToken = await dbGetUserGithubOAuthToken(session.user.uuid);
12811289
if (!oauthToken || !oauthToken.accessToken) {
1282-
return { error: "GitHub account not linked or token missing." };
1290+
return { error: "GitHub account not linked or token missing. Please connect your GitHub account from the CodeSpace tab." };
12831291
}
12841292

12851293
const [owner, repo] = project.githubRepoName.split('/');
@@ -1301,9 +1309,39 @@ export async function getFileContentAction(projectUuid: string, filePath: string
13011309
// @ts-ignore
13021310
const content = Buffer.from(data.content, data.encoding as BufferEncoding || 'base64').toString('utf8');
13031311
// @ts-ignore
1304-
return { content, sha: data.sha };
1312+
return { content, sha: data.sha, name: data.name, path: data.path, html_url: data.html_url, download_url: data.download_url, encoding: data.encoding, size: data.size };
13051313
} catch (error: any) {
1306-
console.error(`[getFileContentAction] Error fetching file content for ${owner}/${repo}/${filePath}:`, error);
1307-
return { error: error.message || "Failed to fetch file content." };
1314+
console.error(`[getFileContentAction] Error fetching file content for ${owner}/${repo}/${filePath}:`, error.status, error.message, error.response?.data);
1315+
let userMessage = "Failed to fetch file content.";
1316+
if (error.status === 404) {
1317+
userMessage = `File '${filePath}' not found in repository '${project.githubRepoName}'.`;
1318+
} else if (error.status === 403) {
1319+
userMessage = `Access denied to file '${filePath}' in repository '${project.githubRepoName}'. Check token permissions.`;
1320+
} else if (error.response?.data?.message) {
1321+
userMessage = error.response.data.message;
1322+
} else if (error.message) {
1323+
userMessage = error.message;
1324+
}
1325+
return { error: userMessage };
13081326
}
13091327
}
1328+
1329+
// Placeholder for future GitHub App related auth, if needed
1330+
// export async function fetchUserGithubAccessDetailsAction(): Promise<{
1331+
// installation: UserGithubInstallation | null;
1332+
// oauthToken: UserGithubOAuthToken | null;
1333+
// } | { error: string }> {
1334+
// const session = await auth();
1335+
// if (!session?.user?.uuid) {
1336+
// return { error: "Authentication required." };
1337+
// }
1338+
// try {
1339+
// const installation = await dbGetUserGithubInstallation(session.user.uuid);
1340+
// const oauthToken = await dbGetUserGithubOAuthToken(session.user.uuid);
1341+
// return { installation, oauthToken };
1342+
// } catch (error: any) {
1343+
// return { error: "Failed to fetch GitHub access details: " + error.message };
1344+
// }
1345+
// }
1346+
1347+

0 commit comments

Comments
 (0)