Compare commits

..
Author SHA1 Message Date
Vivian Phung efc5a45bac Fix Input component react hook 2024-05-22 19:04:59 +00:00
56 changed files with 746 additions and 1275 deletions
+1 -4
View File
@@ -6,7 +6,4 @@ 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
+210 -13
View File
@@ -1,23 +1,220 @@
# snowballtools-base # snowballtools-base
This is a [yarn workspace](https://yarnpkg.com/features/workspaces) monorepo for the dashboard. ## Setup
## Getting Started - Clone the `snowballtools-base` repo
### Install dependencies ```bash
git clone git@github.com:snowball-tools/snowballtools-base.git
cd snowballtools-base
```
In the root of the project, run: - Install dependencies in root
```zsh ```bash
yarn yarn
``` ```
### Build backend - Build packages
```zsh ```bash
yarn build --ignore-frontend yarn build --ignore frontend
``` ```
### Environment variables, running the development server, and deployment Let us assume the following domains for backend and frontend
- Backend server: `api.snowballtools.com`
- Frontend app: `dashboard.snowballtools.com`
Follow the instructions in the README.md files of the [backend](packages/backend/README.md) and [frontend](packages/frontend/README.md) packages. - Configuration Files
- 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 -76
View File
@@ -1,76 +1 @@
# backend # Backend for Snowball Tools
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)
-1
View File
@@ -19,7 +19,6 @@
"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
View File
@@ -1,4 +1,3 @@
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';
+39 -57
View File
@@ -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,15 +25,8 @@ export const createResolvers = async (service: Service): Promise<any> => {
return service.getProjectById(projectId); return service.getProjectById(projectId);
}, },
projectsInOrganization: async ( projectsInOrganization: async (_: any, { organizationSlug }: {organizationSlug: string }, context: any) => {
_: any, return service.getProjectsInOrganization(context.user, organizationSlug);
{ organizationSlug }: { organizationSlug: string },
context: any,
) => {
return service.getProjectsInOrganization(
context.user,
organizationSlug,
);
}, },
deployments: async (_: any, { projectId }: { projectId: string }) => { deployments: async (_: any, { projectId }: { projectId: string }) => {
@@ -42,7 +35,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);
}, },
@@ -51,11 +44,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
return service.getProjectMembersByProjectId(projectId); return service.getProjectMembersByProjectId(projectId);
}, },
searchProjects: async ( searchProjects: async (_: any, { searchText }: { searchText: string }, context: any) => {
_: any,
{ searchText }: { searchText: string },
context: any,
) => {
return service.searchProjects(context.user, searchText); return service.searchProjects(context.user, searchText);
}, },
@@ -63,11 +52,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
@@ -75,13 +64,10 @@ 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( return await service.removeProjectMember(context.user, projectMemberId);
context.user,
projectMemberId,
);
} catch (err) { } catch (err) {
log(err); log(err);
return false; return false;
@@ -92,13 +78,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);
@@ -112,14 +98,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));
@@ -133,15 +119,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);
@@ -153,16 +139,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);
@@ -172,7 +158,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);
@@ -185,12 +171,10 @@ 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( return Boolean(await service.updateDeploymentToProd(context.user, deploymentId));
await service.updateDeploymentToProd(context.user, deploymentId),
);
} catch (err) { } catch (err) {
log(err); log(err);
return false; return false;
@@ -201,9 +185,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);
@@ -215,7 +199,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);
@@ -228,12 +212,10 @@ 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( return Boolean(await service.redeployToProd(context.user, deploymentId));
await service.redeployToProd(context.user, deploymentId),
);
} catch (err) { } catch (err) {
log(err); log(err);
return false; return false;
@@ -262,8 +244,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);
@@ -275,7 +257,9 @@ 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);
@@ -287,7 +271,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));
@@ -299,7 +283,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);
@@ -312,7 +296,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);
@@ -324,14 +308,12 @@ 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, { return service.unauthenticateGitHub(context.user, { gitHubToken: null });
gitHubToken: null,
});
} catch (err) { } catch (err) {
log(err); log(err);
return false; return false;
} }
}, }
}, }
}; };
}; };
-15
View File
@@ -5,19 +5,6 @@ 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
// //
@@ -32,7 +19,6 @@ 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,
@@ -45,7 +31,6 @@ 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'),
-9
View File
@@ -1,9 +0,0 @@
import { Router } from 'express';
const router = Router();
router.get('/version', async (req, res) => {
return res.send({ version: '0.0.8' });
});
export default router;
-7
View File
@@ -17,7 +17,6 @@ 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');
@@ -110,12 +109,6 @@ 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}`);
File diff suppressed because it is too large Load Diff
+2 -9
View File
@@ -1,11 +1,9 @@
# 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
``` ```
@@ -14,13 +12,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
``` ```
@@ -29,7 +27,6 @@
## 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:
... ...
@@ -38,7 +35,6 @@
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
@@ -48,7 +44,6 @@
- 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
@@ -56,9 +51,7 @@
# 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
``` ```
+2 -2
View File
@@ -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: 89c75c7bc5759861d10285aff6f9e7227d6855e446b77ad5d8324822dfec7deb bondId: 098c906850b87412f02200e41f449bc79e055eab77acfef32c0b22443bb46661
chainId: laconic_9000-1 chainId: laconic_9000-1
gas: 1200000 gas: 550000
fees: 200000aphoton fees: 200000aphoton
+8 -40
View File
@@ -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/test-progressive-web-app LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools-platform/test-progressive-web-app
LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/test-progressive-web-app LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools-platform/test-progressive-web-app
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/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,13 +64,7 @@ echo "Files generated successfully."
RECORD_FILE=records/application-record.yml RECORD_FILE=records/application-record.yml
# Publish ApplicationRecord # Publish ApplicationRecord
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE) RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
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,36 +73,15 @@ 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
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN") APP_RECORD=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN" | jq '.[0]')
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
@@ -117,11 +90,6 @@ fi
RECORD_FILE=records/application-deployment-request.yml RECORD_FILE=records/application-deployment-request.yml
sleep 2 sleep 2
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE) DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
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
+10 -42
View File
@@ -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: Ov23liOaoahRTYd4nSCV LACONIC_HOSTED_CONFIG_github_clientid: 905c09553f527d2cdff5
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/test-progressive-web-app LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/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: dashboard.staging.apps.snowballtools.com LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: localhost
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,13 +70,7 @@ echo "Files generated successfully."
RECORD_FILE=staging-records/application-record.yml RECORD_FILE=staging-records/application-record.yml
# Publish ApplicationRecord # Publish ApplicationRecord
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE) RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
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
@@ -85,36 +79,15 @@ 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
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN") APP_RECORD=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN" | jq '.[0]')
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
@@ -123,11 +96,6 @@ fi
RECORD_FILE=staging-records/application-deployment-request.yml RECORD_FILE=staging-records/application-deployment-request.yml
sleep 2 sleep 2
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE) DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
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/test-progressive-web-app LACONIC_HOSTED_CONFIG_app_github_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_pwa_templaterepo: snowball-tools-platform/test-progressive-web-app
LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools-platform/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: Ov23liOaoahRTYd4nSCV LACONIC_HOSTED_CONFIG_github_clientid: 905c09553f527d2cdff5
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/test-progressive-web-app LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools-platform/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: dashboard.staging.apps.snowballtools.com LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: localhost
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 @ Sat Jun 22 02:49:45 UTC 2024 note: Added by Snowball @ Tuesday 21 May 2024 06:17:23 AM UTC
repository: "https://git.vdb.to/cerc-io/snowballtools-base" repository: "https://git.vdb.to/cerc-io/snowballtools-base"
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601 repository_ref: 8488cfab8353321ed05c4234bf1b914c9ad3aa99
@@ -1,7 +1,7 @@
record: record:
type: ApplicationRecord type: ApplicationRecord
version: 0.0.1 version: 0.0.2
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601 repository_ref: 8488cfab8353321ed05c4234bf1b914c9ad3aa99
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
+1 -1
View File
@@ -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/test-progressive-web-app> (main branch) - Web app repo used: https://github.com/snowball-tools-platform/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/test-progressive-web-app" repository: "https://github.com/snowball-tools-platform/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/test-progressive-web-app"] repository: ["https://github.com/snowball-tools-platform/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/test-progressive-web-app" REPO_URL="https://github.com/snowball-tools-platform/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
+1 -1
View File
@@ -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=
-2
View File
@@ -9,7 +9,6 @@ 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: [
@@ -27,7 +26,6 @@ const config: StorybookConfig = {
docs: { docs: {
autodocs: 'tag', autodocs: 'tag',
}, },
staticDirs: ['../public'],
}; };
export default config; export default config;
+23 -56
View File
@@ -1,63 +1,30 @@
# frontend # React + TypeScript + Vite
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). This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
## Getting Started Currently, two official plugins are available:
### Install dependencies - [@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
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
In the root of the project, run: ## Expanding the ESLint configuration
```zsh If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
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,
},
};
``` ```
### Build backend - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
```zsh - 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
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)
+2 -3
View File
@@ -1,4 +1,3 @@
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';
@@ -11,8 +10,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([
{ {
@@ -60,7 +59,7 @@ function App() {
// Hacky way of checking session // Hacky way of checking session
// TODO: Handle redirect backs // TODO: Handle redirect backs
useEffect(() => { useEffect(() => {
fetch(`${baseUrl}/auth/session`, { fetch(`${import.meta.env.VITE_SERVER_URL}/auth/session`, {
credentials: 'include', credentials: 'include',
}).then((res) => { }).then((res) => {
if (res.status !== 200) { if (res.status !== 200) {
+2 -7
View File
@@ -1,21 +1,16 @@
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: `${VITE_GITHUB_PWA_TEMPLATE_REPO}`, repoFullName: `${import.meta.env.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: `${VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`, repoFullName: `${import.meta.env.VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
isComingSoon: false, isComingSoon: false,
}, },
{ {
@@ -14,10 +14,11 @@ 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=${VITE_GITHUB_CLIENT_ID}&scope=${encodeURIComponent(SCOPES)}`; const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${
import.meta.env.VITE_GITHUB_CLIENT_ID
}&scope=${encodeURIComponent(SCOPES)}`;
interface ConnectAccountInterface { interface ConnectAccountInterface {
onAuth: (token: string) => void; onAuth: (token: string) => void;
@@ -1,8 +1,13 @@
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 { Modal } from 'components/shared/Modal'; import {
import { Button } from 'components/shared/Button'; Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
} from '@snowballtools/material-tailwind-react-fork';
interface AssignDomainProps { interface AssignDomainProps {
open: boolean; open: boolean;
@@ -11,31 +16,36 @@ interface AssignDomainProps {
const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => { const AssignDomainDialog = ({ open, handleOpen }: AssignDomainProps) => {
return ( return (
<Modal open={open} onOpenChange={handleOpen}> <Dialog open={open} handler={handleOpen}>
<Modal.Content> <DialogHeader>Assign Domain</DialogHeader>
<Modal.Header>Assign Domain</Modal.Header> <DialogBody>
<Modal.Body> In order to assign a domain to your production deployments, configure it
In order to assign a domain to your production deployments, configure in the{' '}
it in the{' '} {/* TODO: Fix selection of project settings tab on navigation to domains */}
{/* TODO: Fix selection of project settings tab on navigation to domains */} <Link to="../settings/domains" className="text-light-blue-800 inline">
<Link to="../settings/domains" className="text-light-blue-800 inline"> project settings{' '}
project settings{' '} </Link>
</Link> (recommended). If you want to assign to this specific deployment,
(recommended). If you want to assign to this specific deployment, however, you can do so using our command-line interface:
however, you can do so using our command-line interface: {/* https://github.com/rajinwonderland/react-code-blocks/issues/138 */}
{/* https://github.com/rajinwonderland/react-code-blocks/issues/138 */} <CopyBlock
<CopyBlock text="snowball alias <deployment> <domain>"
text="snowball alias <deployment> <domain>" language=""
language="" showLineNumbers={false}
showLineNumbers={false} theme={atomOneLight}
theme={atomOneLight} />
/> </DialogBody>
</Modal.Body> <DialogFooter className="flex justify-start">
<Modal.Footer className="flex justify-start"> <Button
<Button onClick={handleOpen}>Okay</Button> className="rounded-3xl"
</Modal.Footer> variant="gradient"
</Modal.Content> color="blue"
</Modal> onClick={handleOpen}
>
<span>Okay</span>
</Button>
</DialogFooter>
</Dialog>
); );
}; };
@@ -1,12 +1,5 @@
import { useState, ComponentPropsWithRef } from 'react'; import { useState } 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 {
@@ -18,11 +11,17 @@ 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;
@@ -42,7 +41,6 @@ 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);
@@ -53,19 +51,9 @@ 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({ toast.success('Deployment changed to production');
id: 'deployment_changed_to_production',
title: 'Deployment changed to production',
variant: 'success',
onDismiss: dismiss,
});
} else { } else {
toast({ toast.error('Unable to change deployment to production');
id: 'deployment_not_changed_to_production',
title: 'Error changing deployment to production',
variant: 'error',
onDismiss: dismiss,
});
} }
}; };
@@ -73,19 +61,9 @@ 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({ toast.success('Redeployed to production');
id: 'redeployed_to_production',
title: 'Redeployed to production',
variant: 'success',
onDismiss: dismiss,
});
} else { } else {
toast({ toast.error('Unable to redeploy to production');
id: 'redeployed_to_production_failed',
title: 'Error redeploying to production',
variant: 'error',
onDismiss: dismiss,
});
} }
}; };
@@ -96,19 +74,9 @@ export const DeploymentMenu = ({
); );
if (isRollbacked) { if (isRollbacked) {
await onUpdate(); await onUpdate();
toast({ toast.success('Deployment rolled back');
id: 'deployment_rolled_back',
title: 'Deployment rolled back',
variant: 'success',
onDismiss: dismiss,
});
} else { } else {
toast({ toast.error('Unable to rollback deployment');
id: 'deployment_rollback_failed',
title: 'Error rolling back deployment',
variant: 'error',
onDismiss: dismiss,
});
} }
}; };
@@ -116,19 +84,9 @@ 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({ toast.success('Deleted deployment');
id: 'deployment_deleted',
title: 'Deployment deleted',
variant: 'success',
onDismiss: dismiss,
});
} else { } else {
toast({ toast.error('Unable to delete deployment');
id: 'deployment_not_deleted',
title: 'Error deleting deployment',
variant: 'error',
onDismiss: dismiss,
});
} }
}; };
@@ -20,7 +20,6 @@ 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;
@@ -84,7 +83,7 @@ export const Sidebar = ({ mobileOpen }: SidebarProps) => {
}, [orgSlug]); }, [orgSlug]);
const handleLogOut = useCallback(async () => { const handleLogOut = useCallback(async () => {
await fetch(`${baseUrl}/auth/logout`, { await fetch(`${import.meta.env.VITE_SERVER_URL}/auth/logout`, {
method: 'POST', method: 'POST',
credentials: 'include', credentials: 'include',
}); });
@@ -1,117 +0,0 @@
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>
);
};
@@ -1 +0,0 @@
export { VerifyCodeInput } from './VerifyCodeInput';
@@ -1,11 +1,9 @@
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 (!VITE_WALLET_CONNECT_ID) { if (!import.meta.env.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');
} }
+2 -6
View File
@@ -14,17 +14,13 @@ 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(baseUrl, 'VITE_SERVER_URL is not set in env'); assert(import.meta.env.VITE_SERVER_URL, 'VITE_SERVER_URL is not set in env');
const gqlEndpoint = `${baseUrl}/${SERVER_GQL_PATH}`; const gqlEndpoint = `${import.meta.env.VITE_SERVER_URL}/${SERVER_GQL_PATH}`;
const gqlClient = new GQLClient({ gqlEndpoint }); const gqlClient = new GQLClient({ gqlEndpoint });
@@ -1,99 +0,0 @@
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>
);
};
@@ -1,20 +0,0 @@
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,9 +47,7 @@ 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,7 +21,6 @@ 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';
@@ -46,9 +45,7 @@ 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 });
@@ -112,29 +109,6 @@ 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,15 +1,14 @@
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: React.FC = () => { export const SnowballAuth = () => {
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',
@@ -31,7 +30,7 @@ export const SnowballAuth: React.FC = () => {
if (screen === 'signup') { if (screen === 'signup') {
return ( return (
<AccessSignUp <SignUp
onDone={() => { onDone={() => {
setScreen('success'); setScreen('success');
}} }}
@@ -6,13 +6,9 @@ const meta: Meta<typeof ChevronDownSmallIcon> = {
title: 'Icons/ChevronDownSmallIcon', title: 'Icons/ChevronDownSmallIcon',
component: ChevronDownSmallIcon, component: ChevronDownSmallIcon,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { args: {
size: { size: 'string | number' as unknown as any,
control: 'text', name: 'string',
},
name: {
control: 'text',
},
}, },
}; };
@@ -6,13 +6,9 @@ const meta: Meta<typeof ChevronRight> = {
title: 'Icons/ChevronRight', title: 'Icons/ChevronRight',
component: ChevronRight, component: ChevronRight,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { args: {
size: { size: 'string | number' as unknown as any,
control: 'text', name: 'string',
},
name: {
control: 'text',
},
}, },
}; };
@@ -6,13 +6,9 @@ const meta: Meta<typeof ChevronUpDown> = {
title: 'Icons/ChevronUpDown', title: 'Icons/ChevronUpDown',
component: ChevronUpDown, component: ChevronUpDown,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { args: {
size: { size: 'string | number' as unknown as any,
control: 'text', name: 'string',
},
name: {
control: 'text',
},
}, },
}; };
@@ -3,16 +3,12 @@ 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: 'Icons/ChevronUpSmallIcon', title: 'Components/ChevronUpSmallIcon',
component: ChevronUpSmallIcon, component: ChevronUpSmallIcon,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { args: {
size: { size: 'string | number' as unknown as any,
control: 'text', name: 'string',
},
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: 'Icons/CollaboratorsIcon', title: 'Components/CollaboratorsIcon',
component: CollaboratorsIcon, component: CollaboratorsIcon,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {
-28
View File
@@ -1,28 +0,0 @@
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()}`,
);
}
}
-11
View File
@@ -1,11 +0,0 @@
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;
+8 -8
View File
@@ -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';
import { VITE_BUGSNAG_API_KEY } from './constants'; const bugsnagApiKey = import.meta.env.VITE_BUGSNAG_API_KEY;
if (VITE_BUGSNAG_API_KEY) { if (bugsnagApiKey) {
Bugsnag.start({ Bugsnag.start({
apiKey: VITE_BUGSNAG_API_KEY, apiKey: bugsnagApiKey,
plugins: [new BugsnagPluginReact()], plugins: [new BugsnagPluginReact()],
}); });
BugsnagPerformance.start({ apiKey: VITE_BUGSNAG_API_KEY }); BugsnagPerformance.start({ apiKey: bugsnagApiKey });
} }
export const errorLoggingEnabled = !!VITE_BUGSNAG_API_KEY; export const errorLoggingEnabled = !!bugsnagApiKey;
export const LogErrorBoundary = VITE_BUGSNAG_API_KEY export const LogErrorBoundary = bugsnagApiKey
? 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 (VITE_BUGSNAG_API_KEY) { if (import.meta.env.VITE_BUGSNAG_API_KEY) {
Bugsnag.notify(error); Bugsnag.notify(error);
} }
} }
+1 -3
View File
@@ -2,8 +2,6 @@ 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;
@@ -19,7 +17,7 @@ export async function signInWithEthereum(
); );
const signature = await wallet.signMessage(message); const signature = await wallet.signMessage(message);
const res = await fetch(`${baseUrl}/auth/validate`, { const res = await fetch(`${import.meta.env.VITE_SERVER_URL}/auth/validate`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -1,7 +1,10 @@
import { TurnkeyClient, getWebAuthnAttestation } from '@turnkey/http'; import { TurnkeyClient, getWebAuthnAttestation } from '@turnkey/http';
import { WebauthnStamper } from '@turnkey/webauthn-stamper'; import { WebauthnStamper } from '@turnkey/webauthn-stamper';
import { baseUrl, PASSKEY_WALLET_RPID, TURNKEY_BASE_URL } from './constants'; const baseUrl = import.meta.env.VITE_SERVER_URL;
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
+3 -4
View File
@@ -5,17 +5,16 @@ 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: VITE_LIT_RELAY_API_KEY!, litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
}), }),
// apple: LitAppleAuth.configure({ // apple: LitAppleAuth.configure({
// litRelayApiKey: VITE_LIT_RELAY_API_KEY!, // litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
// }), // }),
passkey: LitPasskeyAuth.configure({ passkey: LitPasskeyAuth.configure({
litRelayApiKey: VITE_LIT_RELAY_API_KEY!, litRelayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
}), }),
}).create({ }).create({
initialChain: SnowballChain.sepolia, initialChain: SnowballChain.sepolia,
-5
View File
@@ -1,7 +1,5 @@
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({
@@ -18,8 +16,5 @@ export default defineConfig({
}, },
define: { define: {
'process.env': {}, 'process.env': {},
__VERSION__: JSON.stringify(
(await promisify(exec)('git rev-parse --short HEAD')).stdout.trim(),
),
}, },
}); });
File diff suppressed because one or more lines are too long
+1 -4
View File
@@ -20,10 +20,7 @@ var __async = (__this, __arguments, generator) => {
}; };
// src/client.ts // src/client.ts
import { import { ApolloClient, InMemoryCache } from "@apollo/client";
ApolloClient,
InMemoryCache
} from "@apollo/client";
// src/queries.ts // src/queries.ts
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
File diff suppressed because one or more lines are too long
+89 -146
View File
@@ -1,13 +1,8 @@
import { import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
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;
@@ -16,372 +11,320 @@ 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( async getProjectsInOrganization (organizationSlug: string) : Promise<types.GetProjectsInOrganizationResponse> {
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( async getDeployments (projectId: string) : Promise<types.GetDeploymentsResponse> {
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( async getEnvironmentVariables (projectId: string) : Promise<types.GetEnvironmentVariablesResponse> {
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( async getProjectMembers (projectId: string) : Promise<types.GetProjectMembersResponse> {
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( async addProjectMember (projectId: string, data: types.AddProjectMemberInput) : Promise<types.AddProjectMemberResponse> {
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( async updateProjectMember (projectMemberId: string, data: types.UpdateProjectMemberInput): Promise<types.UpdateProjectMemberResponse> {
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( async removeProjectMember (projectMemberId: string): Promise<types.RemoveProjectMemberResponse> {
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( async searchProjects (searchText: string) : Promise<types.SearchProjectsResponse> {
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( async addEnvironmentVariables (projectId: string, data: types.AddEnvironmentVariableInput[]): Promise<types.AddEnvironmentVariablesResponse> {
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( async updateEnvironmentVariable (environmentVariableId: string, data: types.UpdateEnvironmentVariableInput): Promise<types.UpdateEnvironmentVariableResponse> {
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( async removeEnvironmentVariable (environmentVariableId: string): Promise<types.RemoveEnvironmentVariableResponse> {
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( async updateDeploymentToProd (deploymentId: string): Promise<types.UpdateDeploymentToProdResponse> {
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( async addProject (organizationSlug: string, data: types.AddProjectInput): Promise<types.AddProjectResponse> {
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( async updateProject (projectId: string, data: types.UpdateProjectInput): Promise<types.UpdateProjectResponse> {
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( async updateDomain (domainId: string, data: types.UpdateDomainInput): Promise<types.UpdateDomainResponse> {
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( async redeployToProd (deploymentId: string): Promise<types.RedeployToProdResponse> {
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( async rollbackDeployment (projectId: string, deploymentId: string): Promise<types.RollbackDeploymentResponse> {
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( async deleteDeployment (deploymentId: string): Promise<types.DeleteDeploymentResponse> {
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( async addDomain (projectId: string, data: types.AddDomainInput): Promise<types.AddDomainResponse> {
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( async getDomains (projectId: string, filter?: types.FilterDomainInput): Promise<types.GetDomainsResponse> {
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( async authenticateGitHub (code: string): Promise<types.AuthenticateGitHubResponse> {
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;
-5
View File
@@ -10575,11 +10575,6 @@ 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"