Compare commits

..

No commits in common. "dd98781cc54a16d36d1cfd4bbe4dd00881c65572" and "6377e46c5c34d33c4aa479f76a3311f8aba40746" have entirely different histories.

13 changed files with 60 additions and 750 deletions

View File

@ -1,39 +0,0 @@
"use server";
import prisma from "@/lib/prisma";
export type NewApplication = {
serviceProvider: string;
git: {
repositoryId: number;
repositoryName: string;
branch: string;
path: string;
};
autodeploy: boolean;
env: {
[key: string]: string;
};
name: string;
};
export async function createApplication(app: NewApplication, workspace: string) {
const application = await prisma.application.create({
data: {
name: app.name,
autoDeploy: app.autodeploy,
branch: app.git.branch,
path: app.git.path,
repositoryId: app.git.repositoryId.toString(),
repository: app.git.repositoryName,
serviceProvider: app.serviceProvider,
Workspace: {
connect: {
slug: workspace
}
}
},
});
return application;
}

View File

@ -46,16 +46,3 @@ export async function getUserRepository(userId: string, username: string): Promi
return repositories.repositories as GithubRepository[]; return repositories.repositories as GithubRepository[];
} }
export async function getRepositoryBranches(userId: string, username: string, repository: string): Promise<string[]> {
const branches = await fetch(`https://api.github.com/repos/${username}/${repository}/branches`, {
headers: {
Authorization: `Bearer ${await getInstallationAccessToken(userId, username)}`,
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
method: 'GET',
}).then(res => res.json());
return branches.map((branch: { name: string }) => branch.name) as string[];
}

View File

@ -2,19 +2,11 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
import { Plus, Link as LinkIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
import { Progress } from '@/components/ui/progress';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Plus } from 'lucide-react';
import { useEffect, useState } from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import Link from 'next/link'; import Link from 'next/link';
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { getRepositoryBranches, getUserRepository, GithubRepository } from '@/actions/github/repository';
import { signOut, useSession } from 'next-auth/react';
import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox';
import { createApplication, NewApplication } from '@/actions/deploy/application';
import { useParams, useRouter } from 'next/navigation';
export const ServiceProviderList = [ export const ServiceProviderList = [
{ {
@ -36,52 +28,10 @@ export const ServiceProviderList = [
]; ];
export default function CreateApplicationForm() { export default function CreateApplicationForm() {
const [steps, setSteps] = useState(1); const [serviceProvider, setServiceProvider] = useState<string>('github');
const [open, setOpen] = useState(false);
const params = useParams();
const [repositories, setRepositories] = useState<GithubRepository[]>([]);
const [branches, setBranches] = useState<string[]>([]);
const [newApplication, setNewApplication] = useState<NewApplication>({
serviceProvider: 'github',
git: {
repositoryId: 0,
repositoryName: '',
branch: '',
path: '',
},
autodeploy: true,
env: {},
name: '',
});
const { data: session } = useSession();
useEffect(() => {
async function fetchRepositories() {
if (newApplication.serviceProvider == 'github') {
const repos = await getUserRepository(session?.user.id as string, session?.user.username as string);
setRepositories(repos);
}
}
fetchRepositories();
}, [newApplication.serviceProvider, session]);
useEffect(() => {
async function fetchBranches() {
if (newApplication.serviceProvider == 'github' && newApplication.git.repositoryId != 0) {
const branches = await getRepositoryBranches(session?.user.id as string, session?.user.username as string, newApplication.git.repositoryName);
setBranches(branches);
}
}
fetchBranches();
}, [newApplication.serviceProvider, newApplication.git.repositoryId, newApplication.git.repositoryName, session]);
return ( return (
<Sheet open={open} onOpenChange={() => setOpen((open) => !open)}> <Sheet>
<SheetTrigger asChild> <SheetTrigger asChild>
<Button className="flex gap-1 justify-center items-center bg-[#3A7BFE]"> <Button className="flex gap-1 justify-center items-center bg-[#3A7BFE]">
<Plus /> <Plus />
@ -90,204 +40,27 @@ export default function CreateApplicationForm() {
</SheetTrigger> </SheetTrigger>
<SheetContent className="min-w-[500px]"> <SheetContent className="min-w-[500px]">
<SheetHeader> <SheetHeader>
<SheetTitle className="text-2xl">New Application</SheetTitle> <SheetTitle>New Application</SheetTitle>
<SheetDescription>Deploy a new application from source</SheetDescription> <SheetDescription>Deploy a new application from source</SheetDescription>
</SheetHeader> </SheetHeader>
<div className="flex flex-col"> <div>
<div className="flex flex-col gap-2 my-6"> <h3>Select Provider</h3>
<div className="flex flex-row justify-between"> <RadioGroup defaultValue="github" className="flex flex-col gap-4" onValueChange={setServiceProvider}>
<p> {ServiceProviderList.map((provider) => (
Step: <span className="text-muted-foreground">{(steps == 1 && 'Select Provider') || (steps == 2 && 'Resources') || (steps == 3 && 'Environment Variables') || (steps == 4 && 'Information') || (steps == 5 && 'Review')}</span> <div key={provider.value} className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', provider.value == serviceProvider ? 'border-[#3A7BFE]' : 'border-gray-300')}>
</p> <RadioGroupItem value={provider.value} id={provider.value} />
<p>{steps} / 5</p> <label htmlFor={provider.value} className="flex flex-row items-center gap-4">
</div> <img src={provider.image} alt={provider.name} className="w-8 h-8 rounded-full" />
<Progress className="bg-gray-300 h-3 " color="red" value={steps * 20} max={100} indicatorColor="bg-[#3A7BFE]" /> {provider.name}
</div> </label>
<div>
{steps == 1 && (
<div>
<RadioGroup defaultValue={newApplication.serviceProvider || 'github'} className="flex flex-col gap-2 mb-4" onValueChange={(e) => setNewApplication((prev) => ({ ...prev, serviceProvider: e }))}>
{ServiceProviderList.map((provider) => (
<div key={provider.value} className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', provider.value == newApplication.serviceProvider ? 'border-[#3A7BFE]' : 'border-gray-300')}>
<RadioGroupItem value={provider.value} id={provider.value} />
<label htmlFor={provider.value} className="flex flex-row items-center gap-4">
<img src={provider.image} alt={provider.name} className="w-8 h-8 rounded-full" />
{provider.name}
</label>
</div>
))}
</RadioGroup>
<div className="my-2 flex flex-col gap-2">
{newApplication.serviceProvider == 'github' && !!repositories && (
<div>
<p className="text-muted-foreground text-sm">Repository</p>
<Select defaultValue={newApplication.git.repositoryId.toString() || ''} onValueChange={(e) => setNewApplication((prev) => ({ ...prev, git: { ...prev.git, repositoryId: parseInt(e), repositoryName: repositories.find((w) => w.id == parseInt(e))?.name as string } }))}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a repository" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{repositories?.map((repository) => (
<SelectItem key={repository.id} value={repository.id.toString()}>
{repository.full_name}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
)}
{newApplication.serviceProvider == 'github' && newApplication.git.repositoryId != 0 && (
<>
<div>
<p className="text-muted-foreground text-sm">Branch</p>
<Select onValueChange={(e) => setNewApplication((prev) => ({ ...prev, git: { ...prev.git, branch: e } }))} defaultValue={newApplication.git.branch}>
<SelectTrigger>
<SelectValue placeholder="Select a branch" />
</SelectTrigger>
<SelectContent>
{branches.map((branch) => (
<SelectItem key={branch} value={branch}>
{branch}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<p className="text-muted-foreground text-sm">Path</p>
<Input placeholder="Path" value={newApplication.git.path} onChange={(e) => setNewApplication((prev) => ({ ...prev, git: { ...prev.git, path: e.target.value } }))} />
</div>
<div>
<div className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', newApplication.autodeploy ? 'border-[#3A7BFE]' : 'border-gray-300')} onClick={() => setNewApplication((prev) => ({ ...prev, autodeploy: !prev.autodeploy }))}>
<Checkbox
checked={newApplication.autodeploy}
onChange={(e) => {
console.log(e);
}}
id={'autodeploy'}
color="bg-[#3A7BFE]"
/>
<div>
<label htmlFor="autodeploy" className="text-sm">
Auto Deploy
</label>
<p className="text-xs text-muted-foreground">Every time an update is made to this branch, your application will be re-deployed.</p>
</div>
</div>
</div>
</>
)}
</div>
{newApplication.serviceProvider == 'github' &&
(!repositories ? (
<p className="text-sm">
<span className="text-muted-foreground">We don't have access to your repositories.</span> <Link href={ServiceProviderList.find((s) => s.value == newApplication.serviceProvider)?.permission || ''}>Link your GitHub account</Link>
</p>
) : (
<p className="text-sm">
<span className="text-muted-foreground">Not seeing the repositories you expected here?</span> <Link href={ServiceProviderList.find((s) => s.value == newApplication.serviceProvider)?.permission || ''}>Edit Your GitHub Permissions</Link>
</p>
))}
</div> </div>
)} ))}
</RadioGroup>
{steps == 2 && ( {(serviceProvider == 'github' || serviceProvider == 'github-registry') && (
<div> <p>
<p>Resources WIP</p> Not seeing the repositories you expected here? <Link href={ServiceProviderList.find((s) => s.value == serviceProvider)?.permission || ''}>Edit Your GitHub Permissions</Link>
<p>For now 1 vCPU & 1024Mb RAM</p> </p>
</div> )}
)}
{steps == 3 && (
<div>
<p>Environment Variables WIP</p>
</div>
)}
{steps == 4 && (
<div>
<p className="text-muted-foreground text-sm">Application Name</p>
<Input placeholder="App Name" value={newApplication.name} onChange={(e) => setNewApplication((prev) => ({ ...prev, name: e.target.value }))} />
</div>
)}
{steps == 5 && (
<div>
<div className="flex flex-col gap-2">
<div>
<p className="text-muted-foreground text-sm">Service Provider</p>
<p>{newApplication.serviceProvider}</p>
</div>
<div>
<p className="text-muted-foreground text-sm">Repository</p>
<p>{newApplication.git.repositoryName}</p>
</div>
<div>
<p className="text-muted-foreground text-sm">Branch</p>
<p>{newApplication.git.branch}</p>
</div>
<div>
<p className="text-muted-foreground text-sm">Path</p>
<p>{newApplication.git.path}</p>
</div>
<div>
<p className="text-muted-foreground text-sm">Auto Deploy</p>
<p>{newApplication.autodeploy ? 'Enabled' : 'Disabled'}</p>
</div>
<div>
<p className="text-muted-foreground text-sm">Application Name</p>
<p>{newApplication.name}</p>
</div>
</div>
</div>
)}
</div>
<div className="flex flex-row justify-between my-6">
<Button
variant={'outline'}
className={cn(steps == 1 && 'opacity-0 pointer-events-none select-none')}
onClick={() => {
setSteps((prev) => (prev - 1 < 1 ? 1 : prev - 1));
}}
>
Back
</Button>
{steps == 5 ? (
<Button
className="bg-[#3A7BFE]"
onClick={async () => {
const app = await createApplication(newApplication, params.workspace as string);
console.log(app);
setOpen(false);
setNewApplication({
serviceProvider: 'github',
git: {
repositoryId: 0,
repositoryName: '',
branch: '',
path: '',
},
autodeploy: true,
env: {},
name: '',
});
}}
>
Deploy
</Button>
) : (
<Button
className="bg-[#3A7BFE]"
onClick={() => {
setSteps((prev) => (prev + 1 > 5 ? 5 : prev + 1));
console.log(newApplication);
}}
>
Next
</Button>
)}
</div>
</div> </div>
</SheetContent> </SheetContent>
</Sheet> </Sheet>

View File

@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button';
import prisma from '@/lib/prisma'; import prisma from '@/lib/prisma';
import { PackagePlus, Plus } from 'lucide-react'; import { PackagePlus, Plus } from 'lucide-react';
import CreateApplicationForm from './CreateApplicationForm'; import CreateApplicationForm from './CreateApplicationForm';
import { getSession } from 'next-auth/react';
export default async function Workspace({ params }: { params: { workspace: string } }) { export default async function Workspace({ params }: { params: { workspace: string } }) {
const applications = await prisma.application.findMany({ const applications = await prisma.application.findMany({
@ -12,6 +13,21 @@ export default async function Workspace({ params }: { params: { workspace: strin
}, },
}); });
const session = await getSession();
const account = await prisma.account.findFirst({
where: {
userId: session?.user.id as string,
},
});
fetch('https://api.github.com/users/fayorg/repos', {
headers: {
Authorization: `Bearer ${account?.access_token}`,
},
})
.then((res) => res.json())
.then(console.log);
if (applications.length == 0) { if (applications.length == 0) {
return ( return (
<div className="mt-12"> <div className="mt-12">

View File

@ -5,7 +5,6 @@ import { redirect } from 'next/navigation';
import { getServerSession } from 'next-auth'; import { getServerSession } from 'next-auth';
import prisma from '@/lib/prisma'; import prisma from '@/lib/prisma';
import WorkspaceNavigation from './WorkspaceNavigation'; import WorkspaceNavigation from './WorkspaceNavigation';
import { signOut } from 'next-auth/react';
export default async function DashboardLayout({ children }: Readonly<{ children: React.ReactNode }>) { export default async function DashboardLayout({ children }: Readonly<{ children: React.ReactNode }>) {
const session = await getServerSession(); const session = await getServerSession();
@ -13,9 +12,6 @@ export default async function DashboardLayout({ children }: Readonly<{ children:
if (!session) { if (!session) {
redirect('/sign-in'); redirect('/sign-in');
} }
if (session.error == 'RefreshAccessTokenError') {
signOut();
}
const workspaces = await prisma.workspace.findMany({ const workspaces = await prisma.workspace.findMany({
where: { where: {

View File

@ -1,18 +0,0 @@
'use client';
import * as React from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import { Check } from 'lucide-react';
import { cn } from '@/lib/utils';
const Checkbox = React.forwardRef<React.ElementRef<typeof CheckboxPrimitive.Root>, React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root ref={ref} className={cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', className)} {...props}>
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
export { Checkbox };

View File

@ -1,25 +0,0 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

View File

@ -1,18 +0,0 @@
'use client';
import * as React from 'react';
import * as ProgressPrimitive from '@radix-ui/react-progress';
import { cn } from '@/lib/utils';
interface CustomProgressProps extends React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> {
indicatorColor: string;
}
const Progress = React.forwardRef<React.ElementRef<typeof ProgressPrimitive.Root>, CustomProgressProps>(({ className, value, indicatorColor, ...props }, ref) => (
<ProgressPrimitive.Root ref={ref} className={cn('relative h-4 w-full overflow-hidden rounded-full bg-secondary', className)} {...props}>
<ProgressPrimitive.Indicator className={`h-full w-full flex-1 transition-all ${indicatorColor}`} style={{ transform: `translateX(-${100 - (value || 0)}%)` }} />
</ProgressPrimitive.Root>
));
Progress.displayName = ProgressPrimitive.Root.displayName;
export { Progress };

View File

@ -1,78 +0,0 @@
'use client';
import * as React from 'react';
import * as SelectPrimitive from '@radix-ui/react-select';
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
import { cn } from '@/lib/utils';
const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger ref={ref} className={cn('flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 [&>span]:line-clamp-1', className)} {...props}>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton ref={ref} className={cn('flex cursor-default items-center justify-center py-1', className)} {...props}>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton ref={ref} className={cn('flex cursor-default items-center justify-center py-1', className)} {...props}>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Content>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>>(({ className, children, position = 'popper', ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport className={cn('p-1', position === 'popper' && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]')}>{children}</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Label>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>>(({ className, ...props }, ref) => <SelectPrimitive.Label ref={ref} className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)} {...props} />);
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Item>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item ref={ref} className={cn('relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground', className)} {...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Separator>, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>>(({ className, ...props }, ref) => <SelectPrimitive.Separator ref={ref} className={cn('-mx-1 my-1 h-px bg-muted', className)} {...props} />);
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton };

View File

@ -15,7 +15,7 @@ export const authOptions: NextAuthOptions = {
name: profile.name, name: profile.name,
email: profile.email, email: profile.email,
image: profile.avatar_url, image: profile.avatar_url,
username: profile.login, username: profile.username,
} as User; } as User;
}, },
}), }),
@ -35,28 +35,25 @@ export const authOptions: NextAuthOptions = {
if ((github.expires_at || 0) * 1000 < Date.now()) { if ((github.expires_at || 0) * 1000 < Date.now()) {
// If the access token has expired, try to refresh it // If the access token has expired, try to refresh it
try { try {
const response = await fetch("https://github.com/login/oauth/access_token", {
const params = new URLSearchParams({ headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: process.env.GITHUB_ID as string, client_id: process.env.GITHUB_ID as string,
client_secret: process.env.GITHUB_SECRET as string, client_secret: process.env.GITHUB_SECRET as string,
grant_type: "refresh_token", grant_type: "refresh_token",
refresh_token: github.refresh_token as string, refresh_token: github.refresh_token as string,
}); }),
const response = await fetch("https://github.com/login/oauth/access_token?" + params, {
headers: { "Accept": "application/json" },
method: "POST", method: "POST",
}) })
const tok = await response.json() const tokens: TokenSet = await response.json()
const tokens: TokenSet = tok;
if (!response.ok) throw tokens if (!response.ok) throw tokens
await prisma.account.update({ await prisma.account.update({
data: { data: {
access_token: tokens.access_token, access_token: tokens.access_token,
expires_at: Math.floor(Date.now() / 1000 + (tokens.expires_in as number)), expires_at: Math.floor(Date.now() / 1000 + (tokens.expires_at as number)),
refresh_token: tokens.refresh_token ?? github.refresh_token, refresh_token: tokens.refresh_token ?? github.refresh_token,
}, },
where: { where: {
@ -90,6 +87,7 @@ export const authOptions: NextAuthOptions = {
username: u.username, username: u.username,
}; };
} }
console.log(token);
return { ...token, username: token.username }; return { ...token, username: token.username };
}, },
}, },

254
package-lock.json generated
View File

@ -12,17 +12,13 @@
"@auth/prisma-adapter": "^1.5.0", "@auth/prisma-adapter": "^1.5.0",
"@prisma/client": "^5.11.0", "@prisma/client": "^5.11.0",
"@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.363.0", "lucide-react": "^0.363.0",
"next": "14.1.4", "next": "14.1.4",
"next-auth": "^4.24.7", "next-auth": "^4.24.7",
@ -32,7 +28,6 @@
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
@ -620,14 +615,6 @@
"@prisma/debug": "5.11.0" "@prisma/debug": "5.11.0"
} }
}, },
"node_modules/@radix-ui/number": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
"integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
"dependencies": {
"@babel/runtime": "^7.13.10"
}
},
"node_modules/@radix-ui/primitive": { "node_modules/@radix-ui/primitive": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
@ -685,36 +672,6 @@
} }
} }
}, },
"node_modules/@radix-ui/react-checkbox": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz",
"integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-previous": "1.0.1",
"@radix-ui/react-use-size": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": { "node_modules/@radix-ui/react-collection": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
@ -1054,30 +1011,6 @@
} }
} }
}, },
"node_modules/@radix-ui/react-progress": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz",
"integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-radio-group": { "node_modules/@radix-ui/react-radio-group": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz",
@ -1141,49 +1074,6 @@
} }
} }
}, },
"node_modules/@radix-ui/react-select": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz",
"integrity": "sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/number": "1.0.1",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-collection": "1.0.3",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-direction": "1.0.1",
"@radix-ui/react-dismissable-layer": "1.0.5",
"@radix-ui/react-focus-guards": "1.0.1",
"@radix-ui/react-focus-scope": "1.0.4",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-popper": "1.1.3",
"@radix-ui/react-portal": "1.0.4",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-slot": "1.0.2",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1",
"@radix-ui/react-use-previous": "1.0.1",
"@radix-ui/react-visually-hidden": "1.0.3",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "2.5.5"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": { "node_modules/@radix-ui/react-slot": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@ -1325,29 +1215,6 @@
} }
} }
}, },
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
"integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": { "node_modules/@radix-ui/rect": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
@ -1381,15 +1248,6 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true "dev": true
}, },
"node_modules/@types/jsonwebtoken": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz",
"integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.11.30", "version": "20.11.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
@ -1984,11 +1842,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
"node_modules/busboy": { "node_modules/busboy": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@ -2385,14 +2238,6 @@
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
}, },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.715", "version": "1.4.715",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
@ -4010,27 +3855,6 @@
"json5": "lib/cli.js" "json5": "lib/cli.js"
} }
}, },
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jsx-ast-utils": { "node_modules/jsx-ast-utils": {
"version": "3.3.5", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@ -4046,25 +3870,6 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -4133,47 +3938,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
},
"node_modules/loose-envify": { "node_modules/loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -4253,7 +4023,8 @@
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}, },
"node_modules/mz": { "node_modules/mz": {
"version": "2.7.0", "version": "2.7.0",
@ -5263,25 +5034,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safe-regex-test": { "node_modules/safe-regex-test": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
@ -5311,6 +5063,7 @@
"version": "7.6.0", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
}, },
@ -5325,6 +5078,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": { "dependencies": {
"yallist": "^4.0.0" "yallist": "^4.0.0"
}, },

View File

@ -12,17 +12,13 @@
"@auth/prisma-adapter": "^1.5.0", "@auth/prisma-adapter": "^1.5.0",
"@prisma/client": "^5.11.0", "@prisma/client": "^5.11.0",
"@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.363.0", "lucide-react": "^0.363.0",
"next": "14.1.4", "next": "14.1.4",
"next-auth": "^4.24.7", "next-auth": "^4.24.7",
@ -32,7 +28,6 @@
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",

View File

@ -42,16 +42,15 @@ model Session {
} }
model User { model User {
id String @id @default(cuid()) id String @id @default(cuid())
name String? name String?
username String @unique username String @unique
email String? @unique email String? @unique
emailVerified DateTime? emailVerified DateTime?
image String? image String?
accounts Account[] accounts Account[]
sessions Session[] sessions Session[]
Workspace Workspace[] Workspace Workspace[]
Deployment Deployment[]
} }
model VerificationToken { model VerificationToken {
@ -72,43 +71,13 @@ model Workspace {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
Application Application[] Application Application[]
Deployment Deployment[]
} }
model Application { model Application {
id String @id @default(cuid()) id String @id @default(cuid())
name String name String
serviceProvider String Workspace Workspace @relation(fields: [workspaceId], references: [id])
repository String workspaceId String
branch String createdAt DateTime @default(now())
repositoryId String updatedAt DateTime @updatedAt
path String
autoDeploy Boolean
workspaceId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Deployment Deployment[]
Workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
}
enum DeploymentStatus {
PENDING
IN_PROGRESS
FAILED
SUCCESS
}
model Deployment {
id String @id @default(cuid())
applicationId String
workspaceId String
userId String
status DeploymentStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
Workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
} }