Compare commits
No commits in common. "dd98781cc54a16d36d1cfd4bbe4dd00881c65572" and "6377e46c5c34d33c4aa479f76a3311f8aba40746" have entirely different histories.
dd98781cc5
...
6377e46c5c
|
@ -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;
|
|
||||||
}
|
|
|
@ -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[];
|
|
||||||
}
|
|
|
@ -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>
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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 };
|
|
|
@ -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 }
|
|
|
@ -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 };
|
|
|
@ -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 };
|
|
|
@ -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 };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue