Merge branch 'canary' into feat/docker-build-stage

This commit is contained in:
Mauricio Siu
2024-09-01 23:52:19 -06:00
104 changed files with 15369 additions and 2000 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,11 @@
"reference-api/reference-registry",
"reference-api/reference-security",
"reference-api/reference-settings",
"reference-api/reference-sshKey",
"reference-api/reference-gitProvider",
"reference-api/reference-bitbucket",
"reference-api/reference-github",
"reference-api/reference-gitlab",
"reference-api/reference-user"
]
}

View File

@@ -116,7 +116,7 @@ In: `header`
<Property name={"email"} type={"string"} required={true} deprecated={undefined}>
<span>Format: `"email"`</span>
<span>Format: `"email"`</span>
</Property>
@@ -218,7 +218,7 @@ In: `header`
<Property name={"authId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -320,7 +320,7 @@ In: `header`
<Property name={"token"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -419,7 +419,7 @@ In: `header`
<Property name={"userId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -439,11 +439,11 @@ In: `header`
</Property>
<Property name={"accesedProjects"} type={"array of string"} required={true} deprecated={undefined}>
<Property name={"accesedProjects"} type={"array<string>"} required={true} deprecated={undefined}>
</Property>
<Property name={"accesedServices"} type={"array of string"} required={true} deprecated={undefined}>
<Property name={"accesedServices"} type={"array<string>"} required={true} deprecated={undefined}>
</Property>
@@ -459,6 +459,14 @@ In: `header`
</Property>
<Property name={"canAccessToSSHKeys"} type={"boolean"} required={true} deprecated={undefined}>
</Property>
<Property name={"canAccessToGitProviders"} type={"boolean"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -488,7 +496,9 @@ curl -X POST "http://localhost:3000/api/admin.assignPermissions" \
],
"canAccessToTraefikFiles": true,
"canAccessToDocker": true,
"canAccessToAPI": true
"canAccessToAPI": true,
"canAccessToSSHKeys": true,
"canAccessToGitProviders": true
}'
```
@@ -552,382 +562,4 @@ export interface Response {
</API>
<API>
<APIInfo method={"POST"} route={"/admin.cleanGithubApp"}>
## admin-cleanGithubApp
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/admin.cleanGithubApp"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/admin.cleanGithubApp", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/admin.getRepositories"}>
## admin-getRepositories
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/admin.getRepositories"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/admin.getRepositories", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/admin.getBranches"}>
## admin-getBranches
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"repo"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"owner"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/admin.getBranches?repo=string&owner=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/admin.getBranches?repo=string&owner=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/admin.haveGithubConfigured"}>
## admin-haveGithubConfigured
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/admin.haveGithubConfigured"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/admin.haveGithubConfigured", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -750,6 +750,10 @@ In: `header`
</Property>
<Property name={"buildArgs"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -767,7 +771,8 @@ In: `header`
curl -X POST "http://localhost:3000/api/application.saveEnvironment" \
-d '{
"applicationId": "string",
"env": "string"
"env": "string",
"buildArgs": "string"
}'
```
@@ -853,7 +858,7 @@ In: `header`
<Property name={"buildType"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"dockerfile" | "heroku_buildpacks" | "paketo_buildpacks" | "nixpacks"`</span>
<span>Value in: `"dockerfile" | "heroku_buildpacks" | "paketo_buildpacks" | "nixpacks" | "static"`</span>
</Property>
@@ -861,6 +866,14 @@ In: `header`
</Property>
<Property name={"dockerContextPath"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"publishDirectory"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -879,7 +892,9 @@ curl -X POST "http://localhost:3000/api/application.saveBuildType" \
-d '{
"applicationId": "string",
"buildType": "dockerfile",
"dockerfile": "string"
"dockerfile": "string",
"dockerContextPath": "string",
"publishDirectory": "string"
}'
```
@@ -979,6 +994,10 @@ In: `header`
</Property>
<Property name={"githubId"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -999,7 +1018,8 @@ curl -X POST "http://localhost:3000/api/application.saveGithubProvider" \
"repository": "string",
"branch": "string",
"owner": "string",
"buildPath": "string"
"buildPath": "string",
"githubId": "string"
}'
```
@@ -1065,6 +1085,266 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/application.saveGitlabProvider"}>
## application-saveGitlabProvider
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabBranch"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabBuildPath"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabOwner"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabRepository"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabId"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabProjectId"} type={"number | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"gitlabPathNamespace"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/application.saveGitlabProvider" \
-d '{
"applicationId": "string",
"gitlabBranch": "string",
"gitlabBuildPath": "string",
"gitlabOwner": "string",
"gitlabRepository": "string",
"gitlabId": "string",
"gitlabProjectId": 0,
"gitlabPathNamespace": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/application.saveGitlabProvider", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/application.saveBitbucketProvider"}>
## application-saveBitbucketProvider
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"bitbucketBranch"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"bitbucketBuildPath"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"bitbucketOwner"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"bitbucketRepository"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"bitbucketId"} type={"string | null"} required={true} deprecated={undefined}>
</Property>
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/application.saveBitbucketProvider" \
-d '{
"bitbucketBranch": "string",
"bitbucketBuildPath": "string",
"bitbucketOwner": "string",
"bitbucketRepository": "string",
"bitbucketId": "string",
"applicationId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/application.saveBitbucketProvider", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/application.saveDockerProvider"}>
## application-saveDockerProvider
@@ -1210,6 +1490,10 @@ In: `header`
</Property>
<Property name={"customGitSSHKeyId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -1229,7 +1513,8 @@ curl -X POST "http://localhost:3000/api/application.saveGitProdiver" \
"customGitBranch": "string",
"applicationId": "string",
"customGitBuildPath": "string",
"customGitUrl": "string"
"customGitUrl": "string",
"customGitSSHKeyId": "string"
}'
```
@@ -1295,206 +1580,6 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/application.generateSSHKey"}>
## application-generateSSHKey
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/application.generateSSHKey" \
-d '{
"applicationId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/application.generateSSHKey", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/application.removeSSHKey"}>
## application-removeSSHKey
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/application.removeSSHKey" \
-d '{
"applicationId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/application.removeSSHKey", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/application.markRunning"}>
## application-markRunning
@@ -1611,13 +1696,13 @@ In: `header`
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1633,6 +1718,10 @@ In: `header`
</Property>
<Property name={"buildArgs"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"memoryReservation"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
@@ -1671,7 +1760,7 @@ In: `header`
<Property name={"sourceType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"github" | "docker" | "git"`</span>
<span>Value in: `"github" | "docker" | "git"`</span>
</Property>
@@ -1695,6 +1784,46 @@ In: `header`
</Property>
<Property name={"gitlabProjectId"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabRepository"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabOwner"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabBranch"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabBuildPath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabPathNamespace"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketRepository"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketOwner"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketBranch"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketBuildPath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"username"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
@@ -1719,7 +1848,7 @@ In: `header`
</Property>
<Property name={"customGitSSHKey"} type={"string | null"} required={false} deprecated={undefined}>
<Property name={"customGitSSHKeyId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
@@ -1727,11 +1856,19 @@ In: `header`
</Property>
<Property name={"dockerContextPath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"dropBuildPath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"healthCheckSwarm"} type={"object | null"} required={false} deprecated={undefined}>
<ObjectCollapsible name={"healthCheckSwarm"}>
<Property name={"Test"} type={"array of string"} required={false} deprecated={undefined}>
<Property name={"Test"} type={"array<string>"} required={false} deprecated={undefined}>
</Property>
@@ -1783,13 +1920,13 @@ In: `header`
<ObjectCollapsible name={"placementSwarm"}>
<Property name={"Constraints"} type={"array of string"} required={false} deprecated={undefined}>
<Property name={"Constraints"} type={"array<string>"} required={false} deprecated={undefined}>
</Property>
<Property name={"Preferences"} type={"array of object"} required={false} deprecated={undefined}>
<Property name={"Preferences"} type={"array<object>"} required={false} deprecated={undefined}>
<ObjectCollapsible name={"Preferences"}>
<ObjectCollapsible name={"Object 1"}>
<Property name={"Spread"} type={"object"} required={true} deprecated={undefined}>
@@ -1811,9 +1948,9 @@ In: `header`
</Property>
<Property name={"Platforms"} type={"array of object"} required={false} deprecated={undefined}>
<Property name={"Platforms"} type={"array<object>"} required={false} deprecated={undefined}>
<ObjectCollapsible name={"Platforms"}>
<ObjectCollapsible name={"Object 1"}>
<Property name={"Architecture"} type={"string"} required={true} deprecated={undefined}>
@@ -1959,15 +2096,15 @@ In: `header`
</Property>
<Property name={"networkSwarm"} type={"array of object | null"} required={false} deprecated={undefined}>
<Property name={"networkSwarm"} type={"array<object> | null"} required={false} deprecated={undefined}>
<ObjectCollapsible name={"networkSwarm"}>
<ObjectCollapsible name={"Object 1"}>
<Property name={"Target"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"Aliases"} type={"array of string"} required={false} deprecated={undefined}>
<Property name={"Aliases"} type={"array<string>"} required={false} deprecated={undefined}>
</Property>
@@ -1989,13 +2126,17 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
<Property name={"buildType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"dockerfile" | "heroku_buildpacks" | "paketo_buildpacks" | "nixpacks"`</span>
<span>Value in: `"dockerfile" | "heroku_buildpacks" | "paketo_buildpacks" | "nixpacks" | "static"`</span>
</Property>
<Property name={"publishDirectory"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
@@ -2011,6 +2152,18 @@ In: `header`
</Property>
<Property name={"githubId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -2032,6 +2185,7 @@ curl -X POST "http://localhost:3000/api/application.update" \
"appName": "string",
"description": "string",
"env": "string",
"buildArgs": "string",
"memoryReservation": 0,
"memoryLimit": 0,
"cpuReservation": 0,
@@ -2047,14 +2201,26 @@ curl -X POST "http://localhost:3000/api/application.update" \
"branch": "string",
"buildPath": "string",
"autoDeploy": true,
"gitlabProjectId": 0,
"gitlabRepository": "string",
"gitlabOwner": "string",
"gitlabBranch": "string",
"gitlabBuildPath": "string",
"gitlabPathNamespace": "string",
"bitbucketRepository": "string",
"bitbucketOwner": "string",
"bitbucketBranch": "string",
"bitbucketBuildPath": "string",
"username": "string",
"password": "string",
"dockerImage": "string",
"customGitUrl": "string",
"customGitBranch": "string",
"customGitBuildPath": "string",
"customGitSSHKey": "string",
"customGitSSHKeyId": "string",
"dockerfile": "string",
"dockerContextPath": "string",
"dropBuildPath": "string",
"healthCheckSwarm": {
"Test": [
"string"
@@ -2132,9 +2298,13 @@ curl -X POST "http://localhost:3000/api/application.update" \
"replicas": 0,
"applicationStatus": "idle",
"buildType": "dockerfile",
"publishDirectory": "string",
"createdAt": "string",
"registryId": "string",
"projectId": "string"
"projectId": "string",
"githubId": "string",
"gitlabId": "string",
"bitbucketId": "string"
}'
```

View File

@@ -25,13 +25,13 @@ In: `header`
<Property name={"email"} type={"string"} required={true} deprecated={undefined}>
<span>Format: `"email"`</span>
<span>Format: `"email"`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `8`</span>
<span>Minimum length: `8`</span>
</Property>
@@ -134,7 +134,7 @@ In: `header`
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `8`</span>
<span>Minimum length: `8`</span>
</Property>
@@ -144,7 +144,7 @@ In: `header`
<Property name={"token"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -248,13 +248,13 @@ In: `header`
<Property name={"email"} type={"string"} required={true} deprecated={undefined}>
<span>Format: `"email"`</span>
<span>Format: `"email"`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `8`</span>
<span>Minimum length: `8`</span>
</Property>
@@ -551,7 +551,7 @@ In: `header`
<Property name={"rol"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"admin" | "user"`</span>
<span>Value in: `"admin" | "user"`</span>
</Property>
@@ -869,7 +869,7 @@ In: `header`
<Property name={"id"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -883,7 +883,7 @@ In: `header`
<Property name={"rol"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"admin" | "user"`</span>
<span>Value in: `"admin" | "user"`</span>
</Property>
@@ -1104,13 +1104,13 @@ In: `header`
<Property name={"pin"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `6`</span>
<span>Minimum length: `6`</span>
</Property>
<Property name={"secret"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1213,7 +1213,7 @@ In: `header`
<Property name={"pin"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `6`</span>
<span>Minimum length: `6`</span>
</Property>

View File

@@ -33,7 +33,7 @@ In: `header`
<Property name={"prefix"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -43,7 +43,7 @@ In: `header`
<Property name={"database"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -65,7 +65,7 @@ In: `header`
<Property name={"databaseType"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"postgres" | "mariadb" | "mysql" | "mongo"`</span>
<span>Value in: `"postgres" | "mariadb" | "mysql" | "mongo"`</span>
</Property>
@@ -281,7 +281,7 @@ In: `header`
<Property name={"prefix"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -295,7 +295,7 @@ In: `header`
<Property name={"database"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -0,0 +1,779 @@
---
title: bitbucket
full: true
---
import { Root, API, APIInfo, APIExample, Responses, Response, ResponseTypes, ExampleResponse, TypeScriptResponse, Property, ObjectCollapsible, Requests, Request } from "fumadocs-ui/components/api";
<Root>
<API>
<APIInfo method={"POST"} route={"/bitbucket.create"}>
## bitbucket-create
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"bitbucketId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketUsername"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"appPassword"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketWorkspaceName"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitProviderId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"authId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/bitbucket.create" \
-d '{
"bitbucketId": "string",
"bitbucketUsername": "string",
"appPassword": "string",
"bitbucketWorkspaceName": "string",
"gitProviderId": "string",
"authId": "string",
"name": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.create", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/bitbucket.one"}>
## bitbucket-one
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"bitbucketId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/bitbucket.one?bitbucketId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.one?bitbucketId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/bitbucket.bitbucketProviders"}>
## bitbucket-bitbucketProviders
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/bitbucket.bitbucketProviders"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.bitbucketProviders", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/bitbucket.getBitbucketRepositories"}>
## bitbucket-getBitbucketRepositories
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"bitbucketId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/bitbucket.getBitbucketRepositories?bitbucketId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.getBitbucketRepositories?bitbucketId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/bitbucket.getBitbucketBranches"}>
## bitbucket-getBitbucketBranches
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"owner"} type={"string"} required={true} deprecated={false}>
</Property>
<Property name={"repo"} type={"string"} required={true} deprecated={false}>
</Property>
<Property name={"bitbucketId"} type={"string"} required={false} deprecated={false}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/bitbucket.getBitbucketBranches?owner=string&repo=string&bitbucketId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.getBitbucketBranches?owner=string&repo=string&bitbucketId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/bitbucket.testConnection"}>
## bitbucket-testConnection
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"bitbucketId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"bitbucketUsername"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"workspaceName"} type={"string"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/bitbucket.testConnection" \
-d '{
"bitbucketId": "string",
"bitbucketUsername": "string",
"workspaceName": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.testConnection", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/bitbucket.update"}>
## bitbucket-update
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"bitbucketId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"bitbucketUsername"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"appPassword"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketWorkspaceName"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitProviderId"} type={"string"} required={true} deprecated={undefined}>
</Property>
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/bitbucket.update" \
-d '{
"bitbucketId": "string",
"bitbucketUsername": "string",
"appPassword": "string",
"bitbucketWorkspaceName": "string",
"gitProviderId": "string",
"name": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/bitbucket.update", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -29,19 +29,19 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"certificateData"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"privateKey"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -156,7 +156,7 @@ In: `header`
<Property name={"certificateId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -255,7 +255,7 @@ In: `header`
<Property name={"certificateId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -39,7 +39,7 @@ In: `header`
<Property name={"composeType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"docker-compose" | "stack"`</span>
<span>Value in: `"docker-compose" | "stack"`</span>
</Property>
@@ -149,7 +149,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -252,7 +252,7 @@ In: `header`
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -278,13 +278,13 @@ In: `header`
<Property name={"sourceType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"git" | "github" | "raw"`</span>
<span>Value in: `"git" | "github" | "gitlab" | "bitbucket" | "raw"`</span>
</Property>
<Property name={"composeType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"docker-compose" | "stack"`</span>
<span>Value in: `"docker-compose" | "stack"`</span>
</Property>
@@ -304,6 +304,38 @@ In: `header`
</Property>
<Property name={"gitlabProjectId"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabRepository"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabOwner"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabBranch"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabPathNamespace"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketRepository"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketOwner"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketBranch"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"customGitUrl"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
@@ -312,7 +344,7 @@ In: `header`
</Property>
<Property name={"customGitSSHKey"} type={"string | null"} required={false} deprecated={undefined}>
<Property name={"customGitSSHKeyId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
@@ -322,13 +354,13 @@ In: `header`
<Property name={"composePath"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"composeStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -340,6 +372,18 @@ In: `header`
</Property>
<Property name={"githubId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitlabId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"bitbucketId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
@@ -369,14 +413,25 @@ curl -X POST "http://localhost:3000/api/compose.update" \
"owner": "string",
"branch": "string",
"autoDeploy": true,
"gitlabProjectId": 0,
"gitlabRepository": "string",
"gitlabOwner": "string",
"gitlabBranch": "string",
"gitlabPathNamespace": "string",
"bitbucketRepository": "string",
"bitbucketOwner": "string",
"bitbucketBranch": "string",
"customGitUrl": "string",
"customGitBranch": "string",
"customGitSSHKey": "string",
"customGitSSHKeyId": "string",
"command": "string",
"composePath": "string",
"composeStatus": "idle",
"projectId": "string",
"createdAt": "string"
"createdAt": "string",
"githubId": "string",
"gitlabId": "string",
"bitbucketId": "string"
}'
```
@@ -458,7 +513,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -560,7 +615,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -646,9 +701,9 @@ export interface Response {
<API>
<APIInfo method={"GET"} route={"/compose.allServices"}>
<APIInfo method={"GET"} route={"/compose.loadServices"}>
## compose-allServices
## compose-loadServices
### Authorization
@@ -662,7 +717,13 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"type"} type={"Any properties in not unknown, string"} required={false} deprecated={false}>
<span>Default: `"cache"`</span>
</Property>
@@ -680,7 +741,7 @@ In: `header`
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/compose.allServices?composeId=string"
curl -X GET "http://localhost:3000/api/compose.loadServices?composeId=string&type=cache"
```
</Request>
@@ -688,7 +749,7 @@ curl -X GET "http://localhost:3000/api/compose.allServices?composeId=string"
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.allServices?composeId=string", {
fetch("http://localhost:3000/api/compose.loadServices?composeId=string&type=cache", {
method: "GET"
});
```
@@ -745,6 +806,108 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/compose.fetchSourceType"}>
## compose-fetchSourceType
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/compose.fetchSourceType" \
-d '{
"composeId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.fetchSourceType", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/compose.randomizeCompose"}>
## compose-randomizeCompose
@@ -761,7 +924,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -852,6 +1015,105 @@ export interface Response {
<API>
<APIInfo method={"GET"} route={"/compose.getConvertedCompose"}>
## compose-getConvertedCompose
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/compose.getConvertedCompose?composeId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.getConvertedCompose?composeId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/compose.deploy"}>
## compose-deploy
@@ -868,7 +1130,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -970,7 +1232,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1072,7 +1334,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1174,7 +1436,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1257,108 +1519,6 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/compose.generateSSHKey"}>
## compose-generateSSHKey
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/compose.generateSSHKey" \
-d '{
"composeId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.generateSSHKey", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/compose.refreshToken"}>
## compose-refreshToken
@@ -1375,7 +1535,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1461,108 +1621,6 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/compose.removeSSHKey"}>
## compose-removeSSHKey
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"composeId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/compose.removeSSHKey" \
-d '{
"composeId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.removeSSHKey", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/compose.deployTemplate"}>
## compose-deployTemplate
@@ -1583,7 +1641,7 @@ In: `header`
<Property name={"id"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1759,4 +1817,95 @@ export interface Response {
</API>
<API>
<APIInfo method={"GET"} route={"/compose.getTags"}>
## compose-getTags
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/compose.getTags"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/compose.getTags", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"applicationId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -124,7 +124,7 @@ In: `header`
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -152,7 +152,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -567,7 +567,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -116,7 +116,7 @@ In: `header`
<Property name={"containerId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -213,9 +213,13 @@ In: `header`
### Query Parameters
<Property name={"appType"} type={"Any properties in string, string"} required={false} deprecated={false}>
</Property>
<Property name={"appName"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -233,7 +237,7 @@ In: `header`
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/docker.getContainersByAppNameMatch?appName=string"
curl -X GET "http://localhost:3000/api/docker.getContainersByAppNameMatch?appType=stack&appName=string"
```
</Request>
@@ -241,7 +245,7 @@ curl -X GET "http://localhost:3000/api/docker.getContainersByAppNameMatch?appNam
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/docker.getContainersByAppNameMatch?appName=string", {
fetch("http://localhost:3000/api/docker.getContainersByAppNameMatch?appType=stack&appName=string", {
method: "GET"
});
```
@@ -314,7 +318,7 @@ In: `header`
<Property name={"appName"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -25,31 +25,49 @@ In: `header`
<Property name={"host"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"path"} type={"string | null"} required={true} deprecated={undefined}>
<Property name={"path"} type={"string | null"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"port"} type={"number | null"} required={true} deprecated={undefined}>
<Property name={"port"} type={"number | null"} required={false} deprecated={undefined}>
<span>Minimum: `1`</span>
<span>Maximum: `65535`</span>
</Property>
<Property name={"https"} type={"boolean"} required={true} deprecated={undefined}>
<Property name={"https"} type={"boolean"} required={false} deprecated={undefined}>
</Property>
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
<Property name={"applicationId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"certificateType"} type={"string"} required={true} deprecated={undefined}>
<Property name={"certificateType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"letsencrypt" | "none"`</span>
<span>Value in: `"letsencrypt" | "none"`</span>
</Property>
<Property name={"composeId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"serviceName"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"domainType"} type={"string | null"} required={false} deprecated={undefined}>
<span>Value in: `"compose" | "application"`</span>
</Property>
@@ -71,10 +89,13 @@ curl -X POST "http://localhost:3000/api/domain.create" \
-d '{
"host": "string",
"path": "string",
"port": 0,
"port": 1,
"https": true,
"applicationId": "string",
"certificateType": "letsencrypt"
"certificateType": "letsencrypt",
"composeId": "string",
"serviceName": "string",
"domainType": "compose"
}'
```
@@ -237,9 +258,9 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/domain.generateDomain"}>
<APIInfo method={"GET"} route={"/domain.byComposeId"}>
## domain-generateDomain
## domain-byComposeId
### Authorization
@@ -249,9 +270,11 @@ In: `header`
</Property>
### Request Body
### Query Parameters
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
<Property name={"composeId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
@@ -269,10 +292,7 @@ In: `header`
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/domain.generateDomain" \
-d '{
"applicationId": "string"
}'
curl -X GET "http://localhost:3000/api/domain.byComposeId?composeId=string"
```
</Request>
@@ -280,8 +300,8 @@ curl -X POST "http://localhost:3000/api/domain.generateDomain" \
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/domain.generateDomain", {
method: "POST"
fetch("http://localhost:3000/api/domain.byComposeId?composeId=string", {
method: "GET"
});
```
@@ -337,9 +357,9 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/domain.generateWildcard"}>
<APIInfo method={"POST"} route={"/domain.generateDomain"}>
## domain-generateWildcard
## domain-generateDomain
### Authorization
@@ -351,7 +371,9 @@ In: `header`
### Request Body
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
@@ -369,9 +391,9 @@ In: `header`
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/domain.generateWildcard" \
curl -X POST "http://localhost:3000/api/domain.generateDomain" \
-d '{
"applicationId": "string"
"appName": "string"
}'
```
@@ -380,7 +402,7 @@ curl -X POST "http://localhost:3000/api/domain.generateWildcard" \
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/domain.generateWildcard", {
fetch("http://localhost:3000/api/domain.generateDomain", {
method: "POST"
});
```
@@ -451,35 +473,47 @@ In: `header`
### Request Body
<Property name={"domainId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"host"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"path"} type={"string | null"} required={true} deprecated={undefined}>
<Property name={"path"} type={"string | null"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"port"} type={"number | null"} required={true} deprecated={undefined}>
<Property name={"port"} type={"number | null"} required={false} deprecated={undefined}>
<span>Minimum: `1`</span>
<span>Maximum: `65535`</span>
</Property>
<Property name={"https"} type={"boolean"} required={true} deprecated={undefined}>
<Property name={"https"} type={"boolean"} required={false} deprecated={undefined}>
</Property>
<Property name={"certificateType"} type={"string"} required={true} deprecated={undefined}>
<Property name={"certificateType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"letsencrypt" | "none"`</span>
<span>Value in: `"letsencrypt" | "none"`</span>
</Property>
<Property name={"serviceName"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"domainType"} type={"string | null"} required={false} deprecated={undefined}>
<span>Value in: `"compose" | "application"`</span>
</Property>
<Property name={"domainId"} type={"string"} required={true} deprecated={undefined}>
</Property>
@@ -499,12 +533,14 @@ In: `header`
```bash
curl -X POST "http://localhost:3000/api/domain.update" \
-d '{
"domainId": "string",
"host": "string",
"path": "string",
"port": 0,
"port": 1,
"https": true,
"certificateType": "letsencrypt"
"certificateType": "letsencrypt",
"serviceName": "string",
"domainType": "compose",
"domainId": "string"
}'
```
@@ -586,8 +622,6 @@ In: `header`
<Property name={"domainId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
@@ -685,8 +719,6 @@ In: `header`
<Property name={"domainId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |

View File

@@ -0,0 +1,203 @@
---
title: gitProvider
full: true
---
import { Root, API, APIInfo, APIExample, Responses, Response, ResponseTypes, ExampleResponse, TypeScriptResponse, Property, ObjectCollapsible, Requests, Request } from "fumadocs-ui/components/api";
<Root>
<API>
<APIInfo method={"GET"} route={"/gitProvider.getAll"}>
## gitProvider-getAll
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/gitProvider.getAll"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitProvider.getAll", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/gitProvider.remove"}>
## gitProvider-remove
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"gitProviderId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/gitProvider.remove" \
-d '{
"gitProviderId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitProvider.remove", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -0,0 +1,661 @@
---
title: github
full: true
---
import { Root, API, APIInfo, APIExample, Responses, Response, ResponseTypes, ExampleResponse, TypeScriptResponse, Property, ObjectCollapsible, Requests, Request } from "fumadocs-ui/components/api";
<Root>
<API>
<APIInfo method={"GET"} route={"/github.one"}>
## github-one
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"githubId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/github.one?githubId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.one?githubId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/github.getGithubRepositories"}>
## github-getGithubRepositories
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"githubId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/github.getGithubRepositories?githubId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.getGithubRepositories?githubId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/github.getGithubBranches"}>
## github-getGithubBranches
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"repo"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"owner"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"githubId"} type={"string"} required={false} deprecated={false}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/github.getGithubBranches?repo=string&owner=string&githubId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.getGithubBranches?repo=string&owner=string&githubId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/github.githubProviders"}>
## github-githubProviders
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/github.githubProviders"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.githubProviders", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/github.testConnection"}>
## github-testConnection
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"githubId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/github.testConnection" \
-d '{
"githubId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.testConnection", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/github.update"}>
## github-update
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"githubId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"githubAppName"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubAppId"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubClientId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubClientSecret"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubInstallationId"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubPrivateKey"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"githubWebhookSecret"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitProviderId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/github.update" \
-d '{
"githubId": "string",
"githubAppName": "string",
"githubAppId": 0,
"githubClientId": "string",
"githubClientSecret": "string",
"githubInstallationId": "string",
"githubPrivateKey": "string",
"githubWebhookSecret": "string",
"gitProviderId": "string",
"name": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/github.update", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -0,0 +1,816 @@
---
title: gitlab
full: true
---
import { Root, API, APIInfo, APIExample, Responses, Response, ResponseTypes, ExampleResponse, TypeScriptResponse, Property, ObjectCollapsible, Requests, Request } from "fumadocs-ui/components/api";
<Root>
<API>
<APIInfo method={"POST"} route={"/gitlab.create"}>
## gitlab-create
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"gitlabId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"applicationId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"redirectUri"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"secret"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"accessToken"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"refreshToken"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"groupName"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"expiresAt"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitProviderId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"authId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/gitlab.create" \
-d '{
"gitlabId": "string",
"applicationId": "string",
"redirectUri": "string",
"secret": "string",
"accessToken": "string",
"refreshToken": "string",
"groupName": "string",
"expiresAt": 0,
"gitProviderId": "string",
"authId": "string",
"name": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.create", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/gitlab.one"}>
## gitlab-one
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"gitlabId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/gitlab.one?gitlabId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.one?gitlabId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/gitlab.gitlabProviders"}>
## gitlab-gitlabProviders
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/gitlab.gitlabProviders"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.gitlabProviders", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/gitlab.getGitlabRepositories"}>
## gitlab-getGitlabRepositories
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"gitlabId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/gitlab.getGitlabRepositories?gitlabId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.getGitlabRepositories?gitlabId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/gitlab.getGitlabBranches"}>
## gitlab-getGitlabBranches
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"id"} type={"number"} required={false} deprecated={false}>
</Property>
<Property name={"owner"} type={"string"} required={true} deprecated={false}>
</Property>
<Property name={"repo"} type={"string"} required={true} deprecated={false}>
</Property>
<Property name={"gitlabId"} type={"string"} required={false} deprecated={false}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/gitlab.getGitlabBranches?id=0&owner=string&repo=string&gitlabId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.getGitlabBranches?id=0&owner=string&repo=string&gitlabId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/gitlab.testConnection"}>
## gitlab-testConnection
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"gitlabId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"groupName"} type={"string"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/gitlab.testConnection" \
-d '{
"gitlabId": "string",
"groupName": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.testConnection", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/gitlab.update"}>
## gitlab-update
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"gitlabId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"applicationId"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"redirectUri"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"secret"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"accessToken"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"refreshToken"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"groupName"} type={"string"} required={false} deprecated={undefined}>
</Property>
<Property name={"expiresAt"} type={"number | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"gitProviderId"} type={"string"} required={true} deprecated={undefined}>
</Property>
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/gitlab.update" \
-d '{
"gitlabId": "string",
"applicationId": "string",
"redirectUri": "string",
"secret": "string",
"accessToken": "string",
"refreshToken": "string",
"groupName": "string",
"expiresAt": 0,
"gitProviderId": "string",
"name": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/gitlab.update", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -25,19 +25,19 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mariadb:6"`</span>
<span>Default: `"mariadb:6"`</span>
</Property>
@@ -55,13 +55,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -681,7 +681,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -993,7 +993,7 @@ In: `header`
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1096,19 +1096,19 @@ In: `header`
<Property name={"mariadbId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1118,13 +1118,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1138,7 +1138,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mariadb:6"`</span>
<span>Default: `"mariadb:6"`</span>
</Property>
@@ -1172,7 +1172,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>

View File

@@ -25,19 +25,19 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mongo:15"`</span>
<span>Default: `"mongo:15"`</span>
</Property>
@@ -51,7 +51,7 @@ In: `header`
<Property name={"databaseUser"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -669,7 +669,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -776,7 +776,7 @@ In: `header`
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1084,19 +1084,19 @@ In: `header`
<Property name={"mongoId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1106,7 +1106,7 @@ In: `header`
<Property name={"databaseUser"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1116,7 +1116,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mongo:15"`</span>
<span>Default: `"mongo:15"`</span>
</Property>
@@ -1150,7 +1150,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"type"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"bind" | "volume" | "file"`</span>
<span>Value in: `"bind" | "volume" | "file"`</span>
</Property>
@@ -43,21 +43,25 @@ In: `header`
<Property name={"mountPath"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"serviceType"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"application"`</span>
<span>Default: `"application"`</span>
<span>Value in: `"application" | "postgres" | "mysql" | "mariadb" | "mongo" | "redis" | "compose"`</span>
<span>Value in: `"application" | "postgres" | "mysql" | "mariadb" | "mongo" | "redis" | "compose"`</span>
</Property>
<Property name={"filePath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"serviceId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -83,6 +87,7 @@ curl -X POST "http://localhost:3000/api/mounts.create" \
"content": "string",
"mountPath": "string",
"serviceType": "application",
"filePath": "string",
"serviceId": "string"
}'
```
@@ -362,13 +367,13 @@ In: `header`
<Property name={"mountId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"type"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"bind" | "volume" | "file"`</span>
<span>Value in: `"bind" | "volume" | "file"`</span>
</Property>
@@ -380,21 +385,25 @@ In: `header`
</Property>
<Property name={"filePath"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"content"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"serviceType"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"application"`</span>
<span>Default: `"application"`</span>
<span>Value in: `"application" | "postgres" | "mysql" | "mariadb" | "mongo" | "redis" | "compose"`</span>
<span>Value in: `"application" | "postgres" | "mysql" | "mariadb" | "mongo" | "redis" | "compose"`</span>
</Property>
<Property name={"mountPath"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -446,6 +455,7 @@ curl -X POST "http://localhost:3000/api/mounts.update" \
"type": "bind",
"hostPath": "string",
"volumeName": "string",
"filePath": "string",
"content": "string",
"serviceType": "application",
"mountPath": "string",

View File

@@ -25,19 +25,19 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mysql:8"`</span>
<span>Default: `"mysql:8"`</span>
</Property>
@@ -51,13 +51,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -681,7 +681,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -788,7 +788,7 @@ In: `header`
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1096,19 +1096,19 @@ In: `header`
<Property name={"mysqlId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1118,13 +1118,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1138,7 +1138,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"mysql:8"`</span>
<span>Default: `"mysql:8"`</span>
</Property>
@@ -1172,7 +1172,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>

File diff suppressed because it is too large Load Diff

View File

@@ -33,15 +33,15 @@ In: `header`
<Property name={"protocol"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"tcp"`</span>
<span>Default: `"tcp"`</span>
<span>Value in: `"tcp" | "udp"`</span>
<span>Value in: `"tcp" | "udp"`</span>
</Property>
<Property name={"applicationId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -146,7 +146,7 @@ In: `header`
<Property name={"portId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -245,7 +245,7 @@ In: `header`
<Property name={"portId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -347,7 +347,7 @@ In: `header`
<Property name={"portId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -361,9 +361,9 @@ In: `header`
<Property name={"protocol"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"tcp"`</span>
<span>Default: `"tcp"`</span>
<span>Value in: `"tcp" | "udp"`</span>
<span>Value in: `"tcp" | "udp"`</span>
</Property>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -35,13 +35,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -51,7 +51,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"postgres:15"`</span>
<span>Default: `"postgres:15"`</span>
</Property>
@@ -674,7 +674,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -1087,13 +1087,13 @@ In: `header`
<Property name={"postgresId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1103,13 +1103,13 @@ In: `header`
<Property name={"databaseName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"databaseUser"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1123,7 +1123,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"postgres:15"`</span>
<span>Default: `"postgres:15"`</span>
</Property>
@@ -1157,7 +1157,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>

View File

@@ -25,7 +25,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -132,7 +132,7 @@ In: `header`
<Property name={"projectId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -322,7 +322,7 @@ In: `header`
<Property name={"projectId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -424,7 +424,7 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -434,7 +434,7 @@ In: `header`
<Property name={"projectId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -25,13 +25,13 @@ In: `header`
<Property name={"regex"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"replacement"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -144,7 +144,7 @@ In: `header`
<Property name={"redirectId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -243,7 +243,7 @@ In: `header`
<Property name={"redirectId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -345,19 +345,19 @@ In: `header`
<Property name={"redirectId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"regex"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"replacement"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -25,13 +25,13 @@ In: `header`
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -41,7 +41,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"redis:8"`</span>
<span>Default: `"redis:8"`</span>
</Property>
@@ -357,7 +357,7 @@ In: `header`
<Property name={"appName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -769,7 +769,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>
@@ -1077,19 +1077,19 @@ In: `header`
<Property name={"redisId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"appName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1103,7 +1103,7 @@ In: `header`
<Property name={"dockerImage"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"redis:8"`</span>
<span>Default: `"redis:8"`</span>
</Property>
@@ -1141,7 +1141,7 @@ In: `header`
<Property name={"applicationStatus"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
<span>Value in: `"idle" | "running" | "done" | "error"`</span>
</Property>

View File

@@ -25,19 +25,19 @@ In: `header`
<Property name={"registryName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"username"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -47,7 +47,7 @@ In: `header`
<Property name={"registryType"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"selfHosted" | "cloud"`</span>
<span>Value in: `"selfHosted" | "cloud"`</span>
</Property>
@@ -158,7 +158,7 @@ In: `header`
<Property name={"registryId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -260,13 +260,13 @@ In: `header`
<Property name={"registryId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"registryName"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -276,19 +276,19 @@ In: `header`
<Property name={"username"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"registryUrl"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -298,13 +298,13 @@ In: `header`
<Property name={"registryType"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"selfHosted" | "cloud"`</span>
<span>Value in: `"selfHosted" | "cloud"`</span>
</Property>
<Property name={"adminId"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -505,7 +505,7 @@ In: `header`
<Property name={"registryId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -604,19 +604,19 @@ In: `header`
<Property name={"registryName"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"username"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -626,7 +626,7 @@ In: `header`
<Property name={"registryType"} type={"string"} required={true} deprecated={undefined}>
<span>Value in: `"selfHosted" | "cloud"`</span>
<span>Value in: `"selfHosted" | "cloud"`</span>
</Property>
@@ -737,19 +737,19 @@ In: `header`
<Property name={"registryUrl"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"username"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -29,13 +29,13 @@ In: `header`
<Property name={"username"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -139,7 +139,7 @@ In: `header`
<Property name={"securityId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -238,7 +238,7 @@ In: `header`
<Property name={"securityId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -340,19 +340,19 @@ In: `header`
<Property name={"securityId"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"username"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"password"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -191,6 +191,106 @@ export interface Response {
<API>
<APIInfo method={"POST"} route={"/settings.toggleDashboard"}>
## settings-toggleDashboard
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"enableDashboard"} type={"boolean"} required={false} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/settings.toggleDashboard" \
-d '{
"enableDashboard": true
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/settings.toggleDashboard", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/settings.cleanUnusedImages"}>
## settings-cleanUnusedImages
@@ -952,9 +1052,9 @@ In: `header`
<Property name={"certificateType"} type={"string"} required={false} deprecated={undefined}>
<span>Default: `"none"`</span>
<span>Default: `"none"`</span>
<span>Value in: `"letsencrypt" | "none"`</span>
<span>Value in: `"letsencrypt" | "none"`</span>
</Property>
@@ -1340,7 +1440,7 @@ In: `header`
<Property name={"traefikConfig"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1533,7 +1633,7 @@ In: `header`
<Property name={"traefikConfig"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -1726,7 +1826,7 @@ In: `header`
<Property name={"traefikConfig"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -2192,13 +2292,13 @@ In: `header`
<Property name={"path"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
<Property name={"traefikConfig"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -2301,7 +2401,7 @@ In: `header`
<Property name={"path"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -2384,6 +2484,97 @@ export interface Response {
<API>
<APIInfo method={"GET"} route={"/settings.getIp"}>
## settings-getIp
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/settings.getIp"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/settings.getIp", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/settings.getOpenApiDocument"}>
## settings-getOpenApiDocument
@@ -2473,4 +2664,286 @@ export interface Response {
</API>
<API>
<APIInfo method={"GET"} route={"/settings.readTraefikEnv"}>
## settings-readTraefikEnv
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/settings.readTraefikEnv"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/settings.readTraefikEnv", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/settings.writeTraefikEnv"}>
## settings-writeTraefikEnv
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"env"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/settings.writeTraefikEnv" \
-d '{
"env": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/settings.writeTraefikEnv", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/settings.haveTraefikDashboardPortEnabled"}>
## settings-haveTraefikDashboardPortEnabled
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/settings.haveTraefikDashboardPortEnabled"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/settings.haveTraefikDashboardPortEnabled", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -0,0 +1,634 @@
---
title: sshKey
full: true
---
import { Root, API, APIInfo, APIExample, Responses, Response, ResponseTypes, ExampleResponse, TypeScriptResponse, Property, ObjectCollapsible, Requests, Request } from "fumadocs-ui/components/api";
<Root>
<API>
<APIInfo method={"POST"} route={"/sshKey.create"}>
## sshKey-create
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"name"} type={"string"} required={true} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"description"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"publicKey"} type={"string"} required={true} deprecated={undefined}>
</Property>
<Property name={"privateKey"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/sshKey.create" \
-d '{
"name": "string",
"description": "string",
"publicKey": "string",
"privateKey": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.create", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/sshKey.remove"}>
## sshKey-remove
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"sshKeyId"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/sshKey.remove" \
-d '{
"sshKeyId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.remove", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/sshKey.one"}>
## sshKey-one
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Query Parameters
<Property name={"sshKeyId"} type={"string"} required={true} deprecated={false}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/sshKey.one?sshKeyId=string"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.one?sshKeyId=string", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"GET"} route={"/sshKey.all"}>
## sshKey-all
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X GET "http://localhost:3000/api/sshKey.all"
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.all", {
method: "GET"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/sshKey.generate"}>
## sshKey-generate
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"type"} type={"string"} required={false} deprecated={undefined}>
<span>Value in: `"rsa" | "ed25519"`</span>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/sshKey.generate" \
-d '{
"type": "rsa"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.generate", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
<API>
<APIInfo method={"POST"} route={"/sshKey.update"}>
## sshKey-update
### Authorization
<Property name={"Authorization"} type={"Bearer <token>"} required={true}>
In: `header`
</Property>
### Request Body
<Property name={"name"} type={"string"} required={false} deprecated={undefined}>
<span>Minimum length: `1`</span>
</Property>
<Property name={"description"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"lastUsedAt"} type={"string | null"} required={false} deprecated={undefined}>
</Property>
<Property name={"sshKeyId"} type={"string"} required={true} deprecated={undefined}>
</Property>
| Status code | Description |
| ----------- | ----------- |
| `200` | Successful response |
| `default` | Error response |
</APIInfo>
<APIExample>
<Requests items={["cURL","JavaScript"]}>
<Request value={"cURL"}>
```bash
curl -X POST "http://localhost:3000/api/sshKey.update" \
-d '{
"name": "string",
"description": "string",
"lastUsedAt": "string",
"sshKeyId": "string"
}'
```
</Request>
<Request value={"JavaScript"}>
```js
fetch("http://localhost:3000/api/sshKey.update", {
method: "POST"
});
```
</Request>
</Requests>
<Responses items={["default"]}>
<Response value={"default"}>
<ResponseTypes>
<ExampleResponse>
```json
{
"message": "string",
"code": "string",
"issues": [
{
"message": "string"
}
]
}
```
</ExampleResponse>
<TypeScriptResponse>
```ts
export interface Response {
message: string;
code: string;
issues?: {
message: string;
}[];
}
```
</TypeScriptResponse>
</ResponseTypes>
</Response>
</Responses>
</APIExample>
</API>
</Root>

View File

@@ -116,7 +116,7 @@ In: `header`
<Property name={"authId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>
@@ -215,7 +215,7 @@ In: `header`
<Property name={"userId"} type={"string"} required={true} deprecated={false}>
<span>Minimum length: `1`</span>
<span>Minimum length: `1`</span>
</Property>

View File

@@ -6,14 +6,30 @@ description: Learn how to use providers in your application.
Dokploy offers several deployment methods, streamlining the process whether you're utilizing GitHub, any Git provider, Docker, or automated deployments.
- GitHub
- Gitlab
- Bitbucket
- Git (Any Git Provider)
- Docker
## GitHub
Deploying via GitHub is straightforward:
Deploying via GitHub:
1. Configure your GitHub repository in the `/dashboard/settings/server`.
1. Configure your GitHub repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Gitlab
Deploying via Gitlab:
1. Configure your Gitlab repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Bitbucket
Deploying via Bitbucket:
1. Configure your Bitbucket repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Git
@@ -27,11 +43,21 @@ For deployments from any Git repository, whether public or private, you can use
For private repositories, authenticate using SSH. We provide a lock icon to generate an SSH key.
<ImageZoom src="/assets/dokploy-ssh-key.png" width={800} height={630} className="rounded-lg"/>
<ImageZoom
src="/assets/dokploy-ssh-key.png"
width={800}
height={630}
className="rounded-lg"
/>
You can then copy the SSH key and paste it into the settings of your account.
<ImageZoom src="/assets/private-repository.png" width={800} height={630} className="rounded-lg"/>
<ImageZoom
src="/assets/private-repository.png"
width={800}
height={630}
className="rounded-lg"
/>
This enables you to pull repositories from your private repository, a method consistent across nearly all providers.

View File

@@ -10,12 +10,20 @@ Key Steps:
1. Add the service to the `dokploy-network`.
2. Use Traefik labels to configure routing.
import { Callout } from "fumadocs-ui/components/callout";
<Callout title="Attention" type="info">
Since v0.7.0 Dokploy support domains natively. This means that you can
configure your domain directly in the Dokploy UI, without doing the rest of
the steps.
</Callout>
Example Scenario
Let's consider an application with three components: a frontend, a backend, and a database. We'll start with a basic Docker Compose file and then enhance it with domain configuration.
```yaml
version: '3.8'
version: "3.8"
services:
frontend:
@@ -60,7 +68,7 @@ volumes:
First, we'll add the dokploy-network to our services:
```yaml
version: '3.8'
version: "3.8"
services:
frontend:
@@ -90,28 +98,8 @@ Step 2: Configuring Traefik Labels
Now, let's add Traefik labels to route domains to our services. We'll focus on the frontend and backend services:
{/* It is necessary to add these labels:
1. `traefik.enable=true`
This label tells Traefik that this service should be routed by Traefik.
2. `traefik.http.routers.<UNIQUE-RULE>.rule=Host('your-domain.dokploy.com')`
This label tells Traefik that the domain to be used is `your-domain.dokploy.com`
3. `traefik.http.routers.<UNIQUE-RULE>.entrypoints=web`
This label tells Traefik that the service should be accessible via the `http` protocol.
4. `traefik.http.services.<UNIQUE-RULE>.loadbalancer.server.port=3000`
This label tells Traefik that the port to be used is `3000`
Note: For loadbalancer.server.port, ensure you assign the port that your service is using. It's important to note that you do not need to expose the port like this:
Nota: en el loadbalancer.server.port asegurate de asignar el puerto que tu servicio esta utilizando, y alcaramos no es necesario que expongas el puerto de esta manera
'3000:3000' esto es incorrecto, unicamente debes de asignar el puerto que tu servicio esta utilizando, en este caso `3000`
asegurate de crear los registros `A` que apunten a tu dominio, esto hazlo desde tu DNS provider. */}
```yaml
version: '3.8'
version: "3.8"
services:
frontend:
@@ -165,21 +153,18 @@ networks:
Understanding Traefik Labels
1. `traefik.enable=true` Enables Traefik routing for the service.
1. `traefik.enable=true` Enables Traefik routing for the service.
2. `traefik.http.routers.<UNIQUE-RULE>.rule=Host('your-domain.dokploy.com')` Specifies the domain for the service
3. `traefik.http.routers.<UNIQUE-RULE>.entrypoints=web` Sets the service to be accessible via HTTP.
3. `traefik.http.routers.<UNIQUE-RULE>.entrypoints=web` Sets the service to be accessible via HTTP.
4. `traefik.http.services.<UNIQUE-RULE>.loadbalancer.server.port=3000` Specifies the port your service is using internally.
**Note**: Replace `<UNIQUE-RULE>` with a unique identifier for each service (e.g., frontend-app, backend-app, etc.).
**Note**: Replace `<UNIQUE-RULE>` with a unique identifier for each service (e.g., frontend-app, backend-app, etc.).
## Important Considerations
1. **Port Exposure**: Use `expose` instead of `ports` to expose ports to the host machine. This ensures that the ports are not exposed to the host machine.
2. **DNS Configuration**: Ensure you create `A` records pointing to your domain in your DNS Provider Settings.
3. **HTTPS**: For HTTPS, you can use Let's Encrypt or other SSL/TLS certificates.
3. **HTTPS**: For HTTPS, you can use Let's Encrypt or other SSL/TLS certificates.
## Deployment

View File

@@ -6,14 +6,30 @@ description: "Learn how to use Docker Compose with Dokploy"
Dokploy offers several deployment methods, streamlining the process whether you're utilizing GitHub, any Git provider, Raw, or automated deployments.
- GitHub
- Gitlab
- Bitbucket
- Git (Any Git Provider)
- Raw
## GitHub
Deploying via GitHub is straightforward:
Deploying via GitHub:
1. Configure your GitHub repository in the `/dashboard/settings/server`.
1. Configure your GitHub repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Gitlab
Deploying via Gitlab:
1. Configure your Gitlab repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Bitbucket
Deploying via Bitbucket:
1. Configure your Bitbucket repository in the `/dashboard/settings/git-providers`.
2. When creating an application, Dokploy automatically retrieves the available repositories and branches.
## Git
@@ -27,15 +43,24 @@ For deployments from any Git repository, whether public or private, you can use
For private repositories, authenticate using SSH. We provide a lock icon to generate an SSH key.
<ImageZoom src="/assets/dokploy-ssh-compose.png" width={800} height={630} className="rounded-lg"/>
<ImageZoom
src="/assets/dokploy-ssh-compose.png"
width={800}
height={630}
className="rounded-lg"
/>
You can then copy the SSH key and paste it into the settings of your account.
<ImageZoom src="/assets/private-repository.png" width={800} height={630} className="rounded-lg"/>
<ImageZoom
src="/assets/private-repository.png"
width={800}
height={630}
className="rounded-lg"
/>
This enables you to pull repositories from your private repository, a method consistent across nearly all providers.
## Raw
You specify a docker compose file directly in the code editor and trigger a deployment.
You specify a docker compose file directly in the code editor and trigger a deployment.

View File

@@ -16,16 +16,9 @@ const baseAdmin: Admin = {
createdAt: "",
authId: "",
adminId: "string",
githubAppId: null,
githubAppName: null,
serverIp: null,
certificateType: "none",
host: null,
githubClientId: null,
githubClientSecret: null,
githubInstallationId: null,
githubPrivateKey: null,
githubWebhookSecret: null,
letsEncryptEmail: null,
sshPrivateKey: null,
enableDockerCleanup: false,

View File

@@ -12,7 +12,20 @@ const baseApp: ApplicationNested = {
branch: null,
buildArgs: null,
buildPath: "/",
gitlabPathNamespace: "",
buildType: "nixpacks",
bitbucketBranch: "",
bitbucketBuildPath: "",
bitbucketId: "",
bitbucketRepository: "",
bitbucketOwner: "",
githubId: "",
gitlabProjectId: 0,
gitlabBranch: "",
gitlabBuildPath: "",
gitlabId: "",
gitlabRepository: "",
gitlabOwner: "",
command: null,
cpuLimit: null,
cpuReservation: null,

View File

@@ -0,0 +1,378 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const BitbucketProviderSchema = z.object({
buildPath: z.string().min(1, "Path is required").default("/"),
repository: z
.object({
repo: z.string().min(1, "Repo is required"),
owner: z.string().min(1, "Owner is required"),
})
.required(),
branch: z.string().min(1, "Branch is required"),
bitbucketId: z.string().min(1, "Bitbucket Provider is required"),
});
type BitbucketProvider = z.infer<typeof BitbucketProviderSchema>;
interface Props {
applicationId: string;
}
export const SaveBitbucketProvider = ({ applicationId }: Props) => {
const { data: bitbucketProviders } =
api.bitbucket.bitbucketProviders.useQuery();
const { data, refetch } = api.application.one.useQuery({ applicationId });
const { mutateAsync, isLoading: isSavingBitbucketProvider } =
api.application.saveBitbucketProvider.useMutation();
const form = useForm<BitbucketProvider>({
defaultValues: {
buildPath: "/",
repository: {
owner: "",
repo: "",
},
bitbucketId: "",
branch: "",
},
resolver: zodResolver(BitbucketProviderSchema),
});
const repository = form.watch("repository");
const bitbucketId = form.watch("bitbucketId");
const {
data: repositories,
isLoading: isLoadingRepositories,
error,
isError,
} = api.bitbucket.getBitbucketRepositories.useQuery(
{
bitbucketId,
},
{
enabled: !!bitbucketId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.bitbucket.getBitbucketBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
bitbucketId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!bitbucketId,
},
);
useEffect(() => {
if (data) {
form.reset({
branch: data.bitbucketBranch || "",
repository: {
repo: data.bitbucketRepository || "",
owner: data.bitbucketOwner || "",
},
buildPath: data.bitbucketBuildPath || "/",
bitbucketId: data.bitbucketId || "",
});
}
}, [form.reset, data, form]);
const onSubmit = async (data: BitbucketProvider) => {
await mutateAsync({
bitbucketBranch: data.branch,
bitbucketRepository: data.repository.repo,
bitbucketOwner: data.repository.owner,
bitbucketBuildPath: data.buildPath,
bitbucketId: data.bitbucketId,
applicationId,
})
.then(async () => {
toast.success("Service Provided Saved");
await refetch();
})
.catch(() => {
toast.error("Error to save the Bitbucket provider");
});
};
return (
<div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4 py-3"
>
{error && (
<AlertBlock type="error">Repositories: {error.message}</AlertBlock>
)}
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="bitbucketId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Bitbucket Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Bitbucket Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{bitbucketProviders?.map((bitbucketProvider) => (
<SelectItem
key={bitbucketProvider.bitbucketId}
value={bitbucketProvider.bitbucketId}
>
{bitbucketProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Repository</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{isLoadingRepositories
? "Loading...."
: field.value.owner
? repositories?.find(
(repo) => repo.name === field.value.repo,
)?.name
: "Select repository"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search repository..."
className="h-9"
/>
{isLoadingRepositories && (
<span className="py-6 text-center text-sm">
Loading Repositories....
</span>
)}
<CommandEmpty>No repositories found.</CommandEmpty>
<ScrollArea className="h-96">
<CommandGroup>
{repositories?.map((repo) => (
<CommandItem
value={repo.url}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
owner: repo.owner.username as string,
repo: repo.name,
});
form.setValue("branch", "");
}}
>
{repo.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
</Popover>
{form.formState.errors.repository && (
<p className={cn("text-sm font-medium text-destructive")}>
Repository is required
</p>
)}
</FormItem>
)}
/>
<FormField
control={form.control}
name="branch"
render={({ field }) => (
<FormItem className="block w-full">
<FormLabel>Branch</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
" w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{status === "loading" && fetchStatus === "fetching"
? "Loading...."
: field.value
? branches?.find(
(branch) => branch.name === field.value,
)?.name
: "Select branch"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search branch..."
className="h-9"
/>
{status === "loading" && fetchStatus === "fetching" && (
<span className="py-6 text-center text-sm text-muted-foreground">
Loading Branches....
</span>
)}
{!repository?.owner && (
<span className="py-6 text-center text-sm text-muted-foreground">
Select a repository
</span>
)}
<ScrollArea className="h-96">
<CommandEmpty>No branch found.</CommandEmpty>
<CommandGroup>
{branches?.map((branch) => (
<CommandItem
value={branch.name}
key={branch.commit.sha}
onSelect={() => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
branch.name === field.value
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
<FormMessage />
</Popover>
</FormItem>
)}
/>
<FormField
control={form.control}
name="buildPath"
render={({ field }) => (
<FormItem>
<FormLabel>Build Path</FormLabel>
<FormControl>
<Input placeholder="/" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex w-full justify-end">
<Button
isLoading={isSavingBitbucketProvider}
type="submit"
className="w-fit"
>
Save
</Button>
</div>
</form>
</Form>
</div>
);
};

View File

@@ -21,6 +21,13 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -39,6 +46,7 @@ const GithubProviderSchema = z.object({
})
.required(),
branch: z.string().min(1, "Branch is required"),
githubId: z.string().min(1, "Github Provider is required"),
});
type GithubProvider = z.infer<typeof GithubProviderSchema>;
@@ -48,6 +56,7 @@ interface Props {
}
export const SaveGithubProvider = ({ applicationId }: Props) => {
const { data: githubProviders } = api.github.githubProviders.useQuery();
const { data, refetch } = api.application.one.useQuery({ applicationId });
const { mutateAsync, isLoading: isSavingGithubProvider } =
@@ -60,26 +69,38 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
owner: "",
repo: "",
},
githubId: "",
branch: "",
},
resolver: zodResolver(GithubProviderSchema),
});
const repository = form.watch("repository");
const githubId = form.watch("githubId");
const { data: repositories, isLoading: isLoadingRepositories } =
api.admin.getRepositories.useQuery();
api.github.getGithubRepositories.useQuery(
{
githubId,
},
{
enabled: !!githubId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.admin.getBranches.useQuery(
} = api.github.getGithubBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
githubId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!githubId,
},
{ enabled: !!repository?.owner && !!repository?.repo },
);
useEffect(() => {
@@ -91,6 +112,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
owner: data.owner || "",
},
buildPath: data.buildPath || "/",
githubId: data.githubId || "",
});
}
}, [form.reset, data, form]);
@@ -102,6 +124,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
applicationId,
owner: data.repository.owner,
buildPath: data.buildPath,
githubId: data.githubId,
})
.then(async () => {
toast.success("Service Provided Saved");
@@ -120,6 +143,45 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
className="grid w-full gap-4 py-3"
>
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="githubId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Github Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Github Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{githubProviders?.map((githubProvider) => (
<SelectItem
key={githubProvider.githubId}
value={githubProvider.githubId}
>
{githubProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"

View File

@@ -0,0 +1,394 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const GitlabProviderSchema = z.object({
buildPath: z.string().min(1, "Path is required").default("/"),
repository: z
.object({
repo: z.string().min(1, "Repo is required"),
owner: z.string().min(1, "Owner is required"),
gitlabPathNamespace: z.string().min(1),
id: z.number().nullable(),
})
.required(),
branch: z.string().min(1, "Branch is required"),
gitlabId: z.string().min(1, "Gitlab Provider is required"),
});
type GitlabProvider = z.infer<typeof GitlabProviderSchema>;
interface Props {
applicationId: string;
}
export const SaveGitlabProvider = ({ applicationId }: Props) => {
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
const { data, refetch } = api.application.one.useQuery({ applicationId });
const { mutateAsync, isLoading: isSavingGitlabProvider } =
api.application.saveGitlabProvider.useMutation();
const form = useForm<GitlabProvider>({
defaultValues: {
buildPath: "/",
repository: {
owner: "",
repo: "",
gitlabPathNamespace: "",
id: null,
},
gitlabId: "",
branch: "",
},
resolver: zodResolver(GitlabProviderSchema),
});
const repository = form.watch("repository");
const gitlabId = form.watch("gitlabId");
const {
data: repositories,
isLoading: isLoadingRepositories,
error,
} = api.gitlab.getGitlabRepositories.useQuery(
{
gitlabId,
},
{
enabled: !!gitlabId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.gitlab.getGitlabBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
id: repository?.id || 0,
gitlabId: gitlabId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!gitlabId,
},
);
useEffect(() => {
if (data) {
form.reset({
branch: data.gitlabBranch || "",
repository: {
repo: data.gitlabRepository || "",
owner: data.gitlabOwner || "",
gitlabPathNamespace: data.gitlabPathNamespace || "",
id: data.gitlabProjectId,
},
buildPath: data.gitlabBuildPath || "/",
gitlabId: data.gitlabId || "",
});
}
}, [form.reset, data, form]);
const onSubmit = async (data: GitlabProvider) => {
await mutateAsync({
gitlabBranch: data.branch,
gitlabRepository: data.repository.repo,
gitlabOwner: data.repository.owner,
gitlabBuildPath: data.buildPath,
gitlabId: data.gitlabId,
applicationId,
gitlabProjectId: data.repository.id,
gitlabPathNamespace: data.repository.gitlabPathNamespace,
})
.then(async () => {
toast.success("Service Provided Saved");
await refetch();
})
.catch(() => {
toast.error("Error to save the gitlab provider");
});
};
return (
<div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4 py-3"
>
{error && <AlertBlock type="error">{error?.message}</AlertBlock>}
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="gitlabId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Gitlab Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
id: null,
gitlabPathNamespace: "",
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Gitlab Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{gitlabProviders?.map((gitlabProvider) => (
<SelectItem
key={gitlabProvider.gitlabId}
value={gitlabProvider.gitlabId}
>
{gitlabProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Repository</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{isLoadingRepositories
? "Loading...."
: field.value.owner
? repositories?.find(
(repo) => repo.name === field.value.repo,
)?.name
: "Select repository"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search repository..."
className="h-9"
/>
{isLoadingRepositories && (
<span className="py-6 text-center text-sm">
Loading Repositories....
</span>
)}
<CommandEmpty>No repositories found.</CommandEmpty>
<ScrollArea className="h-96">
<CommandGroup>
{repositories && repositories.length === 0 && (
<CommandEmpty>
No repositories found.
</CommandEmpty>
)}
{repositories?.map((repo) => {
return (
<CommandItem
value={repo.url}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
owner: repo.owner.username as string,
repo: repo.name,
id: repo.id,
gitlabPathNamespace: repo.url,
});
form.setValue("branch", "");
}}
>
{repo.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
);
})}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
</Popover>
{form.formState.errors.repository && (
<p className={cn("text-sm font-medium text-destructive")}>
Repository is required
</p>
)}
</FormItem>
)}
/>
<FormField
control={form.control}
name="branch"
render={({ field }) => (
<FormItem className="block w-full">
<FormLabel>Branch</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
" w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{status === "loading" && fetchStatus === "fetching"
? "Loading...."
: field.value
? branches?.find(
(branch) => branch.name === field.value,
)?.name
: "Select branch"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search branch..."
className="h-9"
/>
{status === "loading" && fetchStatus === "fetching" && (
<span className="py-6 text-center text-sm text-muted-foreground">
Loading Branches....
</span>
)}
{!repository?.owner && (
<span className="py-6 text-center text-sm text-muted-foreground">
Select a repository
</span>
)}
<ScrollArea className="h-96">
<CommandEmpty>No branch found.</CommandEmpty>
<CommandGroup>
{branches?.map((branch) => (
<CommandItem
value={branch.name}
key={branch.commit.id}
onSelect={() => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
branch.name === field.value
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
<FormMessage />
</Popover>
</FormItem>
)}
/>
<FormField
control={form.control}
name="buildPath"
render={({ field }) => (
<FormItem>
<FormLabel>Build Path</FormLabel>
<FormControl>
<Input placeholder="/" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex w-full justify-end">
<Button
isLoading={isSavingGitlabProvider}
type="submit"
className="w-fit"
>
Save
</Button>
</div>
</form>
</Form>
</div>
);
};

View File

@@ -1,23 +1,34 @@
import { SaveDockerProvider } from "@/components/dashboard/application/general/generic/save-docker-provider";
import { SaveGitProvider } from "@/components/dashboard/application/general/generic/save-git-provider";
import { SaveGithubProvider } from "@/components/dashboard/application/general/generic/save-github-provider";
import {
BitbucketIcon,
DockerIcon,
GitIcon,
GithubIcon,
GitlabIcon,
} from "@/components/icons/data-tools-icons";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { api } from "@/utils/api";
import { GitBranch, LockIcon } from "lucide-react";
import { GitBranch, LockIcon, UploadCloud } from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import { SaveBitbucketProvider } from "./save-bitbucket-provider";
import { SaveDragNDrop } from "./save-drag-n-drop";
import { SaveGitlabProvider } from "./save-gitlab-provider";
type TabState = "github" | "docker" | "git" | "drop";
type TabState = "github" | "docker" | "git" | "drop" | "gitlab" | "bitbucket";
interface Props {
applicationId: string;
}
export const ShowProviderForm = ({ applicationId }: Props) => {
const { data: haveGithubConfigured } =
api.admin.haveGithubConfigured.useQuery();
const { data: githubProviders } = api.github.githubProviders.useQuery();
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
const { data: bitbucketProviders } =
api.bitbucket.bitbucketProviders.useQuery();
const { data: application } = api.application.one.useQuery({ applicationId });
const [tab, setSab] = useState<TabState>(application?.sourceType || "github");
@@ -44,43 +55,104 @@ export const ShowProviderForm = ({ applicationId }: Props) => {
setSab(e as TabState);
}}
>
<TabsList className="grid w-fit grid-cols-4 bg-transparent">
<TabsTrigger
value="github"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Github
</TabsTrigger>
<TabsTrigger
value="docker"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Docker
</TabsTrigger>
<TabsTrigger
value="git"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Git
</TabsTrigger>
<TabsTrigger
value="drop"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Drop
</TabsTrigger>
</TabsList>
<div className="flex flex-row items-center justify-between w-full gap-4">
<TabsList className="md:grid md:w-fit md:grid-cols-7 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" />
Github
</TabsTrigger>
<TabsTrigger
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"
>
<GitlabIcon className="size-4 text-current fill-current" />
Gitlab
</TabsTrigger>
<TabsTrigger
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"
>
<BitbucketIcon className="size-4 text-current fill-current" />
Bitbucket
</TabsTrigger>
<TabsTrigger
value="docker"
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
<DockerIcon className="size-5 text-current" />
Docker
</TabsTrigger>
<TabsTrigger
value="git"
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
<GitIcon />
Git
</TabsTrigger>
<TabsTrigger
value="drop"
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
<UploadCloud className="size-5 text-current" />
Drop
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="github" className="w-full p-2">
{haveGithubConfigured ? (
{githubProviders && githubProviders?.length > 0 ? (
<SaveGithubProvider applicationId={applicationId} />
) : (
<div className="flex flex-col items-center gap-3">
<LockIcon className="size-8 text-muted-foreground" />
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<GithubIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using GitHub, you need to configure your account
first. Please, go to{" "}
<Link
href="/dashboard/settings/server"
href="/dashboard/settings/git-providers"
className="text-foreground"
>
Settings
</Link>{" "}
to do so.
</span>
</div>
)}
</TabsContent>
<TabsContent value="gitlab" className="w-full p-2">
{gitlabProviders && gitlabProviders?.length > 0 ? (
<SaveGitlabProvider applicationId={applicationId} />
) : (
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<GitlabIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using GitLab, 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>
)}
</TabsContent>
<TabsContent value="bitbucket" className="w-full p-2">
{bitbucketProviders && bitbucketProviders?.length > 0 ? (
<SaveBitbucketProvider applicationId={applicationId} />
) : (
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<BitbucketIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using Bitbucket, you need to configure your account
first. Please, go to{" "}
<Link
href="/dashboard/settings/git-providers"
className="text-foreground"
>
Settings
@@ -93,6 +165,7 @@ export const ShowProviderForm = ({ applicationId }: Props) => {
<TabsContent value="docker" className="w-full p-2">
<SaveDockerProvider applicationId={applicationId} />
</TabsContent>
<TabsContent value="git" className="w-full p-2">
<SaveGitProvider applicationId={applicationId} />
</TabsContent>

View File

@@ -166,7 +166,13 @@ export const AddDomainCompose = ({
<DialogTitle>Domain</DialogTitle>
<DialogDescription>{dictionary.dialogDescription}</DialogDescription>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<div className="flex flex-col gap-4">
<AlertBlock type="info">
Deploy is required to apply changes after creating or updating a
domain.
</AlertBlock>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
</div>
<Form {...form}>
<form

View File

@@ -0,0 +1,380 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const BitbucketProviderSchema = z.object({
composePath: z.string().min(1),
repository: z
.object({
repo: z.string().min(1, "Repo is required"),
owner: z.string().min(1, "Owner is required"),
})
.required(),
branch: z.string().min(1, "Branch is required"),
bitbucketId: z.string().min(1, "Bitbucket Provider is required"),
});
type BitbucketProvider = z.infer<typeof BitbucketProviderSchema>;
interface Props {
composeId: string;
}
export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
const { data: bitbucketProviders } =
api.bitbucket.bitbucketProviders.useQuery();
const { data, refetch } = api.compose.one.useQuery({ composeId });
const { mutateAsync, isLoading: isSavingBitbucketProvider } =
api.compose.update.useMutation();
const form = useForm<BitbucketProvider>({
defaultValues: {
composePath: "./docker-compose.yml",
repository: {
owner: "",
repo: "",
},
bitbucketId: "",
branch: "",
},
resolver: zodResolver(BitbucketProviderSchema),
});
const repository = form.watch("repository");
const bitbucketId = form.watch("bitbucketId");
const {
data: repositories,
isLoading: isLoadingRepositories,
error,
isError,
} = api.bitbucket.getBitbucketRepositories.useQuery(
{
bitbucketId,
},
{
enabled: !!bitbucketId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.bitbucket.getBitbucketBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
bitbucketId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!bitbucketId,
},
);
useEffect(() => {
if (data) {
form.reset({
branch: data.bitbucketBranch || "",
repository: {
repo: data.bitbucketRepository || "",
owner: data.bitbucketOwner || "",
},
composePath: data.composePath,
bitbucketId: data.bitbucketId || "",
});
}
}, [form.reset, data, form]);
const onSubmit = async (data: BitbucketProvider) => {
await mutateAsync({
bitbucketBranch: data.branch,
bitbucketRepository: data.repository.repo,
bitbucketOwner: data.repository.owner,
bitbucketId: data.bitbucketId,
composePath: data.composePath,
composeId,
sourceType: "bitbucket",
composeStatus: "idle",
})
.then(async () => {
toast.success("Service Provided Saved");
await refetch();
})
.catch(() => {
toast.error("Error to save the Bitbucket provider");
});
};
return (
<div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4 py-3"
>
{error && (
<AlertBlock type="error">Repositories: {error.message}</AlertBlock>
)}
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="bitbucketId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Bitbucket Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Bitbucket Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{bitbucketProviders?.map((bitbucketProvider) => (
<SelectItem
key={bitbucketProvider.bitbucketId}
value={bitbucketProvider.bitbucketId}
>
{bitbucketProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Repository</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{isLoadingRepositories
? "Loading...."
: field.value.owner
? repositories?.find(
(repo) => repo.name === field.value.repo,
)?.name
: "Select repository"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search repository..."
className="h-9"
/>
{isLoadingRepositories && (
<span className="py-6 text-center text-sm">
Loading Repositories....
</span>
)}
<CommandEmpty>No repositories found.</CommandEmpty>
<ScrollArea className="h-96">
<CommandGroup>
{repositories?.map((repo) => (
<CommandItem
value={repo.url}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
owner: repo.owner.username as string,
repo: repo.name,
});
form.setValue("branch", "");
}}
>
{repo.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
</Popover>
{form.formState.errors.repository && (
<p className={cn("text-sm font-medium text-destructive")}>
Repository is required
</p>
)}
</FormItem>
)}
/>
<FormField
control={form.control}
name="branch"
render={({ field }) => (
<FormItem className="block w-full">
<FormLabel>Branch</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
" w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{status === "loading" && fetchStatus === "fetching"
? "Loading...."
: field.value
? branches?.find(
(branch) => branch.name === field.value,
)?.name
: "Select branch"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search branch..."
className="h-9"
/>
{status === "loading" && fetchStatus === "fetching" && (
<span className="py-6 text-center text-sm text-muted-foreground">
Loading Branches....
</span>
)}
{!repository?.owner && (
<span className="py-6 text-center text-sm text-muted-foreground">
Select a repository
</span>
)}
<ScrollArea className="h-96">
<CommandEmpty>No branch found.</CommandEmpty>
<CommandGroup>
{branches?.map((branch) => (
<CommandItem
value={branch.name}
key={branch.commit.sha}
onSelect={() => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
branch.name === field.value
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
<FormMessage />
</Popover>
</FormItem>
)}
/>
<FormField
control={form.control}
name="composePath"
render={({ field }) => (
<FormItem>
<FormLabel>Compose Path</FormLabel>
<FormControl>
<Input placeholder="docker-compose.yml" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex w-full justify-end">
<Button
isLoading={isSavingBitbucketProvider}
type="submit"
className="w-fit"
>
Save
</Button>
</div>
</form>
</Form>
</div>
);
};

View File

@@ -21,6 +21,13 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -39,6 +46,7 @@ const GithubProviderSchema = z.object({
})
.required(),
branch: z.string().min(1, "Branch is required"),
githubId: z.string().min(1, "Github Provider is required"),
});
type GithubProvider = z.infer<typeof GithubProviderSchema>;
@@ -48,6 +56,7 @@ interface Props {
}
export const SaveGithubProviderCompose = ({ composeId }: Props) => {
const { data: githubProviders } = api.github.githubProviders.useQuery();
const { data, refetch } = api.compose.one.useQuery({ composeId });
const { mutateAsync, isLoading: isSavingGithubProvider } =
@@ -60,26 +69,38 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
owner: "",
repo: "",
},
githubId: "",
branch: "",
},
resolver: zodResolver(GithubProviderSchema),
});
const repository = form.watch("repository");
const githubId = form.watch("githubId");
const { data: repositories, isLoading: isLoadingRepositories } =
api.admin.getRepositories.useQuery();
api.github.getGithubRepositories.useQuery(
{
githubId,
},
{
enabled: !!githubId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.admin.getBranches.useQuery(
} = api.github.getGithubBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
githubId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!githubId,
},
{ enabled: !!repository?.owner && !!repository?.repo },
);
useEffect(() => {
@@ -91,19 +112,21 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
owner: data.owner || "",
},
composePath: data.composePath,
githubId: data.githubId || "",
});
}
}, [form.reset, data, form]);
const onSubmit = async (data: GithubProvider) => {
console.log(data);
await mutateAsync({
branch: data.branch,
repository: data.repository.repo,
composeId: composeId,
composeId,
owner: data.repository.owner,
sourceType: "github",
composePath: data.composePath,
githubId: data.githubId,
sourceType: "github",
composeStatus: "idle",
})
.then(async () => {
toast.success("Service Provided Saved");
@@ -122,6 +145,45 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
className="grid w-full gap-4 py-3"
>
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="githubId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Github Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Github Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{githubProviders?.map((githubProvider) => (
<SelectItem
key={githubProvider.githubId}
value={githubProvider.githubId}
>
{githubProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"
@@ -278,7 +340,6 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="composePath"

View File

@@ -0,0 +1,396 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CheckIcon, ChevronsUpDown } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const GitlabProviderSchema = z.object({
composePath: z.string().min(1),
repository: z
.object({
repo: z.string().min(1, "Repo is required"),
owner: z.string().min(1, "Owner is required"),
id: z.number().nullable(),
gitlabPathNamespace: z.string().min(1),
})
.required(),
branch: z.string().min(1, "Branch is required"),
gitlabId: z.string().min(1, "Gitlab Provider is required"),
});
type GitlabProvider = z.infer<typeof GitlabProviderSchema>;
interface Props {
composeId: string;
}
export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
const { data, refetch } = api.compose.one.useQuery({ composeId });
const { mutateAsync, isLoading: isSavingGitlabProvider } =
api.compose.update.useMutation();
const form = useForm<GitlabProvider>({
defaultValues: {
composePath: "./docker-compose.yml",
repository: {
owner: "",
repo: "",
gitlabPathNamespace: "",
id: null,
},
gitlabId: "",
branch: "",
},
resolver: zodResolver(GitlabProviderSchema),
});
const repository = form.watch("repository");
const gitlabId = form.watch("gitlabId");
const {
data: repositories,
isLoading: isLoadingRepositories,
error,
} = api.gitlab.getGitlabRepositories.useQuery(
{
gitlabId,
},
{
enabled: !!gitlabId,
},
);
const {
data: branches,
fetchStatus,
status,
} = api.gitlab.getGitlabBranches.useQuery(
{
owner: repository?.owner,
repo: repository?.repo,
id: repository?.id || 0,
gitlabId: gitlabId,
},
{
enabled: !!repository?.owner && !!repository?.repo && !!gitlabId,
},
);
useEffect(() => {
if (data) {
form.reset({
branch: data.gitlabBranch || "",
repository: {
repo: data.gitlabRepository || "",
owner: data.gitlabOwner || "",
id: data.gitlabProjectId,
gitlabPathNamespace: data.gitlabPathNamespace || "",
},
composePath: data.composePath,
gitlabId: data.gitlabId || "",
});
}
}, [form.reset, data, form]);
const onSubmit = async (data: GitlabProvider) => {
await mutateAsync({
gitlabBranch: data.branch,
gitlabRepository: data.repository.repo,
gitlabOwner: data.repository.owner,
composePath: data.composePath,
gitlabId: data.gitlabId,
composeId,
gitlabProjectId: data.repository.id,
gitlabPathNamespace: data.repository.gitlabPathNamespace,
sourceType: "gitlab",
composeStatus: "idle",
})
.then(async () => {
toast.success("Service Provided Saved");
await refetch();
})
.catch(() => {
toast.error("Error to save the gitlab provider");
});
};
return (
<div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4 py-3"
>
{error && <AlertBlock type="error">{error?.message}</AlertBlock>}
<div className="grid md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="gitlabId"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Gitlab Account</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
form.setValue("repository", {
owner: "",
repo: "",
gitlabPathNamespace: "",
id: null,
});
form.setValue("branch", "");
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a Gitlab Account" />
</SelectTrigger>
</FormControl>
<SelectContent>
{gitlabProviders?.map((gitlabProvider) => (
<SelectItem
key={gitlabProvider.gitlabId}
value={gitlabProvider.gitlabId}
>
{gitlabProvider.gitProvider.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="repository"
render={({ field }) => (
<FormItem className="md:col-span-2 flex flex-col">
<FormLabel>Repository</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{isLoadingRepositories
? "Loading...."
: field.value.owner
? repositories?.find(
(repo) => repo.name === field.value.repo,
)?.name
: "Select repository"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search repository..."
className="h-9"
/>
{isLoadingRepositories && (
<span className="py-6 text-center text-sm">
Loading Repositories....
</span>
)}
<CommandEmpty>No repositories found.</CommandEmpty>
<ScrollArea className="h-96">
<CommandGroup>
{repositories && repositories.length === 0 && (
<CommandEmpty>
No repositories found.
</CommandEmpty>
)}
{repositories?.map((repo) => {
return (
<CommandItem
value={repo.url}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
owner: repo.owner.username as string,
repo: repo.name,
id: repo.id,
gitlabPathNamespace: repo.url,
});
form.setValue("branch", "");
}}
>
{repo.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
);
})}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
</Popover>
{form.formState.errors.repository && (
<p className={cn("text-sm font-medium text-destructive")}>
Repository is required
</p>
)}
</FormItem>
)}
/>
<FormField
control={form.control}
name="branch"
render={({ field }) => (
<FormItem className="block w-full">
<FormLabel>Branch</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
" w-full justify-between !bg-input",
!field.value && "text-muted-foreground",
)}
>
{status === "loading" && fetchStatus === "fetching"
? "Loading...."
: field.value
? branches?.find(
(branch) => branch.name === field.value,
)?.name
: "Select branch"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput
placeholder="Search branch..."
className="h-9"
/>
{status === "loading" && fetchStatus === "fetching" && (
<span className="py-6 text-center text-sm text-muted-foreground">
Loading Branches....
</span>
)}
{!repository?.owner && (
<span className="py-6 text-center text-sm text-muted-foreground">
Select a repository
</span>
)}
<ScrollArea className="h-96">
<CommandEmpty>No branch found.</CommandEmpty>
<CommandGroup>
{branches?.map((branch) => (
<CommandItem
value={branch.name}
key={branch.commit.id}
onSelect={() => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
branch.name === field.value
? "opacity-100"
: "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</ScrollArea>
</Command>
</PopoverContent>
<FormMessage />
</Popover>
</FormItem>
)}
/>
<FormField
control={form.control}
name="composePath"
render={({ field }) => (
<FormItem>
<FormLabel>Compose Path</FormLabel>
<FormControl>
<Input placeholder="docker-compose.yml" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex w-full justify-end">
<Button
isLoading={isSavingGitlabProvider}
type="submit"
className="w-fit"
>
Save
</Button>
</div>
</form>
</Form>
</div>
);
};

View File

@@ -1,22 +1,32 @@
import {
BitbucketIcon,
GitIcon,
GithubIcon,
GitlabIcon,
} from "@/components/icons/data-tools-icons";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { api } from "@/utils/api";
import { GitBranch, LockIcon } from "lucide-react";
import { CodeIcon, GitBranch, LockIcon } from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import { ComposeFileEditor } from "../compose-file-editor";
import { ShowConvertedCompose } from "../show-converted-compose";
import { SaveBitbucketProviderCompose } from "./save-bitbucket-provider-compose";
import { SaveGitProviderCompose } from "./save-git-provider-compose";
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
import { SaveGitlabProviderCompose } from "./save-gitlab-provider-compose";
type TabState = "github" | "git" | "raw";
type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket";
interface Props {
composeId: string;
}
export const ShowProviderFormCompose = ({ composeId }: Props) => {
const { data: haveGithubConfigured } =
api.admin.haveGithubConfigured.useQuery();
const { data: githubProviders } = api.github.githubProviders.useQuery();
const { data: gitlabProviders } = api.gitlab.gitlabProviders.useQuery();
const { data: bitbucketProviders } =
api.bitbucket.bitbucketProviders.useQuery();
const { data: compose } = api.compose.one.useQuery({ composeId });
const [tab, setSab] = useState<TabState>(compose?.sourceType || "github");
@@ -44,38 +54,97 @@ export const ShowProviderFormCompose = ({ composeId }: Props) => {
setSab(e as TabState);
}}
>
<TabsList className="grid w-fit grid-cols-4 bg-transparent">
<TabsTrigger
value="github"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Github
</TabsTrigger>
<div className="flex flex-row items-center justify-between w-full gap-4">
<TabsList className="md:grid md:w-fit md:grid-cols-5 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" />
Github
</TabsTrigger>
<TabsTrigger
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"
>
<GitlabIcon className="size-4 text-current fill-current" />
Gitlab
</TabsTrigger>
<TabsTrigger
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"
>
<BitbucketIcon className="size-4 text-current fill-current" />
Bitbucket
</TabsTrigger>
<TabsTrigger
value="git"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Git
</TabsTrigger>
<TabsTrigger
value="raw"
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
Raw
</TabsTrigger>
</TabsList>
<TabsTrigger
value="git"
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
>
<GitIcon />
Git
</TabsTrigger>
<TabsTrigger
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"
>
<CodeIcon className="size-4 " />
Raw
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="github" className="w-full p-2">
{haveGithubConfigured ? (
{githubProviders && githubProviders?.length > 0 ? (
<SaveGithubProviderCompose composeId={composeId} />
) : (
<div className="flex flex-col items-center gap-3">
<LockIcon className="size-8 text-muted-foreground" />
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<GithubIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using GitHub, you need to configure your account
first. Please, go to{" "}
<Link
href="/dashboard/settings/server"
href="/dashboard/settings/git-providers"
className="text-foreground"
>
Settings
</Link>{" "}
to do so.
</span>
</div>
)}
</TabsContent>
<TabsContent value="gitlab" className="w-full p-2">
{gitlabProviders && gitlabProviders?.length > 0 ? (
<SaveGitlabProviderCompose composeId={composeId} />
) : (
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<GitlabIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using GitLab, 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>
)}
</TabsContent>
<TabsContent value="bitbucket" className="w-full p-2">
{bitbucketProviders && bitbucketProviders?.length > 0 ? (
<SaveBitbucketProviderCompose composeId={composeId} />
) : (
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
<BitbucketIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To deploy using Bitbucket, you need to configure your account
first. Please, go to{" "}
<Link
href="/dashboard/settings/git-providers"
className="text-foreground"
>
Settings

View File

@@ -0,0 +1,229 @@
import {
BitbucketIcon,
GithubIcon,
GitlabIcon,
} from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { zodResolver } from "@hookform/resolvers/zod";
import { ExternalLink } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
username: z.string().min(1, {
message: "Username is required",
}),
password: z.string().min(1, {
message: "App Password is required",
}),
workspaceName: z.string().optional(),
});
type Schema = z.infer<typeof Schema>;
export const AddBitbucketProvider = () => {
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const url = useUrl();
const { mutateAsync, error, isError } = api.bitbucket.create.useMutation();
const { data: auth } = api.auth.get.useQuery();
const router = useRouter();
const form = useForm<Schema>({
defaultValues: {
username: "",
password: "",
workspaceName: "",
},
resolver: zodResolver(Schema),
});
useEffect(() => {
form.reset({
username: "",
password: "",
workspaceName: "",
});
}, [form, isOpen]);
const onSubmit = async (data: Schema) => {
await mutateAsync({
bitbucketUsername: data.username,
appPassword: data.password,
bitbucketWorkspaceName: data.workspaceName || "",
authId: auth?.id || "",
name: data.name || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("Bitbucket configured successfully");
setIsOpen(false);
})
.catch(() => {
toast.error("Error configuring Bitbucket");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button
variant="secondary"
className="flex items-center space-x-1 bg-blue-700 text-white hover:bg-blue-600"
>
<BitbucketIcon />
<span>Bitbucket</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
Bitbucket Provider <BitbucketIcon className="size-5" />
</DialogTitle>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-bitbucket"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-1"
>
<CardContent className="p-0">
<div className="flex flex-col gap-4">
<p className="text-muted-foreground text-sm">
To integrate your Bitbucket account, you need to create a new
App Password in your Bitbucket settings. Follow these steps:
</p>
<ol className="list-decimal list-inside text-sm text-muted-foreground">
<li className="flex flex-row gap-2 items-center">
Create new App Password{" "}
<Link
href="https://bitbucket.org/account/settings/app-passwords/new"
target="_blank"
>
<ExternalLink className="w-fit text-primary size-4" />
</Link>
</li>
<li>
When creating the App Password, ensure you grant the
following permissions:
<ul className="list-disc list-inside ml-4">
<li>Account: Read</li>
<li>Workspace membership: Read</li>
<li>Projects: Read</li>
<li>Repositories: Read</li>
<li>Pull requests: Read</li>
<li>Webhooks: Read and write</li>
</ul>
</li>
<li>
After creating, you'll receive an App Password. Copy it and
paste it below along with your Bitbucket username.
</li>
</ol>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Bitbucket Username</FormLabel>
<FormControl>
<Input
placeholder="Your Bitbucket username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>App Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder="ATBBPDYUC94nR96Nj7Cqpp4pfwKk03573DD2"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="workspaceName"
render={({ field }) => (
<FormItem>
<FormLabel>Workspace Name (Optional)</FormLabel>
<FormControl>
<Input
placeholder="For organization accounts"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button isLoading={form.formState.isSubmitting}>
Configure Bitbucket
</Button>
</div>
</CardContent>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,201 @@
import { BitbucketIcon } from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { Edit } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
username: z.string().min(1, {
message: "Username is required",
}),
workspaceName: z.string().optional(),
});
type Schema = z.infer<typeof Schema>;
interface Props {
bitbucketId: string;
}
export const EditBitbucketProvider = ({ bitbucketId }: Props) => {
const { data: bitbucket } = api.bitbucket.one.useQuery(
{
bitbucketId,
},
{
enabled: !!bitbucketId,
},
);
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError } = api.bitbucket.update.useMutation();
const { mutateAsync: testConnection, isLoading } =
api.bitbucket.testConnection.useMutation();
const form = useForm<Schema>({
defaultValues: {
username: "",
workspaceName: "",
},
resolver: zodResolver(Schema),
});
const username = form.watch("username");
const workspaceName = form.watch("workspaceName");
useEffect(() => {
form.reset({
username: bitbucket?.bitbucketUsername || "",
workspaceName: bitbucket?.bitbucketWorkspaceName || "",
name: bitbucket?.gitProvider.name || "",
});
}, [form, isOpen]);
const onSubmit = async (data: Schema) => {
await mutateAsync({
bitbucketId,
gitProviderId: bitbucket?.gitProviderId || "",
bitbucketUsername: data.username,
bitbucketWorkspaceName: data.workspaceName || "",
name: data.name || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("Bitbucket updated successfully");
setIsOpen(false);
})
.catch(() => {
toast.error("Error to update Bitbucket");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Edit className="size-4" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
Update Bitbucket Provider <BitbucketIcon className="size-5" />
</DialogTitle>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-bitbucket"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-1"
>
<CardContent className="p-0">
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Bitbucket Username</FormLabel>
<FormControl>
<Input
placeholder="Your Bitbucket username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="workspaceName"
render={({ field }) => (
<FormItem>
<FormLabel>Workspace Name (Optional)</FormLabel>
<FormControl>
<Input
placeholder="For organization accounts"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex w-full justify-end gap-4 mt-4">
<Button
type="button"
variant={"secondary"}
isLoading={isLoading}
onClick={async () => {
await testConnection({
bitbucketId,
bitbucketUsername: username,
workspaceName: workspaceName,
})
.then(async (message) => {
toast.info(`Message: ${message}`);
})
.catch((error) => {
toast.error(`Error: ${error.message}`);
});
}}
>
Test Connection
</Button>
<Button type="submit" isLoading={form.formState.isSubmitting}>
Update
</Button>
</div>
</div>
</CardContent>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,128 @@
import { GithubIcon } from "@/components/icons/data-tools-icons";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { format } from "date-fns";
import { useEffect, useState } from "react";
export const AddGithubProvider = () => {
const [isOpen, setIsOpen] = useState(false);
const url = useUrl();
const { data } = api.auth.get.useQuery();
const [manifest, setManifest] = useState("");
const [isOrganization, setIsOrganization] = useState(false);
const [organizationName, setOrganization] = useState("");
useEffect(() => {
const url = document.location.origin;
const manifest = JSON.stringify(
{
redirect_url: `${origin}/api/providers/github/setup?authId=${data?.id}`,
name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`,
url: origin,
hook_attributes: {
url: `${url}/api/deploy/github`,
},
callback_urls: [`${origin}/api/providers/github/setup`],
public: false,
request_oauth_on_install: true,
default_permissions: {
contents: "read",
metadata: "read",
emails: "read",
pull_requests: "write",
},
default_events: ["pull_request", "push"],
},
null,
4,
);
setManifest(manifest);
}, [data?.id]);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="secondary" className="flex items-center space-x-1">
<GithubIcon />
<span>Github</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl ">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
Github Provider <GithubIcon className="size-5" />
</DialogTitle>
</DialogHeader>
<div id="hook-form-add-project" className="grid w-full gap-1">
<CardContent className="p-0">
<div className="flex flex-col ">
<p className="text-muted-foreground text-sm">
To integrate your GitHub account with our services, you'll need
to create and install a GitHub app. This process is
straightforward and only takes a few minutes. Click the button
below to get started.
</p>
<div className="mt-4 flex flex-col gap-4">
<div className="flex flex-row gap-4">
<span>Organization?</span>
<Switch
checked={isOrganization}
onCheckedChange={(checked) => setIsOrganization(checked)}
/>
</div>
{isOrganization && (
<Input
required
placeholder="Organization name"
onChange={(e) => setOrganization(e.target.value)}
/>
)}
</div>
<form
action={
isOrganization
? `https://github.com/organizations/${organizationName}/settings/apps/new?state=gh_init:${data?.id}`
: `https://github.com/settings/apps/new?state=gh_init:${data?.id}`
}
method="post"
>
<input
type="text"
name="manifest"
id="manifest"
defaultValue={manifest}
className="invisible"
/>
<br />
<div className="flex w-full justify-end">
<Button
disabled={isOrganization && organizationName.length < 1}
type="submit"
className="self-end"
>
Create GitHub App
</Button>
</div>
</form>
</div>
</CardContent>
</div>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,154 @@
import { GithubIcon } from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { Edit } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
});
type Schema = z.infer<typeof Schema>;
interface Props {
githubId: string;
}
export const EditGithubProvider = ({ githubId }: Props) => {
const { data: github } = api.github.one.useQuery(
{
githubId,
},
{
enabled: !!githubId,
},
);
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError } = api.github.update.useMutation();
const { mutateAsync: testConnection, isLoading } =
api.github.testConnection.useMutation();
const form = useForm<Schema>({
defaultValues: {
name: "",
},
resolver: zodResolver(Schema),
});
useEffect(() => {
form.reset({
name: github?.gitProvider.name || "",
});
}, [form, isOpen]);
const onSubmit = async (data: Schema) => {
await mutateAsync({
githubId,
name: data.name || "",
gitProviderId: github?.gitProviderId || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("Github updated successfully");
setIsOpen(false);
})
.catch(() => {
toast.error("Error to update Github");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Edit className="size-4" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
Update Github Provider <GithubIcon className="size-5" />
</DialogTitle>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-github"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-1"
>
<CardContent className="p-0">
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex w-full justify-end gap-4 mt-4">
<Button
type="button"
variant={"secondary"}
isLoading={isLoading}
onClick={async () => {
await testConnection({
githubId,
})
.then(async (message) => {
toast.info(`Message: ${message}`);
})
.catch((error) => {
toast.error(`Error: ${error.message}`);
});
}}
>
Test Connection
</Button>
<Button type="submit" isLoading={form.formState.isSubmitting}>
Update
</Button>
</div>
</div>
</CardContent>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,249 @@
import { GitlabIcon } from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { zodResolver } from "@hookform/resolvers/zod";
import { ExternalLink } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
applicationId: z.string().min(1, {
message: "Application ID is required",
}),
applicationSecret: z.string().min(1, {
message: "Application Secret is required",
}),
redirectUri: z.string().min(1, {
message: "Redirect URI is required",
}),
groupName: z.string().optional(),
});
type Schema = z.infer<typeof Schema>;
export const AddGitlabProvider = () => {
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const url = useUrl();
const { data: auth } = api.auth.get.useQuery();
const { mutateAsync, error, isError } = api.gitlab.create.useMutation();
const webhookUrl = `${url}/api/providers/gitlab/callback`;
const form = useForm<Schema>({
defaultValues: {
applicationId: "",
applicationSecret: "",
groupName: "",
redirectUri: webhookUrl,
},
resolver: zodResolver(Schema),
});
useEffect(() => {
form.reset({
applicationId: "",
applicationSecret: "",
groupName: "",
redirectUri: webhookUrl,
});
}, [form, isOpen]);
const onSubmit = async (data: Schema) => {
await mutateAsync({
applicationId: data.applicationId || "",
secret: data.applicationSecret || "",
groupName: data.groupName || "",
authId: auth?.id || "",
name: data.name || "",
redirectUri: data.redirectUri || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("GitLab created successfully");
setIsOpen(false);
})
.catch(() => {
toast.error("Error configuring GitLab");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button
variant="default"
className="flex items-center space-x-1 bg-purple-700 text-white hover:bg-purple-600"
>
<GitlabIcon />
<span>GitLab</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen ">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
GitLab Provider <GitlabIcon className="size-5" />
</DialogTitle>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-gitlab"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-1"
>
<CardContent className="p-0">
<div className="flex flex-col gap-4">
<p className="text-muted-foreground text-sm">
To integrate your GitLab account, you need to create a new
application in your GitLab settings. Follow these steps:
</p>
<ol className="list-decimal list-inside text-sm text-muted-foreground">
<li className="flex flex-row gap-2 items-center">
Go to your GitLab profile settings{" "}
<Link
href="https://gitlab.com/-/profile/applications"
target="_blank"
>
<ExternalLink className="w-fit text-primary size-4" />
</Link>
</li>
<li>Navigate to Applications</li>
<li>
Create a new application with the following details:
<ul className="list-disc list-inside ml-4">
<li>Name: Dokploy</li>
<li>
Redirect URI:{" "}
<span className="text-primary">{webhookUrl}</span>{" "}
</li>
<li>Scopes: api, read_user, read_repository</li>
</ul>
</li>
<li>
After creating, you'll receive an Application ID and Secret,
copy them and paste them below.
</li>
</ol>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="redirectUri"
render={({ field }) => (
<FormItem>
<FormLabel>Redirect URI</FormLabel>
<FormControl>
<Input
disabled
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="applicationId"
render={({ field }) => (
<FormItem>
<FormLabel>Application ID</FormLabel>
<FormControl>
<Input placeholder="Application ID" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="applicationSecret"
render={({ field }) => (
<FormItem>
<FormLabel>Application Secret</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Application Secret"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="groupName"
render={({ field }) => (
<FormItem>
<FormLabel>Group Name (Optional)</FormLabel>
<FormControl>
<Input
placeholder="For organization/group access"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button isLoading={form.formState.isSubmitting}>
Configure GitLab App
</Button>
</div>
</CardContent>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,180 @@
import { GitlabIcon } from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { zodResolver } from "@hookform/resolvers/zod";
import { Edit } from "lucide-react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
groupName: z.string().optional(),
});
type Schema = z.infer<typeof Schema>;
interface Props {
gitlabId: string;
}
export const EditGitlabProvider = ({ gitlabId }: Props) => {
const { data: gitlab } = api.gitlab.one.useQuery(
{
gitlabId,
},
{
enabled: !!gitlabId,
},
);
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError } = api.gitlab.update.useMutation();
const { mutateAsync: testConnection, isLoading } =
api.gitlab.testConnection.useMutation();
const form = useForm<Schema>({
defaultValues: {
groupName: "",
name: "",
},
resolver: zodResolver(Schema),
});
const groupName = form.watch("groupName");
useEffect(() => {
form.reset({
groupName: gitlab?.groupName || "",
name: gitlab?.gitProvider.name || "",
});
}, [form, isOpen]);
const onSubmit = async (data: Schema) => {
await mutateAsync({
gitlabId,
gitProviderId: gitlab?.gitProviderId || "",
groupName: data.groupName || "",
name: data.name || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("Gitlab updated successfully");
setIsOpen(false);
})
.catch(() => {
toast.error("Error to update Gitlab");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<Edit className="size-4" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl overflow-y-auto max-h-screen">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
Update GitLab Provider <GitlabIcon className="size-5" />
</DialogTitle>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-bitbucket"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-1"
>
<CardContent className="p-0">
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Random Name eg(my-personal-account)"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="groupName"
render={({ field }) => (
<FormItem>
<FormLabel>Group Name (Optional)</FormLabel>
<FormControl>
<Input
placeholder="For organization/group access"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex w-full justify-end gap-4 mt-4">
<Button
type="button"
variant={"secondary"}
isLoading={isLoading}
onClick={async () => {
await testConnection({
gitlabId,
groupName: groupName || "",
})
.then(async (message) => {
toast.info(`Message: ${message}`);
})
.catch((error) => {
toast.error(`Error: ${error.message}`);
});
}}
>
Test Connection
</Button>
<Button type="submit" isLoading={form.formState.isSubmitting}>
Update
</Button>
</div>
</div>
</CardContent>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -17,31 +17,40 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { api } from "@/utils/api";
import { InfoIcon } from "lucide-react";
import { InfoIcon, TrashIcon } from "lucide-react";
import React from "react";
import { toast } from "sonner";
export const RemoveGithubApp = () => {
const { refetch } = api.auth.get.useQuery();
interface Props {
gitProviderId: string;
gitProviderType: "github" | "gitlab" | "bitbucket";
}
export const RemoveGitProvider = ({
gitProviderId,
gitProviderType,
}: Props) => {
const utils = api.useUtils();
const { mutateAsync } = api.admin.cleanGithubApp.useMutation();
const { mutateAsync } = api.gitProvider.remove.useMutation();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">
Remove Current Github App
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="size-4 fill-muted-destructive text-muted-destructive" />
</TooltipTrigger>
<TooltipContent>
We recommend deleting the GitHub app first, and then removing
the current one from here.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button variant="ghost">
<TrashIcon className="size-4 text-muted-destructive" />
{gitProviderType === "github" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="size-4 fill-muted-destructive text-muted-destructive" />
</TooltipTrigger>
<TooltipContent>
We recommend deleting the GitHub app first, and then removing
the current one from here.
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
@@ -56,15 +65,15 @@ export const RemoveGithubApp = () => {
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync()
await mutateAsync({
gitProviderId: gitProviderId,
})
.then(async () => {
await refetch();
utils.admin.one.invalidate();
await utils.admin.haveGithubConfigured.invalidate();
toast.success("Github application deleted succesfully.");
utils.gitProvider.getAll.invalidate();
toast.success("Git Provider deleted succesfully.");
})
.catch(() => {
toast.error("Error to delete your github application.");
toast.error("Error to delete your git provider.");
});
}}
>

View File

@@ -0,0 +1,185 @@
import {
BitbucketIcon,
GitIcon,
GithubIcon,
GitlabIcon,
} from "@/components/icons/data-tools-icons";
import { buttonVariants } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { formatDate } from "date-fns";
import Link from "next/link";
import { AddBitbucketProvider } from "./bitbucket/add-bitbucket-provider";
import { EditBitbucketProvider } from "./bitbucket/edit-bitbucket-provider";
import { AddGithubProvider } from "./github/add-github-provider";
import { EditGithubProvider } from "./github/edit-github-provider";
import { AddGitlabProvider } from "./gitlab/add-gitlab-provider";
import { EditGitlabProvider } from "./gitlab/edit-gitlab-provider";
import { RemoveGitProvider } from "./remove-git-provider";
export const ShowGitProviders = () => {
const { data } = api.gitProvider.getAll.useQuery();
const url = useUrl();
const getGitlabUrl = (clientId: string, gitlabId: string) => {
const redirectUri = `${url}/api/providers/gitlab/callback?gitlabId=${gitlabId}`;
const scope = "api read_user read_repository";
const authUrl = `https://gitlab.com/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}`;
return authUrl;
};
return (
<div className="p-6 space-y-6">
<div className="space-y-2">
<h1 className="text-2xl font-bold">Git Providers</h1>
<p className="text-muted-foreground">
Connect your Git provider for authentication.
</p>
</div>
<Card className=" bg-transparent">
<CardContent className="p-4">
<div className="flex gap-4 sm:flex-row flex-col w-full">
<AddGithubProvider />
<AddGitlabProvider />
<AddBitbucketProvider />
</div>
</CardContent>
</Card>
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-1">
{data && data.length === 0 && (
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
<GitIcon className="size-8" />
<span className="text-base text-muted-foreground">
No Git Providers found. To add a provider, create a new one such
as GitHub, GitLab, or Bitbucket.
</span>
</div>
)}
{data?.map((gitProvider, index) => {
const isGithub = gitProvider.providerType === "github";
const isGitlab = gitProvider.providerType === "gitlab";
const isBitbucket = gitProvider.providerType === "bitbucket";
const haveGithubRequirements =
gitProvider.providerType === "github" &&
gitProvider.github?.githubPrivateKey &&
gitProvider.github?.githubAppId &&
gitProvider.github?.githubInstallationId;
const haveGitlabRequirements =
gitProvider.gitlab?.accessToken && gitProvider.gitlab?.refreshToken;
return (
<div
className="space-y-4"
key={`${gitProvider.gitProviderId}-${index}`}
>
<Card className="flex sm:flex-row max-sm:gap-2 flex-col justify-between items-center p-4 h-full bg-transparent">
<div className="flex items-center space-x-4 w-full">
{gitProvider.providerType === "github" && (
<GithubIcon className="w-6 h-6" />
)}
{gitProvider.providerType === "gitlab" && (
<GitlabIcon className="w-6 h-6" />
)}
{gitProvider.providerType === "bitbucket" && (
<BitbucketIcon className="w-6 h-6" />
)}
<div className="flex flex-col gap-1">
<p className="font-medium">
{gitProvider.providerType === "github"
? "GitHub"
: gitProvider.providerType === "gitlab"
? "GitLab"
: "Bitbucket"}
</p>
<p className="text-sm text-muted-foreground">
{gitProvider.name}
</p>
<span>
<p className="text-sm text-muted-foreground">
Created{" "}
{formatDate(
gitProvider.createdAt,
"yyyy-MM-dd hh:mm:ss a",
)}
</p>
</span>
</div>
</div>
<div className="flex sm:gap-4 sm:flex-row flex-col">
{!haveGithubRequirements && isGithub && (
<div className="flex flex-col gap-1">
<Link
href={`${gitProvider?.github?.githubAppName}/installations/new?state=gh_setup:${gitProvider?.github.githubId}`}
className={buttonVariants({ className: "w-fit" })}
>
Install
</Link>
</div>
)}
{haveGithubRequirements && isGithub && (
<div className="flex flex-col gap-1">
<Link
href={`${gitProvider?.github?.githubAppName}`}
target="_blank"
className={buttonVariants({
className: "w-fit",
variant: "secondary",
})}
>
<span className="text-sm">Manage</span>
</Link>
</div>
)}
{!haveGitlabRequirements && isGitlab && (
<div className="flex flex-col gap-1">
<Link
href={getGitlabUrl(
gitProvider.gitlab?.applicationId || "",
gitProvider.gitlab?.gitlabId || "",
)}
target="_blank"
className={buttonVariants({
className: "w-fit",
variant: "secondary",
})}
>
<span className="text-sm">Install</span>
</Link>
</div>
)}
<div className="flex flex-row gap-1">
{isBitbucket && (
<EditBitbucketProvider
bitbucketId={gitProvider.bitbucket.bitbucketId}
/>
)}
{isGitlab && haveGitlabRequirements && (
<EditGitlabProvider
gitlabId={gitProvider.gitlab.gitlabId}
/>
)}
{isGithub && haveGithubRequirements && (
<EditGithubProvider
githubId={gitProvider.github.githubId}
/>
)}
<RemoveGitProvider
gitProviderId={gitProvider.gitProviderId}
gitProviderType={gitProvider.providerType}
/>
</div>
</div>
</Card>
</div>
);
})}
</div>
</div>
);
};

View File

@@ -1,166 +0,0 @@
import { Button, buttonVariants } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { format } from "date-fns";
import { BadgeCheck } from "lucide-react";
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { RemoveGithubApp } from "./remove-github-app";
export const GithubSetup = () => {
const [isOrganization, setIsOrganization] = useState(false);
const { data: haveGithubConfigured } =
api.admin.haveGithubConfigured.useQuery();
const [manifest, setManifest] = useState<string>("");
const [organizationName, setOrganization] = useState<string>("");
const { data } = api.admin.one.useQuery();
useEffect(() => {
const url = document.location.origin;
const manifest = JSON.stringify(
{
redirect_url: `${origin}/api/redirect?authId=${data?.authId}`,
name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`,
url: origin,
hook_attributes: {
url: `${url}/api/deploy/github`,
// url: `${origin}/api/webhook`, // Aquí especificas la URL del endpoint de tu webhook
},
callback_urls: [`${origin}/api/redirect`], // Los URLs de callback para procesos de autenticación
public: false,
request_oauth_on_install: true,
default_permissions: {
contents: "read",
metadata: "read",
emails: "read",
pull_requests: "write",
},
default_events: ["pull_request", "push"],
},
null,
4,
);
setManifest(manifest);
}, [data?.authId]);
return (
<Card className="bg-transparent">
<CardHeader>
<CardTitle className="text-xl">Configure Github </CardTitle>
<CardDescription>
Setup your github account to access to your repositories.
</CardDescription>
</CardHeader>
<CardContent className="h-full space-y-2">
{haveGithubConfigured ? (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-4">
<span className="text-muted-foreground text-sm">
Github account configured succesfully.
</span>
<BadgeCheck className="size-4 text-green-700" />
</div>
<div className="flex items-end gap-4 flex-wrap">
<RemoveGithubApp />
<Link
href={`${data?.githubAppName}`}
target="_blank"
className={buttonVariants({
className: "w-fit",
variant: "secondary",
})}
>
<span className="text-sm">Manage Github App</span>
</Link>
</div>
</div>
) : (
<>
{data?.githubAppName ? (
<div className="flex w-fit flex-col gap-4">
<span className="text-muted-foreground">
You've successfully created a github app named{" "}
<strong>{data.githubAppName}</strong>! The next step is to
install this app in your GitHub account.
</span>
<div className="flex flex-row gap-4">
<Link
href={`${
data.githubAppName
}/installations/new?state=gh_setup:${data?.authId}`}
className={buttonVariants({ className: "w-fit" })}
>
Install Github App
</Link>
<RemoveGithubApp />
</div>
</div>
) : (
<div>
<div className="flex items-center gap-2">
<p className="text-muted-foreground text-sm">
To integrate your GitHub account with our services, you'll
need to create and install a GitHub app. This process is
straightforward and only takes a few minutes. Click the
button below to get started.
</p>
</div>
<div className="mt-4 flex flex-col gap-4">
<div className="flex flex-row gap-4">
<span>Organization?</span>
<Switch
checked={isOrganization}
onCheckedChange={(checked) => setIsOrganization(checked)}
/>
</div>
{isOrganization && (
<Input
required
placeholder="Organization name"
onChange={(e) => setOrganization(e.target.value)}
/>
)}
</div>
<form
action={
isOrganization
? `https://github.com/organizations/${organizationName}/settings/apps/new?state=gh_init:${data?.authId}`
: `https://github.com/settings/apps/new?state=gh_init:${data?.authId}`
}
method="post"
>
<input
type="text"
name="manifest"
id="manifest"
defaultValue={manifest}
className="invisible"
/>
<br />
<Button
disabled={isOrganization && organizationName.length < 1}
type="submit"
>
Create GitHub App
</Button>
</form>
</div>
)}
</>
)}
</CardContent>
</Card>
);
};

View File

@@ -1,40 +0,0 @@
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import React from "react";
import { AppearanceForm } from "./appearance-form";
import { ShowCertificates } from "./certificates/show-certificates";
import { ShowDestinations } from "./destination/show-destinations";
import { GithubSetup } from "./github/github-setup";
import { ProfileForm } from "./profile/profile-form";
import { ShowUsers } from "./users/show-users";
import { WebDomain } from "./web-domain";
import { WebServer } from "./web-server";
export const ShowSettings = () => {
const { data } = api.auth.get.useQuery();
return (
<div
className={cn(
"mt-6 md:grid flex flex-col gap-4 pb-20 md:grid-cols-2",
data?.rol === "user" && "col-span-2",
)}
>
<div className={cn(data?.rol === "user" && "col-span-2")}>
<ProfileForm />
</div>
{data?.rol === "admin" && (
<>
<GithubSetup />
<AppearanceForm />
<ShowDestinations />
<ShowCertificates />
<WebDomain />
<WebServer />
<ShowUsers />
</>
)}
</div>
);
};

View File

@@ -40,6 +40,7 @@ const addPermissions = z.object({
canAccessToDocker: z.boolean().optional().default(false),
canAccessToAPI: z.boolean().optional().default(false),
canAccessToSSHKeys: z.boolean().optional().default(false),
canAccessToGitProviders: z.boolean().optional().default(false),
});
type AddPermissions = z.infer<typeof addPermissions>;
@@ -84,6 +85,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
canAccessToGitProviders: data.canAccessToGitProviders,
});
}
}, [form, form.formState.isSubmitSuccessful, form.reset, data]);
@@ -101,6 +103,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
canAccessToGitProviders: data.canAccessToGitProviders,
})
.then(async () => {
toast.success("Permissions updated");
@@ -293,6 +296,26 @@ export const AddUserPermissions = ({ userId }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="canAccessToGitProviders"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Access to Git Providers</FormLabel>
<FormDescription>
Allow to users to access to the Git Providers section
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="accesedProjects"

View File

@@ -60,7 +60,9 @@ export const EditTraefikEnv = ({ children }: Props) => {
}, [form, form.reset, data]);
const onSubmit = async (data: Schema) => {
await mutateAsync(data.env)
await mutateAsync({
env: data.env,
})
.then(async () => {
toast.success("Traefik Env Updated");
})

View File

@@ -1,3 +1,4 @@
import { cn } from "@/lib/utils";
import React from "react";
// https://worldvectorlogo.com/downloaded/redis Ref
@@ -155,3 +156,118 @@ export const RedisIcon = ({ className }: Props) => {
</svg>
);
};
export const GitlabIcon = ({ className }: Props) => {
return (
<svg
aria-label="gitlab"
height="14"
viewBox="0 0 24 22"
width="14"
className={cn("fill-white text-white", className)}
>
<path
d="M1.279 8.29L.044 12.294c-.117.367 0 .78.325 1.014l11.323 8.23-.009-.012-.03-.039L1.279 8.29zM22.992 13.308a.905.905 0 00.325-1.014L22.085 8.29 11.693 21.52l11.299-8.212z"
fill="currentColor"
/>
<path
d="M1.279 8.29l10.374 13.197.03.039.01-.006L22.085 8.29H1.28z"
fill="currentColor"
opacity="0.4"
/>
<path
d="M15.982 8.29l-4.299 13.236-.004.011.014-.017L22.085 8.29h-6.103zM7.376 8.29H1.279l10.374 13.197L7.376 8.29z"
fill="currentColor"
opacity="0.6"
/>
<path
d="M18.582.308l-2.6 7.982h6.103L19.48.308c-.133-.41-.764-.41-.897 0zM1.279 8.29L3.88.308c.133-.41.764-.41.897 0l2.6 7.982H1.279z"
fill="currentColor"
opacity="0.4"
/>
</svg>
);
};
export const GithubIcon = ({ className }: Props) => {
return (
<svg
aria-label="github"
height="18"
viewBox="0 0 14 14"
width="18"
className={className}
>
<path
d="M7 .175c-3.872 0-7 3.128-7 7 0 3.084 2.013 5.71 4.79 6.65.35.066.482-.153.482-.328v-1.181c-1.947.415-2.363-.941-2.363-.941-.328-.81-.787-1.028-.787-1.028-.634-.438.044-.416.044-.416.7.044 1.071.722 1.071.722.635 1.072 1.641.766 2.035.59.066-.459.24-.765.437-.94-1.553-.175-3.193-.787-3.193-3.456 0-.766.262-1.378.721-1.881-.065-.175-.306-.897.066-1.86 0 0 .59-.197 1.925.722a6.754 6.754 0 0 1 1.75-.24c.59 0 1.203.087 1.75.24 1.335-.897 1.925-.722 1.925-.722.372.963.131 1.685.066 1.86.46.48.722 1.115.722 1.88 0 2.691-1.641 3.282-3.194 3.457.24.219.481.634.481 1.29v1.926c0 .197.131.415.481.328C11.988 12.884 14 10.259 14 7.175c0-3.872-3.128-7-7-7z"
fill="#fff"
fillRule="nonzero"
/>
</svg>
);
};
export const BitbucketIcon = ({ className }: Props) => {
return (
<svg height="14" viewBox="-2 -2 65 59" width="14" className={className}>
<defs>
<linearGradient
id="bitbucket-:R7aq37rqjt7rrrmpjtuj7l9qjtsr:"
x1="104.953%"
x2="46.569%"
y1="21.921%"
y2="75.234%"
>
<stop offset="7%" stopColor="currentColor" stopOpacity=".4" />
<stop offset="100%" stopColor="currentColor" />
</linearGradient>
</defs>
<path
d="M59.696 18.86h-18.77l-3.15 18.39h-13L9.426 55.47a2.71 2.71 0 001.75.66h40.74a2 2 0 002-1.68l5.78-35.59z"
fill="url(#bitbucket-:R7aq37rqjt7rrrmpjtuj7l9qjtsr:)"
fillRule="nonzero"
transform="translate(-.026 .82)"
/>
<path
d="M2 .82a2 2 0 00-2 2.32l8.49 51.54a2.7 2.7 0 00.91 1.61 2.71 2.71 0 001.75.66l15.76-18.88H24.7l-3.47-18.39h38.44l2.7-16.53a2 2 0 00-2-2.32L2 .82z"
fill="currentColor"
fillRule="nonzero"
/>
</svg>
);
};
export const DockerIcon = ({ className }: Props) => {
return (
<svg
height="24"
viewBox="-.557 117.607 598.543 423.631"
width="24"
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<g fill="#0091e2">
<path d="m592.162 277.804c-1.664-1.37-16.642-12.597-48.815-12.597-8.321 0-16.92.822-25.24 2.191-6.102-41.898-41.327-62.162-42.714-63.257l-8.598-4.93-5.547 7.942c-6.934 10.68-12.204 22.729-15.255 35.052-5.824 23.824-2.219 46.279 9.985 65.447-14.7 8.216-38.553 10.133-43.545 10.406h-393.853c-10.262 0-18.583 8.216-18.583 18.348-.554 33.956 5.27 67.912 17.197 99.951 13.59 35.052 33.838 61.067 59.91 76.95 29.4 17.799 77.383 27.931 131.468 27.931 24.408 0 48.815-2.19 72.946-6.572 33.56-6.025 65.734-17.526 95.412-34.23a260.485 260.485 0 0 0 64.902-52.577c31.342-34.778 49.925-73.663 63.515-108.167h5.547c34.116 0 55.195-13.418 66.844-24.92 7.766-7.12 13.59-15.882 17.751-25.74l2.497-7.12z" />
<path d="m55.193 306.83h52.698c2.497 0 4.716-1.916 4.716-4.654v-46.553c0-2.465-1.942-4.655-4.716-4.655h-52.698c-2.496 0-4.715 1.916-4.715 4.655v46.553c.277 2.738 2.219 4.655 4.715 4.655zm72.668 0h52.699c2.496 0 4.715-1.916 4.715-4.654v-46.553c0-2.465-1.942-4.655-4.715-4.655h-52.7c-2.496 0-4.715 1.916-4.715 4.655v46.553c.278 2.738 2.22 4.655 4.715 4.655m74.055 0h52.699c2.496 0 4.715-1.917 4.715-4.655v-46.553c0-2.465-1.942-4.655-4.715-4.655h-52.699c-2.496 0-4.715 1.916-4.715 4.655v46.553c0 2.738 1.942 4.655 4.715 4.655zm72.946 0h52.699c2.496 0 4.715-1.917 4.715-4.655v-46.553c0-2.465-1.942-4.655-4.715-4.655h-52.699c-2.496 0-4.715 1.916-4.715 4.655v46.553c0 2.738 2.219 4.655 4.715 4.655zm-147-66.543h52.698c2.496 0 4.715-2.19 4.715-4.655v-46.553c0-2.465-1.942-4.656-4.715-4.656h-52.699c-2.496 0-4.715 1.917-4.715 4.656v46.553c.278 2.464 2.22 4.655 4.715 4.655m74.055 0h52.699c2.496 0 4.715-2.19 4.715-4.655v-46.553c0-2.465-1.942-4.656-4.715-4.656h-52.699c-2.496 0-4.715 1.917-4.715 4.656v46.553c0 2.464 1.942 4.655 4.715 4.655m72.946 0h52.699c2.496 0 4.715-2.19 4.715-4.655v-46.553c0-2.465-2.22-4.656-4.715-4.656h-52.699c-2.496 0-4.715 1.917-4.715 4.656v46.553c0 2.464 2.219 4.655 4.715 4.655m0-66.817h52.699c2.496 0 4.715-1.917 4.715-4.655v-46.553c0-2.465-2.22-4.656-4.715-4.656h-52.699c-2.496 0-4.715 1.917-4.715 4.656v46.553c0 2.464 2.219 4.655 4.715 4.655m73.5 133.36h52.699c2.496 0 4.715-1.917 4.715-4.655v-46.553c0-2.465-1.941-4.655-4.715-4.655h-52.698c-2.497 0-4.716 1.916-4.716 4.655v46.553c.278 2.738 2.22 4.655 4.716 4.655" />
</g>
</svg>
);
};
export const GitIcon = ({ className }: Props) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMinYMin meet"
className={className}
>
<path
d="M251.172 116.594L139.4 4.828c-6.433-6.437-16.873-6.437-23.314 0l-23.21 23.21 29.443 29.443c6.842-2.312 14.688-.761 20.142 4.693 5.48 5.489 7.02 13.402 4.652 20.266l28.375 28.376c6.865-2.365 14.786-.835 20.269 4.657 7.663 7.66 7.663 20.075 0 27.74-7.665 7.666-20.08 7.666-27.749 0-5.764-5.77-7.188-14.235-4.27-21.336l-26.462-26.462-.003 69.637a19.82 19.82 0 0 1 5.188 3.71c7.663 7.66 7.663 20.076 0 27.747-7.665 7.662-20.086 7.662-27.74 0-7.663-7.671-7.663-20.086 0-27.746a19.654 19.654 0 0 1 6.421-4.281V94.196a19.378 19.378 0 0 1-6.421-4.281c-5.806-5.798-7.202-14.317-4.227-21.446L81.47 39.442l-76.64 76.635c-6.44 6.443-6.44 16.884 0 23.322l111.774 111.768c6.435 6.438 16.873 6.438 23.316 0l111.251-111.249c6.438-6.44 6.438-16.887 0-23.324"
fill="#DE4C36"
/>
</svg>
);
};

View File

@@ -59,6 +59,12 @@ export const SettingsLayout = ({ children }: Props) => {
icon: KeyRound,
href: "/dashboard/settings/ssh-keys",
},
{
title: "Git ",
label: "",
icon: GitBranch,
href: "/dashboard/settings/git-providers",
},
{
title: "Users",
label: "",
@@ -89,6 +95,16 @@ export const SettingsLayout = ({ children }: Props) => {
},
]
: []),
...(user?.canAccessToGitProviders
? [
{
title: "Git",
label: "",
icon: GitBranch,
href: "/dashboard/settings/git-providers",
},
]
: []),
]}
/>
</div>
@@ -102,6 +118,7 @@ import {
Activity,
Bell,
Database,
GitBranch,
KeyRound,
type LucideIcon,
Route,

View File

@@ -0,0 +1,142 @@
DO $$ BEGIN
CREATE TYPE "public"."gitProviderType" AS ENUM('github', 'gitlab', 'bitbucket');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
ALTER TYPE "sourceType" ADD VALUE 'gitlab';--> statement-breakpoint
ALTER TYPE "sourceType" ADD VALUE 'bitbucket';--> statement-breakpoint
ALTER TYPE "sourceTypeCompose" ADD VALUE 'gitlab';--> statement-breakpoint
ALTER TYPE "sourceTypeCompose" ADD VALUE 'bitbucket';--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "git_provider" (
"gitProviderId" text PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"providerType" "gitProviderType" DEFAULT 'github' NOT NULL,
"createdAt" text NOT NULL,
"authId" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "bitbucket" (
"bitbucketId" text PRIMARY KEY NOT NULL,
"bitbucketUsername" text,
"appPassword" text,
"bitbucketWorkspaceName" text,
"gitProviderId" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "github" (
"githubId" text PRIMARY KEY NOT NULL,
"githubAppName" text,
"githubAppId" integer,
"githubClientId" text,
"githubClientSecret" text,
"githubInstallationId" text,
"githubPrivateKey" text,
"githubWebhookSecret" text,
"gitProviderId" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "gitlab" (
"gitlabId" text PRIMARY KEY NOT NULL,
"application_id" text,
"redirect_uri" text,
"secret" text,
"access_token" text,
"refresh_token" text,
"group_name" text,
"expires_at" integer,
"gitProviderId" text NOT NULL
);
--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabProjectId" integer;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabRepository" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabOwner" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabBranch" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabBuildPath" text DEFAULT '/';--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabPathNamespace" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "bitbucketRepository" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "bitbucketOwner" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "bitbucketBranch" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "bitbucketBuildPath" text DEFAULT '/';--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "githubId" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "gitlabId" text;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "bitbucketId" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "canAccessToGitProviders" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabProjectId" integer;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabRepository" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabOwner" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabBranch" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabPathNamespace" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "bitbucketRepository" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "bitbucketOwner" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "bitbucketBranch" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "githubId" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "gitlabId" text;--> statement-breakpoint
ALTER TABLE "compose" ADD COLUMN "bitbucketId" text;--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_authId_auth_id_fk" FOREIGN KEY ("authId") REFERENCES "public"."auth"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "bitbucket" ADD CONSTRAINT "bitbucket_gitProviderId_git_provider_gitProviderId_fk" FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "github" ADD CONSTRAINT "github_gitProviderId_git_provider_gitProviderId_fk" FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "gitlab" ADD CONSTRAINT "gitlab_gitProviderId_git_provider_gitProviderId_fk" FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "application" ADD CONSTRAINT "application_githubId_github_githubId_fk" FOREIGN KEY ("githubId") REFERENCES "public"."github"("githubId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "application" ADD CONSTRAINT "application_gitlabId_gitlab_gitlabId_fk" FOREIGN KEY ("gitlabId") REFERENCES "public"."gitlab"("gitlabId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "application" ADD CONSTRAINT "application_bitbucketId_bitbucket_bitbucketId_fk" FOREIGN KEY ("bitbucketId") REFERENCES "public"."bitbucket"("bitbucketId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "compose" ADD CONSTRAINT "compose_githubId_github_githubId_fk" FOREIGN KEY ("githubId") REFERENCES "public"."github"("githubId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "compose" ADD CONSTRAINT "compose_gitlabId_gitlab_gitlabId_fk" FOREIGN KEY ("gitlabId") REFERENCES "public"."gitlab"("gitlabId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "compose" ADD CONSTRAINT "compose_bitbucketId_bitbucket_bitbucketId_fk" FOREIGN KEY ("bitbucketId") REFERENCES "public"."bitbucket"("bitbucketId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubAppId";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubAppName";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubClientId";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubClientSecret";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubInstallationId";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubPrivateKey";--> statement-breakpoint
ALTER TABLE "admin" DROP COLUMN IF EXISTS "githubWebhookSecret";

View File

@@ -1,5 +1,5 @@
{
"id": "37bd7ad6-1522-4882-bd87-96486fc67acc",
"id": "4b757666-9e18-454e-9cfa-762d03bf378f",
"prevId": "ce8a8861-2970-4889-ac2e-3cfe60d12736",
"version": "6",
"dialect": "postgresql",
@@ -137,6 +137,68 @@
"primaryKey": false,
"notNull": false
},
"gitlabProjectId": {
"name": "gitlabProjectId",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"gitlabRepository": {
"name": "gitlabRepository",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabOwner": {
"name": "gitlabOwner",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabBranch": {
"name": "gitlabBranch",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabBuildPath": {
"name": "gitlabBuildPath",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'/'"
},
"gitlabPathNamespace": {
"name": "gitlabPathNamespace",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketRepository": {
"name": "bitbucketRepository",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketOwner": {
"name": "bitbucketOwner",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketBranch": {
"name": "bitbucketBranch",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketBuildPath": {
"name": "bitbucketBuildPath",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'/'"
},
"username": {
"name": "username",
"type": "text",
@@ -191,12 +253,6 @@
"primaryKey": false,
"notNull": false
},
"dockerBuildStage": {
"name": "dockerBuildStage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"dropBuildPath": {
"name": "dropBuildPath",
"type": "text",
@@ -297,6 +353,24 @@
"type": "text",
"primaryKey": false,
"notNull": true
},
"githubId": {
"name": "githubId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabId": {
"name": "gitlabId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketId": {
"name": "bitbucketId",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
@@ -339,6 +413,45 @@
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"application_githubId_github_githubId_fk": {
"name": "application_githubId_github_githubId_fk",
"tableFrom": "application",
"tableTo": "github",
"columnsFrom": [
"githubId"
],
"columnsTo": [
"githubId"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"application_gitlabId_gitlab_gitlabId_fk": {
"name": "application_gitlabId_gitlab_gitlabId_fk",
"tableFrom": "application",
"tableTo": "gitlab",
"columnsFrom": [
"gitlabId"
],
"columnsTo": [
"gitlabId"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"application_bitbucketId_bitbucket_bitbucketId_fk": {
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
"tableFrom": "application",
"tableTo": "bitbucket",
"columnsFrom": [
"bitbucketId"
],
"columnsTo": [
"bitbucketId"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
@@ -578,6 +691,13 @@
"notNull": true,
"default": false
},
"canAccessToGitProviders": {
"name": "canAccessToGitProviders",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"canAccessToTraefikFiles": {
"name": "canAccessToTraefikFiles",
"type": "boolean",
@@ -654,18 +774,6 @@
"primaryKey": true,
"notNull": true
},
"githubAppId": {
"name": "githubAppId",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"githubAppName": {
"name": "githubAppName",
"type": "text",
"primaryKey": false,
"notNull": false
},
"serverIp": {
"name": "serverIp",
"type": "text",
@@ -686,36 +794,6 @@
"primaryKey": false,
"notNull": false
},
"githubClientId": {
"name": "githubClientId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubClientSecret": {
"name": "githubClientSecret",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubInstallationId": {
"name": "githubInstallationId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubPrivateKey": {
"name": "githubPrivateKey",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubWebhookSecret": {
"name": "githubWebhookSecret",
"type": "text",
"primaryKey": false,
"notNull": false
},
"letsEncryptEmail": {
"name": "letsEncryptEmail",
"type": "text",
@@ -2438,6 +2516,54 @@
"primaryKey": false,
"notNull": false
},
"gitlabProjectId": {
"name": "gitlabProjectId",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"gitlabRepository": {
"name": "gitlabRepository",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabOwner": {
"name": "gitlabOwner",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabBranch": {
"name": "gitlabBranch",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabPathNamespace": {
"name": "gitlabPathNamespace",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketRepository": {
"name": "bitbucketRepository",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketOwner": {
"name": "bitbucketOwner",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketBranch": {
"name": "bitbucketBranch",
"type": "text",
"primaryKey": false,
"notNull": false
},
"customGitUrl": {
"name": "customGitUrl",
"type": "text",
@@ -2489,6 +2615,24 @@
"type": "text",
"primaryKey": false,
"notNull": true
},
"githubId": {
"name": "githubId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitlabId": {
"name": "gitlabId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketId": {
"name": "bitbucketId",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
@@ -2518,6 +2662,45 @@
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"compose_githubId_github_githubId_fk": {
"name": "compose_githubId_github_githubId_fk",
"tableFrom": "compose",
"tableTo": "github",
"columnsFrom": [
"githubId"
],
"columnsTo": [
"githubId"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"compose_gitlabId_gitlab_gitlabId_fk": {
"name": "compose_gitlabId_gitlab_gitlabId_fk",
"tableFrom": "compose",
"tableTo": "gitlab",
"columnsFrom": [
"gitlabId"
],
"columnsTo": [
"gitlabId"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"compose_bitbucketId_bitbucket_bitbucketId_fk": {
"name": "compose_bitbucketId_bitbucket_bitbucketId_fk",
"tableFrom": "compose",
"tableTo": "bitbucket",
"columnsFrom": [
"bitbucketId"
],
"columnsTo": [
"bitbucketId"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
@@ -2925,6 +3108,272 @@
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.git_provider": {
"name": "git_provider",
"schema": "",
"columns": {
"gitProviderId": {
"name": "gitProviderId",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"providerType": {
"name": "providerType",
"type": "gitProviderType",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'github'"
},
"createdAt": {
"name": "createdAt",
"type": "text",
"primaryKey": false,
"notNull": true
},
"authId": {
"name": "authId",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"git_provider_authId_auth_id_fk": {
"name": "git_provider_authId_auth_id_fk",
"tableFrom": "git_provider",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.bitbucket": {
"name": "bitbucket",
"schema": "",
"columns": {
"bitbucketId": {
"name": "bitbucketId",
"type": "text",
"primaryKey": true,
"notNull": true
},
"bitbucketUsername": {
"name": "bitbucketUsername",
"type": "text",
"primaryKey": false,
"notNull": false
},
"appPassword": {
"name": "appPassword",
"type": "text",
"primaryKey": false,
"notNull": false
},
"bitbucketWorkspaceName": {
"name": "bitbucketWorkspaceName",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitProviderId": {
"name": "gitProviderId",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"bitbucket_gitProviderId_git_provider_gitProviderId_fk": {
"name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk",
"tableFrom": "bitbucket",
"tableTo": "git_provider",
"columnsFrom": [
"gitProviderId"
],
"columnsTo": [
"gitProviderId"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.github": {
"name": "github",
"schema": "",
"columns": {
"githubId": {
"name": "githubId",
"type": "text",
"primaryKey": true,
"notNull": true
},
"githubAppName": {
"name": "githubAppName",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubAppId": {
"name": "githubAppId",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"githubClientId": {
"name": "githubClientId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubClientSecret": {
"name": "githubClientSecret",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubInstallationId": {
"name": "githubInstallationId",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubPrivateKey": {
"name": "githubPrivateKey",
"type": "text",
"primaryKey": false,
"notNull": false
},
"githubWebhookSecret": {
"name": "githubWebhookSecret",
"type": "text",
"primaryKey": false,
"notNull": false
},
"gitProviderId": {
"name": "gitProviderId",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"github_gitProviderId_git_provider_gitProviderId_fk": {
"name": "github_gitProviderId_git_provider_gitProviderId_fk",
"tableFrom": "github",
"tableTo": "git_provider",
"columnsFrom": [
"gitProviderId"
],
"columnsTo": [
"gitProviderId"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.gitlab": {
"name": "gitlab",
"schema": "",
"columns": {
"gitlabId": {
"name": "gitlabId",
"type": "text",
"primaryKey": true,
"notNull": true
},
"application_id": {
"name": "application_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"redirect_uri": {
"name": "redirect_uri",
"type": "text",
"primaryKey": false,
"notNull": false
},
"secret": {
"name": "secret",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"group_name": {
"name": "group_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"gitProviderId": {
"name": "gitProviderId",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"gitlab_gitProviderId_git_provider_gitProviderId_fk": {
"name": "gitlab_gitProviderId_git_provider_gitProviderId_fk",
"tableFrom": "gitlab",
"tableTo": "git_provider",
"columnsFrom": [
"gitProviderId"
],
"columnsTo": [
"gitProviderId"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
@@ -2946,6 +3395,8 @@
"docker",
"git",
"github",
"gitlab",
"bitbucket",
"drop"
]
},
@@ -3046,6 +3497,8 @@
"values": [
"git",
"github",
"gitlab",
"bitbucket",
"raw"
]
},
@@ -3066,6 +3519,15 @@
"discord",
"email"
]
},
"public.gitProviderType": {
"name": "gitProviderType",
"schema": "public",
"values": [
"github",
"gitlab",
"bitbucket"
]
}
},
"schemas": {},

View File

@@ -236,8 +236,8 @@
{
"idx": 33,
"version": "6",
"when": 1725167062595,
"tag": "0033_new_mandrill",
"when": 1725250322137,
"tag": "0033_white_hawkeye",
"breakpoints": true
}
]

View File

@@ -66,6 +66,18 @@ export default async function handler(
res.status(301).json({ message: "Branch Not Match" });
return;
}
} else if (sourceType === "gitlab") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== application.gitlabBranch) {
res.status(301).json({ message: "Branch Not Match" });
return;
}
} else if (sourceType === "bitbucket") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== application.bitbucketBranch) {
res.status(301).json({ message: "Branch Not Match" });
return;
}
}
try {

View File

@@ -1,6 +1,6 @@
import { findAdmin } from "@/server/api/services/admin";
import { db } from "@/server/db";
import { applications, compose } from "@/server/db/schema";
import { applications, compose, github } from "@/server/db/schema";
import type { DeploymentJob } from "@/server/queues/deployments-queue";
import { myQueue } from "@/server/queues/queueSetup";
import { Webhooks } from "@octokit/webhooks";
@@ -19,20 +19,33 @@ export default async function handler(
return;
}
if (!admin.githubWebhookSecret) {
res.status(200).json({ message: "Github Webhook Secret not set" });
const signature = req.headers["x-hub-signature-256"];
const githubBody = req.body;
if (!githubBody?.installation.id) {
res.status(400).json({ message: "Github Installation not found" });
return;
}
const webhooks = new Webhooks({
secret: admin.githubWebhookSecret,
const githubResult = await db.query.github.findFirst({
where: eq(github.githubInstallationId, githubBody.installation.id),
});
const signature = req.headers["x-hub-signature-256"];
const github = req.body;
if (!githubResult) {
res.status(400).json({ message: "Github Installation not found" });
return;
}
if (!githubResult.githubWebhookSecret) {
res.status(400).json({ message: "Github Webhook Secret not set" });
return;
}
const webhooks = new Webhooks({
secret: githubResult.githubWebhookSecret,
});
const verified = await webhooks.verify(
JSON.stringify(github),
JSON.stringify(githubBody),
signature as string,
);
@@ -52,8 +65,8 @@ export default async function handler(
}
try {
const branchName = github?.ref?.replace("refs/heads/", "");
const repository = github?.repository?.name;
const branchName = githubBody?.ref?.replace("refs/heads/", "");
const repository = githubBody?.repository?.name;
const deploymentTitle = extractCommitMessage(req.headers, req.body);
const deploymentHash = extractHash(req.headers, req.body);

View File

@@ -1,5 +1,6 @@
import { createGithub } from "@/server/api/services/github";
import { db } from "@/server/db";
import { admins } from "@/server/db/schema";
import { github } from "@/server/db/schema";
import { eq } from "drizzle-orm";
import type { NextApiRequest, NextApiResponse } from "next";
import { Octokit } from "octokit";
@@ -17,10 +18,12 @@ export default async function handler(
) {
const { code, state, installation_id, setup_action }: Query =
req.query as Query;
if (!code) {
return res.status(400).json({ error: "Missing code parameter" });
}
const [action, authId] = state?.split(":");
const [action, value] = state?.split(":");
// Value could be the authId or the githubProviderId
if (action === "gh_init") {
const octokit = new Octokit({});
@@ -31,27 +34,25 @@ export default async function handler(
},
);
const result = await db
.update(admins)
.set({
githubAppId: data.id,
githubAppName: data.html_url,
githubClientId: data.client_id,
githubClientSecret: data.client_secret,
githubWebhookSecret: data.webhook_secret,
githubPrivateKey: data.pem,
})
.where(eq(admins.authId, authId as string))
.returning();
await createGithub({
name: data.name,
githubAppName: data.html_url,
githubAppId: data.id,
githubClientId: data.client_id,
githubClientSecret: data.client_secret,
githubWebhookSecret: data.webhook_secret,
githubPrivateKey: data.pem,
authId: value as string,
});
} else if (action === "gh_setup") {
await db
.update(admins)
.update(github)
.set({
githubInstallationId: installation_id,
})
.where(eq(admins.authId, authId as string))
.where(eq(github.githubId, value as string))
.returning();
}
res.redirect(307, "/dashboard/settings/server");
res.redirect(307, "/dashboard/settings/git-providers");
}

View File

@@ -8,9 +8,9 @@ export default async function handler(
const xGitHubEvent = req.headers["x-github-event"];
if (xGitHubEvent === "ping") {
res.redirect(307, "/dashboard/settings");
res.redirect(307, "/dashboard/settings/git-providers");
} else {
res.redirect(307, "/dashboard/settings");
res.redirect(307, "/dashboard/settings/git-providers");
}
} else {
res.setHeader("Allow", ["POST"]);

View File

@@ -0,0 +1,44 @@
import { findGitlabById, updateGitlab } from "@/server/api/services/gitlab";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const { code, gitlabId } = req.query;
if (!code || Array.isArray(code)) {
return res.status(400).json({ error: "Missing or invalid code" });
}
const gitlab = await findGitlabById(gitlabId as string);
const response = await fetch("https://gitlab.com/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_id: gitlab.applicationId as string,
client_secret: gitlab.secret as string,
code: code as string,
grant_type: "authorization_code",
redirect_uri: `${gitlab.redirectUri}?gitlabId=${gitlabId}`,
}),
});
const result = await response.json();
if (!result.access_token || !result.refresh_token) {
return res.status(400).json({ error: "Missing or invalid code" });
}
const expiresAt = Math.floor(Date.now() / 1000) + result.expires_in;
await updateGitlab(gitlab.gitlabId, {
accessToken: result.access_token,
refreshToken: result.refresh_token,
expiresAt,
});
return res.redirect(307, "/dashboard/settings/git-providers");
}

View File

@@ -0,0 +1,81 @@
import { ShowGitProviders } from "@/components/dashboard/settings/git/show-git-providers";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { SettingsLayout } from "@/components/layouts/settings-layout";
import { appRouter } from "@/server/api/root";
import { validateRequest } from "@/server/auth/auth";
import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import React, { type ReactElement } from "react";
import superjson from "superjson";
const Page = () => {
return (
<div className="flex flex-col gap-4 w-full">
<ShowGitProviders />
</div>
);
};
export default Page;
Page.getLayout = (page: ReactElement) => {
return (
<DashboardLayout tab={"settings"}>
<SettingsLayout>{page}</SettingsLayout>
</DashboardLayout>
);
};
export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>,
) {
const { user, session } = await validateRequest(ctx.req, ctx.res);
if (!user) {
return {
redirect: {
permanent: true,
destination: "/",
},
};
}
const { req, res } = ctx;
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
},
transformer: superjson,
});
try {
await helpers.project.all.prefetch();
const auth = await helpers.auth.get.fetch();
if (auth.rol === "user") {
const user = await helpers.user.byAuthId.fetch({
authId: auth.id,
});
if (!user.canAccessToGitProviders) {
return {
redirect: {
permanent: true,
destination: "/",
},
};
}
}
return {
props: {
trpcState: helpers.dehydrate(),
},
};
} catch (error) {
return {
props: {},
};
}
}

View File

@@ -1,4 +1,3 @@
import { GithubSetup } from "@/components/dashboard/settings/github/github-setup";
import { WebDomain } from "@/components/dashboard/settings/web-domain";
import { WebServer } from "@/components/dashboard/settings/web-server";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
@@ -11,7 +10,6 @@ const Page = () => {
return (
<div className="flex flex-col gap-4 w-full">
<WebDomain />
<GithubSetup />
<WebServer />
</div>
);

View File

@@ -3,6 +3,7 @@ import { createTRPCRouter } from "../api/trpc";
import { adminRouter } from "./routers/admin";
import { applicationRouter } from "./routers/application";
import { backupRouter } from "./routers/backup";
import { bitbucketRouter } from "./routers/bitbucket";
import { certificateRouter } from "./routers/certificate";
import { clusterRouter } from "./routers/cluster";
import { composeRouter } from "./routers/compose";
@@ -10,6 +11,9 @@ import { deploymentRouter } from "./routers/deployment";
import { destinationRouter } from "./routers/destination";
import { dockerRouter } from "./routers/docker";
import { domainRouter } from "./routers/domain";
import { gitProviderRouter } from "./routers/git-provider";
import { githubRouter } from "./routers/github";
import { gitlabRouter } from "./routers/gitlab";
import { mariadbRouter } from "./routers/mariadb";
import { mongoRouter } from "./routers/mongo";
import { mountRouter } from "./routers/mount";
@@ -58,6 +62,10 @@ export const appRouter = createTRPCRouter({
cluster: clusterRouter,
notification: notificationRouter,
sshKey: sshRouter,
gitProvider: gitProviderRouter,
bitbucket: bitbucketRouter,
gitlab: gitlabRouter,
github: githubRouter,
});
// export type definition of API

View File

@@ -3,28 +3,18 @@ import {
apiAssignPermissions,
apiCreateUserInvitation,
apiFindOneToken,
apiGetBranches,
apiRemoveUser,
users,
} from "@/server/db/schema";
import { haveGithubRequirements } from "@/server/utils/providers/github";
import { createAppAuth } from "@octokit/auth-app";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { Octokit } from "octokit";
import {
createInvitation,
findAdmin,
getUserByToken,
removeUserByAuthId,
updateAdmin,
} from "../services/admin";
import {
adminProcedure,
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "../trpc";
import { adminProcedure, createTRPCRouter, publicProcedure } from "../trpc";
export const adminRouter = createTRPCRouter({
one: adminProcedure.query(async () => {
@@ -83,91 +73,4 @@ export const adminRouter = createTRPCRouter({
});
}
}),
cleanGithubApp: adminProcedure.mutation(async ({ ctx }) => {
try {
return await updateAdmin(ctx.user.authId, {
githubAppName: "",
githubClientId: "",
githubClientSecret: "",
githubInstallationId: "",
});
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to delete this github app",
cause: error,
});
}
}),
getRepositories: protectedProcedure.query(async () => {
const admin = await findAdmin();
const completeRequirements = haveGithubRequirements(admin);
if (!completeRequirements) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Admin need to setup correctly github account",
});
}
const octokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: admin.githubAppId,
privateKey: admin.githubPrivateKey,
installationId: admin.githubInstallationId,
},
});
const repositories = (await octokit.paginate(
octokit.rest.apps.listReposAccessibleToInstallation,
)) as unknown as Awaited<
ReturnType<typeof octokit.rest.apps.listReposAccessibleToInstallation>
>["data"]["repositories"];
return repositories;
}),
getBranches: protectedProcedure
.input(apiGetBranches)
.query(async ({ input }) => {
const admin = await findAdmin();
const completeRequirements = haveGithubRequirements(admin);
if (!completeRequirements) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Admin need to setup correctly github account",
});
}
const octokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: admin.githubAppId,
privateKey: admin.githubPrivateKey,
installationId: admin.githubInstallationId,
},
});
const branches = (await octokit.paginate(
octokit.rest.repos.listBranches,
{
owner: input.owner,
repo: input.repo,
},
)) as unknown as Awaited<
ReturnType<typeof octokit.rest.repos.listBranches>
>["data"];
return branches;
}),
haveGithubConfigured: protectedProcedure.query(async () => {
const adminResponse = await findAdmin();
return haveGithubRequirements(adminResponse);
}),
});

View File

@@ -9,11 +9,13 @@ import {
apiFindMonitoringStats,
apiFindOneApplication,
apiReloadApplication,
apiSaveBitbucketProvider,
apiSaveBuildType,
apiSaveDockerProvider,
apiSaveEnvironmentVariables,
apiSaveGitProvider,
apiSaveGithubProvider,
apiSaveGitlabProvider,
apiUpdateApplication,
applications,
} from "@/server/db/schema/application";
@@ -208,6 +210,39 @@ export const applicationRouter = createTRPCRouter({
owner: input.owner,
buildPath: input.buildPath,
applicationStatus: "idle",
githubId: input.githubId,
});
return true;
}),
saveGitlabProvider: protectedProcedure
.input(apiSaveGitlabProvider)
.mutation(async ({ input }) => {
await updateApplication(input.applicationId, {
gitlabRepository: input.gitlabRepository,
gitlabOwner: input.gitlabOwner,
gitlabBranch: input.gitlabBranch,
gitlabBuildPath: input.gitlabBuildPath,
sourceType: "gitlab",
applicationStatus: "idle",
gitlabId: input.gitlabId,
gitlabProjectId: input.gitlabProjectId,
gitlabPathNamespace: input.gitlabPathNamespace,
});
return true;
}),
saveBitbucketProvider: protectedProcedure
.input(apiSaveBitbucketProvider)
.mutation(async ({ input }) => {
await updateApplication(input.applicationId, {
bitbucketRepository: input.bitbucketRepository,
bitbucketOwner: input.bitbucketOwner,
bitbucketBranch: input.bitbucketBranch,
bitbucketBuildPath: input.bitbucketBuildPath,
sourceType: "bitbucket",
applicationStatus: "idle",
bitbucketId: input.bitbucketId,
});
return true;

View File

@@ -13,6 +13,7 @@ import {
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
import { db } from "../../db";
import { getUserByToken } from "../services/admin";
import {
createAdmin,
createUser,
@@ -61,6 +62,13 @@ export const authRouter = createTRPCRouter({
.input(apiCreateUser)
.mutation(async ({ ctx, input }) => {
try {
const token = await getUserByToken(input.token);
if (token.isExpired) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Invalid token",
});
}
const newUser = await createUser(input);
const session = await lucia.createSession(newUser?.authId || "", {});
ctx.res.appendHeader(

View File

@@ -0,0 +1,82 @@
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import { db } from "@/server/db";
import {
apiBitbucketTestConnection,
apiCreateBitbucket,
apiFindBitbucketBranches,
apiFindOneBitbucket,
apiUpdateBitbucket,
} from "@/server/db/schema";
import {
getBitbucketBranches,
getBitbucketRepositories,
testBitbucketConnection,
} from "@/server/utils/providers/bitbucket";
import { TRPCError } from "@trpc/server";
import {
createBitbucket,
findBitbucketById,
updateBitbucket,
} from "../services/bitbucket";
export const bitbucketRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateBitbucket)
.mutation(async ({ input }) => {
try {
return await createBitbucket(input);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to create this bitbucket provider",
cause: error,
});
}
}),
one: protectedProcedure
.input(apiFindOneBitbucket)
.query(async ({ input }) => {
return await findBitbucketById(input.bitbucketId);
}),
bitbucketProviders: protectedProcedure.query(async () => {
const result = await db.query.bitbucket.findMany({
with: {
gitProvider: true,
},
columns: {
bitbucketId: true,
},
});
return result;
}),
getBitbucketRepositories: protectedProcedure
.input(apiFindOneBitbucket)
.query(async ({ input }) => {
return await getBitbucketRepositories(input.bitbucketId);
}),
getBitbucketBranches: protectedProcedure
.input(apiFindBitbucketBranches)
.query(async ({ input }) => {
return await getBitbucketBranches(input);
}),
testConnection: protectedProcedure
.input(apiBitbucketTestConnection)
.mutation(async ({ input }) => {
try {
const result = await testBitbucketConnection(input);
return `Found ${result} repositories`;
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: error instanceof Error ? error?.message : `Error: ${error}`,
});
}
}),
update: protectedProcedure
.input(apiUpdateBitbucket)
.mutation(async ({ input }) => {
return await updateBitbucket(input.bitbucketId, input);
}),
});

View File

@@ -0,0 +1,31 @@
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import { db } from "@/server/db";
import { apiRemoveGitProvider, gitProvider } from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { asc, desc } from "drizzle-orm";
import { removeGitProvider } from "../services/git-provider";
export const gitProviderRouter = createTRPCRouter({
getAll: protectedProcedure.query(async () => {
return await db.query.gitProvider.findMany({
with: {
gitlab: true,
bitbucket: true,
github: true,
},
orderBy: desc(gitProvider.createdAt),
});
}),
remove: protectedProcedure
.input(apiRemoveGitProvider)
.mutation(async ({ input }) => {
try {
return await removeGitProvider(input.gitProviderId);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to delete this git provider",
});
}
}),
});

View File

@@ -0,0 +1,71 @@
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import { db } from "@/server/db";
import {
apiFindGithubBranches,
apiFindOneGithub,
apiUpdateGithub,
} from "@/server/db/schema";
import {
getGithubBranches,
getGithubRepositories,
} from "@/server/utils/providers/github";
import { TRPCError } from "@trpc/server";
import { updateGitProvider } from "../services/git-provider";
import { findGithubById, haveGithubRequirements } from "../services/github";
export const githubRouter = createTRPCRouter({
one: protectedProcedure.input(apiFindOneGithub).query(async ({ input }) => {
return await findGithubById(input.githubId);
}),
getGithubRepositories: protectedProcedure
.input(apiFindOneGithub)
.query(async ({ input }) => {
return await getGithubRepositories(input.githubId);
}),
getGithubBranches: protectedProcedure
.input(apiFindGithubBranches)
.query(async ({ input }) => {
return await getGithubBranches(input);
}),
githubProviders: protectedProcedure.query(async () => {
const result = await db.query.github.findMany({
with: {
gitProvider: true,
},
});
const filtered = result
.filter((provider) => haveGithubRequirements(provider))
.map((provider) => {
return {
githubId: provider.githubId,
gitProvider: {
...provider.gitProvider,
},
};
});
return filtered;
}),
testConnection: protectedProcedure
.input(apiFindOneGithub)
.mutation(async ({ input }) => {
try {
const result = await getGithubRepositories(input.githubId);
return `Found ${result.length} repositories`;
} catch (err) {
throw new TRPCError({
code: "BAD_REQUEST",
message: err instanceof Error ? err?.message : `Error: ${err}`,
});
}
}),
update: protectedProcedure
.input(apiUpdateGithub)
.mutation(async ({ input }) => {
await updateGitProvider(input.gitProviderId, {
name: input.name,
});
}),
});

View File

@@ -0,0 +1,93 @@
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import {
apiCreateGitlab,
apiFindGitlabBranches,
apiFindOneGitlab,
apiGitlabTestConnection,
apiUpdateGitlab,
} from "@/server/db/schema";
import { db } from "@/server/db";
import {
getGitlabBranches,
getGitlabRepositories,
haveGitlabRequirements,
testGitlabConnection,
} from "@/server/utils/providers/gitlab";
import { TRPCError } from "@trpc/server";
import { updateGitProvider } from "../services/git-provider";
import { createGitlab, findGitlabById, updateGitlab } from "../services/gitlab";
export const gitlabRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateGitlab)
.mutation(async ({ input }) => {
try {
return await createGitlab(input);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to create this gitlab provider",
cause: error,
});
}
}),
one: protectedProcedure.input(apiFindOneGitlab).query(async ({ input }) => {
return await findGitlabById(input.gitlabId);
}),
gitlabProviders: protectedProcedure.query(async () => {
const result = await db.query.gitlab.findMany({
with: {
gitProvider: true,
},
});
const filtered = result
.filter((provider) => haveGitlabRequirements(provider))
.map((provider) => {
return {
gitlabId: provider.gitlabId,
gitProvider: {
...provider.gitProvider,
},
};
});
return filtered;
}),
getGitlabRepositories: protectedProcedure
.input(apiFindOneGitlab)
.query(async ({ input }) => {
return await getGitlabRepositories(input.gitlabId);
}),
getGitlabBranches: protectedProcedure
.input(apiFindGitlabBranches)
.query(async ({ input }) => {
return await getGitlabBranches(input);
}),
testConnection: protectedProcedure
.input(apiGitlabTestConnection)
.mutation(async ({ input }) => {
try {
const result = await testGitlabConnection(input);
return `Found ${result} repositories`;
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: error instanceof Error ? error?.message : `Error: ${error}`,
});
}
}),
update: protectedProcedure
.input(apiUpdateGitlab)
.mutation(async ({ input }) => {
if (input.name) {
await updateGitProvider(input.gitProviderId, {
name: input.name,
});
} else {
await updateGitlab(input.gitlabId, input);
}
}),
});

View File

@@ -301,6 +301,10 @@ export const settingsRouter = createTRPCRouter({
"mongo",
"mariadb",
"sshRouter",
"gitProvider",
"bitbucket",
"github",
"gitlab",
],
});
@@ -322,9 +326,9 @@ export const settingsRouter = createTRPCRouter({
}),
writeTraefikEnv: adminProcedure
.input(z.string())
.input(z.object({ env: z.string() }))
.mutation(async ({ input }) => {
const envs = prepareEnvironmentVariables(input);
const envs = prepareEnvironmentVariables(input.env);
await initializeTraefik({
env: envs,
});

View File

@@ -129,13 +129,9 @@ export const getUserByToken = async (token: string) => {
message: "Invitation not found",
});
}
const now = new Date();
const isExpired = isAfter(now, new Date(user.expirationDate));
return {
...user,
isExpired,
isExpired: user.isRegistered,
};
};

View File

@@ -20,6 +20,8 @@ import { createDeployment, updateDeploymentStatus } from "./deployment";
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success";
import { cloneBitbucketRepository } from "@/server/utils/providers/bitbucket";
import { cloneGitlabRepository } from "@/server/utils/providers/gitlab";
import { validUniqueServerAppName } from "./project";
export type Application = typeof applications.$inferSelect;
@@ -81,6 +83,9 @@ export const findApplicationById = async (applicationId: string) => {
security: true,
ports: true,
registry: true,
gitlab: true,
github: true,
bitbucket: true,
},
});
if (!application) {
@@ -141,7 +146,6 @@ export const deployApplication = async ({
}) => {
const application = await findApplicationById(applicationId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
const admin = await findAdmin();
const deployment = await createDeployment({
applicationId: applicationId,
title: titleLog,
@@ -150,7 +154,13 @@ export const deployApplication = async ({
try {
if (application.sourceType === "github") {
await cloneGithubRepository(admin, application, deployment.logPath);
await cloneGithubRepository(application, deployment.logPath);
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "gitlab") {
await cloneGitlabRepository(application, deployment.logPath);
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "bitbucket") {
await cloneBitbucketRepository(application, deployment.logPath);
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "docker") {
await buildDocker(application, deployment.logPath);
@@ -214,6 +224,10 @@ export const rebuildApplication = async ({
try {
if (application.sourceType === "github") {
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "gitlab") {
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "bitbucket") {
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "docker") {
await buildDocker(application, deployment.logPath);
} else if (application.sourceType === "git") {

View File

@@ -72,7 +72,7 @@ export const createUser = async (input: typeof apiCreateUser._type) => {
.update(users)
.set({
isRegistered: true,
expirationDate: new Date().toISOString(),
expirationDate: undefined,
})
.where(eq(users.token, input.token))
.returning()

View File

@@ -0,0 +1,88 @@
import { db } from "@/server/db";
import {
type apiCreateBitbucket,
type apiUpdateBitbucket,
bitbucket,
gitProvider,
} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
export type Bitbucket = typeof bitbucket.$inferSelect;
export const createBitbucket = async (
input: typeof apiCreateBitbucket._type,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "bitbucket",
authId: input.authId,
name: input.name,
})
.returning()
.then((response) => response[0]);
if (!newGitProvider) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to create the git provider",
});
}
await tx
.insert(bitbucket)
.values({
...input,
gitProviderId: newGitProvider?.gitProviderId,
})
.returning()
.then((response) => response[0]);
});
};
export const findBitbucketById = async (bitbucketId: string) => {
const bitbucketProviderResult = await db.query.bitbucket.findFirst({
where: eq(bitbucket.bitbucketId, bitbucketId),
with: {
gitProvider: true,
},
});
if (!bitbucketProviderResult) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Bitbucket Provider not found",
});
}
return bitbucketProviderResult;
};
export const updateBitbucket = async (
bitbucketId: string,
input: typeof apiUpdateBitbucket._type,
) => {
return await db.transaction(async (tx) => {
const result = await tx
.update(bitbucket)
.set({
...input,
})
.where(eq(bitbucket.bitbucketId, bitbucketId))
.returning();
if (input.name) {
await tx
.update(gitProvider)
.set({
name: input.name,
})
.where(eq(gitProvider.gitProviderId, input.gitProviderId))
.returning();
}
return result[0];
});
};

View File

@@ -5,17 +5,18 @@ import { type apiCreateCompose, compose } from "@/server/db/schema";
import { generateAppName } from "@/server/db/schema/utils";
import { buildCompose } from "@/server/utils/builders/compose";
import { cloneCompose, loadDockerCompose } from "@/server/utils/docker/domain";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success";
import { execAsync } from "@/server/utils/process/execAsync";
import { cloneBitbucketRepository } from "@/server/utils/providers/bitbucket";
import { cloneGitRepository } from "@/server/utils/providers/git";
import { cloneGithubRepository } from "@/server/utils/providers/github";
import { cloneGitlabRepository } from "@/server/utils/providers/gitlab";
import { createComposeFile } from "@/server/utils/providers/raw";
import { generatePassword } from "@/templates/utils";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { findAdmin, getDokployUrl } from "./admin";
import { getDokployUrl } from "./admin";
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
import { validUniqueServerAppName } from "./project";
@@ -92,6 +93,9 @@ export const findComposeById = async (composeId: string) => {
deployments: true,
mounts: true,
domains: true,
github: true,
gitlab: true,
bitbucket: true,
},
});
if (!result) {
@@ -151,7 +155,6 @@ export const deployCompose = async ({
descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const admin = await findAdmin();
const buildLink = `${await getDokployUrl()}/dashboard/project/${compose.projectId}/services/compose/${compose.composeId}?tab=deployments`;
const deployment = await createDeploymentCompose({
composeId: composeId,
@@ -161,7 +164,11 @@ export const deployCompose = async ({
try {
if (compose.sourceType === "github") {
await cloneGithubRepository(admin, compose, deployment.logPath, true);
await cloneGithubRepository(compose, deployment.logPath);
} else if (compose.sourceType === "gitlab") {
await cloneGitlabRepository(compose, deployment.logPath);
} else if (compose.sourceType === "bitbucket") {
await cloneBitbucketRepository(compose, deployment.logPath);
} else if (compose.sourceType === "git") {
await cloneGitRepository(compose, deployment.logPath, true);
} else if (compose.sourceType === "raw") {

View File

@@ -0,0 +1,29 @@
import { db } from "@/server/db";
import { type apiCreateGithub, gitProvider, github } from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
export type GitProvider = typeof gitProvider.$inferSelect;
export const removeGitProvider = async (gitProviderId: string) => {
const result = await db
.delete(gitProvider)
.where(eq(gitProvider.gitProviderId, gitProviderId))
.returning();
return result[0];
};
export const updateGitProvider = async (
gitProviderId: string,
input: Partial<GitProvider>,
) => {
return await db
.update(gitProvider)
.set({
...input,
})
.where(eq(gitProvider.gitProviderId, gitProviderId))
.returning()
.then((response) => response[0]);
};

View File

@@ -0,0 +1,75 @@
import { db } from "@/server/db";
import { type apiCreateGithub, gitProvider, github } from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
export type Github = typeof github.$inferSelect;
export const createGithub = async (input: typeof apiCreateGithub._type) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "github",
authId: input.authId,
name: input.name,
})
.returning()
.then((response) => response[0]);
if (!newGitProvider) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to create the git provider",
});
}
return await tx
.insert(github)
.values({
...input,
gitProviderId: newGitProvider?.gitProviderId,
})
.returning()
.then((response) => response[0]);
});
};
export const findGithubById = async (githubId: string) => {
const githubProviderResult = await db.query.github.findFirst({
where: eq(github.githubId, githubId),
with: {
gitProvider: true,
},
});
if (!githubProviderResult) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Github Provider not found",
});
}
return githubProviderResult;
};
export const haveGithubRequirements = (github: Github) => {
return !!(
github?.githubAppId &&
github?.githubPrivateKey &&
github?.githubInstallationId
);
};
export const updateGithub = async (
githubId: string,
input: Partial<Github>,
) => {
return await db
.update(github)
.set({
...input,
})
.where(eq(github.githubId, githubId))
.returning()
.then((response) => response[0]);
};

View File

@@ -0,0 +1,76 @@
import { db } from "@/server/db";
import {
type apiCreateGitlab,
type bitbucket,
gitProvider,
type github,
gitlab,
} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
export type Github = typeof github.$inferSelect;
export type Bitbucket = typeof bitbucket.$inferSelect;
export type Gitlab = typeof gitlab.$inferSelect;
export const createGitlab = async (input: typeof apiCreateGitlab._type) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "gitlab",
authId: input.authId,
name: input.name,
})
.returning()
.then((response) => response[0]);
if (!newGitProvider) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error to create the git provider",
});
}
await tx
.insert(gitlab)
.values({
...input,
gitProviderId: newGitProvider?.gitProviderId,
})
.returning()
.then((response) => response[0]);
});
};
export const findGitlabById = async (gitlabId: string) => {
const gitlabProviderResult = await db.query.gitlab.findFirst({
where: eq(gitlab.gitlabId, gitlabId),
with: {
gitProvider: true,
},
});
if (!gitlabProviderResult) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Gitlab Provider not found",
});
}
return gitlabProviderResult;
};
export const updateGitlab = async (
gitlabId: string,
input: Partial<Gitlab>,
) => {
return await db
.update(gitlab)
.set({
...input,
})
.where(eq(gitlab.gitlabId, gitlabId))
.returning()
.then((response) => response[0]);
};

View File

@@ -1,5 +1,5 @@
import { relations } from "drizzle-orm";
import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
@@ -13,17 +13,9 @@ export const admins = pgTable("admin", {
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
githubAppId: integer("githubAppId"),
githubAppName: text("githubAppName"),
serverIp: text("serverIp"),
certificateType: certificateType("certificateType").notNull().default("none"),
host: text("host"),
githubClientId: text("githubClientId"),
githubClientSecret: text("githubClientSecret"),
githubInstallationId: text("githubInstallationId"),
githubPrivateKey: text("githubPrivateKey"),
githubWebhookSecret: text("githubWebhookSecret"),
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
@@ -46,12 +38,6 @@ export const adminsRelations = relations(admins, ({ one, many }) => ({
const createSchema = createInsertSchema(admins, {
adminId: z.string(),
githubAppName: z.string().optional(),
githubClientId: z.string().optional(),
githubClientSecret: z.string().optional(),
githubInstallationId: z.string().optional(),
githubPrivateKey: z.string().optional(),
githubAppId: z.number().optional(),
enableDockerCleanup: z.boolean().optional(),
sshPrivateKey: z.string().optional(),
certificateType: z.enum(["letsencrypt", "none"]).default("none"),
@@ -82,10 +68,6 @@ export const apiTraefikConfig = z.object({
traefikConfig: z.string().min(1),
});
export const apiGetBranches = z.object({
repo: z.string().min(1),
owner: z.string().min(1),
});
export const apiModifyTraefikConfig = z.object({
path: z.string().min(1),
traefikConfig: z.string().min(1),

View File

@@ -1,4 +1,3 @@
import { generatePassword } from "@/templates/utils";
import { relations } from "drizzle-orm";
import {
boolean,
@@ -11,6 +10,7 @@ import {
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { bitbucket, github, gitlab } from ".";
import { deployments } from "./deployment";
import { domains } from "./domain";
import { mounts } from "./mount";
@@ -27,6 +27,8 @@ export const sourceType = pgEnum("sourceType", [
"docker",
"git",
"github",
"gitlab",
"bitbucket",
"drop",
]);
@@ -126,6 +128,18 @@ export const applications = pgTable("application", {
branch: text("branch"),
buildPath: text("buildPath").default("/"),
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
// Gitlab
gitlabProjectId: integer("gitlabProjectId"),
gitlabRepository: text("gitlabRepository"),
gitlabOwner: text("gitlabOwner"),
gitlabBranch: text("gitlabBranch"),
gitlabBuildPath: text("gitlabBuildPath").default("/"),
gitlabPathNamespace: text("gitlabPathNamespace"),
// Bitbucket
bitbucketRepository: text("bitbucketRepository"),
bitbucketOwner: text("bitbucketOwner"),
bitbucketBranch: text("bitbucketBranch"),
bitbucketBuildPath: text("bitbucketBuildPath").default("/"),
// Docker
username: text("username"),
password: text("password"),
@@ -170,6 +184,15 @@ export const applications = pgTable("application", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
githubId: text("githubId").references(() => github.githubId, {
onDelete: "set null",
}),
gitlabId: text("gitlabId").references(() => gitlab.gitlabId, {
onDelete: "set null",
}),
bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, {
onDelete: "set null",
}),
});
export const applicationsRelations = relations(
@@ -193,6 +216,18 @@ export const applicationsRelations = relations(
fields: [applications.registryId],
references: [registry.registryId],
}),
github: one(github, {
fields: [applications.githubId],
references: [github.githubId],
}),
gitlab: one(gitlab, {
fields: [applications.gitlabId],
references: [gitlab.gitlabId],
}),
bitbucket: one(bitbucket, {
fields: [applications.bitbucketId],
references: [bitbucket.bitbucketId],
}),
}),
);
@@ -371,6 +406,31 @@ export const apiSaveGithubProvider = createSchema
branch: true,
owner: true,
buildPath: true,
githubId: true,
})
.required();
export const apiSaveGitlabProvider = createSchema
.pick({
applicationId: true,
gitlabBranch: true,
gitlabBuildPath: true,
gitlabOwner: true,
gitlabRepository: true,
gitlabId: true,
gitlabProjectId: true,
gitlabPathNamespace: true,
})
.required();
export const apiSaveBitbucketProvider = createSchema
.pick({
bitbucketBranch: true,
bitbucketBuildPath: true,
bitbucketOwner: true,
bitbucketRepository: true,
bitbucketId: true,
applicationId: true,
})
.required();

View File

@@ -70,6 +70,7 @@ export const apiCreateUser = createSchema
.pick({
password: true,
id: true,
token: true,
})
.required()
.extend({

View File

@@ -0,0 +1,64 @@
import { relations } from "drizzle-orm";
import { pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { gitProvider } from "./git-provider";
export const bitbucket = pgTable("bitbucket", {
bitbucketId: text("bitbucketId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
bitbucketUsername: text("bitbucketUsername"),
appPassword: text("appPassword"),
bitbucketWorkspaceName: text("bitbucketWorkspaceName"),
gitProviderId: text("gitProviderId")
.notNull()
.references(() => gitProvider.gitProviderId, { onDelete: "cascade" }),
});
export const bitbucketProviderRelations = relations(bitbucket, ({ one }) => ({
gitProvider: one(gitProvider, {
fields: [bitbucket.gitProviderId],
references: [gitProvider.gitProviderId],
}),
}));
const createSchema = createInsertSchema(bitbucket);
export const apiCreateBitbucket = createSchema.extend({
bitbucketUsername: z.string().optional(),
appPassword: z.string().optional(),
bitbucketWorkspaceName: z.string().optional(),
gitProviderId: z.string().optional(),
authId: z.string().min(1),
name: z.string().min(1),
});
export const apiFindOneBitbucket = createSchema
.extend({
bitbucketId: z.string().min(1),
})
.pick({ bitbucketId: true });
export const apiBitbucketTestConnection = createSchema
.extend({
bitbucketId: z.string().min(1),
bitbucketUsername: z.string().optional(),
workspaceName: z.string().optional(),
})
.pick({ bitbucketId: true, bitbucketUsername: true, workspaceName: true });
export const apiFindBitbucketBranches = z.object({
owner: z.string(),
repo: z.string(),
bitbucketId: z.string().optional(),
});
export const apiUpdateBitbucket = createSchema.extend({
bitbucketId: z.string().min(1),
name: z.string().min(1),
bitbucketUsername: z.string().optional(),
bitbucketWorkspaceName: z.string().optional(),
});

View File

@@ -1,9 +1,10 @@
import { sshKeys } from "@/server/db/schema/ssh-key";
import { relations } from "drizzle-orm";
import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { bitbucket, github, gitlab } from ".";
import { deployments } from "./deployment";
import { domains } from "./domain";
import { mounts } from "./mount";
@@ -14,6 +15,8 @@ import { generateAppName } from "./utils";
export const sourceTypeCompose = pgEnum("sourceTypeCompose", [
"git",
"github",
"gitlab",
"bitbucket",
"raw",
]);
@@ -39,6 +42,16 @@ export const compose = pgTable("compose", {
owner: text("owner"),
branch: text("branch"),
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
// Gitlab
gitlabProjectId: integer("gitlabProjectId"),
gitlabRepository: text("gitlabRepository"),
gitlabOwner: text("gitlabOwner"),
gitlabBranch: text("gitlabBranch"),
gitlabPathNamespace: text("gitlabPathNamespace"),
// Bitbucket
bitbucketRepository: text("bitbucketRepository"),
bitbucketOwner: text("bitbucketOwner"),
bitbucketBranch: text("bitbucketBranch"),
// Git
customGitUrl: text("customGitUrl"),
customGitBranch: text("customGitBranch"),
@@ -48,7 +61,6 @@ export const compose = pgTable("compose", {
onDelete: "set null",
},
),
//
command: text("command").notNull().default(""),
//
composePath: text("composePath").notNull().default("./docker-compose.yml"),
@@ -59,6 +71,16 @@ export const compose = pgTable("compose", {
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
githubId: text("githubId").references(() => github.githubId, {
onDelete: "set null",
}),
gitlabId: text("gitlabId").references(() => gitlab.gitlabId, {
onDelete: "set null",
}),
bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, {
onDelete: "set null",
}),
});
export const composeRelations = relations(compose, ({ one, many }) => ({
@@ -73,6 +95,18 @@ export const composeRelations = relations(compose, ({ one, many }) => ({
references: [sshKeys.sshKeyId],
}),
domains: many(domains),
github: one(github, {
fields: [compose.githubId],
references: [github.githubId],
}),
gitlab: one(gitlab, {
fields: [compose.gitlabId],
references: [gitlab.gitlabId],
}),
bitbucket: one(bitbucket, {
fields: [compose.bitbucketId],
references: [bitbucket.bitbucketId],
}),
}));
const createSchema = createInsertSchema(compose, {

View File

@@ -0,0 +1,57 @@
import { relations } from "drizzle-orm";
import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { auth } from "./auth";
import { bitbucket } from "./bitbucket";
import { github } from "./github";
import { gitlab } from "./gitlab";
export const gitProviderType = pgEnum("gitProviderType", [
"github",
"gitlab",
"bitbucket",
]);
export const gitProvider = pgTable("git_provider", {
gitProviderId: text("gitProviderId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
providerType: gitProviderType("providerType").notNull().default("github"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
authId: text("authId")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
});
export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({
github: one(github, {
fields: [gitProvider.gitProviderId],
references: [github.gitProviderId],
}),
gitlab: one(gitlab, {
fields: [gitProvider.gitProviderId],
references: [gitlab.gitProviderId],
}),
bitbucket: one(bitbucket, {
fields: [gitProvider.gitProviderId],
references: [bitbucket.gitProviderId],
}),
auth: one(auth, {
fields: [gitProvider.authId],
references: [auth.id],
}),
}));
const createSchema = createInsertSchema(gitProvider);
export const apiRemoveGitProvider = createSchema
.extend({
gitProviderId: z.string().min(1),
})
.pick({ gitProviderId: true });

View File

@@ -0,0 +1,62 @@
import { relations } from "drizzle-orm";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { gitProvider } from "./git-provider";
export const github = pgTable("github", {
githubId: text("githubId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
githubAppName: text("githubAppName"),
githubAppId: integer("githubAppId"),
githubClientId: text("githubClientId"),
githubClientSecret: text("githubClientSecret"),
githubInstallationId: text("githubInstallationId"),
githubPrivateKey: text("githubPrivateKey"),
githubWebhookSecret: text("githubWebhookSecret"),
gitProviderId: text("gitProviderId")
.notNull()
.references(() => gitProvider.gitProviderId, { onDelete: "cascade" }),
});
export const githubProviderRelations = relations(github, ({ one }) => ({
gitProvider: one(gitProvider, {
fields: [github.gitProviderId],
references: [gitProvider.gitProviderId],
}),
}));
const createSchema = createInsertSchema(github);
export const apiCreateGithub = createSchema.extend({
githubAppName: z.string().optional(),
githubAppId: z.number().optional(),
githubClientId: z.string().optional(),
githubClientSecret: z.string().optional(),
githubInstallationId: z.string().optional(),
githubPrivateKey: z.string().optional(),
githubWebhookSecret: z.string().nullable(),
gitProviderId: z.string().optional(),
name: z.string().min(1),
authId: z.string().min(1),
});
export const apiFindGithubBranches = z.object({
repo: z.string().min(1),
owner: z.string().min(1),
githubId: z.string().optional(),
});
export const apiFindOneGithub = createSchema
.extend({
githubId: z.string().min(1),
})
.pick({ githubId: true });
export const apiUpdateGithub = createSchema.extend({
githubId: z.string().min(1),
name: z.string().min(1),
gitProviderId: z.string().min(1),
});

View File

@@ -0,0 +1,70 @@
import { relations } from "drizzle-orm";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { gitProvider } from "./git-provider";
export const gitlab = pgTable("gitlab", {
gitlabId: text("gitlabId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
applicationId: text("application_id"),
redirectUri: text("redirect_uri"),
secret: text("secret"),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
groupName: text("group_name"),
expiresAt: integer("expires_at"),
gitProviderId: text("gitProviderId")
.notNull()
.references(() => gitProvider.gitProviderId, { onDelete: "cascade" }),
});
export const gitlabProviderRelations = relations(gitlab, ({ one }) => ({
gitProvider: one(gitProvider, {
fields: [gitlab.gitProviderId],
references: [gitProvider.gitProviderId],
}),
}));
const createSchema = createInsertSchema(gitlab);
export const apiCreateGitlab = createSchema.extend({
applicationId: z.string().optional(),
secret: z.string().optional(),
groupName: z.string().optional(),
gitProviderId: z.string().optional(),
redirectUri: z.string().optional(),
authId: z.string().min(1),
name: z.string().min(1),
});
export const apiFindOneGitlab = createSchema
.extend({
gitlabId: z.string().min(1),
})
.pick({ gitlabId: true });
export const apiGitlabTestConnection = createSchema
.extend({
groupName: z.string().optional(),
})
.pick({ gitlabId: true, groupName: true });
export const apiFindGitlabBranches = z.object({
id: z.number().optional(),
owner: z.string(),
repo: z.string(),
gitlabId: z.string().optional(),
});
export const apiUpdateGitlab = createSchema.extend({
applicationId: z.string().optional(),
secret: z.string().optional(),
groupName: z.string().optional(),
redirectUri: z.string().optional(),
name: z.string().min(1),
gitlabId: z.string().min(1),
});

View File

@@ -23,3 +23,7 @@ export * from "./compose";
export * from "./registry";
export * from "./notification";
export * from "./ssh-key";
export * from "./git-provider";
export * from "./bitbucket";
export * from "./github";
export * from "./gitlab";

View File

@@ -34,6 +34,9 @@ export const users = pgTable("user", {
canDeleteServices: boolean("canDeleteServices").notNull().default(false),
canAccessToDocker: boolean("canAccessToDocker").notNull().default(false),
canAccessToAPI: boolean("canAccessToAPI").notNull().default(false),
canAccessToGitProviders: boolean("canAccessToGitProviders")
.notNull()
.default(false),
canAccessToTraefikFiles: boolean("canAccessToTraefikFiles")
.notNull()
.default(false),
@@ -109,6 +112,7 @@ export const apiAssignPermissions = createSchema
canAccessToDocker: true,
canAccessToAPI: true,
canAccessToSSHKeys: true,
canAccessToGitProviders: true,
})
.required();

View File

@@ -18,15 +18,7 @@ export type ComposeNested = InferResultType<
>;
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
const writeStream = createWriteStream(logPath, { flags: "a" });
const {
sourceType,
appName,
mounts,
composeType,
env,
composePath,
domains,
} = compose;
const { sourceType, appName, mounts, composeType, domains } = compose;
try {
const command = createCommand(compose);
await writeDomainsToCompose(compose, domains);

View File

@@ -1,12 +1,14 @@
import fs, { existsSync, readFileSync, writeSync } from "node:fs";
import fs, { existsSync, readFileSync } from "node:fs";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import type { Compose } from "@/server/api/services/compose";
import type { Domain } from "@/server/api/services/domain";
import { COMPOSE_PATH } from "@/server/constants";
import { dump, load } from "js-yaml";
import { cloneRawBitbucketRepository } from "../providers/bitbucket";
import { cloneGitRawRepository } from "../providers/git";
import { cloneRawGithubRepository } from "../providers/github";
import { cloneRawGitlabRepository } from "../providers/gitlab";
import { createComposeFileRaw } from "../providers/raw";
import type {
ComposeSpecification,
@@ -17,6 +19,10 @@ import type {
export const cloneCompose = async (compose: Compose) => {
if (compose.sourceType === "github") {
await cloneRawGithubRepository(compose);
} else if (compose.sourceType === "gitlab") {
await cloneRawGitlabRepository(compose);
} else if (compose.sourceType === "bitbucket") {
await cloneRawBitbucketRepository(compose);
} else if (compose.sourceType === "git") {
await cloneGitRawRepository(compose);
} else if (compose.sourceType === "raw") {

View File

@@ -72,6 +72,10 @@ export const getBuildAppDirectory = (application: Application) => {
if (sourceType === "github") {
buildPath = application?.buildPath || "";
} else if (sourceType === "gitlab") {
buildPath = application?.gitlabBuildPath || "";
} else if (sourceType === "bitbucket") {
buildPath = application?.bitbucketBuildPath || "";
} else if (sourceType === "drop") {
buildPath = application?.dropBuildPath || "";
} else if (sourceType === "git") {

View File

@@ -0,0 +1,262 @@
import { createWriteStream } from "node:fs";
import { join } from "node:path";
import { findBitbucketById } from "@/server/api/services/bitbucket";
import type { Compose } from "@/server/api/services/compose";
import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants";
import type {
apiBitbucketTestConnection,
apiFindBitbucketBranches,
} from "@/server/db/schema";
import type { InferResultType } from "@/server/types/with";
import { TRPCError } from "@trpc/server";
import { recreateDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
export type ApplicationWithBitbucket = InferResultType<
"applications",
{ bitbucket: true }
>;
export type ComposeWithBitbucket = InferResultType<
"compose",
{ bitbucket: true }
>;
export const cloneBitbucketRepository = async (
entity: ApplicationWithBitbucket | ComposeWithBitbucket,
logPath: string,
isCompose = false,
) => {
const writeStream = createWriteStream(logPath, { flags: "a" });
const {
appName,
bitbucketRepository,
bitbucketOwner,
bitbucketBranch,
bitbucketId,
bitbucket,
} = entity;
if (!bitbucketId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Bitbucket Provider not found",
});
}
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath);
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
const cloneUrl = `https://${bitbucket?.bitbucketUsername}:${bitbucket?.appPassword}@${repoclone}`;
try {
writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`);
await spawnAsync(
"git",
[
"clone",
"--branch",
bitbucketBranch!,
"--depth",
"1",
cloneUrl,
outputPath,
"--progress",
],
(data) => {
if (writeStream.writable) {
writeStream.write(data);
}
},
);
writeStream.write(`\nCloned ${repoclone} to ${outputPath}: ✅\n`);
} catch (error) {
writeStream.write(`ERROR Clonning: ${error}: ❌`);
throw error;
} finally {
writeStream.end();
}
};
export const cloneRawBitbucketRepository = async (entity: Compose) => {
const {
appName,
bitbucketRepository,
bitbucketOwner,
bitbucketBranch,
bitbucketId,
} = entity;
if (!bitbucketId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Bitbucket Provider not found",
});
}
const bitbucketProvider = await findBitbucketById(bitbucketId);
const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath);
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
const cloneUrl = `https://${bitbucketProvider?.bitbucketUsername}:${bitbucketProvider?.appPassword}@${repoclone}`;
try {
await spawnAsync("git", [
"clone",
"--branch",
bitbucketBranch!,
"--depth",
"1",
cloneUrl,
outputPath,
"--progress",
]);
} catch (error) {
throw error;
}
};
export const getBitbucketRepositories = async (bitbucketId?: string) => {
if (!bitbucketId) {
return [];
}
const bitbucketProvider = await findBitbucketById(bitbucketId);
const username =
bitbucketProvider.bitbucketWorkspaceName ||
bitbucketProvider.bitbucketUsername;
const url = `https://api.bitbucket.org/2.0/repositories/${username}`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}`).toString("base64")}`,
},
});
if (!response.ok) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `Failed to fetch repositories: ${response.statusText}`,
});
}
const data = await response.json();
const mappedData = data.values.map((repo: any) => {
return {
name: repo.name,
url: repo.links.html.href,
owner: {
username: repo.workspace.slug,
},
};
});
return mappedData as {
name: string;
url: string;
owner: {
username: string;
};
}[];
} catch (error) {
throw error;
}
};
export const getBitbucketBranches = async (
input: typeof apiFindBitbucketBranches._type,
) => {
if (!input.bitbucketId) {
return [];
}
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
const { owner, repo } = input;
const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}`).toString("base64")}`,
},
});
if (!response.ok) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `HTTP error! status: ${response.status}`,
});
}
const data = await response.json();
const mappedData = data.values.map((branch: any) => {
return {
name: branch.name,
commit: {
sha: branch.target.hash,
},
};
});
return mappedData as {
name: string;
commit: {
sha: string;
};
}[];
} catch (error) {
throw error;
}
};
export const testBitbucketConnection = async (
input: typeof apiBitbucketTestConnection._type,
) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if (!bitbucketProvider) {
throw new Error("Bitbucket provider not found");
}
const { bitbucketUsername, workspaceName } = input;
const username = workspaceName || bitbucketUsername;
const url = `https://api.bitbucket.org/2.0/repositories/${username}`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Basic ${Buffer.from(`${bitbucketProvider.bitbucketUsername}:${bitbucketProvider.appPassword}`).toString("base64")}`,
},
});
if (!response.ok) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `Failed to fetch repositories: ${response.statusText}`,
});
}
const data = await response.json();
const mappedData = data.values.map((repo: any) => {
return {
name: repo.name,
url: repo.links.html.href,
owner: {
username: repo.workspace.slug,
},
};
}) as [];
return mappedData.length;
} catch (error) {
throw error;
}
};

Some files were not shown because too many files have changed in this diff Show More