mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Simplify Gitea authorization flow with shared utilities
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
@@ -35,7 +36,6 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@@ -433,7 +433,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2 mb-2">
|
<div className="flex flex-wrap gap-2 mb-2">
|
||||||
{field.value && field.value.map((path: string, index: number) => (
|
{field.value?.map((path: string, index: number) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={index}
|
key={index}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -504,4 +504,4 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import type { Branch, Repository } from "@/utils/gitea-utils";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -47,31 +48,6 @@ import { useForm } from "react-hook-form";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
interface Repository {
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
id: number;
|
|
||||||
owner: {
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Branch {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GiteaProviderType {
|
|
||||||
giteaId: string;
|
|
||||||
gitProvider: {
|
|
||||||
name: string;
|
|
||||||
gitProviderId: string;
|
|
||||||
providerType: "github" | "gitlab" | "bitbucket" | "gitea";
|
|
||||||
createdAt: string;
|
|
||||||
organizationId: string;
|
|
||||||
};
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GiteaProviderSchema = z.object({
|
const GiteaProviderSchema = z.object({
|
||||||
composePath: z.string().min(1),
|
composePath: z.string().min(1),
|
||||||
repository: z
|
repository: z
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
BitbucketIcon,
|
BitbucketIcon,
|
||||||
|
GitIcon,
|
||||||
GiteaIcon,
|
GiteaIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
GitlabIcon,
|
GitlabIcon,
|
||||||
GitIcon,
|
} from "@/components/icons/data-tools-icons";
|
||||||
} from "@/components/icons/data-tools-icons";
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { CodeIcon, GitBranch } from "lucide-react";
|
import { CodeIcon, GitBranch } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState, useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { ComposeFileEditor } from "../compose-file-editor";
|
import { ComposeFileEditor } from "../compose-file-editor";
|
||||||
import { ShowConvertedCompose } from "../show-converted-compose";
|
import { ShowConvertedCompose } from "../show-converted-compose";
|
||||||
import { SaveBitbucketProviderCompose } from "./save-bitbucket-provider-compose";
|
import { SaveBitbucketProviderCompose } from "./save-bitbucket-provider-compose";
|
||||||
@@ -18,141 +18,174 @@ import { SaveGitProviderCompose } from "./save-git-provider-compose";
|
|||||||
import { SaveGiteaProviderCompose } from "./save-gitea-provider-compose";
|
import { SaveGiteaProviderCompose } from "./save-gitea-provider-compose";
|
||||||
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
|
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
|
||||||
import { SaveGitlabProviderCompose } from "./save-gitlab-provider-compose";
|
import { SaveGitlabProviderCompose } from "./save-gitlab-provider-compose";
|
||||||
|
|
||||||
type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket" | "gitea"; // Adding gitea to the TabState
|
type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket" | "gitea"; // Adding gitea to the TabState
|
||||||
interface Props {
|
interface Props {
|
||||||
composeId: string;
|
composeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShowProviderFormCompose = ({ composeId }: Props) => {
|
export const ShowProviderFormCompose = ({ composeId }: Props) => {
|
||||||
const { data: githubProviders } = api.github.githubProviders.useQuery();
|
const { data: githubProviders } = api.github.githubProviders.useQuery();
|
||||||
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
|
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
|
||||||
const { data: bitbucketProviders } = api.bitbucket.bitbucketProviders.useQuery();
|
const { data: bitbucketProviders } =
|
||||||
|
api.bitbucket.bitbucketProviders.useQuery();
|
||||||
const { data: giteaProviders } = api.gitea.giteaProviders.useQuery(); // Fetching Gitea providers
|
const { data: giteaProviders } = api.gitea.giteaProviders.useQuery(); // Fetching Gitea providers
|
||||||
|
|
||||||
const { data: compose } = api.compose.one.useQuery({ composeId });
|
const { data: compose } = api.compose.one.useQuery({ composeId });
|
||||||
const [tab, setSab] = useState<TabState>(compose?.sourceType || "github");
|
const [tab, setSab] = useState<TabState>(compose?.sourceType || "github");
|
||||||
|
|
||||||
// Ensure we fall back to empty arrays if the data is undefined
|
// Ensure we fall back to empty arrays if the data is undefined
|
||||||
const safeGithubProviders = githubProviders || [];
|
const safeGithubProviders = githubProviders || [];
|
||||||
const safeGitlabProviders = gitlabProviders || [];
|
const safeGitlabProviders = gitlabProviders || [];
|
||||||
const safeBitbucketProviders = bitbucketProviders || [];
|
const safeBitbucketProviders = bitbucketProviders || [];
|
||||||
const safeGiteaProviders = giteaProviders || [];
|
const safeGiteaProviders = giteaProviders || [];
|
||||||
|
|
||||||
const renderProviderContent = (providers: any[], providerType: string, ProviderComponent: React.ComponentType<any>) => {
|
const renderProviderContent = (
|
||||||
if (providers.length > 0) {
|
providers: any[],
|
||||||
return <ProviderComponent composeId={composeId} />;
|
providerType: string,
|
||||||
} else {
|
ProviderComponent: React.ComponentType<any>,
|
||||||
|
) => {
|
||||||
|
if (providers.length > 0) {
|
||||||
|
return <ProviderComponent composeId={composeId} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
|
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
|
||||||
{providerType === "github" && <GithubIcon className="size-8 text-muted-foreground" />}
|
{providerType === "github" && (
|
||||||
{providerType === "gitlab" && <GitlabIcon className="size-8 text-muted-foreground" />}
|
<GithubIcon className="size-8 text-muted-foreground" />
|
||||||
{providerType === "bitbucket" && <BitbucketIcon className="size-8 text-muted-foreground" />}
|
)}
|
||||||
{providerType === "gitea" && <GiteaIcon className="size-8 text-muted-foreground" />}
|
{providerType === "gitlab" && (
|
||||||
<span className="text-base text-muted-foreground">
|
<GitlabIcon className="size-8 text-muted-foreground" />
|
||||||
To deploy using {providerType.charAt(0).toUpperCase() + providerType.slice(1)}, you need to configure your account first.
|
)}
|
||||||
Please, go to{" "}
|
{providerType === "bitbucket" && (
|
||||||
<Link href="/dashboard/settings/git-providers" className="text-foreground">
|
<BitbucketIcon className="size-8 text-muted-foreground" />
|
||||||
Settings
|
)}
|
||||||
</Link>{" "}
|
{providerType === "gitea" && (
|
||||||
to do so.
|
<GiteaIcon className="size-8 text-muted-foreground" />
|
||||||
</span>
|
)}
|
||||||
</div>
|
<span className="text-base text-muted-foreground">
|
||||||
|
To deploy using{" "}
|
||||||
|
{providerType.charAt(0).toUpperCase() + providerType.slice(1)}, you
|
||||||
|
need to configure your account first. Please, go to{" "}
|
||||||
|
<Link
|
||||||
|
href="/dashboard/settings/git-providers"
|
||||||
|
className="text-foreground"
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>{" "}
|
||||||
|
to do so.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="group relative w-full bg-transparent">
|
<Card className="group relative w-full bg-transparent">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-start justify-between">
|
<CardTitle className="flex items-start justify-between">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<span className="flex flex-col space-y-0.5">Provider</span>
|
<span className="flex flex-col space-y-0.5">Provider</span>
|
||||||
<p className="flex items-center text-sm font-normal text-muted-foreground">
|
<p className="flex items-center text-sm font-normal text-muted-foreground">
|
||||||
Select the source of your code
|
Select the source of your code
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden space-y-1 text-sm font-normal md:flex flex-row items-center gap-2">
|
<div className="hidden space-y-1 text-sm font-normal md:flex flex-row items-center gap-2">
|
||||||
<ShowConvertedCompose composeId={composeId} />
|
<ShowConvertedCompose composeId={composeId} />
|
||||||
<GitBranch className="size-6 text-muted-foreground" />
|
<GitBranch className="size-6 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Tabs
|
<Tabs
|
||||||
value={tab}
|
value={tab}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
setSab(e as TabState);
|
setSab(e as TabState);
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
|
||||||
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-x-scroll justify-start bg-transparent overflow-y-hidden">
|
|
||||||
<TabsTrigger
|
|
||||||
value="github"
|
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
|
||||||
>
|
>
|
||||||
<GithubIcon className="size-4 text-current fill-current" />
|
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||||
GitHub
|
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-x-scroll justify-start bg-transparent overflow-y-hidden">
|
||||||
</TabsTrigger>
|
<TabsTrigger
|
||||||
<TabsTrigger
|
value="github"
|
||||||
value="gitlab"
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
>
|
||||||
>
|
<GithubIcon className="size-4 text-current fill-current" />
|
||||||
<GitlabIcon className="size-4 text-current fill-current" />
|
GitHub
|
||||||
GitLab
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
<TabsTrigger
|
||||||
<TabsTrigger
|
value="gitlab"
|
||||||
value="bitbucket"
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
>
|
||||||
>
|
<GitlabIcon className="size-4 text-current fill-current" />
|
||||||
<BitbucketIcon className="size-4 text-current fill-current" />
|
GitLab
|
||||||
Bitbucket
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
<TabsTrigger
|
||||||
<TabsTrigger
|
value="bitbucket"
|
||||||
value="gitea" // Added Gitea tab
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
>
|
||||||
>
|
<BitbucketIcon className="size-4 text-current fill-current" />
|
||||||
<GiteaIcon className="size-4 text-current fill-current" /> Gitea
|
Bitbucket
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="git"
|
value="gitea" // Added Gitea tab
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
>
|
>
|
||||||
<GitIcon />
|
<GiteaIcon className="size-4 text-current fill-current" /> Gitea
|
||||||
Git
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
<TabsTrigger
|
||||||
<TabsTrigger
|
value="git"
|
||||||
value="raw"
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
>
|
||||||
>
|
<GitIcon />
|
||||||
<CodeIcon className="size-4" />
|
Git
|
||||||
Raw
|
</TabsTrigger>
|
||||||
</TabsTrigger>
|
<TabsTrigger
|
||||||
</TabsList>
|
value="raw"
|
||||||
</div>
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
|
>
|
||||||
<TabsContent value="github" className="w-full p-2">
|
<CodeIcon className="size-4" />
|
||||||
{renderProviderContent(safeGithubProviders, "github", SaveGithubProviderCompose)}
|
Raw
|
||||||
</TabsContent>
|
</TabsTrigger>
|
||||||
<TabsContent value="gitea" className="w-full p-2">
|
</TabsList>
|
||||||
{renderProviderContent(safeGiteaProviders, "gitea", SaveGiteaProviderCompose)}
|
</div>
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value="gitlab" className="w-full p-2">
|
<TabsContent value="github" className="w-full p-2">
|
||||||
{renderProviderContent(safeGitlabProviders, "gitlab", SaveGitlabProviderCompose)}
|
{renderProviderContent(
|
||||||
</TabsContent>
|
safeGithubProviders,
|
||||||
<TabsContent value="bitbucket" className="w-full p-2">
|
"github",
|
||||||
{renderProviderContent(safeBitbucketProviders, "bitbucket", SaveBitbucketProviderCompose)}
|
SaveGithubProviderCompose,
|
||||||
</TabsContent>
|
)}
|
||||||
<TabsContent value="git" className="w-full p-2">
|
</TabsContent>
|
||||||
<SaveGitProviderCompose composeId={composeId} />
|
<TabsContent value="gitea" className="w-full p-2">
|
||||||
</TabsContent>
|
{renderProviderContent(
|
||||||
|
safeGiteaProviders,
|
||||||
<TabsContent value="raw" className="w-full p-2 flex flex-col gap-4">
|
"gitea",
|
||||||
<ComposeFileEditor composeId={composeId} />
|
SaveGiteaProviderCompose,
|
||||||
</TabsContent>
|
)}
|
||||||
</Tabs>
|
</TabsContent>
|
||||||
</CardContent>
|
<TabsContent value="gitlab" className="w-full p-2">
|
||||||
</Card>
|
{renderProviderContent(
|
||||||
|
safeGitlabProviders,
|
||||||
|
"gitlab",
|
||||||
|
SaveGitlabProviderCompose,
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="bitbucket" className="w-full p-2">
|
||||||
|
{renderProviderContent(
|
||||||
|
safeBitbucketProviders,
|
||||||
|
"bitbucket",
|
||||||
|
SaveBitbucketProviderCompose,
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="git" className="w-full p-2">
|
||||||
|
<SaveGitProviderCompose composeId={composeId} />
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="raw" className="w-full p-2 flex flex-col gap-4">
|
||||||
|
<ComposeFileEditor composeId={composeId} />
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GiteaIcon } from "@/components/icons/data-tools-icons"; // Use GiteaIcon for Gitea
|
import { GiteaIcon } from "@/components/icons/data-tools-icons";
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { CardContent } from "@/components/ui/card";
|
import { CardContent } from "@/components/ui/card";
|
||||||
@@ -19,9 +19,12 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import {
|
||||||
|
type GiteaProviderResponse,
|
||||||
|
getGiteaOAuthUrl,
|
||||||
|
} from "@/utils/gitea-utils";
|
||||||
import { useUrl } from "@/utils/hooks/use-url";
|
import { useUrl } from "@/utils/hooks/use-url";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -51,11 +54,14 @@ const Schema = z.object({
|
|||||||
type Schema = z.infer<typeof Schema>;
|
type Schema = z.infer<typeof Schema>;
|
||||||
|
|
||||||
export const AddGiteaProvider = () => {
|
export const AddGiteaProvider = () => {
|
||||||
const utils = api.useUtils();
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const url = useUrl();
|
|
||||||
|
const urlObj = useUrl();
|
||||||
|
const baseUrl =
|
||||||
|
typeof urlObj === "string" ? urlObj : (urlObj as any)?.url || "";
|
||||||
|
|
||||||
const { mutateAsync, error, isError } = api.gitea.create.useMutation();
|
const { mutateAsync, error, isError } = api.gitea.create.useMutation();
|
||||||
const webhookUrl = `${url}/api/providers/gitea/callback`;
|
const webhookUrl = `${baseUrl}/api/providers/gitea/callback`;
|
||||||
|
|
||||||
const form = useForm<Schema>({
|
const form = useForm<Schema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -78,24 +84,52 @@ export const AddGiteaProvider = () => {
|
|||||||
name: "",
|
name: "",
|
||||||
giteaUrl: "https://gitea.com",
|
giteaUrl: "https://gitea.com",
|
||||||
});
|
});
|
||||||
}, [form, isOpen]);
|
}, [form, webhookUrl, isOpen]);
|
||||||
|
|
||||||
const onSubmit = async (data: Schema) => {
|
const onSubmit = async (data: Schema) => {
|
||||||
await mutateAsync({
|
try {
|
||||||
clientId: data.clientId || "",
|
// Send the form data to create the Gitea provider
|
||||||
clientSecret: data.clientSecret || "",
|
const result = (await mutateAsync({
|
||||||
name: data.name || "",
|
clientId: data.clientId,
|
||||||
redirectUri: data.redirectUri || "",
|
clientSecret: data.clientSecret,
|
||||||
giteaUrl: data.giteaUrl || "https://gitea.com",
|
name: data.name,
|
||||||
})
|
redirectUri: data.redirectUri,
|
||||||
.then(async () => {
|
giteaUrl: data.giteaUrl,
|
||||||
await utils.gitProvider.getAll.invalidate();
|
organizationName: data.organizationName,
|
||||||
toast.success("Gitea provider created successfully");
|
})) as unknown as GiteaProviderResponse;
|
||||||
setIsOpen(false);
|
|
||||||
})
|
// Check if we have a giteaId from the response
|
||||||
.catch(() => {
|
if (!result || !result.giteaId) {
|
||||||
toast.error("Error configuring Gitea");
|
toast.error("Failed to get Gitea ID from response");
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate OAuth URL using the shared utility
|
||||||
|
const authUrl = getGiteaOAuthUrl(
|
||||||
|
result.giteaId,
|
||||||
|
data.clientId,
|
||||||
|
data.giteaUrl,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the Gitea OAuth URL
|
||||||
|
if (authUrl !== "#") {
|
||||||
|
window.open(authUrl, "_blank");
|
||||||
|
} else {
|
||||||
|
toast.error("Configuration Incomplete", {
|
||||||
|
description: "Please fill in Client ID and Gitea URL first.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("Gitea provider created successfully");
|
||||||
|
setIsOpen(false);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.error(`Error configuring Gitea: ${error.message}`);
|
||||||
|
} else {
|
||||||
|
toast.error("An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -109,7 +143,7 @@ export const AddGiteaProvider = () => {
|
|||||||
<span>Gitea</span>
|
<span>Gitea</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen ">
|
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
Gitea Provider <GiteaIcon className="size-5" />
|
Gitea Provider <GiteaIcon className="size-5" />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { getGiteaOAuthUrl } from "@/utils/gitea-utils";
|
||||||
import { useUrl } from "@/utils/hooks/use-url";
|
import { useUrl } from "@/utils/hooks/use-url";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { PenBoxIcon } from "lucide-react";
|
import { PenBoxIcon } from "lucide-react";
|
||||||
@@ -55,7 +56,6 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { connected, error } = router.query;
|
const { connected, error } = router.query;
|
||||||
|
|
||||||
// Only process if router is ready and query parameters exist
|
|
||||||
if (!router.isReady) return;
|
if (!router.isReady) return;
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
@@ -64,7 +64,6 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
id: "gitea-connection-success",
|
id: "gitea-connection-success",
|
||||||
});
|
});
|
||||||
refetch();
|
refetch();
|
||||||
// Clear the query parameters to prevent re-triggering
|
|
||||||
router.replace(
|
router.replace(
|
||||||
{
|
{
|
||||||
pathname: router.pathname,
|
pathname: router.pathname,
|
||||||
@@ -80,7 +79,6 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
description: decodeURIComponent(error as string),
|
description: decodeURIComponent(error as string),
|
||||||
id: "gitea-connection-error",
|
id: "gitea-connection-error",
|
||||||
});
|
});
|
||||||
// Clear the query parameters to prevent re-triggering
|
|
||||||
router.replace(
|
router.replace(
|
||||||
{
|
{
|
||||||
pathname: router.pathname,
|
pathname: router.pathname,
|
||||||
@@ -102,7 +100,6 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update form values when data is loaded
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gitea) {
|
if (gitea) {
|
||||||
form.reset({
|
form.reset({
|
||||||
@@ -141,7 +138,15 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
description: result,
|
description: result,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const authUrl = error.authorizationUrl || getGiteaOAuthUrl();
|
const formValues = form.getValues();
|
||||||
|
const authUrl =
|
||||||
|
error.authorizationUrl ||
|
||||||
|
getGiteaOAuthUrl(
|
||||||
|
giteaId,
|
||||||
|
formValues.clientId,
|
||||||
|
formValues.giteaUrl,
|
||||||
|
typeof url === "string" ? url : (url as any).url || "",
|
||||||
|
);
|
||||||
|
|
||||||
toast.error("Gitea Not Connected", {
|
toast.error("Gitea Not Connected", {
|
||||||
description:
|
description:
|
||||||
@@ -157,32 +162,6 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate Gitea OAuth URL with state parameter
|
|
||||||
const getGiteaOAuthUrl = () => {
|
|
||||||
const clientId = form.getValues().clientId;
|
|
||||||
const giteaUrl = form.getValues().giteaUrl;
|
|
||||||
|
|
||||||
if (!clientId || !giteaUrl) {
|
|
||||||
toast.error("Configuration Incomplete", {
|
|
||||||
description: "Please fill in Client ID and Gitea URL first.",
|
|
||||||
});
|
|
||||||
return "#";
|
|
||||||
}
|
|
||||||
|
|
||||||
const redirectUri = `${url}/api/providers/gitea/callback`;
|
|
||||||
|
|
||||||
// Use the scopes from the gitea data (if available), else fallback to default scopes
|
|
||||||
const scopes =
|
|
||||||
gitea?.scopes?.split(",").join(" ") ||
|
|
||||||
"repo repo:status read:user read:org";
|
|
||||||
//const scopes = gitea?.scopes || 'repo,repo:status,read:user,read:org';
|
|
||||||
|
|
||||||
const state = giteaId;
|
|
||||||
|
|
||||||
return `${giteaUrl}/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scopes)}&state=${encodeURIComponent(state)}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Show loading state if data is being fetched
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Button variant="ghost" size="icon" disabled>
|
<Button variant="ghost" size="icon" disabled>
|
||||||
@@ -282,7 +261,13 @@ export const EditGiteaProvider = ({ giteaId }: Props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const authUrl = getGiteaOAuthUrl();
|
const formValues = form.getValues();
|
||||||
|
const authUrl = getGiteaOAuthUrl(
|
||||||
|
giteaId,
|
||||||
|
formValues.clientId,
|
||||||
|
formValues.giteaUrl,
|
||||||
|
typeof url === "string" ? url : (url as any).url || "",
|
||||||
|
);
|
||||||
if (authUrl !== "#") {
|
if (authUrl !== "#") {
|
||||||
window.open(authUrl, "_blank");
|
window.open(authUrl, "_blank");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,199 +24,236 @@ import { TRPCError } from "@trpc/server";
|
|||||||
export const giteaRouter = createTRPCRouter({
|
export const giteaRouter = createTRPCRouter({
|
||||||
// Create a new Gitea provider
|
// Create a new Gitea provider
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(apiCreateGitea)
|
.input(apiCreateGitea)
|
||||||
.mutation(async ({ input, ctx }: { input: typeof apiCreateGitea._input; ctx: any }) => {
|
.mutation(
|
||||||
try {
|
async ({
|
||||||
return await createGitea(input, ctx.session.activeOrganizationId);
|
input,
|
||||||
} catch (error) {
|
ctx,
|
||||||
throw new TRPCError({
|
}: { input: typeof apiCreateGitea._input; ctx: any }) => {
|
||||||
code: "BAD_REQUEST",
|
try {
|
||||||
message: "Error creating this Gitea provider",
|
return await createGitea(input, ctx.session.activeOrganizationId);
|
||||||
cause: error,
|
} catch (error) {
|
||||||
});
|
throw new TRPCError({
|
||||||
}
|
code: "BAD_REQUEST",
|
||||||
}),
|
message: "Error creating this Gitea provider",
|
||||||
|
cause: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Fetch a specific Gitea provider by ID
|
// Fetch a specific Gitea provider by ID
|
||||||
one: protectedProcedure
|
one: protectedProcedure
|
||||||
.input(apiFindOneGitea)
|
.input(apiFindOneGitea)
|
||||||
.query(async ({ input, ctx }: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
.query(
|
||||||
const giteaProvider = await findGiteaById(input.giteaId);
|
async ({
|
||||||
if (
|
input,
|
||||||
giteaProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId
|
ctx,
|
||||||
) {
|
}: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
||||||
throw new TRPCError({
|
const giteaProvider = await findGiteaById(input.giteaId);
|
||||||
code: "UNAUTHORIZED",
|
if (
|
||||||
message: "You are not allowed to access this Gitea provider",
|
giteaProvider.gitProvider.organizationId !==
|
||||||
});
|
ctx.session.activeOrganizationId
|
||||||
}
|
) {
|
||||||
return giteaProvider;
|
throw new TRPCError({
|
||||||
}),
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not allowed to access this Gitea provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return giteaProvider;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Fetch all Gitea providers for the active organization
|
// Fetch all Gitea providers for the active organization
|
||||||
giteaProviders: protectedProcedure.query(async ({ ctx }: { ctx: any }) => {
|
giteaProviders: protectedProcedure.query(async ({ ctx }: { ctx: any }) => {
|
||||||
let result = await db.query.gitea.findMany({
|
let result = await db.query.gitea.findMany({
|
||||||
with: {
|
with: {
|
||||||
gitProvider: true,
|
gitProvider: true,
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Filter by organization ID
|
|
||||||
result = result.filter(
|
|
||||||
(provider) =>
|
|
||||||
provider.gitProvider.organizationId === ctx.session.activeOrganizationId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter providers that meet the requirements
|
|
||||||
const filtered = result
|
|
||||||
.filter((provider) => haveGiteaRequirements(provider))
|
|
||||||
.map((provider) => {
|
|
||||||
return {
|
|
||||||
giteaId: provider.giteaId,
|
|
||||||
gitProvider: {
|
|
||||||
...provider.gitProvider,
|
|
||||||
},
|
},
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return filtered;
|
// Filter by organization ID
|
||||||
|
result = result.filter(
|
||||||
|
(provider) =>
|
||||||
|
provider.gitProvider.organizationId ===
|
||||||
|
ctx.session.activeOrganizationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter providers that meet the requirements
|
||||||
|
const filtered = result
|
||||||
|
.filter((provider) => haveGiteaRequirements(provider))
|
||||||
|
.map((provider) => {
|
||||||
|
return {
|
||||||
|
giteaId: provider.giteaId,
|
||||||
|
gitProvider: {
|
||||||
|
...provider.gitProvider,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return filtered;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Fetch repositories from Gitea provider
|
// Fetch repositories from Gitea provider
|
||||||
getGiteaRepositories: protectedProcedure
|
getGiteaRepositories: protectedProcedure
|
||||||
.input(apiFindOneGitea)
|
.input(apiFindOneGitea)
|
||||||
.query(async ({ input, ctx }: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
.query(
|
||||||
const { giteaId } = input;
|
async ({
|
||||||
|
input,
|
||||||
if (!giteaId) {
|
ctx,
|
||||||
throw new TRPCError({
|
}: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
||||||
code: "BAD_REQUEST",
|
const { giteaId } = input;
|
||||||
message: "Gitea provider ID is required.",
|
|
||||||
});
|
if (!giteaId) {
|
||||||
}
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
message: "Gitea provider ID is required.",
|
||||||
if (
|
});
|
||||||
giteaProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId
|
}
|
||||||
) {
|
|
||||||
throw new TRPCError({
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
code: "UNAUTHORIZED",
|
if (
|
||||||
message: "You are not allowed to access this Gitea provider",
|
giteaProvider.gitProvider.organizationId !==
|
||||||
});
|
ctx.session.activeOrganizationId
|
||||||
}
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
try {
|
code: "UNAUTHORIZED",
|
||||||
return await getGiteaRepositories(giteaId);
|
message: "You are not allowed to access this Gitea provider",
|
||||||
} catch (error) {
|
});
|
||||||
console.error("Error fetching Gitea repositories:", error);
|
}
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
try {
|
||||||
message: error instanceof Error ? error.message : String(error),
|
return await getGiteaRepositories(giteaId);
|
||||||
});
|
} catch (error) {
|
||||||
}
|
console.error("Error fetching Gitea repositories:", error);
|
||||||
}),
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Fetch branches of a specific Gitea repository
|
// Fetch branches of a specific Gitea repository
|
||||||
getGiteaBranches: protectedProcedure
|
getGiteaBranches: protectedProcedure
|
||||||
.input(apiFindGiteaBranches)
|
.input(apiFindGiteaBranches)
|
||||||
.query(async ({ input, ctx }: { input: typeof apiFindGiteaBranches._input; ctx: any }) => {
|
.query(
|
||||||
const { giteaId, owner, repositoryName } = input;
|
async ({
|
||||||
|
input,
|
||||||
if (!giteaId || !owner || !repositoryName) {
|
ctx,
|
||||||
throw new TRPCError({
|
}: { input: typeof apiFindGiteaBranches._input; ctx: any }) => {
|
||||||
code: "BAD_REQUEST",
|
const { giteaId, owner, repositoryName } = input;
|
||||||
message: "Gitea provider ID, owner, and repository name are required.",
|
|
||||||
});
|
if (!giteaId || !owner || !repositoryName) {
|
||||||
}
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
message:
|
||||||
if (
|
"Gitea provider ID, owner, and repository name are required.",
|
||||||
giteaProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId
|
});
|
||||||
) {
|
}
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
message: "You are not allowed to access this Gitea provider",
|
if (
|
||||||
});
|
giteaProvider.gitProvider.organizationId !==
|
||||||
}
|
ctx.session.activeOrganizationId
|
||||||
|
) {
|
||||||
try {
|
throw new TRPCError({
|
||||||
return await getGiteaBranches({
|
code: "UNAUTHORIZED",
|
||||||
giteaId,
|
message: "You are not allowed to access this Gitea provider",
|
||||||
owner,
|
});
|
||||||
repo: repositoryName,
|
}
|
||||||
id: 0, // Provide a default value for the optional id
|
|
||||||
});
|
try {
|
||||||
} catch (error) {
|
return await getGiteaBranches({
|
||||||
console.error("Error fetching Gitea branches:", error);
|
giteaId,
|
||||||
throw new TRPCError({
|
owner,
|
||||||
code: "BAD_REQUEST",
|
repo: repositoryName,
|
||||||
message: error instanceof Error ? error.message : String(error),
|
id: 0, // Provide a default value for the optional id
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
}),
|
console.error("Error fetching Gitea branches:", error);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Test connection to Gitea provider
|
// Test connection to Gitea provider
|
||||||
testConnection: protectedProcedure
|
testConnection: protectedProcedure
|
||||||
.input(apiGiteaTestConnection)
|
.input(apiGiteaTestConnection)
|
||||||
.mutation(async ({ input, ctx }: { input: typeof apiGiteaTestConnection._input; ctx: any }) => {
|
.mutation(
|
||||||
const giteaId = input.giteaId ?? "";
|
async ({
|
||||||
|
input,
|
||||||
try {
|
ctx,
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
}: { input: typeof apiGiteaTestConnection._input; ctx: any }) => {
|
||||||
if (
|
const giteaId = input.giteaId ?? "";
|
||||||
giteaProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId
|
|
||||||
) {
|
try {
|
||||||
throw new TRPCError({
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
code: "UNAUTHORIZED",
|
if (
|
||||||
message: "You are not allowed to access this Gitea provider",
|
giteaProvider.gitProvider.organizationId !==
|
||||||
});
|
ctx.session.activeOrganizationId
|
||||||
}
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
const result = await testGiteaConnection({
|
code: "UNAUTHORIZED",
|
||||||
giteaId,
|
message: "You are not allowed to access this Gitea provider",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return `Found ${result} repositories`;
|
|
||||||
} catch (error) {
|
const result = await testGiteaConnection({
|
||||||
console.error("Gitea connection test error:", error);
|
giteaId,
|
||||||
throw new TRPCError({
|
});
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: error instanceof Error ? error.message : String(error),
|
return `Found ${result} repositories`;
|
||||||
});
|
} catch (error) {
|
||||||
}
|
console.error("Gitea connection test error:", error);
|
||||||
}),
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
// Update an existing Gitea provider
|
// Update an existing Gitea provider
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(apiUpdateGitea)
|
.input(apiUpdateGitea)
|
||||||
.mutation(async ({ input, ctx }: { input: typeof apiUpdateGitea._input; ctx: any }) => {
|
.mutation(
|
||||||
const giteaProvider = await findGiteaById(input.giteaId);
|
async ({
|
||||||
if (
|
input,
|
||||||
giteaProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId
|
ctx,
|
||||||
) {
|
}: { input: typeof apiUpdateGitea._input; ctx: any }) => {
|
||||||
throw new TRPCError({
|
const giteaProvider = await findGiteaById(input.giteaId);
|
||||||
code: "UNAUTHORIZED",
|
if (
|
||||||
message: "You are not allowed to access this Gitea provider",
|
giteaProvider.gitProvider.organizationId !==
|
||||||
});
|
ctx.session.activeOrganizationId
|
||||||
}
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
console.log("Updating Gitea provider:", input);
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not allowed to access this Gitea provider",
|
||||||
if (input.name) {
|
});
|
||||||
await updateGitProvider(input.gitProviderId, {
|
}
|
||||||
name: input.name,
|
|
||||||
organizationId: ctx.session.activeOrganizationId,
|
console.log("Updating Gitea provider:", input);
|
||||||
});
|
|
||||||
|
if (input.name) {
|
||||||
await updateGitea(input.giteaId, {
|
await updateGitProvider(input.gitProviderId, {
|
||||||
...input,
|
name: input.name,
|
||||||
});
|
organizationId: ctx.session.activeOrganizationId,
|
||||||
} else {
|
});
|
||||||
await updateGitea(input.giteaId, {
|
|
||||||
...input,
|
await updateGitea(input.giteaId, {
|
||||||
});
|
...input,
|
||||||
}
|
});
|
||||||
|
} else {
|
||||||
return { success: true };
|
await updateGitea(input.giteaId, {
|
||||||
}),
|
...input,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|||||||
87
apps/dokploy/utils/gitea-utils.ts
Normal file
87
apps/dokploy/utils/gitea-utils.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// utils/gitea-utils.ts
|
||||||
|
// This file contains client-safe utilities for Gitea integration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an OAuth URL for Gitea authorization
|
||||||
|
*
|
||||||
|
* @param giteaId The ID of the Gitea provider to be used as state
|
||||||
|
* @param clientId The OAuth client ID
|
||||||
|
* @param giteaUrl The base URL of the Gitea instance
|
||||||
|
* @param baseUrl The base URL of the application for callback
|
||||||
|
* @returns The complete OAuth authorization URL
|
||||||
|
*/
|
||||||
|
export const getGiteaOAuthUrl = (
|
||||||
|
giteaId: string,
|
||||||
|
clientId: string,
|
||||||
|
giteaUrl: string,
|
||||||
|
baseUrl: string,
|
||||||
|
): string => {
|
||||||
|
if (!clientId || !giteaUrl || !baseUrl) {
|
||||||
|
// Return a marker that can be checked by the caller
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
|
||||||
|
const redirectUri = `${baseUrl}/api/providers/gitea/callback`;
|
||||||
|
const scopes = "repo repo:status read:user read:org";
|
||||||
|
|
||||||
|
return `${giteaUrl}/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(
|
||||||
|
redirectUri,
|
||||||
|
)}&response_type=code&scope=${encodeURIComponent(scopes)}&state=${encodeURIComponent(giteaId)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interfaces for Gitea API responses and components
|
||||||
|
export interface Repository {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
id: number;
|
||||||
|
owner: {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Branch {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiteaProviderType {
|
||||||
|
giteaId: string;
|
||||||
|
gitProvider: {
|
||||||
|
name: string;
|
||||||
|
gitProviderId: string;
|
||||||
|
providerType: "github" | "gitlab" | "bitbucket" | "gitea";
|
||||||
|
createdAt: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiteaProviderResponse {
|
||||||
|
giteaId: string;
|
||||||
|
clientId: string;
|
||||||
|
giteaUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GitProvider {
|
||||||
|
gitProviderId: string;
|
||||||
|
name: string;
|
||||||
|
providerType: string;
|
||||||
|
giteaId?: string;
|
||||||
|
gitea?: {
|
||||||
|
giteaId: string;
|
||||||
|
giteaUrl: string;
|
||||||
|
clientId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiteaProvider {
|
||||||
|
gitea?: {
|
||||||
|
giteaId?: string;
|
||||||
|
giteaUrl?: string;
|
||||||
|
clientId?: string;
|
||||||
|
clientSecret?: string;
|
||||||
|
redirectUri?: string;
|
||||||
|
organizationName?: string;
|
||||||
|
};
|
||||||
|
name?: string;
|
||||||
|
gitProviderId?: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
// @ts-ignore: Cannot find module errors
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
|
// @ts-ignore: Cannot find module errors
|
||||||
import {
|
import {
|
||||||
type apiCreateGitea,
|
type apiCreateGitea,
|
||||||
gitProvider,
|
gitProvider,
|
||||||
@@ -32,7 +34,7 @@ export const createGitea = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await tx
|
const giteaProvider = await tx
|
||||||
.insert(gitea)
|
.insert(gitea)
|
||||||
.values({
|
.values({
|
||||||
...input,
|
...input,
|
||||||
@@ -40,6 +42,20 @@ export const createGitea = async (
|
|||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((response: (typeof gitea.$inferSelect)[]) => response[0]);
|
.then((response: (typeof gitea.$inferSelect)[]) => response[0]);
|
||||||
|
|
||||||
|
if (!giteaProvider) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "BAD_REQUEST",
|
||||||
|
message: "Error creating the Gitea provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return just the essential data needed by the frontend
|
||||||
|
return {
|
||||||
|
giteaId: giteaProvider.giteaId,
|
||||||
|
clientId: giteaProvider.clientId,
|
||||||
|
giteaUrl: giteaProvider.giteaUrl,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import * as fs from "node:fs/promises";
|
import * as fs from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
// @ts-ignore: Cannot find module errors
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
|
// @ts-ignore: Cannot find module errors
|
||||||
import { findGiteaById, updateGitea } from "@dokploy/server/services/gitea";
|
import { findGiteaById, updateGitea } from "@dokploy/server/services/gitea";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
|
|||||||
Reference in New Issue
Block a user