Spaces:
Sleeping
Sleeping
Commit ·
e9b2cbc
1
Parent(s): 78e893f
apply changes
Browse files- src/app/api/auth/github/callback/route.ts +1 -3
- src/app/api/sync/run/route.ts +4 -63
- src/app/api/sync/status/route.ts +15 -43
- src/db/schema.ts +5 -4
- src/lib/auth.ts +1 -3
src/app/api/auth/github/callback/route.ts
CHANGED
|
@@ -57,6 +57,7 @@ export async function GET(request: NextRequest) {
|
|
| 57 |
const githubUser = await userResponse.json();
|
| 58 |
|
| 59 |
// Check if user exists
|
|
|
|
| 60 |
const existingUsers = await db
|
| 61 |
.select({
|
| 62 |
id: users.id,
|
|
@@ -65,9 +66,6 @@ export async function GET(request: NextRequest) {
|
|
| 65 |
avatarUrl: users.avatarUrl,
|
| 66 |
role: users.role,
|
| 67 |
githubAccessToken: users.githubAccessToken,
|
| 68 |
-
syncStatus: users.syncStatus,
|
| 69 |
-
lastSyncAt: users.lastSyncAt,
|
| 70 |
-
syncError: users.syncError,
|
| 71 |
createdAt: users.createdAt,
|
| 72 |
updatedAt: users.updatedAt,
|
| 73 |
})
|
|
|
|
| 57 |
const githubUser = await userResponse.json();
|
| 58 |
|
| 59 |
// Check if user exists
|
| 60 |
+
// NOTE: sync_status columns excluded until Turso migration is applied
|
| 61 |
const existingUsers = await db
|
| 62 |
.select({
|
| 63 |
id: users.id,
|
|
|
|
| 66 |
avatarUrl: users.avatarUrl,
|
| 67 |
role: users.role,
|
| 68 |
githubAccessToken: users.githubAccessToken,
|
|
|
|
|
|
|
|
|
|
| 69 |
createdAt: users.createdAt,
|
| 70 |
updatedAt: users.updatedAt,
|
| 71 |
})
|
src/app/api/sync/run/route.ts
CHANGED
|
@@ -7,9 +7,6 @@
|
|
| 7 |
|
| 8 |
import { NextRequest, NextResponse } from "next/server";
|
| 9 |
import { getCurrentUser } from "@/lib/auth";
|
| 10 |
-
import { db } from "@/db";
|
| 11 |
-
import { users } from "@/db/schema";
|
| 12 |
-
import { eq } from "drizzle-orm";
|
| 13 |
import { runMaintainerSync, runContributorSync, syncContributorPRsDirect, reconcileOpenTriageIssue1 } from "@/lib/sync/github-sync";
|
| 14 |
|
| 15 |
const SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
@@ -29,7 +26,7 @@ export async function POST(request: NextRequest) {
|
|
| 29 |
return NextResponse.json({ error: "GitHub access token not found" }, { status: 400 });
|
| 30 |
}
|
| 31 |
|
| 32 |
-
//
|
| 33 |
if (activeSyncs.has(user.id)) {
|
| 34 |
console.log(`[SyncRun] User ${user.id} sync blocked by in-memory guard`);
|
| 35 |
return NextResponse.json({
|
|
@@ -39,56 +36,13 @@ export async function POST(request: NextRequest) {
|
|
| 39 |
}, { status: 429 });
|
| 40 |
}
|
| 41 |
|
| 42 |
-
//
|
| 43 |
-
const userRecord = await db.select({
|
| 44 |
-
id: users.id,
|
| 45 |
-
syncStatus: users.syncStatus,
|
| 46 |
-
lastSyncAt: users.lastSyncAt,
|
| 47 |
-
})
|
| 48 |
-
.from(users)
|
| 49 |
-
.where(eq(users.id, user.id))
|
| 50 |
-
.limit(1);
|
| 51 |
-
|
| 52 |
-
if (!userRecord || userRecord.length === 0) {
|
| 53 |
-
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
const currentSyncStatus = userRecord[0].syncStatus;
|
| 57 |
-
|
| 58 |
-
// Circuit breaker: Check if sync already in progress
|
| 59 |
-
if (currentSyncStatus === 'SYNCING') {
|
| 60 |
-
console.log(`[SyncRun] User ${user.id} sync already in progress (DB status)`);
|
| 61 |
-
return NextResponse.json({
|
| 62 |
-
error: "Sync already in progress",
|
| 63 |
-
status: "SYNCING",
|
| 64 |
-
message: "Please wait for the current sync to complete"
|
| 65 |
-
}, { status: 429 });
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
if (currentSyncStatus === 'PENDING') {
|
| 69 |
-
console.log(`[SyncRun] User ${user.id} sync is pending`);
|
| 70 |
-
return NextResponse.json({
|
| 71 |
-
error: "Sync is queued",
|
| 72 |
-
status: "PENDING",
|
| 73 |
-
message: "Sync request is queued and will start shortly"
|
| 74 |
-
}, { status: 202 });
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
// Add to in-memory set FIRST (before DB update)
|
| 78 |
activeSyncs.add(user.id);
|
| 79 |
console.log(`[SyncRun] Added user ${user.id} to activeSyncs. Size: ${activeSyncs.size}`);
|
| 80 |
|
| 81 |
-
// Set database status to SYNCING
|
| 82 |
-
await db.update(users)
|
| 83 |
-
.set({
|
| 84 |
-
syncStatus: 'SYNCING',
|
| 85 |
-
syncError: null
|
| 86 |
-
})
|
| 87 |
-
.where(eq(users.id, user.id));
|
| 88 |
-
|
| 89 |
-
console.log(`[SyncRun] Starting sync for user ${user.id}`);
|
| 90 |
-
|
| 91 |
try {
|
|
|
|
|
|
|
| 92 |
// Determine sync type based on user role
|
| 93 |
const userRole = user.role?.toUpperCase();
|
| 94 |
let stats;
|
|
@@ -109,13 +63,6 @@ export async function POST(request: NextRequest) {
|
|
| 109 |
// Always reconcile the critical openTriage#1 issue to ensure immediate state sync
|
| 110 |
const reconcileResult = await reconcileOpenTriageIssue1(user.githubAccessToken);
|
| 111 |
|
| 112 |
-
// Mark sync as COMPLETED
|
| 113 |
-
await db.update(users).set({
|
| 114 |
-
syncStatus: 'COMPLETED',
|
| 115 |
-
lastSyncAt: new Date().toISOString(),
|
| 116 |
-
syncError: null
|
| 117 |
-
}).where(eq(users.id, user.id));
|
| 118 |
-
|
| 119 |
console.log(`[SyncRun] Sync completed for user ${user.id}`);
|
| 120 |
|
| 121 |
return NextResponse.json({
|
|
@@ -133,12 +80,6 @@ export async function POST(request: NextRequest) {
|
|
| 133 |
} catch (syncError: any) {
|
| 134 |
console.error(`[SyncRun] Error syncing for user ${user.id}:`, syncError);
|
| 135 |
|
| 136 |
-
// Mark sync as FAILED with error message
|
| 137 |
-
await db.update(users).set({
|
| 138 |
-
syncStatus: 'FAILED',
|
| 139 |
-
syncError: syncError.message || 'Unknown error'
|
| 140 |
-
}).where(eq(users.id, user.id));
|
| 141 |
-
|
| 142 |
return NextResponse.json({
|
| 143 |
error: "Sync failed",
|
| 144 |
message: syncError.message || "Internal server error"
|
|
|
|
| 7 |
|
| 8 |
import { NextRequest, NextResponse } from "next/server";
|
| 9 |
import { getCurrentUser } from "@/lib/auth";
|
|
|
|
|
|
|
|
|
|
| 10 |
import { runMaintainerSync, runContributorSync, syncContributorPRsDirect, reconcileOpenTriageIssue1 } from "@/lib/sync/github-sync";
|
| 11 |
|
| 12 |
const SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
|
|
| 26 |
return NextResponse.json({ error: "GitHub access token not found" }, { status: 400 });
|
| 27 |
}
|
| 28 |
|
| 29 |
+
// Circuit Breaker: In-memory check (prevents concurrent syncs per user)
|
| 30 |
if (activeSyncs.has(user.id)) {
|
| 31 |
console.log(`[SyncRun] User ${user.id} sync blocked by in-memory guard`);
|
| 32 |
return NextResponse.json({
|
|
|
|
| 36 |
}, { status: 429 });
|
| 37 |
}
|
| 38 |
|
| 39 |
+
// Add to in-memory set BEFORE starting sync
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
activeSyncs.add(user.id);
|
| 41 |
console.log(`[SyncRun] Added user ${user.id} to activeSyncs. Size: ${activeSyncs.size}`);
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
try {
|
| 44 |
+
console.log(`[SyncRun] Starting sync for user ${user.id}`);
|
| 45 |
+
|
| 46 |
// Determine sync type based on user role
|
| 47 |
const userRole = user.role?.toUpperCase();
|
| 48 |
let stats;
|
|
|
|
| 63 |
// Always reconcile the critical openTriage#1 issue to ensure immediate state sync
|
| 64 |
const reconcileResult = await reconcileOpenTriageIssue1(user.githubAccessToken);
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
console.log(`[SyncRun] Sync completed for user ${user.id}`);
|
| 67 |
|
| 68 |
return NextResponse.json({
|
|
|
|
| 80 |
} catch (syncError: any) {
|
| 81 |
console.error(`[SyncRun] Error syncing for user ${user.id}:`, syncError);
|
| 82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
return NextResponse.json({
|
| 84 |
error: "Sync failed",
|
| 85 |
message: syncError.message || "Internal server error"
|
src/app/api/sync/status/route.ts
CHANGED
|
@@ -2,50 +2,22 @@
|
|
| 2 |
* Sync Status API Route
|
| 3 |
*
|
| 4 |
* GET /api/sync/status - Check current sync status for the authenticated user
|
| 5 |
-
*
|
|
|
|
| 6 |
*/
|
| 7 |
|
| 8 |
-
import {
|
| 9 |
-
import { getCurrentUser } from "@/lib/auth";
|
| 10 |
-
import { db } from "@/db";
|
| 11 |
-
import { users } from "@/db/schema";
|
| 12 |
-
import { eq } from "drizzle-orm";
|
| 13 |
|
| 14 |
-
export async function GET(
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
})
|
| 27 |
-
.from(users)
|
| 28 |
-
.where(eq(users.id, user.id))
|
| 29 |
-
.limit(1);
|
| 30 |
-
|
| 31 |
-
if (!userRecord || userRecord.length === 0) {
|
| 32 |
-
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
const { syncStatus, lastSyncAt, syncError } = userRecord[0];
|
| 36 |
-
|
| 37 |
-
return NextResponse.json({
|
| 38 |
-
status: syncStatus || 'IDLE',
|
| 39 |
-
lastSyncAt: lastSyncAt || null,
|
| 40 |
-
error: syncError || null,
|
| 41 |
-
isIdle: syncStatus === 'IDLE' || syncStatus === 'COMPLETED',
|
| 42 |
-
isSyncing: syncStatus === 'SYNCING',
|
| 43 |
-
isPending: syncStatus === 'PENDING',
|
| 44 |
-
hasFailed: syncStatus === 'FAILED',
|
| 45 |
-
});
|
| 46 |
-
|
| 47 |
-
} catch (error) {
|
| 48 |
-
console.error("GET /api/sync/status error:", error);
|
| 49 |
-
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
| 50 |
-
}
|
| 51 |
}
|
|
|
|
| 2 |
* Sync Status API Route
|
| 3 |
*
|
| 4 |
* GET /api/sync/status - Check current sync status for the authenticated user
|
| 5 |
+
* DISABLED: sync_status columns not yet migrated in Turso database
|
| 6 |
+
* Re-enable after running: turso db shell <db-name> < src/db/migrations/add_sync_status.sql
|
| 7 |
*/
|
| 8 |
|
| 9 |
+
import { NextResponse } from "next/server";
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
+
export async function GET() {
|
| 12 |
+
// Temporarily return IDLE since sync_status columns don't exist in DB yet
|
| 13 |
+
return NextResponse.json({
|
| 14 |
+
status: 'IDLE',
|
| 15 |
+
lastSyncAt: null,
|
| 16 |
+
error: null,
|
| 17 |
+
isIdle: true,
|
| 18 |
+
isSyncing: false,
|
| 19 |
+
isPending: false,
|
| 20 |
+
hasFailed: false,
|
| 21 |
+
message: 'Sync status tracking will be available after database migration'
|
| 22 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
src/db/schema.ts
CHANGED
|
@@ -39,10 +39,11 @@ export const users = sqliteTable("users", {
|
|
| 39 |
role: text("role"), // email: text("email"),
|
| 40 |
githubAccessToken: text("github_access_token"),
|
| 41 |
|
| 42 |
-
// Sync status tracking (Phase 2) -
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
| 46 |
|
| 47 |
createdAt: text("created_at").notNull(),
|
| 48 |
updatedAt: text("updated_at").notNull(),
|
|
|
|
| 39 |
role: text("role"), // email: text("email"),
|
| 40 |
githubAccessToken: text("github_access_token"),
|
| 41 |
|
| 42 |
+
// Sync status tracking (Phase 2) - DISABLED until Turso migration is applied
|
| 43 |
+
// Run: turso db shell <db-name> < src/db/migrations/add_sync_status.sql
|
| 44 |
+
// syncStatus: text("sync_status").default("IDLE"), // IDLE | PENDING | SYNCING | COMPLETED | FAILED
|
| 45 |
+
// lastSyncAt: text("last_sync_at"), // ISO timestamp
|
| 46 |
+
// syncError: text("sync_error"), // Error message if failed
|
| 47 |
|
| 48 |
createdAt: text("created_at").notNull(),
|
| 49 |
updatedAt: text("updated_at").notNull(),
|
src/lib/auth.ts
CHANGED
|
@@ -66,6 +66,7 @@ export async function getCurrentUser(request: NextRequest) {
|
|
| 66 |
console.log("[getCurrentUser] Token verified, user_id:", payload.user_id);
|
| 67 |
|
| 68 |
// Fetch full user from database
|
|
|
|
| 69 |
const userRecords = await db
|
| 70 |
.select({
|
| 71 |
id: users.id,
|
|
@@ -74,9 +75,6 @@ export async function getCurrentUser(request: NextRequest) {
|
|
| 74 |
avatarUrl: users.avatarUrl,
|
| 75 |
role: users.role,
|
| 76 |
githubAccessToken: users.githubAccessToken,
|
| 77 |
-
syncStatus: users.syncStatus,
|
| 78 |
-
lastSyncAt: users.lastSyncAt,
|
| 79 |
-
syncError: users.syncError,
|
| 80 |
createdAt: users.createdAt,
|
| 81 |
updatedAt: users.updatedAt,
|
| 82 |
})
|
|
|
|
| 66 |
console.log("[getCurrentUser] Token verified, user_id:", payload.user_id);
|
| 67 |
|
| 68 |
// Fetch full user from database
|
| 69 |
+
// NOTE: sync_status columns excluded until Turso migration is applied
|
| 70 |
const userRecords = await db
|
| 71 |
.select({
|
| 72 |
id: users.id,
|
|
|
|
| 75 |
avatarUrl: users.avatarUrl,
|
| 76 |
role: users.role,
|
| 77 |
githubAccessToken: users.githubAccessToken,
|
|
|
|
|
|
|
|
|
|
| 78 |
createdAt: users.createdAt,
|
| 79 |
updatedAt: users.updatedAt,
|
| 80 |
})
|