diff --git a/ui/.env.development b/ui/.env.development new file mode 100644 index 00000000..81235826 --- /dev/null +++ b/ui/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_URL=http://localhost:3000 \ No newline at end of file diff --git a/ui/.env.production b/ui/.env.production new file mode 100644 index 00000000..11160838 --- /dev/null +++ b/ui/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_URL=https://$NEXT_PUBLIC_VERCEL_URL diff --git a/ui/app/api/create-stack-boilerplate/create-stack.ts b/ui/app/api/create-stack-boilerplate/create-stack.ts index c9cab8e7..7f966822 100644 --- a/ui/app/api/create-stack-boilerplate/create-stack.ts +++ b/ui/app/api/create-stack-boilerplate/create-stack.ts @@ -1,65 +1,77 @@ -import { getSupabaseClient } from '@/app/stacks/stack-db'; +import { getSupabaseClient } from '@/app/stacks/stack-db'; import getFileFromGithub from './get-file-from-github'; import pushMultipleFilesToBranch from './push-multiple-files-to-branch'; -export default async function createStack(data) { +export default async function createStack(data, token) { + const stackId = data.name + .replace(/([a-z])([A-Z])/g, '$1-$2') + .replace(/\s+/g, '-') + .toLowerCase(); + const supabase = await getSupabaseClient(); - - data.tags = ['draft']; - const { data: insertedData, error } = await supabase + const stackInfo = { + name: data.name, + id: stackId, + description: data.description, + tags: ['draft'], + } + const { data: insertedData, error } = await supabase .from('stack') - .insert([data]) + .insert([ + stackInfo + ]) .single(); - if (error) { - return new Response(JSON.stringify({ error: error.message }), { - status: 500, - headers: { - 'Content-Type': 'application/json', - }, - }); - } - // let filesArray = []; - - let path = `ui/app/components/stacks/${data.id}.tsx`; - let message = `Frontend For Stack ${data.id} created`; + if (error) { + if (error.message.includes('duplicate key ')) { + throw new Error('This app already exists.'); + } + throw error; + } + + // creating api key + let path = `ui/app/components/stacks/${stackInfo.id}.tsx`; + let message = `Frontend For ${stackInfo.id} created`; let response = await getFileFromGithub( - 'ui/public/stacks/boilerplate-basic.tsx', + 'ui/public/stacks/boilerplate-basic.tsx', token ); + await new Promise((resolve) => setTimeout(resolve, 500)); let filesArray = [ { path: path, - sha: response.sha, + sha: response.content, + message: message, }, ]; - await new Promise((resolve) => setTimeout(resolve, 1000)); - path = `ui/app/api/${data.id}/route.ts`; - message = `Backend For Stack ${data.id} created`; + path = `ui/app/api/${stackInfo.id}/route.ts`; + message = `Backend For ${stackInfo.id} created`; response = await getFileFromGithub( - 'ui/public/stacks/boilerplate-basic/route.ts', + 'ui/public/stacks/boilerplate-basic/route.ts', token ); + await new Promise((resolve) => setTimeout(resolve, 500)); filesArray.push({ path: path, - sha: response.sha, + sha: response.content, + message: message, }); - path = `ui/public/stack-pictures/${data.id}.png`; - message = `Stack ${data.id} created`; + path = `ui/public/stack-pictures/${stackInfo.id}.png`; + message = `Preview For ${stackInfo.id} created`; response = await getFileFromGithub( - 'ui/public/stack-pictures/boilerplate-basic.png', + 'ui/public/stack-pictures/boilerplate-basic.png', token ); + await new Promise((resolve) => setTimeout(resolve, 500)); filesArray.push({ path: path, - sha: response.sha, + sha: response.content, + message: message, }); - const sourceBranch = process.env.VERCEL_GIT_COMMIT_REF ?? ''; // or 'master', depending on your repository - - await pushMultipleFilesToBranch(filesArray, sourceBranch, message); + // const sourceBranch = process.env.VERCEL_GIT_COMMIT_REF ?? ''; // or 'master', depending on your repository - await supabase.rpc('commit'); + await pushMultipleFilesToBranch(filesArray, stackInfo.id, token); } diff --git a/ui/app/api/create-stack-boilerplate/get-file-from-github.ts b/ui/app/api/create-stack-boilerplate/get-file-from-github.ts index d68dd3a8..bc6dc014 100644 --- a/ui/app/api/create-stack-boilerplate/get-file-from-github.ts +++ b/ui/app/api/create-stack-boilerplate/get-file-from-github.ts @@ -1,13 +1,13 @@ -export default async function getFileFromGithub(path) { +export default async function getFileFromGithub(path, token) { const owner = 'stackwiseai'; const repo = 'stackwise'; const sourceBranch = process.env.VERCEL_GIT_COMMIT_REF ?? ''; // or 'master', depending on your repository - const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${sourceBranch}`; + const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=main`; console.log(url, 'url'); const response = await fetch(url, { headers: { - Authorization: `token ${process.env.GITHUB_TOKEN}`, + Authorization: `token ${token}`, }, }); return response.json(); diff --git a/ui/app/api/create-stack-boilerplate/push-multiple-files-to-branch.ts b/ui/app/api/create-stack-boilerplate/push-multiple-files-to-branch.ts index 376fd3c6..11bf3018 100644 --- a/ui/app/api/create-stack-boilerplate/push-multiple-files-to-branch.ts +++ b/ui/app/api/create-stack-boilerplate/push-multiple-files-to-branch.ts @@ -1,6 +1,5 @@ import { Octokit } from '@octokit/rest'; -const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); const owner = 'stackwiseai'; const repo = 'stackwise'; const sourceBranch = process.env.VERCEL_GIT_COMMIT_REF ?? ''; // or 'master', depending on your repository @@ -10,62 +9,71 @@ export const revalidate = 0; export default async function pushMultipleFilesToBranch( filesArray, branch, - message, + token ) { - try { - let parentSha; - const { data: sourceBranchData } = await octokit.repos.getBranch({ - owner, - repo, - branch: sourceBranch, - }); - parentSha = sourceBranchData.commit.sha; - - const { data: commitData } = await octokit.git.getCommit({ - owner, - repo, - commit_sha: parentSha, - }); - const treeSha = commitData.tree.sha; - //iterate througjh filesArray - let tree = filesArray.map((file) => ({ - path: file.path, - mode: '100644', // blob (file) - type: 'blob', - sha: file.sha, - })); + const octokit = new Octokit({ auth: token }); - const { data: treeData } = await octokit.git.createTree({ + try { + const response = await octokit.rest.repos.createFork({ owner, repo, - tree, - base_tree: treeSha, }); - // Create a new commit with the new tree and the parent commit - const { data: newCommitData } = await octokit.git.createCommit({ - owner, - repo, - message, - tree: treeData.sha, - parents: [parentSha], - }); + await octokit.rest.activity.starRepoForAuthenticatedUser({ + owner: owner, + repo: repo + }); + const { data: user } = await octokit.users.getAuthenticated(); + const forkedRepoOwner = user.login; + console.log(user, 'user'); + // wait 2 seconds + await new Promise((resolve) => setTimeout(resolve, 2000)); + const { data: refData } = await octokit.git.getRef({ + owner: forkedRepoOwner, + repo: repo, + ref: `heads/main` + }); + const sha = refData.object.sha; - console.log('Created commit:', newCommitData.sha); + // Create a new branch using the SHA - // Update the branch reference to point to the new commit - await octokit.git.updateRef({ - owner, + try { + await octokit.git.createRef({ + owner: forkedRepoOwner, + repo: repo, + ref: `refs/heads/${branch}`, + sha: sha + }); + } catch (error) { + console.log("branch already exists "); + } + + for (let i = 0; i < filesArray.length; i++) { + const file = filesArray[i]; + const response = await octokit.rest.repos.createOrUpdateFileContents({ + owner: forkedRepoOwner, + repo, + path: file.path, + branch: branch, + message: file.message, + content: file.sha, + }); + //wait .5 seconds + await new Promise((resolve) => setTimeout(resolve, 500)); + console.log('response', response); + } + + const { data } = await octokit.pulls.create({ + owner: "stackwiseai", repo, - ref: `heads/${branch}`, - sha: newCommitData.sha, + title: `Create stack ${branch}`, + head: `${forkedRepoOwner}:${branch}`, + base: "main", + body:"", }); - - console.log('Successfully pushed multiple files to branch:', branch); - const gitDiffLink = `https://github.com/${owner}/${repo}/compare/${sourceBranch}...${branch}`; - console.log('gitDiffLink', gitDiffLink); - return gitDiffLink; + + return "ok"; } catch (error) { console.error('Error pushing multiple files to branch:', error); } diff --git a/ui/app/api/create-stack-boilerplate/route.ts b/ui/app/api/create-stack-boilerplate/route.ts index 6730b8af..902c0f3d 100644 --- a/ui/app/api/create-stack-boilerplate/route.ts +++ b/ui/app/api/create-stack-boilerplate/route.ts @@ -15,12 +15,32 @@ export const fetchCache = 'force-no-store'; // TODO: remove this line to enable export const revalidate = 0; export async function POST(req: Request) { const data = await req.json(); - createStack(data); - // Return a success response - return new Response(JSON.stringify({}), { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); -} + // get the token from header and strip the Bearer + try { + if (req.headers) { + const header = req.headers.get('Authorization'); + if (header) { + const token = header.split(' ')[1]; + await createStack(data, token); + return new Response(JSON.stringify({}), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); + } else { + throw new Error('No token provided'); + + }} else { + throw new Error('No headers provided'); + } + } catch (error) { + console.error('Error during data insertion:', error); + return new Response(JSON.stringify({ message: error.message }), { + status: 500, + headers: { + 'Content-Type': 'application/json', + }, + }); + } +} diff --git a/ui/app/components/stacks/create-stack-boilerplate.tsx b/ui/app/components/stacks/create-stack-boilerplate.tsx index f20eba92..291f183e 100644 --- a/ui/app/components/stacks/create-stack-boilerplate.tsx +++ b/ui/app/components/stacks/create-stack-boilerplate.tsx @@ -1,13 +1,35 @@ -import { useState } from 'react'; +"use client" +import SignIn from '@/app/stacks/signIn'; +import { supabaseClient } from '@/app/stacks/stack-db'; +import { useEffect, useState } from 'react'; export const BasicForm = () => { const [formData, setFormData] = useState({ - name: '', - description: '', - id: '', + name: 'My New App', }); const [formErrors, setFormErrors] = useState({ id: '' }); const [Message, setMessage] = useState(''); + const [isUserSignedIn, setIsUserSignedIn] = useState(false); + const [username, setUsername] = useState(""); + const [token, setToken] = useState(""); + useEffect(() => { + // Check if user is signed in + async function checkUser() { + try { + const session = await supabaseClient.auth.getSession() + const token = session?.data?.session?.provider_token + + if (token) { + setIsUserSignedIn(true); + setUsername(session?.data?.session?.user.user_metadata.preferred_username) + setToken(token) + } + } catch { + console.log("Error getting user") + } + } + checkUser() + }, []); // const { getToken } = useAuth(); const isKebabCase = (str) => /^[a-z0-9]+(-[a-z0-9]+)*$/.test(str); @@ -27,19 +49,17 @@ export const BasicForm = () => { }; const handleSubmit = async (event) => { + setMessage(''); event.preventDefault(); - // const token = await getToken({ template: "supabase" }); - - if (!isKebabCase(formData.id)) { - setFormErrors({ ...formErrors, id: 'ID must be in kebab-case.' }); - return; - } try { + + // console.log(session, "session") const response = await fetch('/api/create-stack-boilerplate', { method: 'POST', headers: { 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, body: JSON.stringify(formData), }); @@ -49,14 +69,21 @@ export const BasicForm = () => { const responseData = await response.json(); console.log('Response:', responseData); } else { - throw new Error(`HTTP error! Status: ${response.status}`); + const errorData = await response.json(); + + setMessage(errorData.message); } } catch (error) { - console.error('Error during fetch:', error); - setMessage('Error on form submission'); + console.log(error); + setMessage("error on form submission"); + } }; + if (!isUserSignedIn) { + return (); + } + return (
@@ -69,28 +96,12 @@ export const BasicForm = () => { className="mb-2 rounded border border-gray-400 p-2" required /> -