generated from obsidianmd/obsidian-sample-plugin
-
-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f0edda
commit 5440ad4
Showing
22 changed files
with
2,142 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { NextResponse } from "next/server"; | ||
import { db, vercelTokens } from "@/drizzle/schema"; | ||
import { Vercel } from "@vercel/sdk"; | ||
import { headers } from "next/headers"; | ||
|
||
const CRON_SECRET = process.env.CRON_SECRET; | ||
|
||
export async function GET(request: Request) { | ||
console.log("Redeploy cron job started"); | ||
// Verify the request is from Vercel Cron | ||
const headersList = headers(); | ||
const authHeader = headersList.get('authorization'); | ||
|
||
if (authHeader !== `Bearer ${CRON_SECRET}`) { | ||
return new NextResponse('Unauthorized', { status: 401 }); | ||
} | ||
|
||
try { | ||
// Get all tokens from the database | ||
const tokens = await db | ||
.select() | ||
.from(vercelTokens); | ||
|
||
console.log(`Found ${tokens.length} tokens to process`); | ||
|
||
const results = await Promise.allSettled( | ||
tokens.map(async (tokenRecord) => { | ||
try { | ||
const vercel = new Vercel({ | ||
bearerToken: tokenRecord.token, | ||
}); | ||
|
||
if (!tokenRecord.projectId) { | ||
console.log(`No project ID for user ${tokenRecord.userId}`); | ||
return; | ||
} | ||
const repo = "file-organizer-2000"; | ||
const org = "different-ai"; | ||
const ref = "master"; | ||
|
||
// Create new deployment with correct properties | ||
const deployment = await vercel.deployments.createDeployment({ | ||
requestBody: { | ||
name: `file-organizer-redeploy-${Date.now()}`, | ||
target: "production", | ||
project: tokenRecord.projectId, | ||
gitSource: { | ||
type: "github", | ||
repo: repo, | ||
ref: ref, | ||
org: org, | ||
}, | ||
projectSettings: { | ||
framework: "nextjs", | ||
buildCommand: "pnpm build:self-host", | ||
installCommand: "pnpm install", | ||
outputDirectory: ".next", | ||
rootDirectory: "web", | ||
}, | ||
}, | ||
}); | ||
|
||
console.log(`Redeployed project ${tokenRecord.projectId} for user ${tokenRecord.userId}`); | ||
return deployment; | ||
} catch (error) { | ||
console.error(`Failed to redeploy for user ${tokenRecord.userId}:`, error); | ||
throw error; | ||
} | ||
}) | ||
); | ||
|
||
const successful = results.filter((r) => r.status === "fulfilled").length; | ||
const failed = results.filter((r) => r.status === "rejected").length; | ||
|
||
return NextResponse.json({ | ||
message: `Processed ${tokens.length} tokens`, | ||
stats: { | ||
total: tokens.length, | ||
successful, | ||
failed, | ||
}, | ||
}); | ||
} catch (error) { | ||
console.error("Error in redeploy cron:", error); | ||
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { NextResponse } from "next/server"; | ||
import { db, vercelTokens } from "@/drizzle/schema"; | ||
import { Vercel } from "@vercel/sdk"; | ||
import { auth } from "@clerk/nextjs/server"; | ||
import { eq } from "drizzle-orm"; | ||
|
||
export async function POST() { | ||
try { | ||
const { userId } = auth(); | ||
if (!userId) { | ||
return new NextResponse("Unauthorized", { status: 401 }); | ||
} | ||
|
||
const tokenRecord = await db | ||
.select() | ||
.from(vercelTokens) | ||
.where(eq(vercelTokens.userId, userId)) | ||
.limit(1); | ||
|
||
if (!tokenRecord[0] || !tokenRecord[0].projectId) { | ||
return new NextResponse("No deployment found", { status: 404 }); | ||
} | ||
|
||
const vercel = new Vercel({ | ||
bearerToken: tokenRecord[0].token, | ||
}); | ||
const repo = "file-organizer-2000"; | ||
const org = "different-ai"; | ||
const ref = "master"; | ||
|
||
const deployment = await vercel.deployments.createDeployment({ | ||
requestBody: { | ||
name: `file-organizer-redeploy-${Date.now()}`, | ||
target: "production", | ||
project: tokenRecord[0].projectId, | ||
gitSource: { | ||
type: "github", | ||
repo: repo, | ||
ref: ref, | ||
org: org, | ||
}, | ||
projectSettings: { | ||
framework: "nextjs", | ||
buildCommand: "pnpm build:self-host", | ||
installCommand: "pnpm install", | ||
outputDirectory: ".next", | ||
rootDirectory: "web", | ||
}, | ||
}, | ||
}); | ||
|
||
return NextResponse.json({ | ||
success: true, | ||
deploymentUrl: deployment.url, | ||
}); | ||
} catch (error) { | ||
console.error("Error in redeploy:", error); | ||
return NextResponse.json( | ||
{ error: "Failed to redeploy" }, | ||
{ status: 500 } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
"use server"; | ||
import { Vercel } from "@vercel/sdk"; | ||
import { auth } from "@clerk/nextjs/server"; | ||
import { db, vercelTokens } from "@/drizzle/schema"; | ||
import { eq, exists } from "drizzle-orm"; | ||
import { createLicenseKeyFromUserId } from "@/app/actions"; | ||
|
||
const GITHUB_ORG = "different-ai"; | ||
const GITHUB_REPO = "file-organizer-2000"; | ||
const GITHUB_BRANCH = "master"; | ||
|
||
type SetupProjectResult = { | ||
success: boolean; | ||
deploymentUrl: string; | ||
projectId: string; | ||
licenseKey: string; | ||
projectUrl: string; | ||
}; | ||
|
||
export async function setupProject( | ||
vercelToken: string, | ||
openaiKey: string | ||
): Promise<SetupProjectResult> { | ||
const { userId } = auth(); | ||
// create an api key for the user | ||
if (!userId) { | ||
throw new Error("User not authenticated"); | ||
} | ||
const apiKey = await createLicenseKeyFromUserId(userId); | ||
|
||
if (!vercelToken) { | ||
throw new Error("Vercel token is required"); | ||
} | ||
|
||
if (!openaiKey) { | ||
throw new Error("OpenAI API key is required"); | ||
} | ||
console.log("userId", userId); | ||
|
||
// Store or update the token | ||
const existingToken = await db | ||
.select() | ||
.from(vercelTokens) | ||
.where(eq(vercelTokens.userId, userId)); | ||
|
||
console.log("existingToken", existingToken); | ||
|
||
if (existingToken.length > 0) { | ||
console.log("Updating existing token", vercelToken); | ||
// Update existing token | ||
await db | ||
.update(vercelTokens) | ||
.set({ token: vercelToken, updatedAt: new Date() }) | ||
.where(eq(vercelTokens.userId, userId)); | ||
} else { | ||
console.log("Inserting new token"); | ||
// Insert new token | ||
await db.insert(vercelTokens).values({ | ||
userId, | ||
token: vercelToken, | ||
}); | ||
} | ||
|
||
const vercel = new Vercel({ | ||
bearerToken: vercelToken, | ||
}); | ||
|
||
console.log("Starting setupProject process"); | ||
const uniqueProjectName = `file-organizer-${Math.random() | ||
.toString(36) | ||
.substring(2, 15)}`; | ||
|
||
const projectUrl = `https://${uniqueProjectName}.vercel.app`; | ||
|
||
console.log("apiKey", apiKey.key.key); | ||
try { | ||
// Create project with required settings | ||
console.log("Creating new project..."); | ||
const createProjectResponse = await vercel.projects.createProject({ | ||
requestBody: { | ||
name: uniqueProjectName, | ||
rootDirectory: "web", | ||
publicSource: true, | ||
framework: "nextjs", | ||
buildCommand: "pnpm build:self-host", | ||
installCommand: "pnpm install", | ||
outputDirectory: ".next", | ||
environmentVariables: [ | ||
{ | ||
key: "SOLO_API_KEY", | ||
value: apiKey.key.key, | ||
type: "plain", | ||
target: "production", | ||
}, | ||
{ | ||
key: "OPENAI_API_KEY", | ||
type: "plain", | ||
value: openaiKey, | ||
target: "production", | ||
}, | ||
], | ||
}, | ||
}); | ||
console.log(`✅ Project created successfully: ${createProjectResponse.id}`); | ||
|
||
// Create deployment with project settings | ||
console.log("Creating deployment..."); | ||
const deploymentResponse = await vercel.deployments.createDeployment({ | ||
requestBody: { | ||
name: uniqueProjectName, | ||
target: "production", | ||
gitSource: { | ||
type: "github", | ||
repo: GITHUB_REPO, | ||
ref: GITHUB_BRANCH, | ||
org: GITHUB_ORG, | ||
}, | ||
projectSettings: { | ||
framework: "nextjs", | ||
buildCommand: "pnpm build:self-host", | ||
installCommand: "pnpm install", | ||
outputDirectory: ".next", | ||
rootDirectory: "web", | ||
}, | ||
}, | ||
}); | ||
|
||
// Update token record with project details and URL | ||
await db | ||
.update(vercelTokens) | ||
.set({ | ||
projectId: createProjectResponse.id, | ||
deploymentUrl: deploymentResponse.url, | ||
projectUrl, | ||
updatedAt: new Date(), | ||
}) | ||
.where(eq(vercelTokens.userId, userId)); | ||
|
||
return { | ||
success: true, | ||
deploymentUrl: deploymentResponse.url, | ||
projectId: createProjectResponse.id, | ||
licenseKey: apiKey.key.key, | ||
projectUrl, | ||
}; | ||
} catch (error: any) { | ||
console.error("❌ Error in setupProject:", error); | ||
throw error; | ||
} | ||
} | ||
|
||
// Helper function to get user's Vercel deployment info | ||
export async function getVercelDeployment() { | ||
const { userId } = auth(); | ||
if (!userId) { | ||
throw new Error("User not authenticated"); | ||
} | ||
|
||
const tokenRecord = await db | ||
.select() | ||
.from(vercelTokens) | ||
.where(eq(vercelTokens.userId, userId)) | ||
.limit(1); | ||
|
||
return tokenRecord[0] | ||
? { | ||
deploymentUrl: tokenRecord[0].deploymentUrl, | ||
projectId: tokenRecord[0].projectId, | ||
projectUrl: tokenRecord[0].projectUrl, | ||
} | ||
: null; | ||
} |
Oops, something went wrong.