forked from cerc-io/snowballtools-base
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a37f81dd6 | ||
|
|
af31fac3ee | ||
|
|
54ae3f429d | ||
|
|
acfe78bf07 | ||
|
|
ce1833cb51 | ||
|
|
f2e59c11fd | ||
|
|
b261e7e436 | ||
|
|
934aa1a26b | ||
|
|
d975390a1b | ||
|
|
f77323364c | ||
|
|
c6a78f2116 | ||
|
|
cff9a5b2ea | ||
|
|
003b83ba21 | ||
|
|
82a1c151a8 | ||
|
|
2ada11f311 | ||
|
|
6e32d0678a | ||
|
|
198478f5fa | ||
|
|
552dfe783e | ||
|
|
e2bf5d052c | ||
|
|
33323b9bbf | ||
|
|
e2a3254563 | ||
|
|
8589bf4094 | ||
|
|
f8ebdfe7aa | ||
|
|
2166b2f800 | ||
|
|
cdd8d15e73 | ||
|
|
dc7b251988 | ||
|
|
bfb4a3f30b | ||
|
|
8f7fc888a9 | ||
|
|
0e9c3a07fd | ||
|
|
61e3e88a6c |
+4
-1
@@ -6,4 +6,7 @@ yarn-error.log
|
|||||||
|
|
||||||
packages/backend/environments/local.toml
|
packages/backend/environments/local.toml
|
||||||
packages/backend/dev/
|
packages/backend/dev/
|
||||||
packages/frontend/dist/
|
packages/frontend/dist/
|
||||||
|
|
||||||
|
# ignore all .DS_Store files
|
||||||
|
**/.DS_Store
|
||||||
|
|||||||
@@ -1,220 +1,23 @@
|
|||||||
# snowballtools-base
|
# snowballtools-base
|
||||||
|
|
||||||
## Setup
|
This is a [yarn workspace](https://yarnpkg.com/features/workspaces) monorepo for the dashboard.
|
||||||
|
|
||||||
- Clone the `snowballtools-base` repo
|
## Getting Started
|
||||||
|
|
||||||
```bash
|
### Install dependencies
|
||||||
git clone git@github.com:snowball-tools/snowballtools-base.git
|
|
||||||
cd snowballtools-base
|
|
||||||
```
|
|
||||||
|
|
||||||
- Install dependencies in root
|
In the root of the project, run:
|
||||||
|
|
||||||
```bash
|
```zsh
|
||||||
yarn
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
- Build packages
|
### Build backend
|
||||||
|
|
||||||
```bash
|
```zsh
|
||||||
yarn build --ignore frontend
|
yarn build --ignore-frontend
|
||||||
```
|
```
|
||||||
|
|
||||||
Let us assume the following domains for backend and frontend
|
### Environment variables, running the development server, and deployment
|
||||||
- Backend server: `api.snowballtools.com`
|
|
||||||
- Frontend app: `dashboard.snowballtools.com`
|
|
||||||
|
|
||||||
- Configuration Files
|
Follow the instructions in the README.md files of the [backend](packages/backend/README.md) and [frontend](packages/frontend/README.md) packages.
|
||||||
|
|
||||||
- For Backend:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp packages/backend/environments/local.toml.example packages/backend/environments/local.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
- Production
|
|
||||||
|
|
||||||
- Update the following in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[server]
|
|
||||||
...
|
|
||||||
[server.session]
|
|
||||||
# Secret should be changed to a different random string
|
|
||||||
secret = "p4yfpkqnddkui2iw7t6hbhwq74lbqs7sidnc382"
|
|
||||||
# Set URL of the frontend app
|
|
||||||
appOriginUrl = "https://dashboard.snowballtools.com"
|
|
||||||
# Set to true for session cookies to work behind proxy
|
|
||||||
trustProxy = true
|
|
||||||
# Set empty domain when using secure connection
|
|
||||||
domain = ""
|
|
||||||
```
|
|
||||||
|
|
||||||
- Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
- [OAuth App Creation](https://github.com/organizations/<org>/settings/applications/new)
|
|
||||||
- Homepage URL: `https://dashboard.snowballtools.com`
|
|
||||||
- Authorization callback URL: `https://dashboard.snowballtools.com/organization/projects/create`
|
|
||||||
- Generate a new client secret after app is created
|
|
||||||
|
|
||||||
- Set `gitHub.webhookUrl` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[gitHub]
|
|
||||||
webhookUrl = "https://api.snowballtools.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Let us assume domain for Laconicd to be `api.laconic.com` and set the following in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[registryConfig]
|
|
||||||
fetchDeploymentRecordDelay = 5000
|
|
||||||
# Use actual port for REST endpoint
|
|
||||||
restEndpoint = "http://api.laconic.com:1317"
|
|
||||||
# Use actual port for GQL endpoint
|
|
||||||
gqlEndpoint = "http://api.laconic.com:9473/api"
|
|
||||||
# Set private key of account to be used in Laconicd
|
|
||||||
privateKey = "0wtu92cd4f1y791ezpjwgzzazni4dmd3q3mzqc3t6i6r9v06ji784tey6hwmnn69"
|
|
||||||
# Set Bond ID to be used for publishing records
|
|
||||||
bondId = "8xk8c2pb61kajwixpm223zvptr2x2ncajq0vd998p6aqhvqqep2reu6pik245epf"
|
|
||||||
chainId = "laconic_9000-1"
|
|
||||||
# Set authority that is existing in the chain
|
|
||||||
authority = "laconic"
|
|
||||||
[registryConfig.fee]
|
|
||||||
amount = "200000"
|
|
||||||
denom = "aphoton"
|
|
||||||
gas = "750000"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Development
|
|
||||||
|
|
||||||
- Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
- [OAuth App Creation](https://github.com/organizations/<org>/settings/applications/new)
|
|
||||||
- Homepage URL: `http://localhost:3000`
|
|
||||||
- Authorization callback URL: `http://localhost:3000/organization/projects/create`
|
|
||||||
- Generate a new client secret after app is created
|
|
||||||
|
|
||||||
- Setup Laconicd
|
|
||||||
- Run the laconicd stack following this [doc](https://git.vdb.to/cerc-io/stack-orchestrator/src/branch/main/docs/laconicd-with-console.md)
|
|
||||||
- Get the private key and set `registryConfig.privateKey` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
laconic-so deployment --dir laconic-loaded-deployment exec laconicd "laconicd keys export mykey --unarmored-hex --unsafe"
|
|
||||||
# WARNING: The private key will be exported as an unarmored hexadecimal string. USE AT YOUR OWN RISK. Continue? [y/N]: y
|
|
||||||
# 754cca7b4b729a99d156913aea95366411d072856666e95ba09ef6c664357d81
|
|
||||||
```
|
|
||||||
|
|
||||||
- Set authority in `registryConfig.authority` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
- Run the script to create bond, reserve the authority and set authority bond
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn test:registry:init
|
|
||||||
# snowball:initialize-registry bondId: 6af0ab81973b93d3511ae79841756fb5da3fd2f70ea1279e81fae7c9b19af6c4 +0ms
|
|
||||||
```
|
|
||||||
|
|
||||||
- Get the bond id and set `registryConfig.bondId` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
- Setup ngrok for GitHub webhooks
|
|
||||||
- [ngrok getting started](https://ngrok.com/docs/getting-started/)
|
|
||||||
- Start ngrok and point to backend server endpoint
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ngrok http http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
- Look for the forwarding URL in ngrok
|
|
||||||
|
|
||||||
```bash
|
|
||||||
Forwarding https://19c1-61-95-158-116.ngrok-free.app -> http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
- Set `gitHub.webhookUrl` in backend [config file](packages/backend/environments/local.toml)
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[gitHub]
|
|
||||||
webhookUrl = "https://19c1-61-95-158-116.ngrok-free.app"
|
|
||||||
```
|
|
||||||
|
|
||||||
- For Frontend:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp packages/frontend/.env.example packages/frontend/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
- Copy the GitHub OAuth app client ID from previous steps and set it in frontend [.env](packages/frontend/.env) file
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_GITHUB_CLIENT_ID = <CLIENT_ID>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Set `VITE_GITHUB_PWA_TEMPLATE_REPO` and `VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO` in [.env](packages/frontend/.env) file
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_GITHUB_PWA_TEMPLATE_REPO = 'cerc-io/test-progressive-web-app' # Set actual owner/name of the template repo that will be used for creating new repo
|
|
||||||
VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO = 'cerc-io/image-upload-pwa-example' # Set actual owner/name of the template repo that will be used for creating new repo
|
|
||||||
```
|
|
||||||
|
|
||||||
- Production
|
|
||||||
|
|
||||||
- Set the following values in [.env](packages/frontend/.env) file
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_SERVER_URL = 'https://api.snowballtools.com' # Backend server endpoint
|
|
||||||
```
|
|
||||||
|
|
||||||
- Sign in to [wallet connect](https://cloud.walletconnect.com/sign-in) to create a project ID
|
|
||||||
- Create a project and add information to use wallet connect SDK
|
|
||||||
- Add project name and select project type as `App`
|
|
||||||
- Set project home page URL to `https://dashboard.snowballtools.com`
|
|
||||||
- On creation of project, use the `Project ID` and set it in `VITE_WALLET_CONNECT_ID` in [.env](packages/frontend/.env) file
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_WALLET_CONNECT_ID = <PROJECT_ID>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Build the React application
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use a web server for hosting static built files
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m http.server -d build 3000
|
|
||||||
```
|
|
||||||
|
|
||||||
- Development
|
|
||||||
|
|
||||||
- Copy the graphQL endpoint from terminal and add the endpoint in the [.env](packages/frontend/.env) file present in `packages/frontend`
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_SERVER_URL = 'http://localhost:8000'
|
|
||||||
```
|
|
||||||
|
|
||||||
- Sign in to [wallet connect](https://cloud.walletconnect.com/sign-in) to create a project ID.
|
|
||||||
- Create a project and add information to use wallet connect SDK
|
|
||||||
- Add project name and select project type as `App`
|
|
||||||
- Project home page URL is not required to be set
|
|
||||||
- On creation of project, use the `Project ID` and set it in `VITE_WALLET_CONNECT_ID` in [.env](packages/frontend/.env) file
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_WALLET_CONNECT_ID = <Project_ID>
|
|
||||||
```
|
|
||||||
|
|
||||||
- The React application will be running in `http://localhost:3000/`
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
- Start the backend server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn start:backend
|
|
||||||
```
|
|
||||||
|
|
||||||
- Start the frontend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn start
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1 +1,76 @@
|
|||||||
# Backend for Snowball Tools
|
# backend
|
||||||
|
|
||||||
|
This backend is a [node.js](https://nodejs.org/) [express.js](https://expressjs.com/) [apollo server](https://www.apollographql.com/docs/apollo-server/) project in a [yarn workspace](https://yarnpkg.com/features/workspaces).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Install dependencies
|
||||||
|
|
||||||
|
In the root of the project, run:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build backend
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
yarn build --ignore-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
#### Local
|
||||||
|
|
||||||
|
Copy the `envionments/local.toml.example` file to `envionments/local.toml`:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
cp envionments/local.toml.example envionments/local.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Staging environment variables
|
||||||
|
|
||||||
|
In the deployment repository, update staging [staging/configmaps/config/prod.toml](https://git.vdb.to/cerc-io/snowballtools-base-api-deployments/src/commit/318c2bc09f334dca79c3501838512749f9431bf1/deployments/staging/configmaps/config/prod.toml)
|
||||||
|
|
||||||
|
#### Production environment variables
|
||||||
|
|
||||||
|
In the deployment repository, update production [production/configmaps/config/prod.toml](https://git.vdb.to/cerc-io/snowballtools-base-api-deployments/src/commit/318c2bc09f334dca79c3501838512749f9431bf1/deployments/production/configmaps/config/prod.toml)
|
||||||
|
|
||||||
|
### Run development server
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Clone the deployer repository:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
git clone git@git.vdb.to:cerc-io/snowballtools-base-api-deployments.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Staging
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
echo trigger >> .gitea/workflows/triggers/staging-deploy
|
||||||
|
git commit -a -m "Deploy v0.0.8" # replace with version number
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
echo trigger >> .gitea/workflows/triggers/production-deploy
|
||||||
|
git commit -a -m "Deploy v0.0.8" # replace with version number
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment status
|
||||||
|
|
||||||
|
Dumb for now
|
||||||
|
|
||||||
|
- [Staging](https://snowballtools-base-api.staging.apps.snowballtools.com/staging/version)
|
||||||
|
- [Production](https://snowballtools-base-api.apps.snowballtools.com/staging/version)
|
||||||
|
|
||||||
|
Update version number manually in [routes/staging.ts](/packages/backend/src/routes/staging.ts)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"express-async-errors": "^3.1.1",
|
||||||
"express-session": "^1.18.0",
|
"express-session": "^1.18.0",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'express-async-errors';
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
return context.user;
|
return context.user;
|
||||||
},
|
},
|
||||||
|
|
||||||
organizations: async (_:any, __: any, context: any) => {
|
organizations: async (_: any, __: any, context: any) => {
|
||||||
return service.getOrganizationsByUserId(context.user);
|
return service.getOrganizationsByUserId(context.user);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -25,8 +25,15 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
return service.getProjectById(projectId);
|
return service.getProjectById(projectId);
|
||||||
},
|
},
|
||||||
|
|
||||||
projectsInOrganization: async (_: any, { organizationSlug }: {organizationSlug: string }, context: any) => {
|
projectsInOrganization: async (
|
||||||
return service.getProjectsInOrganization(context.user, organizationSlug);
|
_: any,
|
||||||
|
{ organizationSlug }: { organizationSlug: string },
|
||||||
|
context: any,
|
||||||
|
) => {
|
||||||
|
return service.getProjectsInOrganization(
|
||||||
|
context.user,
|
||||||
|
organizationSlug,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
deployments: async (_: any, { projectId }: { projectId: string }) => {
|
deployments: async (_: any, { projectId }: { projectId: string }) => {
|
||||||
@@ -35,7 +42,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
environmentVariables: async (
|
environmentVariables: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ projectId }: { projectId: string }
|
{ projectId }: { projectId: string },
|
||||||
) => {
|
) => {
|
||||||
return service.getEnvironmentVariablesByProjectId(projectId);
|
return service.getEnvironmentVariablesByProjectId(projectId);
|
||||||
},
|
},
|
||||||
@@ -44,7 +51,11 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
return service.getProjectMembersByProjectId(projectId);
|
return service.getProjectMembersByProjectId(projectId);
|
||||||
},
|
},
|
||||||
|
|
||||||
searchProjects: async (_: any, { searchText }: { searchText: string }, context: any) => {
|
searchProjects: async (
|
||||||
|
_: any,
|
||||||
|
{ searchText }: { searchText: string },
|
||||||
|
context: any,
|
||||||
|
) => {
|
||||||
return service.searchProjects(context.user, searchText);
|
return service.searchProjects(context.user, searchText);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -52,11 +63,11 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
filter
|
filter,
|
||||||
}: { projectId: string; filter?: FindOptionsWhere<Domain> }
|
}: { projectId: string; filter?: FindOptionsWhere<Domain> },
|
||||||
) => {
|
) => {
|
||||||
return service.getDomainsByProjectId(projectId, filter);
|
return service.getDomainsByProjectId(projectId, filter);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Return error in GQL response
|
// TODO: Return error in GQL response
|
||||||
@@ -64,10 +75,13 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
removeProjectMember: async (
|
removeProjectMember: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ projectMemberId }: { projectMemberId: string },
|
{ projectMemberId }: { projectMemberId: string },
|
||||||
context: any
|
context: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.removeProjectMember(context.user, projectMemberId);
|
return await service.removeProjectMember(
|
||||||
|
context.user,
|
||||||
|
projectMemberId,
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@@ -78,13 +92,13 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
projectMemberId,
|
projectMemberId,
|
||||||
data
|
data,
|
||||||
}: {
|
}: {
|
||||||
projectMemberId: string;
|
projectMemberId: string;
|
||||||
data: {
|
data: {
|
||||||
permissions: Permission[];
|
permissions: Permission[];
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.updateProjectMember(projectMemberId, data);
|
return await service.updateProjectMember(projectMemberId, data);
|
||||||
@@ -98,14 +112,14 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}: {
|
}: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
data: {
|
data: {
|
||||||
email: string;
|
email: string;
|
||||||
permissions: Permission[];
|
permissions: Permission[];
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return Boolean(await service.addProjectMember(projectId, data));
|
return Boolean(await service.addProjectMember(projectId, data));
|
||||||
@@ -119,15 +133,15 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}: {
|
}: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
data: { environments: string[]; key: string; value: string }[];
|
data: { environments: string[]; key: string; value: string }[];
|
||||||
}
|
},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
await service.addEnvironmentVariables(projectId, data)
|
await service.addEnvironmentVariables(projectId, data),
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
@@ -139,16 +153,16 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
environmentVariableId,
|
environmentVariableId,
|
||||||
data
|
data,
|
||||||
}: {
|
}: {
|
||||||
environmentVariableId: string;
|
environmentVariableId: string;
|
||||||
data: DeepPartial<EnvironmentVariable>;
|
data: DeepPartial<EnvironmentVariable>;
|
||||||
}
|
},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.updateEnvironmentVariable(
|
return await service.updateEnvironmentVariable(
|
||||||
environmentVariableId,
|
environmentVariableId,
|
||||||
data
|
data,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
@@ -158,7 +172,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
removeEnvironmentVariable: async (
|
removeEnvironmentVariable: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ environmentVariableId }: { environmentVariableId: string }
|
{ environmentVariableId }: { environmentVariableId: string },
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.removeEnvironmentVariable(environmentVariableId);
|
return await service.removeEnvironmentVariable(environmentVariableId);
|
||||||
@@ -171,10 +185,12 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
updateDeploymentToProd: async (
|
updateDeploymentToProd: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ deploymentId }: { deploymentId: string },
|
{ deploymentId }: { deploymentId: string },
|
||||||
context: any
|
context: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return Boolean(await service.updateDeploymentToProd(context.user, deploymentId));
|
return Boolean(
|
||||||
|
await service.updateDeploymentToProd(context.user, deploymentId),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@@ -185,9 +201,9 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
organizationSlug,
|
organizationSlug,
|
||||||
data
|
data,
|
||||||
}: { organizationSlug: string; data: DeepPartial<Project> },
|
}: { organizationSlug: string; data: DeepPartial<Project> },
|
||||||
context: any
|
context: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.addProject(context.user, organizationSlug, data);
|
return await service.addProject(context.user, organizationSlug, data);
|
||||||
@@ -199,7 +215,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
updateProject: async (
|
updateProject: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ projectId, data }: { projectId: string; data: DeepPartial<Project> }
|
{ projectId, data }: { projectId: string; data: DeepPartial<Project> },
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.updateProject(projectId, data);
|
return await service.updateProject(projectId, data);
|
||||||
@@ -212,10 +228,12 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
redeployToProd: async (
|
redeployToProd: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ deploymentId }: { deploymentId: string },
|
{ deploymentId }: { deploymentId: string },
|
||||||
context: any
|
context: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return Boolean(await service.redeployToProd(context.user, deploymentId));
|
return Boolean(
|
||||||
|
await service.redeployToProd(context.user, deploymentId),
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@@ -244,8 +262,8 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
_: any,
|
_: any,
|
||||||
{
|
{
|
||||||
projectId,
|
projectId,
|
||||||
deploymentId
|
deploymentId,
|
||||||
}: { deploymentId: string; projectId: string }
|
}: { deploymentId: string; projectId: string },
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.rollbackDeployment(projectId, deploymentId);
|
return await service.rollbackDeployment(projectId, deploymentId);
|
||||||
@@ -257,9 +275,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
deleteDeployment: async (
|
deleteDeployment: async (
|
||||||
_: any,
|
_: any,
|
||||||
{
|
{ deploymentId }: { deploymentId: string },
|
||||||
deploymentId
|
|
||||||
}: { deploymentId: string; }
|
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.deleteDeployment(deploymentId);
|
return await service.deleteDeployment(deploymentId);
|
||||||
@@ -271,7 +287,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
addDomain: async (
|
addDomain: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ projectId, data }: { projectId: string; data: { name: string } }
|
{ projectId, data }: { projectId: string; data: { name: string } },
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return Boolean(await service.addDomain(projectId, data));
|
return Boolean(await service.addDomain(projectId, data));
|
||||||
@@ -283,7 +299,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
updateDomain: async (
|
updateDomain: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ domainId, data }: { domainId: string; data: DeepPartial<Domain> }
|
{ domainId, data }: { domainId: string; data: DeepPartial<Domain> },
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.updateDomain(domainId, data);
|
return await service.updateDomain(domainId, data);
|
||||||
@@ -296,7 +312,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
authenticateGitHub: async (
|
authenticateGitHub: async (
|
||||||
_: any,
|
_: any,
|
||||||
{ code }: { code: string },
|
{ code }: { code: string },
|
||||||
context: any
|
context: any,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
return await service.authenticateGitHub(code, context.user);
|
return await service.authenticateGitHub(code, context.user);
|
||||||
@@ -308,12 +324,14 @@ export const createResolvers = async (service: Service): Promise<any> => {
|
|||||||
|
|
||||||
unauthenticateGitHub: async (_: any, __: object, context: any) => {
|
unauthenticateGitHub: async (_: any, __: object, context: any) => {
|
||||||
try {
|
try {
|
||||||
return service.unauthenticateGitHub(context.user, { gitHubToken: null });
|
return service.unauthenticateGitHub(context.user, {
|
||||||
|
gitHubToken: null,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,19 @@ import { authenticateUser, createUser } from '../turnkey-backend';
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Access Code
|
||||||
|
//
|
||||||
|
router.post('/accesscode', async (req, res) => {
|
||||||
|
console.log('Access Code', req.body);
|
||||||
|
const { accesscode } = req.body;
|
||||||
|
if (accesscode === '44444') {
|
||||||
|
return res.send({ isValid: true });
|
||||||
|
} else {
|
||||||
|
return res.sendStatus(204);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Turnkey
|
// Turnkey
|
||||||
//
|
//
|
||||||
@@ -19,6 +32,7 @@ router.get('/registration/:email', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.post('/register', async (req, res) => {
|
router.post('/register', async (req, res) => {
|
||||||
|
console.log('Register', req.body);
|
||||||
const { email, challenge, attestation } = req.body;
|
const { email, challenge, attestation } = req.body;
|
||||||
const user = await createUser(req.app.get('service'), {
|
const user = await createUser(req.app.get('service'), {
|
||||||
challenge,
|
challenge,
|
||||||
@@ -31,6 +45,7 @@ router.post('/register', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.post('/authenticate', async (req, res) => {
|
router.post('/authenticate', async (req, res) => {
|
||||||
|
console.log('Authenticate', req.body);
|
||||||
const { signedWhoamiRequest } = req.body;
|
const { signedWhoamiRequest } = req.body;
|
||||||
const user = await authenticateUser(
|
const user = await authenticateUser(
|
||||||
req.app.get('service'),
|
req.app.get('service'),
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/version', async (req, res) => {
|
||||||
|
return res.send({ version: '0.0.8' });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -17,6 +17,7 @@ import { ServerConfig } from './config';
|
|||||||
import { DEFAULT_GQL_PATH } from './constants';
|
import { DEFAULT_GQL_PATH } from './constants';
|
||||||
import githubRouter from './routes/github';
|
import githubRouter from './routes/github';
|
||||||
import authRouter from './routes/auth';
|
import authRouter from './routes/auth';
|
||||||
|
import stagingRouter from './routes/staging';
|
||||||
import { Service } from './service';
|
import { Service } from './service';
|
||||||
|
|
||||||
const log = debug('snowball:server');
|
const log = debug('snowball:server');
|
||||||
@@ -109,6 +110,12 @@ export const createAndStartServer = async (
|
|||||||
app.set('service', service);
|
app.set('service', service);
|
||||||
app.use('/auth', authRouter);
|
app.use('/auth', authRouter);
|
||||||
app.use('/api/github', githubRouter);
|
app.use('/api/github', githubRouter);
|
||||||
|
app.use('/staging', stagingRouter);
|
||||||
|
|
||||||
|
app.use((err: any, req: any, res: any, next: any) => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
});
|
||||||
|
|
||||||
httpServer.listen(port, host, () => {
|
httpServer.listen(port, host, () => {
|
||||||
log(`Server is listening on ${host}:${port}${server.graphqlPath}`);
|
log(`Server is listening on ${host}:${port}${server.graphqlPath}`);
|
||||||
|
|||||||
+291
-235
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
|||||||
# deployer
|
# deployer
|
||||||
|
|
||||||
- Install dependencies
|
- Install dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install jq # if you do not have jq installed already
|
brew install jq # if you do not have jq installed already
|
||||||
```
|
```
|
||||||
@@ -12,13 +14,13 @@
|
|||||||
|
|
||||||
- To deploy frontend app to `dashboard.staging.apps.snowballtools.com`
|
- To deploy frontend app to `dashboard.staging.apps.snowballtools.com`
|
||||||
|
|
||||||
```
|
```bash
|
||||||
./deploy-frontend.staging.sh
|
./deploy-frontend.staging.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
- To deploy frontend app to `dashboard.apps.snowballtools.com`
|
- To deploy frontend app to `dashboard.apps.snowballtools.com`
|
||||||
|
|
||||||
```
|
```bash
|
||||||
./deploy-frontend.sh
|
./deploy-frontend.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Any config env can be updated in [records/application-deployment-request.yml](records/application-deployment-request.yml)
|
- Any config env can be updated in [records/application-deployment-request.yml](records/application-deployment-request.yml)
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
record:
|
record:
|
||||||
...
|
...
|
||||||
@@ -35,6 +38,7 @@
|
|||||||
LACONIC_HOSTED_CONFIG_app_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
LACONIC_HOSTED_CONFIG_app_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
- On changing `LACONIC_HOSTED_CONFIG_app_github_clientid`, the GitHub client ID and secret need to be changed in backend config too
|
- On changing `LACONIC_HOSTED_CONFIG_app_github_clientid`, the GitHub client ID and secret need to be changed in backend config too
|
||||||
|
|
||||||
## Troubleshoot
|
## Troubleshoot
|
||||||
@@ -44,6 +48,7 @@
|
|||||||
|
|
||||||
- If deployment fails due to low bond balance
|
- If deployment fails due to low bond balance
|
||||||
- Check balances
|
- Check balances
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Account balance
|
# Account balance
|
||||||
yarn laconic cns account get
|
yarn laconic cns account get
|
||||||
@@ -51,7 +56,9 @@
|
|||||||
# Bond balance
|
# Bond balance
|
||||||
yarn laconic cns bond get --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32
|
yarn laconic cns bond get --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32
|
||||||
```
|
```
|
||||||
|
|
||||||
- Command to refill bond
|
- Command to refill bond
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn laconic cns bond refill --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32 --type aphoton --quantity 10000000
|
yarn laconic cns bond refill --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32 --type aphoton --quantity 10000000
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ services:
|
|||||||
restEndpoint: 'http://console.laconic.com:1317'
|
restEndpoint: 'http://console.laconic.com:1317'
|
||||||
gqlEndpoint: 'http://console.laconic.com:9473/api'
|
gqlEndpoint: 'http://console.laconic.com:9473/api'
|
||||||
userKey: 87d00f66a73e2ca428adeb49ba9164d0ad9a87edc60e33d46ad3031b9c5701fe
|
userKey: 87d00f66a73e2ca428adeb49ba9164d0ad9a87edc60e33d46ad3031b9c5701fe
|
||||||
bondId: 098c906850b87412f02200e41f449bc79e055eab77acfef32c0b22443bb46661
|
bondId: 89c75c7bc5759861d10285aff6f9e7227d6855e446b77ad5d8324822dfec7deb
|
||||||
chainId: laconic_9000-1
|
chainId: laconic_9000-1
|
||||||
gas: 550000
|
gas: 1200000
|
||||||
fees: 200000aphoton
|
fees: 200000aphoton
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; t
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate application-deployment-request.yml
|
# Generate application-deployment-request.yml
|
||||||
cat > ./records/application-deployment-request.yml <<EOF
|
cat >./records/application-deployment-request.yml <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationDeploymentRequest
|
type: ApplicationDeploymentRequest
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
@@ -37,9 +37,9 @@ record:
|
|||||||
env:
|
env:
|
||||||
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_github_clientid: b7c63b235ca1dd5639ab
|
LACONIC_HOSTED_CONFIG_github_clientid: b7c63b235ca1dd5639ab
|
||||||
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools-platform/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools-platform/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
|
||||||
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
||||||
meta:
|
meta:
|
||||||
note: Added by Snowball @ $CURRENT_DATE_TIME
|
note: Added by Snowball @ $CURRENT_DATE_TIME
|
||||||
@@ -48,7 +48,7 @@ record:
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Generate application-record.yml with incremented version
|
# Generate application-record.yml with incremented version
|
||||||
cat > ./records/application-record.yml <<EOF
|
cat >./records/application-record.yml <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationRecord
|
type: ApplicationRecord
|
||||||
version: $NEW_APPLICATION_VERSION
|
version: $NEW_APPLICATION_VERSION
|
||||||
@@ -64,7 +64,13 @@ echo "Files generated successfully."
|
|||||||
RECORD_FILE=records/application-record.yml
|
RECORD_FILE=records/application-record.yml
|
||||||
|
|
||||||
# Publish ApplicationRecord
|
# Publish ApplicationRecord
|
||||||
RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
|
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to publish record"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
RECORD_ID=$(echo $publish_response | jq -r '.id')
|
||||||
echo "ApplicationRecord published"
|
echo "ApplicationRecord published"
|
||||||
echo $RECORD_ID
|
echo $RECORD_ID
|
||||||
|
|
||||||
@@ -73,15 +79,36 @@ REGISTRY_APP_CRN="crn://snowballtools/applications/snowballtools-base-frontend"
|
|||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set name: $REGISTRY_APP_CRN@${PACKAGE_VERSION}"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set hash"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
# Set name if latest release
|
# Set name if latest release
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set release"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
echo "$REGISTRY_APP_CRN set for ApplicationRecord"
|
echo "$REGISTRY_APP_CRN set for ApplicationRecord"
|
||||||
|
|
||||||
# Check if record found for REGISTRY_APP_CRN
|
# Check if record found for REGISTRY_APP_CRN
|
||||||
APP_RECORD=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN" | jq '.[0]')
|
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN")
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to query name"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
APP_RECORD=$(echo $query_response | jq '.[0]')
|
||||||
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
|
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
|
||||||
echo "No record found for $REGISTRY_APP_CRN."
|
echo "No record found for $REGISTRY_APP_CRN."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -90,6 +117,11 @@ fi
|
|||||||
RECORD_FILE=records/application-deployment-request.yml
|
RECORD_FILE=records/application-deployment-request.yml
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
|
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to query deployment request"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
DEPLOYMENT_REQUEST_ID=$(echo $deployment_response | jq -r '.id')
|
||||||
echo "ApplicationDeploymentRequest published"
|
echo "ApplicationDeploymentRequest published"
|
||||||
echo $DEPLOYMENT_REQUEST_ID
|
echo $DEPLOYMENT_REQUEST_ID
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; t
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate application-deployment-request.yml
|
# Generate application-deployment-request.yml
|
||||||
cat > ./staging-records/application-deployment-request.yml <<EOF
|
cat >./staging-records/application-deployment-request.yml <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationDeploymentRequest
|
type: ApplicationDeploymentRequest
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
@@ -36,16 +36,16 @@ record:
|
|||||||
config:
|
config:
|
||||||
env:
|
env:
|
||||||
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
|
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_github_clientid: 905c09553f527d2cdff5
|
LACONIC_HOSTED_CONFIG_github_clientid: Ov23liOaoahRTYd4nSCV
|
||||||
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
|
||||||
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
||||||
LACONIC_HOSTED_CONFIG_lit_relay_api_key: 15DDD969-E75F-404D-AAD9-58A37C4FD354_snowball
|
LACONIC_HOSTED_CONFIG_lit_relay_api_key: 15DDD969-E75F-404D-AAD9-58A37C4FD354_snowball
|
||||||
LACONIC_HOSTED_CONFIG_aplchemy_api_key: THvPart_gqI5x02RNYSBntlmwA66I_qc
|
LACONIC_HOSTED_CONFIG_aplchemy_api_key: THvPart_gqI5x02RNYSBntlmwA66I_qc
|
||||||
LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
|
LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
|
||||||
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: localhost
|
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com/
|
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com
|
||||||
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
|
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
|
||||||
meta:
|
meta:
|
||||||
note: Added by Snowball @ $CURRENT_DATE_TIME
|
note: Added by Snowball @ $CURRENT_DATE_TIME
|
||||||
@@ -54,7 +54,7 @@ record:
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Generate application-record.yml with incremented version
|
# Generate application-record.yml with incremented version
|
||||||
cat > ./staging-records/application-record.yml <<EOF
|
cat >./staging-records/application-record.yml <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationRecord
|
type: ApplicationRecord
|
||||||
version: $NEW_APPLICATION_VERSION
|
version: $NEW_APPLICATION_VERSION
|
||||||
@@ -70,7 +70,13 @@ echo "Files generated successfully."
|
|||||||
RECORD_FILE=staging-records/application-record.yml
|
RECORD_FILE=staging-records/application-record.yml
|
||||||
|
|
||||||
# Publish ApplicationRecord
|
# Publish ApplicationRecord
|
||||||
RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
|
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to publish record"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
RECORD_ID=$(echo $publish_response | jq -r '.id')
|
||||||
echo "ApplicationRecord published"
|
echo "ApplicationRecord published"
|
||||||
echo $RECORD_ID
|
echo $RECORD_ID
|
||||||
|
|
||||||
@@ -79,15 +85,36 @@ REGISTRY_APP_CRN="crn://staging-snowballtools/applications/staging-snowballtools
|
|||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set name: $REGISTRY_APP_CRN@${PACKAGE_VERSION}"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set hash"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
# Set name if latest release
|
# Set name if latest release
|
||||||
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID"
|
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID"
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to set release"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
echo "$REGISTRY_APP_CRN set for ApplicationRecord"
|
echo "$REGISTRY_APP_CRN set for ApplicationRecord"
|
||||||
|
|
||||||
# Check if record found for REGISTRY_APP_CRN
|
# Check if record found for REGISTRY_APP_CRN
|
||||||
APP_RECORD=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN" | jq '.[0]')
|
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN")
|
||||||
|
rc=$?
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to query name"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
APP_RECORD=$(echo $query_response | jq '.[0]')
|
||||||
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
|
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
|
||||||
echo "No record found for $REGISTRY_APP_CRN."
|
echo "No record found for $REGISTRY_APP_CRN."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -96,6 +123,11 @@ fi
|
|||||||
RECORD_FILE=staging-records/application-deployment-request.yml
|
RECORD_FILE=staging-records/application-deployment-request.yml
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
|
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "FATAL: Failed to query deployment request"
|
||||||
|
exit $rc
|
||||||
|
fi
|
||||||
|
DEPLOYMENT_REQUEST_ID=$(echo $deployment_response | jq -r '.id')
|
||||||
echo "ApplicationDeploymentRequest published"
|
echo "ApplicationDeploymentRequest published"
|
||||||
echo $DEPLOYMENT_REQUEST_ID
|
echo $DEPLOYMENT_REQUEST_ID
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
record:
|
record:
|
||||||
type: ApplicationDeploymentRequest
|
type: ApplicationDeploymentRequest
|
||||||
version: '1.0.0'
|
version: "1.0.0"
|
||||||
name: snowballtools-base-frontend@0.1.8
|
name: snowballtools-base-frontend@0.1.8
|
||||||
application: crn://snowballtools/applications/snowballtools-base-frontend@0.1.8
|
application: crn://snowballtools/applications/snowballtools-base-frontend@0.1.8
|
||||||
dns: dashboard
|
dns: dashboard
|
||||||
@@ -8,9 +8,9 @@ record:
|
|||||||
env:
|
env:
|
||||||
LACONIC_HOSTED_CONFIG_app_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
LACONIC_HOSTED_CONFIG_app_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_app_github_clientid: b7c63b235ca1dd5639ab
|
LACONIC_HOSTED_CONFIG_app_github_clientid: b7c63b235ca1dd5639ab
|
||||||
LACONIC_HOSTED_CONFIG_app_github_templaterepo: snowball-tools-platform/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_app_github_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_app_github_pwa_templaterepo: snowball-tools-platform/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_app_github_pwa_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools-platform/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
|
||||||
LACONIC_HOSTED_CONFIG_app_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
LACONIC_HOSTED_CONFIG_app_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
||||||
meta:
|
meta:
|
||||||
note: Added by Snowball @ Thu Apr 4 14:49:41 UTC 2024
|
note: Added by Snowball @ Thu Apr 4 14:49:41 UTC 2024
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ record:
|
|||||||
config:
|
config:
|
||||||
env:
|
env:
|
||||||
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
|
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_github_clientid: 905c09553f527d2cdff5
|
LACONIC_HOSTED_CONFIG_github_clientid: Ov23liOaoahRTYd4nSCV
|
||||||
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app
|
LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/test-progressive-web-app
|
||||||
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/image-upload-pwa-example
|
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
|
||||||
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
|
||||||
LACONIC_HOSTED_CONFIG_lit_relay_api_key: 15DDD969-E75F-404D-AAD9-58A37C4FD354_snowball
|
LACONIC_HOSTED_CONFIG_lit_relay_api_key: 15DDD969-E75F-404D-AAD9-58A37C4FD354_snowball
|
||||||
LACONIC_HOSTED_CONFIG_aplchemy_api_key: THvPart_gqI5x02RNYSBntlmwA66I_qc
|
LACONIC_HOSTED_CONFIG_aplchemy_api_key: THvPart_gqI5x02RNYSBntlmwA66I_qc
|
||||||
LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
|
LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
|
||||||
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: localhost
|
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com
|
||||||
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com/
|
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com
|
||||||
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
|
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
|
||||||
meta:
|
meta:
|
||||||
note: Added by Snowball @ Tuesday 21 May 2024 06:17:23 AM UTC
|
note: Added by Snowball @ Sat Jun 22 02:49:45 UTC 2024
|
||||||
repository: "https://git.vdb.to/cerc-io/snowballtools-base"
|
repository: "https://git.vdb.to/cerc-io/snowballtools-base"
|
||||||
repository_ref: 8488cfab8353321ed05c4234bf1b914c9ad3aa99
|
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
record:
|
record:
|
||||||
type: ApplicationRecord
|
type: ApplicationRecord
|
||||||
version: 0.0.2
|
version: 0.0.1
|
||||||
repository_ref: 8488cfab8353321ed05c4234bf1b914c9ad3aa99
|
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601
|
||||||
repository: ["https://git.vdb.to/cerc-io/snowballtools-base"]
|
repository: ["https://git.vdb.to/cerc-io/snowballtools-base"]
|
||||||
app_type: webapp
|
app_type: webapp
|
||||||
name: staging-snowballtools-base-frontend
|
name: staging-snowballtools-base-frontend
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Check if the live web app deployer is in a working state
|
Check if the live web app deployer is in a working state
|
||||||
|
|
||||||
- Web app repo used: https://github.com/snowball-tools-platform/test-progressive-web-app (main branch)
|
- Web app repo used: <https://github.com/snowball-tools/test-progressive-web-app> (main branch)
|
||||||
- Config used: [../config.yml](../config.yml)
|
- Config used: [../config.yml](../config.yml)
|
||||||
- The script [test-webapp-deployment-undeployment.sh](./test-webapp-deployment-undeployment.sh) performs the following:
|
- The script [test-webapp-deployment-undeployment.sh](./test-webapp-deployment-undeployment.sh) performs the following:
|
||||||
- Create / update [`ApplicationRecord`](./records/application-record.yml) and [`ApplicationDeploymentRequest`](./records/application-deployment-request.yml) records with latest meta data from the repo
|
- Create / update [`ApplicationRecord`](./records/application-record.yml) and [`ApplicationDeploymentRequest`](./records/application-deployment-request.yml) records with latest meta data from the repo
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
record:
|
record:
|
||||||
type: ApplicationDeploymentRequest
|
type: ApplicationDeploymentRequest
|
||||||
version: '1.0.0'
|
version: "1.0.0"
|
||||||
name: deployment-test-app@0.1.24
|
name: deployment-test-app@0.1.24
|
||||||
application: crn://snowballtools/applications/deployment-test-app@0.1.24
|
application: crn://snowballtools/applications/deployment-test-app@0.1.24
|
||||||
dns: deployment-ci-test
|
dns: deployment-ci-test
|
||||||
@@ -11,5 +11,5 @@ record:
|
|||||||
CERC_WEBAPP_DEBUG: 0
|
CERC_WEBAPP_DEBUG: 0
|
||||||
meta:
|
meta:
|
||||||
note: Deployment test @ Thu 11 Apr 2024 07:29:19 AM UTC
|
note: Deployment test @ Thu 11 Apr 2024 07:29:19 AM UTC
|
||||||
repository: "https://github.com/snowball-tools-platform/test-progressive-web-app"
|
repository: "https://github.com/snowball-tools/test-progressive-web-app"
|
||||||
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
|
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ record:
|
|||||||
type: ApplicationRecord
|
type: ApplicationRecord
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
|
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
|
||||||
repository: ["https://github.com/snowball-tools-platform/test-progressive-web-app"]
|
repository: ["https://github.com/snowball-tools/test-progressive-web-app"]
|
||||||
app_type: webapp
|
app_type: webapp
|
||||||
name: deployment-test-app
|
name: deployment-test-app
|
||||||
app_version: 0.1.24
|
app_version: 0.1.24
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Repository URL
|
# Repository URL
|
||||||
REPO_URL="https://github.com/snowball-tools-platform/test-progressive-web-app"
|
REPO_URL="https://github.com/snowball-tools/test-progressive-web-app"
|
||||||
|
|
||||||
# Get the latest commit hash from the repository
|
# Get the latest commit hash from the repository
|
||||||
LATEST_HASH=$(git ls-remote $REPO_URL HEAD | awk '{print $1}')
|
LATEST_HASH=$(git ls-remote $REPO_URL HEAD | awk '{print $1}')
|
||||||
@@ -32,7 +32,7 @@ fi
|
|||||||
# Generate application-record.yml with incremented version
|
# Generate application-record.yml with incremented version
|
||||||
RECORD_FILE=packages/deployer/test/records/application-record.yml
|
RECORD_FILE=packages/deployer/test/records/application-record.yml
|
||||||
|
|
||||||
cat > $RECORD_FILE <<EOF
|
cat >$RECORD_FILE <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationRecord
|
type: ApplicationRecord
|
||||||
version: $NEW_APPLICATION_VERSION
|
version: $NEW_APPLICATION_VERSION
|
||||||
@@ -46,7 +46,7 @@ EOF
|
|||||||
# Generate application-deployment-request.yml
|
# Generate application-deployment-request.yml
|
||||||
REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-request.yml
|
REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-request.yml
|
||||||
|
|
||||||
cat > $REQUEST_RECORD_FILE <<EOF
|
cat >$REQUEST_RECORD_FILE <<EOF
|
||||||
record:
|
record:
|
||||||
type: ApplicationDeploymentRequest
|
type: ApplicationDeploymentRequest
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
@@ -115,7 +115,7 @@ while true; do
|
|||||||
else
|
else
|
||||||
echo "ApplicationDeploymentRecord not found, retrying in $RETRY_INTERVAL sec..."
|
echo "ApplicationDeploymentRecord not found, retrying in $RETRY_INTERVAL sec..."
|
||||||
sleep $RETRY_INTERVAL
|
sleep $RETRY_INTERVAL
|
||||||
retry_count=$((retry_count+1))
|
retry_count=$((retry_count + 1))
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "ApplicationDeploymentRecord found"
|
echo "ApplicationDeploymentRecord found"
|
||||||
@@ -154,7 +154,7 @@ while true; do
|
|||||||
else
|
else
|
||||||
echo "Deployment URL $fetched_url is not active, received code $url_response, retrying in $retry_interval sec..."
|
echo "Deployment URL $fetched_url is not active, received code $url_response, retrying in $retry_interval sec..."
|
||||||
sleep $retry_interval
|
sleep $retry_interval
|
||||||
retry_count=$((retry_count+1))
|
retry_count=$((retry_count + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -162,7 +162,7 @@ done
|
|||||||
# Generate application-deployment-removal-request.yml
|
# Generate application-deployment-removal-request.yml
|
||||||
REMOVAL_REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-removal-request.yml
|
REMOVAL_REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-removal-request.yml
|
||||||
|
|
||||||
cat > $REMOVAL_REQUEST_RECORD_FILE <<EOF
|
cat >$REMOVAL_REQUEST_RECORD_FILE <<EOF
|
||||||
record:
|
record:
|
||||||
deployment: $DEPLOYMENT_RECORD_ID
|
deployment: $DEPLOYMENT_RECORD_ID
|
||||||
type: ApplicationDeploymentRemovalRequest
|
type: ApplicationDeploymentRemovalRequest
|
||||||
@@ -190,7 +190,7 @@ while true; do
|
|||||||
else
|
else
|
||||||
echo "ApplicationDeploymentRemovalRecord not found, retrying in $RETRY_INTERVAL sec..."
|
echo "ApplicationDeploymentRemovalRecord not found, retrying in $RETRY_INTERVAL sec..."
|
||||||
sleep $RETRY_INTERVAL
|
sleep $RETRY_INTERVAL
|
||||||
retry_count=$((retry_count+1))
|
retry_count=$((retry_count + 1))
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "ApplicationDeploymentRemovalRecord found"
|
echo "ApplicationDeploymentRemovalRecord found"
|
||||||
@@ -217,7 +217,7 @@ while true; do
|
|||||||
else
|
else
|
||||||
echo "Deployment URL $fetched_url is still active, received code $url_response, retrying in $retry_interval sec..."
|
echo "Deployment URL $fetched_url is still active, received code $url_response, retrying in $retry_interval sec..."
|
||||||
sleep $retry_interval
|
sleep $retry_interval
|
||||||
retry_count=$((retry_count+1))
|
retry_count=$((retry_count + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ VITE_BUGSNAG_API_KEY=
|
|||||||
|
|
||||||
VITE_PASSKEY_WALLET_RPID=
|
VITE_PASSKEY_WALLET_RPID=
|
||||||
VITE_TURNKEY_API_BASE_URL=
|
VITE_TURNKEY_API_BASE_URL=
|
||||||
VITE_TURNKEY_ORGANIZATION_ID=
|
VITE_TURNKEY_ORGANIZATION_ID=
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { join, dirname } from 'path';
|
|||||||
function getAbsolutePath(value: string): any {
|
function getAbsolutePath(value: string): any {
|
||||||
return dirname(require.resolve(join(value, 'package.json')));
|
return dirname(require.resolve(join(value, 'package.json')));
|
||||||
}
|
}
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||||
addons: [
|
addons: [
|
||||||
@@ -26,6 +27,7 @@ const config: StorybookConfig = {
|
|||||||
docs: {
|
docs: {
|
||||||
autodocs: 'tag',
|
autodocs: 'tag',
|
||||||
},
|
},
|
||||||
|
staticDirs: ['../public'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
+56
-23
@@ -1,30 +1,63 @@
|
|||||||
# React + TypeScript + Vite
|
# frontend
|
||||||
|
|
||||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
This is a [vite](https://vitejs.dev/) [react](https://reactjs.org/) [nextjs](https://nextjs.org/) project in a [yarn workspace](https://yarnpkg.com/features/workspaces).
|
||||||
|
|
||||||
Currently, two official plugins are available:
|
## Getting Started
|
||||||
|
|
||||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
### Install dependencies
|
||||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
||||||
|
|
||||||
## Expanding the ESLint configuration
|
In the root of the project, run:
|
||||||
|
|
||||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
```zsh
|
||||||
|
yarn
|
||||||
- Configure the top-level `parserOptions` property like this:
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
// other rules...
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
sourceType: 'module',
|
|
||||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
### Build backend
|
||||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
|
||||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
```zsh
|
||||||
|
yarn build --ignore-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
#### Local
|
||||||
|
|
||||||
|
Copy the `.env.example` file to `.env`:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Staging environment variables
|
||||||
|
|
||||||
|
Change in [deployer/deploy-frontend.staging.sh](/packages/deployer/deploy-frontend.staging.sh)
|
||||||
|
|
||||||
|
#### Production environment variables
|
||||||
|
|
||||||
|
Change in [deployer/deploy-frontend.sh](/packages/deployer/deploy-frontend.sh)
|
||||||
|
|
||||||
|
### Run development server
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
From the root of the project, run:
|
||||||
|
|
||||||
|
### Staging
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
cd packages/deployer && ./deploy-frontend.staging.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
cd packages/deployer && ./deploy-frontend.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment status
|
||||||
|
|
||||||
|
Check the status of the deployment [here](https://webapp-deployer.apps.snowballtools.com)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
import Projects from './pages/org-slug';
|
import Projects from './pages/org-slug';
|
||||||
@@ -10,8 +11,8 @@ import ProjectSearchLayout from './layouts/ProjectSearch';
|
|||||||
import Index from './pages';
|
import Index from './pages';
|
||||||
import AuthPage from './pages/AuthPage';
|
import AuthPage from './pages/AuthPage';
|
||||||
import { DashboardLayout } from './pages/org-slug/layout';
|
import { DashboardLayout } from './pages/org-slug/layout';
|
||||||
import { useEffect } from 'react';
|
|
||||||
import Web3Provider from 'context/Web3Provider';
|
import Web3Provider from 'context/Web3Provider';
|
||||||
|
import { baseUrl } from 'utils/constants';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -59,7 +60,7 @@ function App() {
|
|||||||
// Hacky way of checking session
|
// Hacky way of checking session
|
||||||
// TODO: Handle redirect backs
|
// TODO: Handle redirect backs
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(`${import.meta.env.VITE_SERVER_URL}/auth/session`, {
|
fetch(`${baseUrl}/auth/session`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
|
import {
|
||||||
|
VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO,
|
||||||
|
VITE_GITHUB_PWA_TEMPLATE_REPO,
|
||||||
|
} from 'utils/constants';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Progressive Web App (PWA)',
|
name: 'Progressive Web App (PWA)',
|
||||||
icon: 'pwa',
|
icon: 'pwa',
|
||||||
repoFullName: `${import.meta.env.VITE_GITHUB_PWA_TEMPLATE_REPO}`,
|
repoFullName: `${VITE_GITHUB_PWA_TEMPLATE_REPO}`,
|
||||||
isComingSoon: false,
|
isComingSoon: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
name: 'Image Upload PWA',
|
name: 'Image Upload PWA',
|
||||||
icon: 'pwa',
|
icon: 'pwa',
|
||||||
repoFullName: `${import.meta.env.VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
|
repoFullName: `${VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
|
||||||
isComingSoon: false,
|
isComingSoon: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ import { useToast } from '../../shared/Toast';
|
|||||||
import { IconWithFrame } from '../../shared/IconWithFrame';
|
import { IconWithFrame } from '../../shared/IconWithFrame';
|
||||||
import { Heading } from '../../shared/Heading';
|
import { Heading } from '../../shared/Heading';
|
||||||
import { MockConnectGitCard } from './MockConnectGitCard';
|
import { MockConnectGitCard } from './MockConnectGitCard';
|
||||||
|
import { VITE_GITHUB_CLIENT_ID } from 'utils/constants';
|
||||||
|
|
||||||
const SCOPES = 'repo user';
|
const SCOPES = 'repo user';
|
||||||
const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${
|
const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${VITE_GITHUB_CLIENT_ID}&scope=${encodeURIComponent(SCOPES)}`;
|
||||||
import.meta.env.VITE_GITHUB_CLIENT_ID
|
|
||||||
}&scope=${encodeURIComponent(SCOPES)}`;
|
|
||||||
|
|
||||||
interface ConnectAccountInterface {
|
interface ConnectAccountInterface {
|
||||||
onAuth: (token: string) => void;
|
onAuth: (token: string) => void;
|
||||||
|
|||||||
+27
-37
@@ -1,13 +1,8 @@
|
|||||||
import { CopyBlock, atomOneLight } from 'react-code-blocks';
|
import { CopyBlock, atomOneLight } from 'react-code-blocks';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import { Modal } from 'components/shared/Modal';
|
||||||
Button,
|
import { Button } from 'components/shared/Button';
|
||||||
Dialog,
|
|
||||||
DialogHeader,
|
|
||||||
DialogBody,
|
|
||||||
DialogFooter,
|
|
||||||
} from '@snowballtools/material-tailwind-react-fork';
|
|
||||||
|
|
||||||
interface AssignDomainProps {
|
interface AssignDomainProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -16,36 +11,31 @@ interface AssignDomainProps {
|
|||||||
|
|
||||||
const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => {
|
const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => {
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} handler={handleOpen}>
|
<Modal open={open} onOpenChange={handleOpen}>
|
||||||
<DialogHeader>Assign Domain</DialogHeader>
|
<Modal.Content>
|
||||||
<DialogBody>
|
<Modal.Header>Assign Domain</Modal.Header>
|
||||||
In order to assign a domain to your production deployments, configure it
|
<Modal.Body>
|
||||||
in the{' '}
|
In order to assign a domain to your production deployments, configure
|
||||||
{/* TODO: Fix selection of project settings tab on navigation to domains */}
|
it in the{' '}
|
||||||
<Link to="../settings/domains" className="text-light-blue-800 inline">
|
{/* TODO: Fix selection of project settings tab on navigation to domains */}
|
||||||
project settings{' '}
|
<Link to="../settings/domains" className="text-light-blue-800 inline">
|
||||||
</Link>
|
project settings{' '}
|
||||||
(recommended). If you want to assign to this specific deployment,
|
</Link>
|
||||||
however, you can do so using our command-line interface:
|
(recommended). If you want to assign to this specific deployment,
|
||||||
{/* https://github.com/rajinwonderland/react-code-blocks/issues/138 */}
|
however, you can do so using our command-line interface:
|
||||||
<CopyBlock
|
{/* https://github.com/rajinwonderland/react-code-blocks/issues/138 */}
|
||||||
text="snowball alias <deployment> <domain>"
|
<CopyBlock
|
||||||
language=""
|
text="snowball alias <deployment> <domain>"
|
||||||
showLineNumbers={false}
|
language=""
|
||||||
theme={atomOneLight}
|
showLineNumbers={false}
|
||||||
/>
|
theme={atomOneLight}
|
||||||
</DialogBody>
|
/>
|
||||||
<DialogFooter className="flex justify-start">
|
</Modal.Body>
|
||||||
<Button
|
<Modal.Footer className="flex justify-start">
|
||||||
className="rounded-3xl"
|
<Button onClick={handleOpen}>Okay</Button>
|
||||||
variant="gradient"
|
</Modal.Footer>
|
||||||
color="blue"
|
</Modal.Content>
|
||||||
onClick={handleOpen}
|
</Modal>
|
||||||
>
|
|
||||||
<span>Okay</span>
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</Dialog>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { useState } from 'react';
|
import { useState, ComponentPropsWithRef } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
|
||||||
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuHandler,
|
||||||
|
MenuItem,
|
||||||
|
MenuList,
|
||||||
|
} from '@snowballtools/material-tailwind-react-fork';
|
||||||
|
|
||||||
import { Deployment, Domain, Environment, Project } from 'gql-client';
|
import { Deployment, Domain, Environment, Project } from 'gql-client';
|
||||||
import { Button } from 'components/shared/Button';
|
import { Button } from 'components/shared/Button';
|
||||||
import {
|
import {
|
||||||
@@ -11,17 +18,11 @@ import {
|
|||||||
UndoIcon,
|
UndoIcon,
|
||||||
CrossCircleIcon,
|
CrossCircleIcon,
|
||||||
} from 'components/shared/CustomIcon';
|
} from 'components/shared/CustomIcon';
|
||||||
import {
|
|
||||||
Menu,
|
|
||||||
MenuHandler,
|
|
||||||
MenuItem,
|
|
||||||
MenuList,
|
|
||||||
} from '@snowballtools/material-tailwind-react-fork';
|
|
||||||
import { ComponentPropsWithRef } from 'react';
|
|
||||||
import AssignDomainDialog from './AssignDomainDialog';
|
import AssignDomainDialog from './AssignDomainDialog';
|
||||||
import { useGQLClient } from 'context/GQLClientContext';
|
import { useGQLClient } from 'context/GQLClientContext';
|
||||||
import { cn } from 'utils/classnames';
|
import { cn } from 'utils/classnames';
|
||||||
import { ChangeStateToProductionDialog } from 'components/projects/Dialog/ChangeStateToProductionDialog';
|
import { ChangeStateToProductionDialog } from 'components/projects/Dialog/ChangeStateToProductionDialog';
|
||||||
|
import { useToast } from 'components/shared/Toast';
|
||||||
|
|
||||||
interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> {
|
interface DeploymentMenuProps extends ComponentPropsWithRef<'div'> {
|
||||||
deployment: Deployment;
|
deployment: Deployment;
|
||||||
@@ -41,6 +42,7 @@ export const DeploymentMenu = ({
|
|||||||
...props
|
...props
|
||||||
}: DeploymentMenuProps) => {
|
}: DeploymentMenuProps) => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
const { toast, dismiss } = useToast();
|
||||||
|
|
||||||
const [changeToProduction, setChangeToProduction] = useState(false);
|
const [changeToProduction, setChangeToProduction] = useState(false);
|
||||||
const [redeployToProduction, setRedeployToProduction] = useState(false);
|
const [redeployToProduction, setRedeployToProduction] = useState(false);
|
||||||
@@ -51,9 +53,19 @@ export const DeploymentMenu = ({
|
|||||||
const isUpdated = await client.updateDeploymentToProd(deployment.id);
|
const isUpdated = await client.updateDeploymentToProd(deployment.id);
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
await onUpdate();
|
await onUpdate();
|
||||||
toast.success('Deployment changed to production');
|
toast({
|
||||||
|
id: 'deployment_changed_to_production',
|
||||||
|
title: 'Deployment changed to production',
|
||||||
|
variant: 'success',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error('Unable to change deployment to production');
|
toast({
|
||||||
|
id: 'deployment_not_changed_to_production',
|
||||||
|
title: 'Error changing deployment to production',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,9 +73,19 @@ export const DeploymentMenu = ({
|
|||||||
const isRedeployed = await client.redeployToProd(deployment.id);
|
const isRedeployed = await client.redeployToProd(deployment.id);
|
||||||
if (isRedeployed) {
|
if (isRedeployed) {
|
||||||
await onUpdate();
|
await onUpdate();
|
||||||
toast.success('Redeployed to production');
|
toast({
|
||||||
|
id: 'redeployed_to_production',
|
||||||
|
title: 'Redeployed to production',
|
||||||
|
variant: 'success',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error('Unable to redeploy to production');
|
toast({
|
||||||
|
id: 'redeployed_to_production_failed',
|
||||||
|
title: 'Error redeploying to production',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,9 +96,19 @@ export const DeploymentMenu = ({
|
|||||||
);
|
);
|
||||||
if (isRollbacked) {
|
if (isRollbacked) {
|
||||||
await onUpdate();
|
await onUpdate();
|
||||||
toast.success('Deployment rolled back');
|
toast({
|
||||||
|
id: 'deployment_rolled_back',
|
||||||
|
title: 'Deployment rolled back',
|
||||||
|
variant: 'success',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error('Unable to rollback deployment');
|
toast({
|
||||||
|
id: 'deployment_rollback_failed',
|
||||||
|
title: 'Error rolling back deployment',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,9 +116,19 @@ export const DeploymentMenu = ({
|
|||||||
const isDeleted = await client.deleteDeployment(deployment.id);
|
const isDeleted = await client.deleteDeployment(deployment.id);
|
||||||
if (isDeleted) {
|
if (isDeleted) {
|
||||||
await onUpdate();
|
await onUpdate();
|
||||||
toast.success('Deleted deployment');
|
toast({
|
||||||
|
id: 'deployment_deleted',
|
||||||
|
title: 'Deployment deleted',
|
||||||
|
variant: 'success',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error('Unable to delete deployment');
|
toast({
|
||||||
|
id: 'deployment_not_deleted',
|
||||||
|
title: 'Error deleting deployment',
|
||||||
|
variant: 'error',
|
||||||
|
onDismiss: dismiss,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { cn } from 'utils/classnames';
|
|||||||
import { useMediaQuery } from 'usehooks-ts';
|
import { useMediaQuery } from 'usehooks-ts';
|
||||||
import { SIDEBAR_MENU } from './constants';
|
import { SIDEBAR_MENU } from './constants';
|
||||||
import { UserSelect } from 'components/shared/UserSelect';
|
import { UserSelect } from 'components/shared/UserSelect';
|
||||||
|
import { baseUrl } from 'utils/constants';
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
mobileOpen?: boolean;
|
mobileOpen?: boolean;
|
||||||
@@ -83,7 +84,7 @@ export const Sidebar = ({ mobileOpen }: SidebarProps) => {
|
|||||||
}, [orgSlug]);
|
}, [orgSlug]);
|
||||||
|
|
||||||
const handleLogOut = useCallback(async () => {
|
const handleLogOut = useCallback(async () => {
|
||||||
await fetch(`${import.meta.env.VITE_SERVER_URL}/auth/logout`, {
|
await fetch(`${baseUrl}/auth/logout`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { Input } from '../Input';
|
||||||
|
|
||||||
|
export interface VerifyCodeInputProps {
|
||||||
|
code: string;
|
||||||
|
setCode: (code: string) => void;
|
||||||
|
submitCode: () => void;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VerifyCodeInput = ({
|
||||||
|
code,
|
||||||
|
setCode,
|
||||||
|
submitCode,
|
||||||
|
loading,
|
||||||
|
}: VerifyCodeInputProps) => {
|
||||||
|
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
|
||||||
|
|
||||||
|
const handlePaste = (
|
||||||
|
e: React.ClipboardEvent<HTMLInputElement>,
|
||||||
|
i: number,
|
||||||
|
) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const pasteData = e.clipboardData.getData('text').replace(/\D/g, ''); // Only digits
|
||||||
|
if (pasteData.length > 0) {
|
||||||
|
let newCodeArray = code.split('');
|
||||||
|
for (let j = 0; j < pasteData.length && i + j < 6; j++) {
|
||||||
|
newCodeArray[i + j] = pasteData[j];
|
||||||
|
}
|
||||||
|
const newCode = newCodeArray.join('');
|
||||||
|
setCode(newCode);
|
||||||
|
const nextIndex = Math.min(i + pasteData.length, 5);
|
||||||
|
const nextInput = inputRefs.current[nextIndex];
|
||||||
|
if (nextInput) nextInput.focus();
|
||||||
|
if (!newCode.includes(' ')) {
|
||||||
|
submitCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (
|
||||||
|
e: React.KeyboardEvent<HTMLInputElement>,
|
||||||
|
i: number,
|
||||||
|
) => {
|
||||||
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
||||||
|
|
||||||
|
if (e.key === 'Backspace') {
|
||||||
|
e.preventDefault();
|
||||||
|
const isEmpty = code[i] === ' ';
|
||||||
|
const newCode = !isEmpty
|
||||||
|
? code.slice(0, i) + ' ' + code.slice(i + 1, 6)
|
||||||
|
: code.slice(0, i - 1) + ' ' + code.slice(i, 6);
|
||||||
|
|
||||||
|
setCode(newCode.slice(0, 6));
|
||||||
|
|
||||||
|
if (i === 0 || !isEmpty) return;
|
||||||
|
const prev = inputRefs.current[i - 1];
|
||||||
|
if (prev) prev.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.key.match(/[0-9]/)) return;
|
||||||
|
|
||||||
|
e.preventDefault(); // Prevent the default event to avoid duplicate input
|
||||||
|
|
||||||
|
const newCode = code.slice(0, i) + e.key + code.slice(i + 1, 6);
|
||||||
|
setCode(newCode);
|
||||||
|
|
||||||
|
if (i === 5) {
|
||||||
|
submitCode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = inputRefs.current[i + 1];
|
||||||
|
if (next) next.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>, i: number) => {
|
||||||
|
const value = e.target.value.slice(-1);
|
||||||
|
if (!value.match(/[0-9]/)) return;
|
||||||
|
|
||||||
|
const newCode = code.slice(0, i) + value + code.slice(i + 1, 6);
|
||||||
|
setCode(newCode);
|
||||||
|
|
||||||
|
if (i < 5) {
|
||||||
|
const next = inputRefs.current[i + 1];
|
||||||
|
if (next) next.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newCode.includes(' ')) {
|
||||||
|
submitCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inputRefs.current[0]) {
|
||||||
|
inputRefs.current[0].focus();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{code.split('').map((char, i) => (
|
||||||
|
<Input
|
||||||
|
key={i}
|
||||||
|
value={char === ' ' ? '' : char}
|
||||||
|
ref={(el) => (inputRefs.current[i] = el)}
|
||||||
|
onChange={(e) => handleChange(e, i)}
|
||||||
|
onPaste={(e) => handlePaste(e, i)}
|
||||||
|
onKeyDown={(e) => handleKeyDown(e, i)}
|
||||||
|
disabled={!!loading}
|
||||||
|
style={{ textAlign: 'center' }} // Add this line to center text
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { VerifyCodeInput } from './VerifyCodeInput';
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { VITE_WALLET_CONNECT_ID } from 'utils/constants';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
if (!import.meta.env.VITE_WALLET_CONNECT_ID) {
|
if (!VITE_WALLET_CONNECT_ID) {
|
||||||
throw new Error('Error: REACT_APP_WALLET_CONNECT_ID env config is not set');
|
throw new Error('Error: REACT_APP_WALLET_CONNECT_ID env config is not set');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,17 @@ import { GQLClientProvider } from './context/GQLClientContext';
|
|||||||
import { SERVER_GQL_PATH } from './constants';
|
import { SERVER_GQL_PATH } from './constants';
|
||||||
import { Toaster } from 'components/shared/Toast';
|
import { Toaster } from 'components/shared/Toast';
|
||||||
import { LogErrorBoundary } from 'utils/log-error';
|
import { LogErrorBoundary } from 'utils/log-error';
|
||||||
|
import { baseUrl } from 'utils/constants';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(`v-${__VERSION__}`);
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement,
|
document.getElementById('root') as HTMLElement,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(import.meta.env.VITE_SERVER_URL, 'VITE_SERVER_URL is not set in env');
|
assert(baseUrl, 'VITE_SERVER_URL is not set in env');
|
||||||
const gqlEndpoint = `${import.meta.env.VITE_SERVER_URL}/${SERVER_GQL_PATH}`;
|
const gqlEndpoint = `${baseUrl}/${SERVER_GQL_PATH}`;
|
||||||
|
|
||||||
const gqlClient = new GQLClient({ gqlEndpoint });
|
const gqlClient = new GQLClient({ gqlEndpoint });
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
import { Button } from 'components/shared/Button';
|
||||||
|
import {
|
||||||
|
ArrowRightCircleFilledIcon,
|
||||||
|
LoaderIcon,
|
||||||
|
} from 'components/shared/CustomIcon';
|
||||||
|
import { WavyBorder } from 'components/shared/WavyBorder';
|
||||||
|
import { VerifyCodeInput } from 'components/shared/VerifyCodeInput';
|
||||||
|
import { verifyAccessCode } from 'utils/accessCode';
|
||||||
|
|
||||||
|
type AccessMethod = 'accesscode' | 'passkey';
|
||||||
|
|
||||||
|
type Err = { type: AccessMethod; message: string };
|
||||||
|
|
||||||
|
type AccessCodeProps = {
|
||||||
|
onCorrectAccessCode: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccessCode: React.FC<AccessCodeProps> = ({
|
||||||
|
onCorrectAccessCode,
|
||||||
|
}) => {
|
||||||
|
const [accessCode, setAccessCode] = useState(' ');
|
||||||
|
const [error, setError] = useState<Err | null>();
|
||||||
|
const [accessMethod, setAccessMethod] = useState<AccessMethod | false>(false);
|
||||||
|
|
||||||
|
async function validateAccessCode() {
|
||||||
|
setAccessMethod('accesscode');
|
||||||
|
try {
|
||||||
|
const isValidAccessCode = await verifyAccessCode(accessCode);
|
||||||
|
|
||||||
|
// add a pause for ux
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||||
|
if (isValidAccessCode) {
|
||||||
|
localStorage.setItem('accessCode', accessCode);
|
||||||
|
onCorrectAccessCode();
|
||||||
|
} else {
|
||||||
|
setError({
|
||||||
|
type: 'accesscode',
|
||||||
|
message: 'Invalid access code',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
setError({ type: 'accesscode', message: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = accessMethod;
|
||||||
|
const isValidAccessCodeLength = accessCode.trim().length === 5;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="self-stretch p-3 xs:p-6 flex-col justify-center items-center gap-5 flex">
|
||||||
|
<div className="self-stretch text-center text-sky-950 text-2xl font-medium font-display leading-tight">
|
||||||
|
Access Code
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<WavyBorder className="self-stretch" variant="stroke" />
|
||||||
|
<div className="self-stretch p-4 xs:p-6 flex-col justify-center items-center gap-8 flex">
|
||||||
|
<div className="self-stretch flex-col gap-8 flex">
|
||||||
|
<div className="flex-col justify-start items-start gap-2 inline-flex">
|
||||||
|
<VerifyCodeInput
|
||||||
|
loading={!!loading}
|
||||||
|
code={accessCode}
|
||||||
|
setCode={setAccessCode}
|
||||||
|
submitCode={validateAccessCode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
rightIcon={
|
||||||
|
loading && loading === 'accesscode' ? (
|
||||||
|
<LoaderIcon className="animate-spin" />
|
||||||
|
) : (
|
||||||
|
<ArrowRightCircleFilledIcon height="16" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={validateAccessCode}
|
||||||
|
variant={'secondary'}
|
||||||
|
disabled={!accessCode || !isValidAccessCodeLength || !!loading}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
{error && error.type === 'accesscode' && (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="justify-center items-center gap-2 inline-flex">
|
||||||
|
<div className="text-red-500 text-sm">
|
||||||
|
Error: {error.message}.{' '}
|
||||||
|
<a href="/signup" className="underline">
|
||||||
|
Try again?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
import { AccessCode } from './AccessCode';
|
||||||
|
import { SignUp } from './SignUp';
|
||||||
|
|
||||||
|
type AccessSignUpProps = {
|
||||||
|
onDone: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccessSignUp: React.FC<AccessSignUpProps> = ({ onDone }) => {
|
||||||
|
const [isValidAccessCode, setIsValidAccessCode] = useState<boolean>(
|
||||||
|
!!localStorage.getItem('accessCode'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return isValidAccessCode ? (
|
||||||
|
<SignUp onDone={onDone} />
|
||||||
|
) : (
|
||||||
|
<AccessCode onCorrectAccessCode={() => setIsValidAccessCode(true)} />
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -47,7 +47,9 @@ export const Login = ({ onDone }: Props) => {
|
|||||||
console.log('Handling google redirect back');
|
console.log('Handling google redirect back');
|
||||||
try {
|
try {
|
||||||
await google.handleOAuthRedirectBack();
|
await google.handleOAuthRedirectBack();
|
||||||
|
// @ts-ignore
|
||||||
wallet = await google.getEthersWallet();
|
wallet = await google.getEthersWallet();
|
||||||
|
// @ts-ignore
|
||||||
const result = await signInWithEthereum(1, 'login', wallet);
|
const result = await signInWithEthereum(1, 'login', wallet);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
setError(result.error);
|
setError(result.error);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
turnkeySignin,
|
turnkeySignin,
|
||||||
turnkeySignup,
|
turnkeySignup,
|
||||||
} from 'utils/turnkey-frontend';
|
} from 'utils/turnkey-frontend';
|
||||||
|
import { verifyAccessCode } from 'utils/accessCode';
|
||||||
|
|
||||||
type Provider = 'google' | 'github' | 'apple' | 'email';
|
type Provider = 'google' | 'github' | 'apple' | 'email';
|
||||||
|
|
||||||
@@ -45,7 +46,9 @@ export const SignUp = ({ onDone }: Props) => {
|
|||||||
setProvider('google');
|
setProvider('google');
|
||||||
try {
|
try {
|
||||||
await google.handleOAuthRedirectBack();
|
await google.handleOAuthRedirectBack();
|
||||||
|
// @ts-ignore
|
||||||
wallet = await google.getEthersWallet();
|
wallet = await google.getEthersWallet();
|
||||||
|
// @ts-ignore
|
||||||
const result = await signInWithEthereum(1, 'signup', wallet);
|
const result = await signInWithEthereum(1, 'signup', wallet);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
setError({ type: 'provider', message: result.error });
|
setError({ type: 'provider', message: result.error });
|
||||||
@@ -109,6 +112,29 @@ export const SignUp = ({ onDone }: Props) => {
|
|||||||
const loading = provider;
|
const loading = provider;
|
||||||
const emailValid = /.@./.test(email);
|
const emailValid = /.@./.test(email);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const validateAccessCode = async () => {
|
||||||
|
const accessCode = localStorage.getItem('accessCode');
|
||||||
|
if (!accessCode) {
|
||||||
|
redirectToSignup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await verifyAccessCode(accessCode);
|
||||||
|
} catch (err: any) {
|
||||||
|
redirectToSignup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToSignup = () => {
|
||||||
|
localStorage.removeItem('accessCode');
|
||||||
|
window.location.href = '/signup';
|
||||||
|
};
|
||||||
|
|
||||||
|
validateAccessCode();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="self-stretch p-3 xs:p-6 flex-col justify-center items-center gap-5 flex">
|
<div className="self-stretch p-3 xs:p-6 flex-col justify-center items-center gap-5 flex">
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { snowball } from 'utils/use-snowball';
|
import { snowball } from 'utils/use-snowball';
|
||||||
import { Login } from './Login';
|
import { Login } from './Login';
|
||||||
import { SignUp } from './SignUp';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { Done } from './Done';
|
import { Done } from './Done';
|
||||||
|
import { AccessSignUp } from './AccessSignUp';
|
||||||
|
|
||||||
type Screen = 'login' | 'signup' | 'success';
|
type Screen = 'login' | 'signup' | 'success';
|
||||||
|
|
||||||
const DASHBOARD_URL = '/';
|
const DASHBOARD_URL = '/';
|
||||||
|
|
||||||
export const SnowballAuth = () => {
|
export const SnowballAuth: React.FC = () => {
|
||||||
const path = window.location.pathname;
|
const path = window.location.pathname;
|
||||||
const [screen, setScreen] = useState<Screen>(
|
const [screen, setScreen] = useState<Screen>(
|
||||||
path === '/login' ? 'login' : 'signup',
|
path === '/login' ? 'login' : 'signup',
|
||||||
@@ -30,7 +31,7 @@ export const SnowballAuth = () => {
|
|||||||
|
|
||||||
if (screen === 'signup') {
|
if (screen === 'signup') {
|
||||||
return (
|
return (
|
||||||
<SignUp
|
<AccessSignUp
|
||||||
onDone={() => {
|
onDone={() => {
|
||||||
setScreen('success');
|
setScreen('success');
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ const meta: Meta<typeof ChevronDownSmallIcon> = {
|
|||||||
title: 'Icons/ChevronDownSmallIcon',
|
title: 'Icons/ChevronDownSmallIcon',
|
||||||
component: ChevronDownSmallIcon,
|
component: ChevronDownSmallIcon,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
args: {
|
argTypes: {
|
||||||
size: 'string | number' as unknown as any,
|
size: {
|
||||||
name: 'string',
|
control: 'text',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ const meta: Meta<typeof ChevronRight> = {
|
|||||||
title: 'Icons/ChevronRight',
|
title: 'Icons/ChevronRight',
|
||||||
component: ChevronRight,
|
component: ChevronRight,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
args: {
|
argTypes: {
|
||||||
size: 'string | number' as unknown as any,
|
size: {
|
||||||
name: 'string',
|
control: 'text',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ const meta: Meta<typeof ChevronUpDown> = {
|
|||||||
title: 'Icons/ChevronUpDown',
|
title: 'Icons/ChevronUpDown',
|
||||||
component: ChevronUpDown,
|
component: ChevronUpDown,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
args: {
|
argTypes: {
|
||||||
size: 'string | number' as unknown as any,
|
size: {
|
||||||
name: 'string',
|
control: 'text',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,16 @@ import { Meta, StoryObj } from '@storybook/react';
|
|||||||
import { ChevronUpSmallIcon } from 'components/shared/CustomIcon';
|
import { ChevronUpSmallIcon } from 'components/shared/CustomIcon';
|
||||||
|
|
||||||
const meta: Meta<typeof ChevronUpSmallIcon> = {
|
const meta: Meta<typeof ChevronUpSmallIcon> = {
|
||||||
title: 'Components/ChevronUpSmallIcon',
|
title: 'Icons/ChevronUpSmallIcon',
|
||||||
component: ChevronUpSmallIcon,
|
component: ChevronUpSmallIcon,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
args: {
|
argTypes: {
|
||||||
size: 'string | number' as unknown as any,
|
size: {
|
||||||
name: 'string',
|
control: 'text',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
|||||||
import { CollaboratorsIcon } from 'components/shared/CustomIcon';
|
import { CollaboratorsIcon } from 'components/shared/CustomIcon';
|
||||||
|
|
||||||
const meta: Meta<typeof CollaboratorsIcon> = {
|
const meta: Meta<typeof CollaboratorsIcon> = {
|
||||||
title: 'Components/CollaboratorsIcon',
|
title: 'Icons/CollaboratorsIcon',
|
||||||
component: CollaboratorsIcon,
|
component: CollaboratorsIcon,
|
||||||
tags: ['autodocs'],
|
tags: ['autodocs'],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { baseUrl } from './constants';
|
||||||
|
|
||||||
|
export async function verifyAccessCode(
|
||||||
|
accesscode: string,
|
||||||
|
): Promise<boolean | null> {
|
||||||
|
const res = await fetch(`${baseUrl}/auth/accesscode`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
accesscode,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
});
|
||||||
|
// If API returns a non-empty 200, this is a valid access code.
|
||||||
|
if (res.status == 200) {
|
||||||
|
const isValid = (await res.json()).isValid as boolean;
|
||||||
|
console.log('isValid', isValid);
|
||||||
|
return isValid;
|
||||||
|
} else if (res.status === 204) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Unexpected response from access code endpoint: ${res.status}: ${await res.text()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export const baseUrl = import.meta.env.VITE_SERVER_URL;
|
||||||
|
export const PASSKEY_WALLET_RPID = import.meta.env.VITE_PASSKEY_WALLET_RPID!;
|
||||||
|
export const TURNKEY_BASE_URL = import.meta.env.VITE_TURNKEY_API_BASE_URL!;
|
||||||
|
export const VITE_GITHUB_PWA_TEMPLATE_REPO = import.meta.env
|
||||||
|
.VITE_GITHUB_PWA_TEMPLATE_REPO;
|
||||||
|
export const VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO = import.meta.env
|
||||||
|
.VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO;
|
||||||
|
export const VITE_GITHUB_CLIENT_ID = import.meta.env.VITE_GITHUB_CLIENT_ID;
|
||||||
|
export const VITE_WALLET_CONNECT_ID = import.meta.env.VITE_WALLET_CONNECT_ID;
|
||||||
|
export const VITE_BUGSNAG_API_KEY = import.meta.env.VITE_BUGSNAG_API_KEY;
|
||||||
|
export const VITE_LIT_RELAY_API_KEY = import.meta.env.VITE_LIT_RELAY_API_KEY;
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
import Bugsnag from '@bugsnag/js';
|
import Bugsnag from '@bugsnag/js';
|
||||||
import BugsnagPluginReact from '@bugsnag/plugin-react';
|
import BugsnagPluginReact from '@bugsnag/plugin-react';
|
||||||
import BugsnagPerformance from '@bugsnag/browser-performance';
|
import BugsnagPerformance from '@bugsnag/browser-performance';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const bugsnagApiKey = import.meta.env.VITE_BUGSNAG_API_KEY;
|
import { VITE_BUGSNAG_API_KEY } from './constants';
|
||||||
|
|
||||||
if (bugsnagApiKey) {
|
if (VITE_BUGSNAG_API_KEY) {
|
||||||
Bugsnag.start({
|
Bugsnag.start({
|
||||||
apiKey: bugsnagApiKey,
|
apiKey: VITE_BUGSNAG_API_KEY,
|
||||||
plugins: [new BugsnagPluginReact()],
|
plugins: [new BugsnagPluginReact()],
|
||||||
});
|
});
|
||||||
BugsnagPerformance.start({ apiKey: bugsnagApiKey });
|
BugsnagPerformance.start({ apiKey: VITE_BUGSNAG_API_KEY });
|
||||||
}
|
}
|
||||||
|
|
||||||
export const errorLoggingEnabled = !!bugsnagApiKey;
|
export const errorLoggingEnabled = !!VITE_BUGSNAG_API_KEY;
|
||||||
|
|
||||||
export const LogErrorBoundary = bugsnagApiKey
|
export const LogErrorBoundary = VITE_BUGSNAG_API_KEY
|
||||||
? Bugsnag.getPlugin('react')!.createErrorBoundary(React)
|
? Bugsnag.getPlugin('react')!.createErrorBoundary(React)
|
||||||
: ({ children }: any) => children;
|
: ({ children }: any) => children;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export function logError(error: Error) {
|
|||||||
}
|
}
|
||||||
console.error(...errors);
|
console.error(...errors);
|
||||||
|
|
||||||
if (import.meta.env.VITE_BUGSNAG_API_KEY) {
|
if (VITE_BUGSNAG_API_KEY) {
|
||||||
Bugsnag.notify(error);
|
Bugsnag.notify(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { SiweMessage } from 'siwe';
|
|||||||
import { PKPEthersWallet } from '@lit-protocol/pkp-ethers';
|
import { PKPEthersWallet } from '@lit-protocol/pkp-ethers';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { baseUrl } from './constants';
|
||||||
|
|
||||||
const domain = window.location.host;
|
const domain = window.location.host;
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ export async function signInWithEthereum(
|
|||||||
);
|
);
|
||||||
const signature = await wallet.signMessage(message);
|
const signature = await wallet.signMessage(message);
|
||||||
|
|
||||||
const res = await fetch(`${import.meta.env.VITE_SERVER_URL}/auth/validate`, {
|
const res = await fetch(`${baseUrl}/auth/validate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { TurnkeyClient, getWebAuthnAttestation } from '@turnkey/http';
|
import { TurnkeyClient, getWebAuthnAttestation } from '@turnkey/http';
|
||||||
import { WebauthnStamper } from '@turnkey/webauthn-stamper';
|
import { WebauthnStamper } from '@turnkey/webauthn-stamper';
|
||||||
|
|
||||||
const baseUrl = import.meta.env.VITE_SERVER_URL;
|
import { baseUrl, PASSKEY_WALLET_RPID, TURNKEY_BASE_URL } from './constants';
|
||||||
|
|
||||||
const PASSKEY_WALLET_RPID = import.meta.env.VITE_PASSKEY_WALLET_RPID!;
|
|
||||||
const TURNKEY_BASE_URL = import.meta.env.VITE_TURNKEY_API_BASE_URL!;
|
|
||||||
|
|
||||||
// All algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
// All algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||||
// We only support ES256, which is listed here
|
// We only support ES256, which is listed here
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ import {
|
|||||||
LitGoogleAuth,
|
LitGoogleAuth,
|
||||||
LitPasskeyAuth,
|
LitPasskeyAuth,
|
||||||
} from '@snowballtools/auth-lit';
|
} from '@snowballtools/auth-lit';
|
||||||
|
import { VITE_LIT_RELAY_API_KEY } from './constants';
|
||||||
|
|
||||||
export const snowball = Snowball.withAuth({
|
export const snowball = Snowball.withAuth({
|
||||||
google: LitGoogleAuth.configure({
|
google: LitGoogleAuth.configure({
|
||||||
litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
|
litRelayApiKey: VITE_LIT_RELAY_API_KEY!,
|
||||||
}),
|
}),
|
||||||
// apple: LitAppleAuth.configure({
|
// apple: LitAppleAuth.configure({
|
||||||
// litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
|
// litRelayApiKey: VITE_LIT_RELAY_API_KEY!,
|
||||||
// }),
|
// }),
|
||||||
passkey: LitPasskeyAuth.configure({
|
passkey: LitPasskeyAuth.configure({
|
||||||
litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
|
litRelayApiKey: VITE_LIT_RELAY_API_KEY!,
|
||||||
}),
|
}),
|
||||||
}).create({
|
}).create({
|
||||||
initialChain: SnowballChain.sepolia,
|
initialChain: SnowballChain.sepolia,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { defineConfig, PluginOption } from 'vite';
|
import { defineConfig, PluginOption } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -16,5 +18,8 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env': {},
|
'process.env': {},
|
||||||
|
__VERSION__: JSON.stringify(
|
||||||
|
(await promisify(exec)('git rev-parse --short HEAD')).stdout.trim(),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-1
@@ -20,7 +20,10 @@ var __async = (__this, __arguments, generator) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// src/client.ts
|
// src/client.ts
|
||||||
import { ApolloClient, InMemoryCache } from "@apollo/client";
|
import {
|
||||||
|
ApolloClient,
|
||||||
|
InMemoryCache
|
||||||
|
} from "@apollo/client";
|
||||||
|
|
||||||
// src/queries.ts
|
// src/queries.ts
|
||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,8 +1,13 @@
|
|||||||
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
import {
|
||||||
|
ApolloClient,
|
||||||
|
DefaultOptions,
|
||||||
|
InMemoryCache,
|
||||||
|
NormalizedCacheObject,
|
||||||
|
} from "@apollo/client";
|
||||||
|
|
||||||
import * as queries from './queries';
|
import * as queries from "./queries";
|
||||||
import * as types from './types';
|
import * as types from "./types";
|
||||||
import * as mutations from './mutations';
|
import * as mutations from "./mutations";
|
||||||
|
|
||||||
export interface GraphQLConfig {
|
export interface GraphQLConfig {
|
||||||
gqlEndpoint: string;
|
gqlEndpoint: string;
|
||||||
@@ -11,320 +16,372 @@ export interface GraphQLConfig {
|
|||||||
// TODO: check options
|
// TODO: check options
|
||||||
const defaultOptions: DefaultOptions = {
|
const defaultOptions: DefaultOptions = {
|
||||||
watchQuery: {
|
watchQuery: {
|
||||||
fetchPolicy: 'no-cache',
|
fetchPolicy: "no-cache",
|
||||||
errorPolicy: 'ignore'
|
errorPolicy: "ignore",
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
fetchPolicy: 'no-cache',
|
fetchPolicy: "no-cache",
|
||||||
errorPolicy: 'all'
|
errorPolicy: "all",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GQLClient {
|
export class GQLClient {
|
||||||
private client: ApolloClient<NormalizedCacheObject>;
|
private client: ApolloClient<NormalizedCacheObject>;
|
||||||
|
|
||||||
constructor (config: GraphQLConfig) {
|
constructor(config: GraphQLConfig) {
|
||||||
this.client = new ApolloClient({
|
this.client = new ApolloClient({
|
||||||
uri: config.gqlEndpoint,
|
uri: config.gqlEndpoint,
|
||||||
cache: new InMemoryCache(),
|
cache: new InMemoryCache(),
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
credentials: 'include'
|
credentials: "include",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUser () : Promise<types.GetUserResponse> {
|
async getUser(): Promise<types.GetUserResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getUser
|
query: queries.getUser,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProject (projectId: string) : Promise<types.GetProjectResponse> {
|
async getProject(projectId: string): Promise<types.GetProjectResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getProject,
|
query: queries.getProject,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjectsInOrganization (organizationSlug: string) : Promise<types.GetProjectsInOrganizationResponse> {
|
async getProjectsInOrganization(
|
||||||
|
organizationSlug: string
|
||||||
|
): Promise<types.GetProjectsInOrganizationResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getProjectsInOrganization,
|
query: queries.getProjectsInOrganization,
|
||||||
variables: {
|
variables: {
|
||||||
organizationSlug
|
organizationSlug,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrganizations () : Promise<types.GetOrganizationsResponse> {
|
async getOrganizations(): Promise<types.GetOrganizationsResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getOrganizations
|
query: queries.getOrganizations,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDeployments (projectId: string) : Promise<types.GetDeploymentsResponse> {
|
async getDeployments(
|
||||||
|
projectId: string
|
||||||
|
): Promise<types.GetDeploymentsResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getDeployments,
|
query: queries.getDeployments,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEnvironmentVariables (projectId: string) : Promise<types.GetEnvironmentVariablesResponse> {
|
async getEnvironmentVariables(
|
||||||
|
projectId: string
|
||||||
|
): Promise<types.GetEnvironmentVariablesResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getEnvironmentVariables,
|
query: queries.getEnvironmentVariables,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjectMembers (projectId: string) : Promise<types.GetProjectMembersResponse> {
|
async getProjectMembers(
|
||||||
|
projectId: string
|
||||||
|
): Promise<types.GetProjectMembersResponse> {
|
||||||
const result = await this.client.query({
|
const result = await this.client.query({
|
||||||
query: queries.getProjectMembers,
|
query: queries.getProjectMembers,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProjectMember (projectId: string, data: types.AddProjectMemberInput) : Promise<types.AddProjectMemberResponse> {
|
async addProjectMember(
|
||||||
|
projectId: string,
|
||||||
|
data: types.AddProjectMemberInput
|
||||||
|
): Promise<types.AddProjectMemberResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.addProjectMember,
|
mutation: mutations.addProjectMember,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProjectMember (projectMemberId: string, data: types.UpdateProjectMemberInput): Promise<types.UpdateProjectMemberResponse> {
|
async updateProjectMember(
|
||||||
|
projectMemberId: string,
|
||||||
|
data: types.UpdateProjectMemberInput
|
||||||
|
): Promise<types.UpdateProjectMemberResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.updateProjectMember,
|
mutation: mutations.updateProjectMember,
|
||||||
variables: {
|
variables: {
|
||||||
projectMemberId,
|
projectMemberId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeProjectMember (projectMemberId: string): Promise<types.RemoveProjectMemberResponse> {
|
async removeProjectMember(
|
||||||
|
projectMemberId: string
|
||||||
|
): Promise<types.RemoveProjectMemberResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.removeProjectMember,
|
mutation: mutations.removeProjectMember,
|
||||||
variables: {
|
variables: {
|
||||||
projectMemberId
|
projectMemberId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchProjects (searchText: string) : Promise<types.SearchProjectsResponse> {
|
async searchProjects(
|
||||||
|
searchText: string
|
||||||
|
): Promise<types.SearchProjectsResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.searchProjects,
|
query: queries.searchProjects,
|
||||||
variables: {
|
variables: {
|
||||||
searchText
|
searchText,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addEnvironmentVariables (projectId: string, data: types.AddEnvironmentVariableInput[]): Promise<types.AddEnvironmentVariablesResponse> {
|
async addEnvironmentVariables(
|
||||||
|
projectId: string,
|
||||||
|
data: types.AddEnvironmentVariableInput[]
|
||||||
|
): Promise<types.AddEnvironmentVariablesResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.addEnvironmentVariables,
|
mutation: mutations.addEnvironmentVariables,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEnvironmentVariable (environmentVariableId: string, data: types.UpdateEnvironmentVariableInput): Promise<types.UpdateEnvironmentVariableResponse> {
|
async updateEnvironmentVariable(
|
||||||
|
environmentVariableId: string,
|
||||||
|
data: types.UpdateEnvironmentVariableInput
|
||||||
|
): Promise<types.UpdateEnvironmentVariableResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.updateEnvironmentVariable,
|
mutation: mutations.updateEnvironmentVariable,
|
||||||
variables: {
|
variables: {
|
||||||
environmentVariableId,
|
environmentVariableId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeEnvironmentVariable (environmentVariableId: string): Promise<types.RemoveEnvironmentVariableResponse> {
|
async removeEnvironmentVariable(
|
||||||
|
environmentVariableId: string
|
||||||
|
): Promise<types.RemoveEnvironmentVariableResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.removeEnvironmentVariable,
|
mutation: mutations.removeEnvironmentVariable,
|
||||||
variables: {
|
variables: {
|
||||||
environmentVariableId
|
environmentVariableId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDeploymentToProd (deploymentId: string): Promise<types.UpdateDeploymentToProdResponse> {
|
async updateDeploymentToProd(
|
||||||
|
deploymentId: string
|
||||||
|
): Promise<types.UpdateDeploymentToProdResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.updateDeploymentToProd,
|
mutation: mutations.updateDeploymentToProd,
|
||||||
variables: {
|
variables: {
|
||||||
deploymentId
|
deploymentId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProject (organizationSlug: string, data: types.AddProjectInput): Promise<types.AddProjectResponse> {
|
async addProject(
|
||||||
|
organizationSlug: string,
|
||||||
|
data: types.AddProjectInput
|
||||||
|
): Promise<types.AddProjectResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.addProject,
|
mutation: mutations.addProject,
|
||||||
variables: {
|
variables: {
|
||||||
organizationSlug,
|
organizationSlug,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProject (projectId: string, data: types.UpdateProjectInput): Promise<types.UpdateProjectResponse> {
|
async updateProject(
|
||||||
|
projectId: string,
|
||||||
|
data: types.UpdateProjectInput
|
||||||
|
): Promise<types.UpdateProjectResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.updateProjectMutation,
|
mutation: mutations.updateProjectMutation,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDomain (domainId: string, data: types.UpdateDomainInput): Promise<types.UpdateDomainResponse> {
|
async updateDomain(
|
||||||
|
domainId: string,
|
||||||
|
data: types.UpdateDomainInput
|
||||||
|
): Promise<types.UpdateDomainResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.updateDomainMutation,
|
mutation: mutations.updateDomainMutation,
|
||||||
variables: {
|
variables: {
|
||||||
domainId,
|
domainId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async redeployToProd (deploymentId: string): Promise<types.RedeployToProdResponse> {
|
async redeployToProd(
|
||||||
|
deploymentId: string
|
||||||
|
): Promise<types.RedeployToProdResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.redeployToProd,
|
mutation: mutations.redeployToProd,
|
||||||
variables: {
|
variables: {
|
||||||
deploymentId
|
deploymentId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProject (projectId: string): Promise<types.DeleteProjectResponse> {
|
async deleteProject(projectId: string): Promise<types.DeleteProjectResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.deleteProject,
|
mutation: mutations.deleteProject,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteDomain (domainId: string): Promise<types.DeleteDomainResponse> {
|
async deleteDomain(domainId: string): Promise<types.DeleteDomainResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.deleteDomain,
|
mutation: mutations.deleteDomain,
|
||||||
variables: {
|
variables: {
|
||||||
domainId
|
domainId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async rollbackDeployment (projectId: string, deploymentId: string): Promise<types.RollbackDeploymentResponse> {
|
async rollbackDeployment(
|
||||||
|
projectId: string,
|
||||||
|
deploymentId: string
|
||||||
|
): Promise<types.RollbackDeploymentResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.rollbackDeployment,
|
mutation: mutations.rollbackDeployment,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
deploymentId
|
deploymentId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteDeployment (deploymentId: string): Promise<types.DeleteDeploymentResponse> {
|
async deleteDeployment(
|
||||||
|
deploymentId: string
|
||||||
|
): Promise<types.DeleteDeploymentResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.deleteDeployment,
|
mutation: mutations.deleteDeployment,
|
||||||
variables: {
|
variables: {
|
||||||
deploymentId
|
deploymentId,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addDomain (projectId: string, data: types.AddDomainInput): Promise<types.AddDomainResponse> {
|
async addDomain(
|
||||||
|
projectId: string,
|
||||||
|
data: types.AddDomainInput
|
||||||
|
): Promise<types.AddDomainResponse> {
|
||||||
const result = await this.client.mutate({
|
const result = await this.client.mutate({
|
||||||
mutation: mutations.addDomain,
|
mutation: mutations.addDomain,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
data
|
data,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDomains (projectId: string, filter?: types.FilterDomainInput): Promise<types.GetDomainsResponse> {
|
async getDomains(
|
||||||
|
projectId: string,
|
||||||
|
filter?: types.FilterDomainInput
|
||||||
|
): Promise<types.GetDomainsResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: queries.getDomains,
|
query: queries.getDomains,
|
||||||
variables: {
|
variables: {
|
||||||
projectId,
|
projectId,
|
||||||
filter
|
filter,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticateGitHub (code: string): Promise<types.AuthenticateGitHubResponse> {
|
async authenticateGitHub(
|
||||||
|
code: string
|
||||||
|
): Promise<types.AuthenticateGitHubResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.authenticateGitHub,
|
mutation: mutations.authenticateGitHub,
|
||||||
variables: {
|
variables: {
|
||||||
code
|
code,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async unauthenticateGithub (): Promise<types.UnauthenticateGitHubResponse> {
|
async unauthenticateGithub(): Promise<types.UnauthenticateGitHubResponse> {
|
||||||
const { data } = await this.client.mutate({
|
const { data } = await this.client.mutate({
|
||||||
mutation: mutations.unauthenticateGitHub
|
mutation: mutations.unauthenticateGitHub,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -10575,6 +10575,11 @@ exponential-backoff@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||||
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
|
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
|
||||||
|
|
||||||
|
express-async-errors@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/express-async-errors/-/express-async-errors-3.1.1.tgz#6053236d61d21ddef4892d6bd1d736889fc9da41"
|
||||||
|
integrity sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==
|
||||||
|
|
||||||
express-session@^1.18.0:
|
express-session@^1.18.0:
|
||||||
version "1.18.0"
|
version "1.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.18.0.tgz#a6ae39d9091f2efba5f20fc5c65a3ce7c9ce16a3"
|
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.18.0.tgz#a6ae39d9091f2efba5f20fc5c65a3ce7c9ce16a3"
|
||||||
|
|||||||
Reference in New Issue
Block a user