Compare commits

..
Author SHA1 Message Date
Vivian Phung 37fff3c171 fix(ProjectSearchBarDialog): getMenuProps error 2024-06-24 22:46:41 +00:00
Vivian Phung dee84f18cb feat: dynamic project success page and update links (#220)
### TL;DR

Integrates project data fetching for dynamic subdomain display on the Project Deployment Success page and the OverviewTabPanel.

### What changed?

- Updated `Id.tsx` to fetch project data and dynamically display project's subdomain after deployment.
- Modified `Overview.tsx` to make project's subdomain a clickable link.

### How to test?

1. Deploy a new project and check the deployment success page for correct subdomain display.
2. Open a project's overview tab and click the subdomain link to ensure it navigates correctly.

### Why make this change?

Improves user experience by displaying the actual subdomain and making it clickable, ensuring users can conveniently verify their deployment and access project domain.

---
2024-06-24 18:44:34 -04:00
Vivian Phung 44015d5451 feat(ui): loading spinner on project creation (#219)
### TL;DR
This pull request adds a loading icon to the 'Deploy' button in the project template creation form.

### What changed?
- Imported `LoadingIcon` from `components/shared/CustomIcon`.
- Modified the `Button` component to conditionally display the `LoadingIcon` when `isLoading` is `true`. The icon will animate by spinning.

### How to test?
1. Navigate to the project template creation form.
2. Fill in the required fields.
3. Click the 'Deploy' button.
4. Ensure the loading icon appears and spins when the button is disabled (when `isLoading` is `true`).

### Why make this change?
This change provides visual feedback to users, indicating that their action is being processed, thereby enhancing user experience.

---
2024-06-24 18:41:31 -04:00
Vivian Phung a684743bd6 feat(template projects): generate git repo on backend (#218)
### TL;DR

- Still cretaes app if user migrates from page

The PR introduces a new `AddProjectFromTemplate` mutation to facilitate project creation using a repository template. This change centralizes the template project creation logic within the backend, improving code maintainability by removing redundant client-side code.

### What changed?
- Added `AddProjectFromTemplate` input type in `schema.gql` and corresponding TypeScript interfaces.
- Implemented `addProjectFromTemplate` resolver with error handling and Octokit integration for repository creation.
- Updated `service.ts` to include the new `addProjectFromTemplate` method.
- Created new GraphQL `Mutation` for `addProjectFromTemplate` in the GraphQL schema.
- Adjusted the client-side GQLClient to support the new mutation.
- Modified frontend to utilize the new backend mutation for project creation from a template.

### How to test?
1. Ensure your backend server is running.
2. Use a GraphQL client like Postman to call the `addProjectFromTemplate` mutation with appropriate input.
3. Verify that the new project is created using the specified template, and appropriate error messages are returned for failures.
4. Check the frontend flow for creating a project from a template to ensure it is working correctly.

### Why make this change?
This change enhances code maintainability by centralizing template project creation logic within the backend, thereby reducing redundancy and potential inconsistencies in client-side implementations.

---
2024-06-24 18:38:01 -04:00
Vivian Phung b12c95b2ff fix(readme) 2024-06-24 15:00:45 -04:00
Vivian Phung a4d9211ffe Refactor(README.md) (#217)
This pull request refactors the main `README.md` by removing detailed backend and frontend setup instructions and adding separate `README.md` files for the backend and frontend with their specific setup and deployment instructions. This makes the main `README.md` cleaner and directs users to specific READMEs for backend and frontend setups.

---
2024-06-22 18:07:47 -04:00
Vivian Phung af31fac3ee chore(storybook): icons to correct folder (#216)
This pull request refactors several aspects of the frontend codebase focused on Storybook configurations and icon story files. It replaces `args` with `argTypes` to enhance control configurations, adds `staticDirs` for public assets in Storybook, and standardizes the titles of some icon stories.
2024-06-22 17:27:37 -04:00
Vivian Phung 54ae3f429d fix(staging): github client id on frontend staging deployment script (#214)
### TL;DR
Update references to the new GitHub repository URLs and make minor formatting fixes in deployment scripts.

### What changed?
Updated GitHub repository URLs from `snowball-tools-platform` to `snowball-tools` in various deployment and test scripts. Made minor formatting adjustments including spacing and indentation.

### How to test?
Run the deployment scripts in their respective environments to ensure they reference the correct repository URLs and all functionalities work as expected. Check for successful creation and updation of application records.

### Why make this change?
This change was made to reflect the new repository structure and ensure consistency across all deployment scripts. The minor formatting fixes improve code readability.

---
2024-06-22 17:24:40 -04:00
Vivian Phung acfe78bf07 chore: ignore .DS_Store 2024-06-22 17:05:34 -04:00
Vivian Phung ce1833cb51 bump: version (#213)
This pull request updates the backend version endpoint to return version '0.0.8' instead of '0.0.7'.

---
2024-06-21 21:16:34 -04:00
Vivian Phung f2e59c11fd Refactor: VerifyCodeInput Component and Modify Access Code Validation (#212)
### TL;DR

Implemented the new `VerifyCodeInput` component and updated the access code validation logic.

### What changed?

1. Added a new reusable `VerifyCodeInput` component for verifying codes in a user-friendly way. This component handles paste events, input changes, and keyboard navigation.
2. Updated the backend route `/accesscode` to accept an arbitrary code for now. 
3. Incorporated the `VerifyCodeInput` component into the `AccessCode` page, replacing the generic `Input` component.
4. Updated the access code validation logic to check for a trimmed length of 5 characters instead of 6.
5. Added a slight pause for UX purposes when validating the access code on the frontend.

### How to test?

1. Go to the Access Code page.
2. Try entering an access code with various inputs (keyboard, paste, etc.) to see if it works seamlessly.
3. Verify that only a 5-digit code is considered valid.
4. Check the backend logs to ensure the validation endpoint is working as expected.

### Why make this change?

This change improves the user experience by providing a custom input component for access code verification and ensures that the access code validation meets the new requirements.

---
2024-06-21 21:13:32 -04:00
Vivian Phung b261e7e436 Feat: Access Code Authentication Flow (#211)
## What changed?

This Pull Request introduces an access code validation feature to the authentication process. Changes encompass backend route for access code validation, new frontend components for handling access code input, and integration of the access code verification in the signup flow.

### Backend:
- Added POST `/accesscode` route in `auth.ts` for validating access codes.

### Frontend:
- Created `AccessCode` component for access code input and validation.
- Added `AccessSignUp` component that integrates access code verification before signup.
- Updated `SignUp` component to check for valid access code on mount.
- Modified `SnowballAuth` to use new `AccessSignUp` instead of `SignUp`.
- Added `verifyAccessCode` utility function for code verification API call.

## How to test?
1. Run the backend and frontend projects.
2. Navigate to the signup page. You should be prompted to enter an access code.
3. Enter the code `444444` and proceed. Any other code should display an error message.
4. Verify that valid access code routes to the signup component.

## Why make this change?

This change improves authentication by adding an extra layer of security through access code verification.
2024-06-21 21:10:31 -04:00
Vivian Phung 934aa1a26b Refactor: Env to utils/constants (#210)
This PR centralizes all the environment variable references into a single constants file. The change includes replacing various `import.meta.env` references with imports from the new `utils/constants` module. This improves maintainability by providing a single place to manage environment variables.
2024-06-21 21:07:41 -04:00
Gilbert d975390a1b Fix url env var 2024-06-20 14:54:17 -05:00
Gilbert f77323364c Bump 2024-06-20 04:06:16 -05:00
Gilbert c6a78f2116 Just send back the message, this is a demo after all 2024-06-20 04:05:48 -05:00
Vivian Phung cff9a5b2ea [nit] DeploymentMenu dependencies cleanup (#204)
### TL;DR

This PR updates the project settings.

### What changed?

The project settings have been refactored for better organization and readability.

### How to test?

To test this change, navigate to the project settings and ensure all options are functioning as expected.

### Why make this change?

This change was made to improve the user experience when navigating through the project settings.

---
2024-06-20 00:40:32 -04:00
Vivian Phung 003b83ba21 Refactor: DeploymentMenu uses toast component (#203)
### TL;DR

This PR includes updates to the project settings.

### What changed?

The project settings have been refactored for better usability and consistency with other components.

### How to test?

To test this change, navigate to the project settings and ensure all options are working as expected.

### Why make this change?

This change was made to improve the user experience and maintain consistency across the application.

---
2024-06-20 00:36:43 -04:00
Vivian Phung 82a1c151a8 Refactor: AssignDomainDialog uses Modal component (#202)
### TL;DR

This PR includes updates to the project settings.

### What changed?

The project settings have been refactored for better organization and readability.

### How to test?

To test this change, navigate to the project settings and ensure all options are functioning as expected.

### Why make this change?

This change was made to improve the user experience when navigating through the project settings.

---
2024-06-20 00:32:41 -04:00
Gilbert 2ada11f311 Support async express handlers 2024-06-19 22:59:20 -05:00
Gilbert 6e32d0678a Add generic error handling 2024-06-19 22:31:07 -05:00
Gilbert 198478f5fa Add logs for debugging 2024-06-19 14:22:55 -05:00
dboreham 552dfe783e Merge pull request #209 from snowball-tools/dboreham/add-rpid-config
Add missing rpid env var
2024-06-18 20:04:19 -06:00
Gilbert e2bf5d052c Bump server version 2024-06-18 17:46:47 -05:00
dboreham 33323b9bbf Merge pull request #208 from snowball-tools/dboreham/error-checked-deployment
Make deployment scripts more robust
2024-06-18 13:51:33 -06:00
dboreham e2a3254563 Add missing rpid env var 2024-06-18 13:47:26 -06:00
dboreham 8589bf4094 Make deployment scripts more robust 2024-06-18 11:15:31 -06:00
dboreham f8ebdfe7aa Merge pull request #207 from snowball-tools/telackey-patch-1
Update config.staging.yml
2024-06-18 06:31:58 -06:00
telackey 2166b2f800 Update config.staging.yml 2024-06-17 23:41:01 -05:00
dboreham cdd8d15e73 Increase gas for staging deployment chain profile to match prod (#205) 2024-06-17 09:49:52 +05:30
Vivian Phung dc7b251988 add version backend (temp) (#201)
* add version backend (temp)

* staging router endpoint

* remove frontend
2024-06-05 17:38:19 +01:00
Gilbert bfb4a3f30b Revert "remove email signup for now"
This reverts commit 0e9c3a07fd.
2024-06-04 16:26:15 -05:00
Gilbert 8f7fc888a9 Log commit hash 2024-06-04 15:56:52 -05:00
Vivian Phung 0e9c3a07fd remove email signup for now 2024-06-04 15:56:52 -05:00
Vivian Phung 61e3e88a6c Refactor Input and SearchBar (#199)
### TL;DR

This pull request refactors the `SearchBar` and `Input` components, adding a `ref` to the former and removing an incorrect understanding of `react-hook-form` (yes, i prev "fix" the component) in the latter.

### What changed?

A ref is added to the SearchBar component for better control and handling. In the Input component, we have eliminated the usage of 'react-hook-form' and as a result, the 'register' prop is removed. This makes the component less reliant on specific libraries and more reusable.

### How to test?

Ensure that proper testing is done on the updated components. Make sure that the `SearchBar` works as expected with its ref and that Input does not depend on 'react-hook-form' anymore.

### Why make this change?

This change was made to improve the functionality of the `SearchBar` and the flexibility of the Input component, making them more effective and reusable respectively. The changes also align with the current code quality standards and best practices.
2024-05-22 15:06:50 -04:00
Vivian Phung 7b5ba1a5d0 correct suppressRefError (#198)
### TL;DR

This PR refactors the `UserSelect` component, adjusting the call to `getToggleButtonProps`.

### What changed?

The `getToggleButtonProps` method in the `UserSelect` component now takes in two separate objects, one for the `ref` and another for `suppressRefError`, instead of a single one.

### How to test?

Verify the component functionality hasn't changed and there are no reference errors.

### Why make this change?

This code changes improve the readability and maintainability of this component by clearly separating the component reference and error suppression configurations in separate objects.
2024-05-22 15:02:51 -04:00
Vivian Phung b35f4033c5 Refactor: Collaborator Project Settings (#197)
### TL;DR

AddMemberDialog component now uses a Select dropdown for permissions instead of Checkboxes. CollaboratorsTabPanel now includes dismiss functionality for toasts.

### What changed?

- Updated AddMemberDialog to use a Select dropdown for permissions
- Added dismiss functionality for toasts in CollaboratorsTabPanel

### How to test?

Test the functionality of selecting permissions using the dropdown and toast dismissal in CollaboratorsTabPanel.

### Why make this change?

To improve user experience and UI consistency in permissions selection and toast management.
2024-05-22 14:58:47 -04:00
Vivian Phung 306d3235b3 Project Search Bar Dialog Update (#196)
### TL;DR

Reordered the properties in the `ProjectSearchBar` component to follow better coding standards.

### What changed?

In `ProjectSearchBarDialog.tsx`, the 'getItemProps' object was moved to the end of the properties list within `ProjectSearchBarItem`.

### How to test?

Verify that the `ProjectSearchBar` component functions as intended and that no properties are unduly affected by this change.

### Why make this change?

This change enhances code readability and consistency, aligning the ordering of the properties more accurately with our standards.
2024-05-22 14:54:51 -04:00
Vivian Phung e148fd8d6b Add dist/ to .prettierignore (#195)
### TL;DR

This small change adds 'dist/' directory to `.prettierignore` in the frontend package.

### What changed?

An entry for 'dist/' was added to `.prettierignore` file in the frontend package. Since we don't want to format the distribution files, we have added it to our list of ignored paths for prettier. The change just includes the addition of single line `dist/` to `.prettierignore` file.

### How to test? 

There is no specific testing needed other than PR build success, as it is a development focused change.

### Why make this change? 

The reason for this change is to prevent Prettier from installing unnecessary dependencies in the dist directory which is generated and can cause linter warnings and errors.
2024-05-22 14:50:37 -04:00
Vivian Phung f84e2c0d9d Refactor: Rename SVG Properties for React Standard (#194)
### TL;DR

A refactor of the Icon components in the front-end package has been carried out. This includes `CollaboratorsIcon.tsx`, `CopyUnfilledIcon.tsx`, and `TrashIcon.tsx`.

### What changed?

Several attributes previously written in kebab-case were changed to camelCase to adhere to JSX syntax standards. These include `stroke-linecap`, `stroke-linejoin`, `fill-rule`, and `clip-rule`.

### How to test?

Ensure that the rendering and functionality of the icons in the application remain unchanged after this update. 

### Why make this change?

The update ensures that our code complies with the preferred casing convention in JSX and avoids all potential related issues.
2024-05-22 14:46:53 -04:00
Vivian Phung 216a5670e6 [nit] remove package.json: No license field warning 2024-05-22 10:59:24 -04:00
Vivian Phung 92016c8837 format 2024-05-22 10:57:20 -04:00
Vivian Phung c6ebcafaac gpl-client/dist and delete unused Web3ModalProvider 2024-05-22 10:44:22 -04:00
Vivian Phung 6dfe85cb1a Merge branch 'ng-check-deployment-removal-record' 2024-05-22 10:41:37 -04:00
Gilbert 2bb5feeb88 Laconic frontend deployment
Squashed commit of the following:

commit 86e6600749
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Tue May 21 11:59:32 2024 +0530

    Update staging deployment records with new config values

commit 3f11ff3da1
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Tue May 21 11:02:43 2024 +0530

    Update build script for new VITE env variables

commit f614199bb8
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Fri May 17 12:34:36 2024 +0530

    Update subdomain

commit 0787d2008b
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Fri May 17 12:03:29 2024 +0530

    Update GitHub client ID and subdomain

commit b9ffb691dc
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Fri May 17 11:28:22 2024 +0530

    Add staging deployment records

commit 25e47aa5ec
Author: Nabarun <nabarun@deepstacksoft.com>
Date:   Fri May 17 10:56:26 2024 +0530

    Add deployment script for staging
2024-05-22 09:21:00 -05:00
Vivian Phung 9a74205bd5 Merge pull request #154 from snowball-tools/nv-handle-repo-exist-error
Show error in toast for creating repository that already exisits in account
2024-05-22 10:19:09 -04:00
Vivian Phung 7147611842 Merge branch 'main' into nv-handle-repo-exist-error 2024-05-22 10:16:27 -04:00
Vivian Phung 8533c41c58 Merge pull request #193 from snowball-tools/main
storybook and project settings styling
2024-05-21 07:50:36 -04:00
Vivian Phung 2074e08a0c [3/n][Storybook] Settings - Delete Project Dialog (#76) 2024-05-16 20:45:32 -04:00
Vivian Phung f01bdf2de7 [2/n][Storybook] Settings - EnvironmentVariables (#75) 2024-05-16 20:42:29 -04:00
Vivian Phung 8cb5eadfb2 EditEnvironmentVariableRow cleanup (#74) 2024-05-16 20:39:42 -04:00
Vivian Phung 41666568f5 AddEnvironmentVariableRow button alignment to bottom (#73) 2024-05-16 20:36:31 -04:00
Vivian Phung 17cf878168 [1/n][Storybook] Settings (#72) 2024-05-16 20:33:33 -04:00
Vivian Phung c72cbce615 AddEnvironmentVariableRow padding (#71) 2024-05-16 20:29:35 -04:00
Vivian Phung a69dd71117 DeleteProjectDialog update to Modal (#70) 2024-05-16 20:26:31 -04:00
Vivian Phung 5dc4d28b50 MemberCard remove x padding (#69) 2024-05-16 20:22:33 -04:00
Vivian Phung 7dce1d66ae remove x padding padding from general tab (#68) 2024-05-16 20:19:31 -04:00
Vivian Phung 46ba6d014d fix duplicate styling (#67) 2024-05-16 20:16:31 -04:00
Gilbert 8488cfab83 Make Lit Work Again 2024-05-15 17:44:59 -05:00
Vivian Phung 8376aff7bd Merge pull request #191 from snowball-tools/main
sync forks
2024-05-15 17:52:42 -04:00
Vivian Phung 583c0b9d26 MemberCard for owner disabled (#66)
* AddMemberDialog remove footer styling from old component

* MemberCard for owner disabled (#64)
2024-05-15 17:18:32 -04:00
Vivian Phung e12c94e087 AddMemberDialog remove footer styling from old component (#63)
### TL;DR

This PR involves a minor change on the AddMemberDialog component style.

### What changed?

The 'justify-start' class was removed from the Modal.Footer in the AddMemberDialog.tsx.

### How to test?

Check the AddMemberDialog on the project settings page to make sure the style changes reflect accurately.

### Why make this change?

It's not specified the specific reason for the change, However, it's aimed towards improving the component's layout and presentation in the project settings page.

---
2024-05-15 17:07:13 -04:00
Vivian Phung 879cfdb2bf env var register pass (#62) 2024-05-14 20:08:18 -04:00
Vivian Phung 04b6a84440 env var fix 2024-05-14 20:07:02 -04:00
Vivian Phung 41b9ce1096 [22/n][Storybook] AuthPage (#61) 2024-05-14 19:33:29 -04:00
Vivian Phung ad69ebe4a0 [21/n][Storybook] AuthPage 2024-05-14 23:31:51 +00:00
Vivian Phung ca8863e1d6 [21/n][Storybook] SearchBar (#60) 2024-05-14 19:29:46 -04:00
Vivian Phung 0fb1127b96 [20/n][Storybook] SearchBar 2024-05-14 23:26:47 +00:00
Vivian Phung b34e0783c1 [20/n][Storybook] CloudyFlow (#59) 2024-05-14 19:25:36 -04:00
Vivian Phung 050f404776 [19/n][Storybook] CloudyFlow 2024-05-14 23:23:32 +00:00
Vivian Phung 2e30a1aae1 Tabs component export TabsTheme (#58) 2024-05-14 19:22:28 -04:00
Vivian Phung 7c8e9f2448 Tabs component export TabsTheme 2024-05-14 23:20:39 +00:00
Vivian Phung 065b12e3d9 [19/n][Storybook] Tag with typed themes (#57) 2024-05-14 19:19:31 -04:00
Vivian Phung 3668a8edf7 [19/n][Storybook] Tag with typed themes 2024-05-14 23:17:28 +00:00
Vivian Phung 28366ea725 [components] basic Table uses themes (#56) 2024-05-14 19:16:50 -04:00
Vivian Phung 4c3072ed50 [19/n][Storybook] basic Table uses themes 2024-05-14 23:13:33 +00:00
Vivian Phung 6975360d48 [18/n][Storybook] Switch update argTypes and add stories (#55) 2024-05-14 19:12:57 -04:00
Vivian Phung 671321ef4d [18/n][Storybook] Switch update argTypes and add stories 2024-05-14 18:16:49 -04:00
Vivian Phung d04ad68f18 [17/n][Storybook] Step (single) story (#54) 2024-05-14 17:52:31 -04:00
Vivian Phung 16df36715a [17/n][Storybook] Step (single) story 2024-05-14 21:50:56 +00:00
Vivian Phung 6fdbcf6f46 [16/n][Storybook] Steps argTypes and typed variants (#53) 2024-05-14 17:48:38 -04:00
Vivian Phung 702cef24b3 [16/n][Storybook] Steps argTypes and typed variants 2024-05-14 21:46:42 +00:00
Vivian Phung d3cf0453e7 [15/n][Storybook] Select argTypes and typed variants (#52) 2024-05-14 17:44:33 -04:00
Vivian Phung d76db4fb96 [12/n][Storybook] Select argTypes and typed variants 2024-05-14 21:42:33 +00:00
Vivian Phung 19545c48bd [14/n][Storybook] SegmentedControls update argTypes and use typed themes (#51) 2024-05-14 17:41:29 -04:00
Vivian Phung 8d96be625e [5/n][Storybook] SegmentedControls update argTypes and use typed themes 2024-05-14 21:40:17 +00:00
Vivian Phung 1c05ba8822 [13/n][Storybook] Radio argTypes update (#50) 2024-05-14 17:37:58 -04:00
Vivian Phung fd7d06b9e2 [13/n][Storybook] Radio argTypes update 2024-05-14 21:36:07 +00:00
Vivian Phung 550ef91968 [12/n][Storybook] OverflownText update argTypes (#49) 2024-05-14 17:33:41 -04:00
Vivian Phung c710fb5a53 [12/n][Storybook] OverflownText update argTypes 2024-05-14 21:31:40 +00:00
Vivian Phung a95f18a66b [11/n][Storybook] Modal argTypes update (#48) 2024-05-14 17:29:38 -04:00
Vivian Phung ccc36fd175 [4/n][Storybook] Modal argTypes update 2024-05-14 21:27:40 +00:00
Vivian Phung ce7e9174e4 [10/n][Storybook] Input update argTypes (#47) 2024-05-14 17:25:37 -04:00
Vivian Phung 43d7c94cb0 [4/n][Storybook] Input update argTypes 2024-05-14 21:24:00 +00:00
Vivian Phung 294675c276 [9/n][Storybook] InlineNotification update argTypes and use theme variants (#46) 2024-05-14 17:22:38 -04:00
Vivian Phung 2e0b228aa5 [9/n][Storybook] InlineNotification update argTypes and use theme variants 2024-05-14 21:20:45 +00:00
Vivian Phung 907b5ee02d [8/n][Storybook] IconWithFrame update argTypes (#45) 2024-05-14 17:18:35 -04:00
Vivian Phung 8f456c04f5 [8/n][Storybook] IconWithFrame update argTypes 2024-05-14 21:16:34 +00:00
Vivian Phung 29caf510ea [7/n][Storybook] DatePicker update argTypes (#44) 2024-05-14 17:15:32 -04:00
Vivian Phung a63d0f69ed [7/n][Storybook] DatePicker update argTypes 2024-05-14 21:13:29 +00:00
Vivian Phung 1aa57aaecc [6/n][Storybook] Checkbox update argTypes (#43) 2024-05-14 17:12:29 -04:00
Vivian Phung a27331f54f [6/n][Storybook] Checkbox update argTypes 2024-05-14 21:10:37 +00:00
Vivian Phung ebe6d35b54 [5/n][Storybook] Calendar update argTypes (#42) 2024-05-14 17:08:33 -04:00
Vivian Phung 29557d7597 [6/n][Storybook] Calendar update argTypes 2024-05-14 21:06:34 +00:00
Vivian Phung df540e06eb [4/n][Storybook] Button using theme variants (#41) 2024-05-14 17:05:00 -04:00
Vivian Phung 3fe0718532 [4/n][Storybook] Button using theme variants 2024-05-14 16:24:25 -04:00
Vivian Phung 039753df56 [7/n][project settings ui] member list cleanup (#40) 2024-05-14 16:23:33 -04:00
Vivian Phung 386f40952e member list cleanup 2024-05-14 20:22:02 +00:00
Vivian Phung e1c4f77ec1 [7/n][project settings ui] env var cleanup start (#35) 2024-05-14 16:19:32 -04:00
Vivian Phung 57601e6b4b env var needs fixing 2024-05-14 20:17:54 +00:00
Vivian Phung e9a367db42 [7/n][project settings ui] cleanup dialogs (#34) 2024-05-14 16:16:29 -04:00
Vivian Phung 6ae04251d2 cleanup dialogs 2024-05-14 20:14:50 +00:00
Vivian Phung 690fc8d2cb [7/n][project settings ui] ProjectSettingContainer (#33) 2024-05-14 16:13:28 -04:00
Vivian Phung 2719b3e385 ProjectSettingContainer 2024-05-14 20:11:57 +00:00
Vivian Phung de6d5c302b [8/n][project settings ui] config domains and react dom context (#32) 2024-05-14 16:09:33 -04:00
Vivian Phung dc91fa0d7f storybook config domains and react dom context 2024-05-14 20:07:46 +00:00
Vivian Phung 571d58d57d [component lib] table basic component (#31) 2024-05-14 16:06:31 -04:00
Vivian Phung 947337acb1 table basic component 2024-05-14 20:04:40 +00:00
Vivian Phung f8908c1c06 [component lib] input forward ref react-hook-form (#30) 2024-05-14 16:03:29 -04:00
Vivian Phung 6e7385b118 input forward ref react-hook-form 2024-05-14 20:01:35 +00:00
Vivian Phung abbda18fc7 [7/n][project settings ui] AddEnvironmentVariableRowProps Input (#29) 2024-05-14 16:00:30 -04:00
Vivian Phung 2d0de785f9 ui cleanup 2024-05-14 19:58:32 +00:00
Vivian Phung 68f1a265f4 [6/n][project settings ui] collaborators ui (#28) 2024-05-14 15:57:47 -04:00
Vivian Phung 9b7a021e8b [6/n][project settings ui] collaborators ui 2024-05-14 19:56:14 +00:00
Vivian Phung cdfc72a5a0 [5/n][project setting ui] MembersTabPanel cleanip (#27) 2024-05-14 15:53:52 -04:00
Vivian Phung 47b322c212 [5/n][project setting ui] MembersTabPanel cleanip 2024-05-14 19:51:41 +00:00
Vivian Phung e4c39e0955 [4/n][project settings ui] GitTabPanel cleanup (#26) 2024-05-14 15:50:44 -04:00
Vivian Phung 9f46290ecc [4/n][project settings ui] GitTabPanel cleanup 2024-05-14 19:49:02 +00:00
Vivian Phung b71b7579a6 [3/n][project settings ui] Domains cleanup (#25) 2024-05-14 15:46:48 -04:00
Vivian Phung 8aa1362be6 [3/n][project settings ui] Domains cleanup 2024-05-14 19:45:08 +00:00
Vivian Phung e1d2b789a3 [2/n][project settings ui] AddEnvironmentVariableRowProps (#24) 2024-05-14 15:42:51 -04:00
Vivian Phung cf161bcba0 AddEnvironmentVariableRowProps UI Cleanip 2024-05-14 19:40:59 +00:00
Vivian Phung 57956ec269 [1/n][project settings ui] GeneralTabPanel (#23) 2024-05-14 15:38:44 -04:00
Vivian Phung fb77433bea GeneralTabPanel cleanup 2024-05-14 19:36:50 +00:00
Vivian Phung 146b904556 [4/n][Storybook] components & icons (#22) 2024-05-14 15:34:37 -04:00
Vivian Phung 756cc52e1c [1/n] storybook components start 2024-05-14 19:32:47 +00:00
Vivian Phung ba5f281671 [3/n][Storybook] Radio (#39) 2024-05-14 15:31:33 -04:00
Vivian Phung 60a66e94bd [3/n][Storybook] Radio 2024-05-14 19:29:48 +00:00
Vivian Phung 5f5b0a4d4f [2/n][Storybook] Move AuthHeader Stories (#21)
### TL;DR

This pull request refactors the directory structure of the `AuthHeader` component, moving it to the `Pages/Auth` directory. The meta property's attributes were also refined.

### What changed?

The `AuthHeader` component and its story were relocated from the `packages/frontend/src/stories/` directory to the `packages/frontend/src/stories/Pages/Auth/` directory. The title in the meta property has been updated to `Pages/Auth/Header`. The previously explicit assertion that meta satisfies `Meta<typeof Header>` has been removed, making the type assertion implicit.

### How to test?

To test these changes, navigate to the new directory and any references associated with `AuthHeader` component, verify that the software builds successfully, and confirm that Storybook correctly displays the AuthHeader story.

### Why make this change?

This change improves the organization of the codebase by categorizing components into functionally relevant directories. This allows for better management and scalability of the code. Additionally, the adjustments made to the component's metadata in Storybook makes the documentation easier to understand.
2024-05-14 15:28:35 -04:00
Vivian Phung 5ebdd3003a Badges 2024-05-14 19:26:48 +00:00
Vivian Phung 987643b153 [1/n][Storybook] Badges (#36)
### TL;DR

This PR updates formatting in `Button.stories.tsx`

### What changed?

Pulled the Button heading inside `<h1>` tags onto a new line for readability and consistency.

### How to test?

Check the Button story in Storybook and ensure that the Button title displays correctly.

### Why make this change?

This small change improves the readability and consistency of code in the `Button` story.
2024-05-14 15:25:30 -04:00
Vivian Phung 090a9054e4 Button strories lint 2024-05-14 19:23:29 +00:00
Vivian Phung 7dcd6581a7 [Storybook] Refactor Component Directories and Imports (#38) 2024-05-14 15:22:43 -04:00
Vivian Phung 928958131c component story directory 2024-05-14 19:19:35 +00:00
Vivian Phung 327ac62186 Configure aliases pages and types in tsconfig and vite (#37)
### TL;DR

Updated paths in `tsconfig.json` and `vite.config.ts`

### What changed?

The paths for `pages` and `types` have been added to both `tsconfig.json` and `vite.config.ts` to support easier importing.

### How to test?

Check if imports using the new paths are working correctly in the code.

### Why make this change?

These changes seem to be for better organization and accessibility of different parts of the code by providing clear and quick paths for `pages` and `types`.
2024-05-14 15:18:19 -04:00
Vivian Phung 37c250873c resolve aliases 2024-05-14 13:27:23 -04:00
Vivian Phung f7fa47dbc8 format prettier (#20)
format prettier

storybook
2024-05-09 16:23:41 -04:00
Vivian Phung 1bba82ee86 storybook 2024-05-09 16:22:03 -04:00
Vivian Phung 99cc35459b format prettier 2024-05-09 16:22:03 -04:00
Vivian Phung 4b54f2fc2e format prettier (#18) 2024-05-09 16:19:32 -04:00
Vivian Phung 4906740876 format prettier 2024-05-09 16:18:52 -04:00
Vivian Phung 73ff7896f8 env example fix (#16) 2024-05-09 14:39:58 -04:00
Vivian Phung 67d28910d4 env example fix 2024-05-09 14:04:28 -04:00
Gilbert 939b1c40e8 Fix default branch bug 2024-05-08 22:27:58 -05:00
Vivian Phung c3b048d273 Revert "sign in with passkeys coming soon"
This reverts commit ed0de90118.
2024-05-08 11:45:24 -04:00
Gilbert 48552310e0 Turnkey auth 2024-05-06 14:36:58 -05:00
Vivian Phung c82e1110d3 remove unneccessary , (#12)
### TL;DR

This pull request includes a minor refactoring in the `index.tsx` file of the frontend package.

### What changed?

A trailing comma and a new line after this comma were removed from the `index.tsx` file.

### How to test?

You can test this change by running the application and checking if everything works as expected. As this is a minor change, it's not expected to break anything.

### Why make this change?

This change is part of the project's cleanup activity to maintain a clean and readable codebase.

Before | After |
| -- | --|
![Screenshot 2024-05-06 at 2.55.10 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/YN8FgzXirh28JBEdfrb9/7876d655-dddf-4140-8bf8-09f5170c2169.png) | ![Screenshot 2024-05-06 at 2.54.59 PM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/YN8FgzXirh28JBEdfrb9/29fdce38-b674-4e51-854e-77d51755a266.png) |
2024-05-06 14:59:14 -04:00
Vivian Phung b7f29781f2 remove unneccessary , 2024-05-06 14:55:34 -04:00
Vivian Phung cf531127c4 sign in with passkeys coming soon (#11)
### TL;DR

Added a toast notification for 'Sign-in with Passkeys' feature which is still under development.

### What changed?

'The Sign-in with Passkey' button now triggers a toast notification informing the user that this feature is coming soon.

### How to test?

Click on the 'Sign-in with Passkey' button on the login page. You should see a notification with the message 'Sign-in with Passkeys is coming soon!'.

### Why make this change?

This change has been made to inform users about upcoming features in an interactive manner and ensure a smooth user experience by preventing confusion about non-functional buttons.
2024-05-06 13:32:12 -04:00
Vivian Phung 44de7bb0f1 font path fix (#9)
### TL;DR

Update font URLs in `index.css`

### What changed?

Updated font URLs from `../public/fonts/InterDisplay/` to `/fonts/InterDisplay/` for better path consistency

### How to test?

Test loading fonts and check for any font display issues

### Why make this change?

To ensure correct font loading and path consistency in the project
2024-05-06 13:31:53 -04:00
Vivian Phung ed0de90118 sign in with passkeys coming soon 2024-05-06 13:31:11 -04:00
Vivian Phung 11c8f7b09b Revert "Build source maps for bugsnag"
This reverts commit df8bc3784a.
2024-05-06 13:31:10 -04:00
Vivian Phung 3dd62114b4 font path fix 2024-05-06 13:31:09 -04:00
Vivian Phung 4f3ea09b79 Revert "Build source maps for bugsnag"
This reverts commit df8bc3784a.
2024-05-06 13:25:26 -04:00
Vivian Phung f4f80903f8 update .gitignore for dev output (#10)
### TL;DR

Added `packages/backend/dev/` to `.gitignore` file.

### What changed?

`packages/backend/dev/` is now ignored from the git repository for better development practices.

### How to test?

Try adding files to `packages/backend/dev/`, they should not be tracked by git now.

### Why make this change?

To ensure unnecessary development files don’t accidentally get checked into the repository.
2024-05-06 13:08:09 -04:00
Vivian Phung 5307eab443 update gitignore for build output 2024-05-06 13:02:37 -04:00
Gilbert 00199424f7 Improve exception handling 2024-05-02 16:36:31 -05:00
Gilbert aaadfeab72 Don't need this 2024-05-02 16:30:49 -05:00
Gilbert 3ffac1a019 ngmi 2024-05-02 15:51:51 -05:00
Gilbert 56a264199b Fix dev script 2024-05-02 15:47:23 -05:00
Gilbert df8bc3784a Build source maps for bugsnag 2024-05-02 10:08:16 -05:00
Vivian Phung 464b0463af remove all icons for now (#8) 2024-05-01 22:01:51 -04:00
Vivian Phung c29134bfd9 remove all icons for now 2024-05-01 21:55:13 -04:00
Vivian Phung 9b0ad34831 ad g around gear icon (#7) 2024-05-01 21:38:28 -04:00
Vivian Phung 89b3cb8c31 ad g around gear icon 2024-05-01 21:38:14 -04:00
Vivian Phung c52d695ca7 gear icon on settings page (#6) 2024-05-01 21:31:48 -04:00
Vivian Phung 187f75d561 gear icon on settings page 2024-05-01 21:31:22 -04:00
Vivian Phung 14deecfad6 add space btw linked learn more (#5)
### TL;DR

Update description text in project transfer section to include a 'Learn more' link.

### What changed?

The description text under the 'Transfer project' section of the project settings page is updated. A 'Learn more' link is added to provide additional information about transferring projects.

### How to test?

Navigate to a project's settings page and look for the 'Transfer project' section. Verify whether the description text contains a clickable 'Learn more' link.

### Why make this change?

To provide users with more information and to make the transfer process clearer by providing relevant resources.
2024-05-01 21:21:55 -04:00
Vivian Phung 808b2ad61b add space btw linked learn more 2024-05-01 21:20:48 -04:00
Vivian Phung 775e731f4d project avatar (#4)
### TL;DR

This pull request replaces the Avatar showing user's initial with a formatted text of user's name in Project Search layout.

### What changed?

In the `ProjectSearch` file, the `Avatar` component that displayed the user's initial is replaced with a paragraph tag that now shows the user's formatted name.

### How to test?

To test the change, navigate to the Project Search page to verify that the user's formatted name is displayed instead of an `Avatar`.
2024-05-01 20:35:57 -04:00
Vivian Phung 813cea055b project avatar 2024-05-01 20:33:32 -04:00
Gilbert 072e2c20e3 Add missing file 2024-05-01 18:32:25 -05:00
Vivian Phung 83d5e3d769 address should be in top view formatted. not just first letter 0 (#3) 2024-05-01 19:29:09 -04:00
Vivian Phung 9ed52c67c9 address should be in top view formatted. not just first letter 0 2024-05-01 19:28:09 -04:00
Gilbert f7d6d02b27 Quick error logging (will clean this up later) 2024-05-01 18:24:06 -05:00
Vivian Phung d7dc9a07f9 checkbox should always be disabled (#2)
### TL;DR

This change removes unused properties from a checkbox component in the `org-slug/projects/create/template` file.

### What changed?

The `value` and `onChange` properties were deleted from the checkbox that determines whether a repo is private or not. It's important to note that this checkbox is currently disabled.

### How to test?

Try to create a new project and check that there aren't any errors or behavior changes when dealing with the 'Make this repo private' checkbox.

### Why make this change?

This change enhances code readability and ensures we're not keeping unnecessary code. It does not affect functionality as the checkbox is currently disabled.
2024-05-01 18:33:58 -04:00
Vivian Phung 6cbc87a7d1 should always be disabled 2024-05-01 18:23:54 -04:00
Vivian Phung ddb54de49b make private templates disabled (#1) 2024-05-01 18:06:59 -04:00
Vivian Phung cf9fd04272 make private templates disabled 2024-05-01 17:50:54 -04:00
Gilbert 535c37d0b4 Support not having bugsnag available 2024-05-01 15:03:59 -05:00
Gilbert 46476bef28 Add bugsnag 2024-05-01 15:00:52 -05:00
nabarun 8b39f664ad Replace REACT_APP env with VITE 2024-04-25 16:59:02 +05:30
nabarun 5f4be30799 Remove current deployment and publish ApplicationDeploymentRemovalRequest for project DNS deployment 2024-04-25 16:59:02 +05:30
nabarun b53e12b94b Add script for publishing ApplicationDeploymentRemovalRecord record 2024-04-25 16:59:02 +05:30
nabarun f290b5c0b5 Implement checking for deployment removal records in intervals 2024-04-25 16:59:02 +05:30
nabarun 4fa6f418ba Lint fix 2024-04-25 16:59:02 +05:30
Eric Lewis 328da7fdc8 feat: submit delete deployment request
doesn't appear to work when the deployment is current?
2024-04-25 16:57:14 +05:30
Eric Lewis 8210512eea fix: missing key 2024-04-25 16:57:14 +05:30
Eric Lewis cd2dce2404 fix: don't use two refs 2024-04-25 16:57:14 +05:30
Eric Lewis c2158510d9 feat: clean up stuck builds 2024-04-25 16:57:14 +05:30
Eric Lewis 096fd04a22 fix: project card settings links 2024-04-25 16:57:14 +05:30
Eric Lewis a8d93732ce feat(gql-client): deployment removal request 2024-04-25 16:57:14 +05:30
Eric Lewis 953c3fc10b feat(gql): deployment removal request 2024-04-25 16:57:14 +05:30
Eric Lewis 3cbea57294 feat(registry): deployment removal request 2024-04-25 16:57:14 +05:30
Gilbert 27f5c57c37 Attempt to fix prod 2024-04-24 19:02:52 -05:00
Gilbert d1c3249b98 Copy tweak 2024-04-24 12:32:09 -05:00
Gilbert d34a5f29cd Prettier format 2024-04-24 12:18:59 -05:00
Gilbert 4a14681753 Support process.env in frontend 2024-04-24 12:18:18 -05:00
Eric Lewis aea6bfde54 feat: support cf workers
Note: we don't really want to be committing the gql-client. it is a stopgap.
2024-04-24 09:59:47 -04:00
Gilbert 4aac93b504 Sign in with google, using snowball's sdk (#188) 2024-04-23 22:02:54 -05:00
Gilbert e6b68dcce4 Bump snowball version 2024-04-23 21:54:46 -05:00
Gilbert d2daed4cac Sign in with google 2024-04-23 21:22:11 -05:00
Gilbert c395be82b5 Signup with sdk 2024-04-23 21:21:58 -05:00
Eric Lewis 748ca507da fix: use correct output directory for build 2024-04-23 09:43:52 -04:00
Gilbert bc210fdb0f Revert "Signup with sdk" until we get everything working (will submit pr)
This reverts commit bace4a6ce6.
2024-04-21 21:47:25 -05:00
Gilbert bace4a6ce6 Signup with sdk 2024-04-21 18:03:01 -05:00
Eric Lewis 0d36cc1b6d Merge pull request #182 from snowball-tools/ericlewis-patch-1
fix: default deployment filter status
2024-04-17 17:29:36 -04:00
Eric Lewis 1ed4ee979c fix: default deployment filter status 2024-04-17 17:11:43 -04:00
nabarunandprathamesh 72f1abcdf6 Add CI to test web app deployment and undeployment (#174)
* Add a script to deploy test records

* Add checks for ApplicationDeploymentRecord and the deployment URL

* Add a CI workflow to run the app deployment test

* Update test deployment request record config

* Add test for deployment removal

* Update test deployment request record

* Increase max retries

* Add retries when checking if URL is up or down

* Rename test script

* Run webapp deployment test CI on PR

* Remove unnecessary jq installation step from CI

* Revert "Run webapp deployment test CI on PR"

This reverts commit 01f373501a95b95cf0abc1dee8fed639ec62daf5.

* Add a step for Slack alerts on a CI failure

* Add a workflow dispatch for manual trigger

* Update workflow title and remove trigger on main

* Document deployment test setup

---------

Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
2024-04-15 14:10:59 +05:30
Vivian Phung 57add027ab Merge pull request #180 from snowball-tools/fix-lint
Add build step to lint action
2024-04-12 16:55:23 -05:00
Gilbert 7ad02d27bf Add build step to lint action 2024-04-12 16:32:09 -05:00
Eric Lewis b17e9df1e4 Merge pull request #169 from snowball-tools/eric/snow-314-dashboarduicreate-template-app-page-auto-select-first-git
fix: auto select first git account
2024-04-12 12:20:41 -04:00
Gilbert baadc507e7 Not yet 2024-04-11 21:59:33 -05:00
Gilbert 6316aa852b Add back .env.example 2024-04-11 21:55:25 -05:00
Gilbert 2274e8d145 Lint using tsc; fix type errors 2024-04-11 21:49:14 -05:00
Gilbert f8d706233e Switch from cra to vite 2024-04-11 21:48:58 -05:00
nabarun cc8f9527da Check for GitHub authentication in template create page and handle GitHub unauthorized errors (#173)
* Fix GitHub auth check on reloading template create page

* Fix error handling for unauthenticated GitHub token
2024-04-11 17:19:15 +05:30
Eric Lewis 47231a6eab Merge pull request #171 from snowball-tools/eric/airf-42-update-org-to-team
fix: use 'team' instead of 'organization'
2024-04-09 17:20:51 -04:00
Eric Lewis 4774074b67 Merge pull request #170 from snowball-tools/eric/airf-28-dashboard-hide-database-from-project-tab
fix: remove database tabs & overviews
2024-04-09 17:20:40 -04:00
Eric Lewis 0da7c3541e fix: use 'team' instead of 'organization' 2024-04-09 14:22:58 -04:00
Eric Lewis b7b4ab1f14 fix: remove database tabs & overviews 2024-04-09 14:19:09 -04:00
Eric Lewis ea6ad52a73 fix: show placeholder select while waiting for load 2024-04-09 14:11:26 -04:00
Eric Lewis f38dfb5604 chore: bump deploy 2024-04-04 10:50:39 -04:00
nabarunandneeraj 351db16336 Upgrade laconic-sdk package for showing error with reason in console (#165)
* Upgrade laconic sdk version

* Update toast message for add project

* Log error messages

* Update bond id in readme

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-04-03 20:26:34 +05:30
Wahyu Kurniawan 78f04c3669 Merge pull request #162 from snowball-tools/ayungavis/T-4943-product-reskin-confirmdialog-styling 2024-03-25 23:04:22 +07:00
Vivian Phung 44a6a8902f Merge pull request #140 from snowball-tools/readme-cleanup
readme
2024-03-15 15:05:36 -04:00
Wahyu Kurniawan 4fcf8e92b7 ♻️ refactor: create another example page for modal only 2024-03-14 22:07:36 +07:00
Wahyu Kurniawan 193dbb058a 🎨 style: adjust overflow text width on mobile 2024-03-14 22:07:05 +07:00
Wahyu Kurniawan 8834de893e 📝 docs: put all the new dialog into component page 2024-03-14 21:58:04 +07:00
Wahyu Kurniawan 9b3ac4654f ♻️ refactor: create transfer project dialog component 2024-03-14 21:57:26 +07:00
Wahyu Kurniawan e4fdae3329 ♻️ refactor: create disconnect repository dialog component 2024-03-14 21:57:00 +07:00
Wahyu Kurniawan 4c936b1eb7 ♻️ refactor: create remove member dialog component 2024-03-14 21:56:29 +07:00
Wahyu Kurniawan f5807c1126 ♻️ refactor: create delete webhook dialog component 2024-03-14 21:56:00 +07:00
Wahyu Kurniawan c1696fbf48 ♻️ refactor: create delete variable dialog component 2024-03-14 21:55:30 +07:00
Wahyu Kurniawan 45e8e9a7f4 ♻️ refactor: create delete domain dialog component 2024-03-14 21:55:02 +07:00
Wahyu Kurniawan 28740ffbee ♻️ refactor: create change state to production dialog reusable component 2024-03-14 21:54:30 +07:00
Wahyu Kurniawan 97289d85a3 ♻️ refactor: create cancel deployment dialog component 2024-03-14 21:53:48 +07:00
Wahyu Kurniawan f3ce0d0621 🎨 style: adjust deployment body card design to match with the figma 2024-03-14 21:52:55 +07:00
Wahyu Kurniawan c2417a9daa ️ feat: export the tag props 2024-03-14 21:52:07 +07:00
Wahyu Kurniawan d50a318e16 ️ feat: create chevron doublw down icon component 2024-03-14 21:50:11 +07:00
Wahyu Kurniawan 3674750011 ️ feat: update confirm dialog to use new modal component 2024-03-14 21:49:45 +07:00
Wahyu Kurniawan 102c861617 🎨 style: adjust modal theme and add wavy border on the header 2024-03-14 21:49:13 +07:00
Wahyu Kurniawan 56e9be59ad 🐛 fix: missing props 2024-03-08 15:32:11 +07:00
Wahyu Kurniawan 300b8e4b5e 📝 docs: add modal to the example page 2024-03-08 15:30:53 +07:00
Wahyu Kurniawan 296e149391 🎨 style: update confirm dialog to use custom modal component 2024-03-08 15:29:06 +07:00
Wahyu Kurniawan cbda1cc652 🎨 style: add new keyframe animation for modal 2024-03-08 15:28:23 +07:00
Wahyu Kurniawan 16e7b22507 ️ feat: create modal comopnent 2024-03-08 15:27:53 +07:00
Wahyu Kurniawan 93fb696d5c Merge pull request #161 from snowball-tools/ayungavis/T-4936-make-deployed-lines-responsive
[T-4936: style] Make deployed lines responsive
2024-03-08 14:54:27 +07:00
Wahyu Kurniawan 32305cedbc 🎨 style: add overflow for the content 2024-03-08 12:44:48 +07:00
Wahyu Kurniawan 519618b456 ♻️ refactor: remove duplicate component and use absolute for the mobile version 2024-03-08 12:42:42 +07:00
Andre Hadianto 4057480df2 Merge pull request #160 from snowball-tools/andrehadianto/T-4939-create-project-connect-github-assets
[T-4939] create project connect GitHub assets
2024-03-08 12:57:02 +08:00
Wahyu Kurniawan de197759de 🎨 style: remove width and height icon style from tag size 2024-03-08 10:36:14 +07:00
Wahyu Kurniawan 65b66fe383 ♻️ refactor: remove empty space 2024-03-08 10:35:22 +07:00
Wahyu Kurniawan c6ea3a8f53 🎨 style: adjust responsive for deployment details card 2024-03-08 09:58:26 +07:00
Wahyu Kurniawan 6d861c71cc 🎨 style: adjust filter form mobile 2024-03-08 09:57:50 +07:00
Andre Hadianto 1d96f6430f Merge pull request #159 from snowball-tools/andrehadianto/T-4968-qol-and-chores
[T-4968] qol and chores
2024-03-07 18:44:04 +08:00
Andre H b922e632bf 🔧 chore: add mock card to template 2024-03-07 18:23:43 +08:00
Andre H b1f318bbbd ️ feat: implement mock connect git card 2024-03-07 18:23:22 +08:00
Andre H 667deb78fb 🎨 style: tabs to use overflow-auto instead of overflow-scroll 2024-03-07 14:23:59 +08:00
Andre H 095008867f 🔧 chore: show loading state on fetching repo activities 2024-03-07 14:23:34 +08:00
Andre H 41033c5241 ️ feat: reskin "no repository found" component 2024-03-07 14:23:11 +08:00
Andre H cdb995205a 🎨 style: truncate overflow text on projectCard 2024-03-07 14:22:32 +08:00
Andre H 3a84832da5 🔧 chore: add tooltip on hover projectcard name 2024-03-07 14:22:10 +08:00
Wahyu Kurniawan 3fdc0b2dff Merge pull request #156 from snowball-tools/ayungavis/T-4931-layout-mobile-sidebar-layout
[T-4931: style] Mobile sidebar layout and search bar
2024-03-07 12:52:12 +07:00
Andre Hadianto 89da03cec6 Merge pull request #158 from snowball-tools/andrehadianto/T-4961-create-project-success-page
[T-4961] create project success page
2024-03-07 13:51:14 +08:00
Andre Hadianto 9b6cd9baae Merge pull request #157 from snowball-tools/andrehadianto/T-4944-layout-template-steps-layout
[T-4944] layout template steps layout
2024-03-07 12:24:29 +08:00
Andre H 658cc0b6b2 🔧 chore: change to isDesktopView for >960px 2024-03-07 12:18:41 +08:00
Andre H be8f0bc4ad 🔧 chore: use `` for text 2024-03-07 12:17:06 +08:00
Andre H 2a8fc30979 🔧 chore: add TODO 2024-03-07 12:16:23 +08:00
Andre H 4074dd4001 🔧 chore: use link-emphasized button 2024-03-07 12:16:03 +08:00
Andre H 028831f806 🔧 chore: change isDesktopView to isTabletView 2024-03-07 11:45:51 +08:00
Andre H 646099707c 🔧 chore: use lottie for success logo 2024-03-07 11:42:12 +08:00
Andre H 6b0548ec47 ⚒️ build: add lottie-react 2024-03-07 11:41:41 +08:00
Andre H 2ed07a6987 ️ feat: reskin success create project 2024-03-07 10:18:04 +08:00
Andre H e9ab034625 🔧 chore: add new icons 2024-03-07 10:17:30 +08:00
Wahyu Kurniawan 02500b3cba 🎨 style: make the sidebar scrollable 2024-03-06 21:03:01 +07:00
Wahyu Kurniawan 4c8d0e2436 🎨 style: change the background color of the avatar 2024-03-06 19:14:32 +07:00
Wahyu Kurniawan efa74898af 🎨 style: adjust padding of the content layout 2024-03-06 19:07:41 +07:00
Wahyu Kurniawan 532732943e ♻️ refactor: add empty space 2024-03-06 16:38:45 +07:00
Wahyu Kurniawan be4900e63f Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4931-layout-mobile-sidebar-layout 2024-03-06 16:28:50 +07:00
Wahyu Kurniawan 9e4e203f5f 🎨 style: remove bottom border radius for mobile 2024-03-06 15:44:35 +07:00
Wahyu Kurniawan d812cdf05f 🎨 style: adjust logo and mobile header padding on mobile 2024-03-06 15:39:46 +07:00
Wahyu Kurniawan b605c1bc5b 🎨 style: adjust media query minimum width for desktop to match with tailwind large responsive min width 2024-03-06 15:27:29 +07:00
Wahyu Kurniawan 8d3ef369bb ♻️ refactor: change project bar item component to button 2024-03-06 15:14:09 +07:00
Wahyu Kurniawan 7aac8cdcef 🚀 perf: change the debounce to 300 ms 2024-03-06 15:11:16 +07:00
Wahyu Kurniawan 6c31113bca 🎨 style: adjust grid width of the project list 2024-03-06 15:08:45 +07:00
Andre Hadianto 30ecd41975 Merge pull request #141 from snowball-tools/andrehadianto/T-4904-home-org-switcher
[T-4904] home org switcher
2024-03-06 15:01:39 +08:00
Andre Hadianto 410375f8c7 Merge branch 'main' into andrehadianto/T-4904-home-org-switcher 2024-03-06 15:01:29 +08:00
Andre H 226d02ea59 🔧 chore: change media query to lg 2024-03-06 14:29:28 +08:00
Wahyu Kurniawan ca64d00355 🎨 style: adjust grid for the project list 2024-03-06 13:22:39 +07:00
Wahyu Kurniawan 287fe360d3 ♻️ refactor: use useMemo to render organization and sidebar menu 2024-03-06 13:22:17 +07:00
Andre H f23c757de9 🔧 chore: create project create repo buttonSize to be responsive 2024-03-06 14:15:19 +08:00
Andre H 3737550f7f 🔧 chore: useMediaQuery change md to >720px 2024-03-06 14:14:49 +08:00
Andre H 274762cbfe ️ feat: create project mobile layout 2024-03-06 14:14:10 +08:00
Wahyu Kurniawan 90295fd620 ♻️ refactor: use dialog overlay instead of div 2024-03-06 13:09:23 +07:00
Wahyu Kurniawan b75957929a 🎨 style: fixed content moves down issue when opening the sidebar 2024-03-06 12:46:49 +07:00
Wahyu Kurniawan daffcf1203 🎨 style: adjust mobile sidebar animation 2024-03-06 11:53:44 +07:00
Wahyu Kurniawan eccf78afdb 🎨 style: add background tot the project search header 2024-03-06 11:40:07 +07:00
Wahyu Kurniawan 0901036f71 Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4931-layout-mobile-sidebar-layout 2024-03-06 11:31:37 +07:00
Wahyu Kurniawan 8d3ef3bafc Merge pull request #142 from snowball-tools/ayungavis/T-4917-project-deployments-layout-and-empty-state
[T-4917: style] Re-styling and refactor project page deployments layout and empty state
2024-03-06 11:29:21 +07:00
Wahyu Kurniawan b6e02fb19d 🐛 fix: on reset error when value is undefined 2024-03-06 11:23:25 +07:00
Wahyu Kurniawan 462d247a86 Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4917-project-deployments-layout-and-empty-state 2024-03-06 11:18:38 +07:00
Wahyu Kurniawan 80097a32ac ️ feat: make the default status to undefined 2024-03-06 11:17:08 +07:00
Wahyu Kurniawan a80d40156f 🎨 style: remove bouncing animation 2024-03-06 11:11:16 +07:00
Wahyu Kurniawan ced50bf7f2 🎨 style: add animation to the layout 2024-03-06 10:55:37 +07:00
Wahyu Kurniawan 90b583aa19 Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4931-layout-mobile-sidebar-layout 2024-03-06 10:54:24 +07:00
Wahyu Kurniawan 48e3581322 ️ feat: add handler when click the project item on mobile 2024-03-06 10:42:06 +07:00
Wahyu Kurniawan 1ddc3b81c7 ️ feat: add search dialog on mobile 2024-03-06 10:31:11 +07:00
Wahyu Kurniawan 5a86abd133 🎨 style: adjust input and search bar input style 2024-03-06 10:30:50 +07:00
Wahyu Kurniawan 6ddf44cddd ♻️ refactor: project search bar 2024-03-06 10:30:26 +07:00
Wahyu Kurniawan b03200c256 🎨 style: add animation when sidebar open on mobile 2024-03-06 09:34:41 +07:00
Wahyu Kurniawan 1648deb64f 🔧 chore: install framer-motion 2024-03-06 09:34:06 +07:00
Andre Hadianto 621ca8926e Merge pull request #150 from snowball-tools/andrehadianto/T-4912-create-project-create-repository-page
[T-4912] create project create repository page
2024-03-06 09:39:33 +08:00
Andre H 8c0162f9f3 🔧 chore: replace Avatar with TemplateIcon on Template 2024-03-06 09:35:18 +08:00
Wahyu Kurniawan e05e2c63c2 🎨 style: adjust sidebar for mobile 2024-03-06 08:25:29 +07:00
Wahyu Kurniawan 3b1f03bcb6 🎨 style: replacee grid of the layout to use flex and fixed width of the sidebar 2024-03-06 08:24:11 +07:00
Wahyu Kurniawan 80fee1f585 ️ feat: create logout icon 2024-03-06 08:23:09 +07:00
Andre Hadianto d50f559a07 Merge branch 'main' into andrehadianto/T-4912-create-project-create-repository-page 2024-03-06 09:04:28 +08:00
Andre Hadianto 46d44378c5 Merge pull request #155 from snowball-tools/andrehadianto/T-4940-project-overview-mobile-layout
[T-4940] project overview mobile layout
2024-03-06 09:02:02 +08:00
Wahyu Kurniawan 3e42899f2e Merge pull request #138 from snowball-tools/ayungavis/T-4911-create-project-import-a-repository-section
[T-4911: feat] Re-styling and refactor create project import a repository section
2024-03-05 22:41:43 +07:00
Andre Hadianto 75a048c7a9 Merge pull request #153 from snowball-tools/andrehadianto/T-4935-timeline-component
[T-4935] timeline component
2024-03-05 18:32:08 +08:00
Wahyu Kurniawan b5f50fe16f 🎨 style: adjust layout for mobile 2024-03-05 16:16:45 +07:00
Wahyu Kurniawan aa4094669d ️ feat: create logo component 2024-03-05 16:16:20 +07:00
Wahyu Kurniawan 33fbdf60ae ️ feat: create some icons needed 2024-03-05 16:15:37 +07:00
Wahyu Kurniawan abe1401a6d 🎨 style: adjust the style of project search bar 2024-03-05 16:15:11 +07:00
Andre H 2182beca22 🔧 chore: remove database tab 2024-03-05 14:22:59 +08:00
Andre H 96f519d5e3 ️ feat: mobile layout for project overview 2024-03-05 14:19:47 +08:00
Andre H f6db1e119b 🔧 chore: add overflow-scroll to Tabs component 2024-03-05 14:18:51 +08:00
Andre H c079a9c336 🔧 chore: prevent Avatar from shrinking 2024-03-05 14:18:31 +08:00
Andre H f9ac778e47 ⚒️ build: add usehooks-ts 2024-03-05 14:16:54 +08:00
Andre H e8ae417772 🔧 chore: whitespace nowrap to prevent text warping on steps 2024-03-05 09:39:00 +08:00
Wahyu Kurniawan 18dde52c9a ️ feat: update select input default icon and options icon 2024-03-05 05:07:21 +07:00
Wahyu Kurniawan d9bae720d7 ️ feat: update date picker input icon 2024-03-05 05:06:53 +07:00
Wahyu Kurniawan e9f32ff668 ♻️ refactor: make the conditional logic to boolean 2024-03-05 05:06:28 +07:00
Wahyu Kurniawan 8a1e84386a ️ feat: create some icons needed 2024-03-05 05:05:54 +07:00
Wahyu Kurniawan 3335c26f82 Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into ayungavis/T-4917-project-deployments-layout-and-empty-state 2024-03-05 04:37:49 +07:00
Andre H 0a2686d5ec 🔧 chore: add divider to render page 2024-03-04 17:50:23 +08:00
Andre H c1da92bc99 🔧 chore: adjust design to figma 2024-03-04 17:50:04 +08:00
Andre H add2559860 🔧 chore: adjust other CheckRoundFilledIcon user 2024-03-04 17:41:33 +08:00
Andre H 460f8a80b8 🔧 chore: replace Stepper with Steps component 2024-03-04 11:52:55 +08:00
Andre H f5296e9b8d 🔧 chore: first index to be '1' for Steps 2024-03-04 11:52:31 +08:00
Andre H 0aeea36dbd Merge remote-tracking branch 'origin/main' into andrehadianto/T-4935-timeline-component 2024-03-04 11:46:27 +08:00
Andre H 8f0c39022d 🔧 chore: add render component 2024-03-04 10:40:06 +08:00
Andre H 6c42a22972 ️ feat: implement steps/ timeline 2024-03-04 10:38:12 +08:00
Andre H adb64f2db1 🔧 chore: remove outer padding on the icon 2024-03-04 10:14:21 +08:00
Zachery 64e3aa5b25 [T-4913] Create Project - Deploy page (#152) 2024-03-03 13:41:25 +08:00
neeraj dc6f436409 Handle review changes 2024-03-01 14:04:43 +05:30
neeraj 691edf3508 Update toast message 2024-03-01 13:26:38 +05:30
Zachery 4fc654f763 [T-4933] Create Project - Mobile modal layout (#151)
* feat: create project mobile modal layout

* style: make dialog content stretch full height
2024-03-01 15:22:16 +08:00
neeraj fe599f97a3 Handle error if repo already exists 2024-03-01 12:20:19 +05:30
Andre H be0ea904ee 🔧 chore: REMOVE framework for now 2024-03-01 14:07:12 +08:00
Andre H 8114b10932 ♻️ refactor: use shared Avatar 2024-03-01 14:05:17 +08:00
Andre H 0ee27df25e 🔧 chore: font hierarchy 2024-03-01 14:00:41 +08:00
Andre H 893a499be5 🔧 chore: dont render when props not provided 2024-03-01 14:00:08 +08:00
Andre H 596d8eb326 🔧 chore: add flex to Input and Select 2024-03-01 13:49:30 +08:00
Andre H e9d38e0d3b 🔧 chore: add htmlFor to label in radioItem 2024-03-01 13:48:54 +08:00
Andre H ab77ce681a 🔧 chore: radioItem leftIcon use span 2024-03-01 13:48:33 +08:00
Vivian Phung 5be6e14db9 Update README.md 2024-02-29 21:59:41 -07:00
Sushan Yadav 409b654f9b Project Deployments - Deployed line items (#147)
* feat: add deployment lines

* fix: typo DeploymentMenu
2024-03-01 10:05:50 +05:45
Andre H 4a86952810 🔧 chore: prevent 'gap' due to empty div on Select 2024-03-01 11:26:38 +08:00
Andre H 207b32ce65 ️ feat: integrate useform 2024-03-01 11:23:38 +08:00
Andre H 6e653774be 🔧 chore: reskin with shared components 2024-03-01 11:13:17 +08:00
Andre H 0b64ccc364 🔧 chore: replace space-y-2 with gap-y-2 for input 2024-03-01 11:12:47 +08:00
Andre H a8ad6c6eec ️ feat: implement card style for radio 2024-03-01 10:41:12 +08:00
nabarun 65f64a3dcd Fix frontend deployment config (#148) 2024-02-29 22:45:33 +05:30
nabarun d64cf46d89 Fetch latest registry record version and use in deploy script (#146)
* Fetch latest registry record version and increment

* Move reference up

* Remove steps to manually update record values

* Update registry config with new key and bond
2024-02-29 22:29:23 +05:30
nabarunandneeraj 1ff5ab3dfd Assign project domain to latest production deployment (#145)
* Create ApplicationDeploymentRequest with project DNS

* Add comment for filtering AppDeploymentRecord

* Fix lint

* Handle delete gitHub event

* Add sleep before registry write methods

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-29 18:33:34 +05:30
Andre H d9392c095d ️ feat: reskin template header 2024-02-29 15:31:10 +07:00
Andre H 2cd1865d19 🔧 chore: add linkchain icon 2024-02-29 15:30:34 +07:00
Eric Lewisandnabarun a7810a34c9 feat: one click deployer (#139)
* feat: one click deployer

This adds a script for deploying whatever is the latest on main, essentially automating the changes specified in the readme.

* fix

* repurpose existing script

* clean up

* fix

* fix

* Update latest record version

---------

Co-authored-by: Nabarun <nabarun@deepstacksoft.com>
2024-02-29 13:11:01 +05:30
Vivian Phung ff9b08e91d Merge pull request #143 from snowball-tools/template-card-redirect-bug
Template card redirect bug
2024-02-29 00:38:12 -07:00
nabarun 94a9bf88a9 Remove check for .env in build script (#144) 2024-02-29 12:51:32 +05:30
Vivian Phung dd5f93050d fix navigate 2024-02-29 00:01:56 -07:00
Andre H 11aeda9453 🔧 chore: remove squiggly line 2024-02-29 13:59:53 +07:00
Andre H dfa38a4cba 🔧 chore: remove useMemo 2024-02-29 13:58:45 +07:00
Andre H 638e31f7fe 🔧 chore: use wavyBorder 2024-02-29 13:58:22 +07:00
Vivian Phung 5946755749 fix navigate 2024-02-28 23:52:37 -07:00
Vivian Phung 4532a962d8 fix navigate 2024-02-28 23:44:10 -07:00
Wahyu Kurniawan 83fec1d273 🎨 style: refactor and re-styling deployment tab page 2024-02-29 11:17:41 +07:00
Wahyu Kurniawan 6db5871868 🎨 style: make the input width to full 2024-02-29 11:17:41 +07:00
Wahyu Kurniawan e0c5895e9c ️ feat: add onReset prop 2024-02-29 11:17:41 +07:00
Wahyu Kurniawan 630af612a2 ️ feat: add some components 2024-02-29 11:17:41 +07:00
Wahyu Kurniawan 87307381cb 🎨 style: refactor and re-styling filter form component 2024-02-29 11:17:40 +07:00
Sushan Yadav 6a108c1a1b Merge pull request #137 from snowball-tools/sushan/T-4914-project-overview-layout
Project overview layout
2024-02-29 09:57:12 +05:45
Andre Hadianto aa5507f309 Merge branch 'main' into andrehadianto/T-4904-home-org-switcher 2024-02-29 10:34:36 +08:00
Andre H 91a80cac8a ️ feat: add squiggly line and personal TODO 2024-02-29 09:30:53 +07:00
Andre H ee397d0e6e 🔧 chore: add squigglyline icon 2024-02-29 09:30:29 +07:00
Wahyu Kurniawan 98ec1915ca ♻️ refactor: remove unused comment 2024-02-29 09:21:49 +07:00
Wahyu Kurniawan f08081932c ♻️ refactor: use custom toast component and handle error 2024-02-29 09:21:28 +07:00
Andre H fe427a9fb8 🐛 fix: onChange not triggered. thanks @ayungavis 2024-02-29 09:16:01 +07:00
Andre H a5b4103515 🔧 chore: bigger img on userselectitem 2024-02-29 09:15:18 +07:00
Andre H f77f7c120a 🔧 chore: follow select changes 2024-02-29 08:52:26 +07:00
Vivian Phung 22f3cbafb7 readme 2024-02-28 17:27:27 -07:00
Andre H 30c50a3eec 🔧 chore: use UserSelect in sidebar 2024-02-28 23:08:21 +07:00
Andre H 0381fdbbed ️ feat: impelment user select 2024-02-28 23:07:54 +07:00
Andre H 2f466d4fbb 🔧 chore: remove dot when description dont exist 2024-02-28 23:07:31 +07:00
Andre H ee35e64f69 🔧 chore: add icons 2024-02-28 23:06:54 +07:00
Sushan Yadav 0fb378386a chore: update comment 2024-02-28 20:51:58 +05:45
Sushan Yadav 563059e887 chore: comment fixes - tab issues and hardcoded strings 2024-02-28 20:46:08 +05:45
Wahyu Kurniawan 075cec58e5 🎨 style: add github icon 2024-02-28 21:27:04 +07:00
Wahyu Kurniawan d52a34da35 🐛 fix: controlled and uncontroller console error 2024-02-28 21:18:29 +07:00
Wahyu Kurniawan eb4ccfbc9c 🎨 style: make the input width full 2024-02-28 21:17:59 +07:00
Wahyu Kurniawan 769593913e 🐛 fix: button inside button console browser error 2024-02-28 21:17:30 +07:00
Wahyu Kurniawan eda2cc76ed ♻️ refactor: restructured and restyling project repo card component 2024-02-28 21:16:59 +07:00
Wahyu Kurniawan 99eb514306 ♻️ refactor: restructured and restryling repository list component 2024-02-28 21:16:26 +07:00
Sushan Yadav f34eb48d1e feat: add layout 2024-02-28 17:35:02 +05:45
Sushan Yadav acf5aab26f Merge branch 'main' into sushan/T-4914-project-overview-layout 2024-02-28 16:57:32 +05:45
Sushan Yadav 6b6582c287 feat: top section 2024-02-28 16:56:28 +05:45
Sushan Yadav b43ee3b7bb feat: add deplyment infos 2024-02-28 16:39:26 +05:45
Wahyu Kurniawan b1bf47d104 [T-4921: feat] Update project card component (#136)
* 🎨 style: add hover interaction to the card

* ️ feat: make the whole card clickable

* 🎨 style: adjust hovered background for wavy border

* ♻️ refactor: move wavy border class to project card theme

* 🎨 style: add transition when hover

* 📝 docs: add todo to experiment using `Link` componnt
2024-02-28 16:50:55 +07:00
Wahyu KurniawanandZachery Ng 8ee61c0c85 [T-4910: feat] Re-styling dashboard, create project layout, project template card (#135)
* 🎨 style: adjust wavy border and add layout wavy border

* ♻️ refactor: change sidebar to use `nav`

* ♻️ refactor: org slug dashboard layout

* ♻️ refactor: create project layout and restyling it

* ♻️ refactor: remove unused style

* ️ feat: restyling template card

* ️ feat: create template icon component

* ️ feat: use `h2` for layout title

* ️ feat: Add isComingSoon property to templates and handle click event in TemplateCard component

* ♻️ refactor: WavyBorder component and update CreateProjectLayout

* 🎨 style: update button medium size padding

* 🎨 style: update layout shadow and add new shadow for the template card

* ️ feat: add wavy border gradient and line svg assets

* refactor: update wavy border svg

* 🎨 style: adjust template card name and arrow also responsive of the list of template

---------

Co-authored-by: Zachery Ng <zachery.ng@gmail.com>
2024-02-28 16:22:54 +07:00
Andre Hadianto 33aa75e341 Merge pull request #133 from snowball-tools/andrehadianto/T-4906-create-project-connect-github
[T-4906] create project connect GitHub
2024-02-28 15:55:59 +08:00
Sushan Yadav a238610522 Reskin Project Overview - Activity section (#134)
* feat: reskin w/ new design system

* feat: add sepeator calc in comment

* chore: limit commit message to 4 line

* comment fixes

* chore: use Heading component
2024-02-28 13:31:41 +05:45
Zachery Ng bd4a51c830 style: update tertiary button 2024-02-28 15:45:57 +08:00
Andre H 4a87c100d6 🔧 chore: use proper type 2024-02-28 14:20:12 +07:00
Andre H 3493d735b9 🔧 chore: replace bgClass with className 2024-02-28 14:19:02 +07:00
Andre H a324d32ebf 🔧 chore: use Heading 2024-02-28 13:51:35 +07:00
Andre H 4519494be8 ♻️ refactor: cherry pick fomr main 2024-02-28 13:49:11 +07:00
Andre H 319822c8e6 🔧 chore: add shadow to button 2024-02-28 13:46:09 +07:00
Andre H c5273ff530 ♻️ refactor: use IconWithFrame 2024-02-28 13:39:50 +07:00
Andre H 2585418ed6 🔧 chore: implement IconWithFrame common component 2024-02-28 13:39:30 +07:00
Wahyu Kurniawan 62734308fc [T-4907: style] Re-styling home page & sidebar component (#132)
* ️ feat: create heading component

* ♻️ refactor: move sidebar inside shared components

* ️ feat: add polymorphic prop type for heading component

* 🎨 style: re-styling project list page

* ♻️ refactor: remove `.env`

* 🎨 style: set default font weight to normal for heading component
2024-02-28 12:50:30 +07:00
Andre H 89306ddcc7 🔧 chore: add toast 2024-02-28 11:19:09 +07:00
Andre H 3b67396c43 ️ feat: reskin connect github 2024-02-28 10:55:10 +07:00
Andre H 3535bd3a58 🔧 chore: add GitTeaIcon 2024-02-28 10:53:05 +07:00
Wahyu Kurniawan 282001c317 [T-4903: chore] Add inter display font (#127)
* 🔧 chore: add `InterDisplay` font families

* 🎨 style: add font `InterDisplay` to tailwind

* 🔧 chore: update `yarn.lock`

* ♻️ refactor: change `InterDisplay` to `inter Display` and `inter-display` to `display
2024-02-28 08:47:43 +07:00
Eric Lewis 2f62d086de Merge pull request #131 from snowball-tools/ericlewis/deploy-2
chore(deployment): 1.1.3
2024-02-27 12:55:54 -05:00
Eric Lewis 4c544e8d15 chore: readme 2024-02-27 12:30:35 -05:00
Eric Lewis a02ee13f9d chore: deploy readme 2024-02-27 12:29:27 -05:00
Eric Lewis e32a51a771 chore(deployment): 1.1.3 2024-02-27 12:25:20 -05:00
Eric Lewis 513ca69d01 Merge pull request #130 from snowball-tools/ericlewis/pretty-login
feat: pretty login
2024-02-27 12:08:40 -05:00
Eric Lewis e34b8d6a2e Merge pull request #129 from snowball-tools/ericlewis/deploy
chore: create new deployment
2024-02-27 12:07:43 -05:00
Eric Lewis f4d5b77784 fix deploy 2024-02-27 11:56:22 -05:00
Eric Lewis 399b80ce3b chore: create new deployment 2024-02-27 11:30:39 -05:00
Eric Lewis fdaec2da94 feat: pretty login 2024-02-27 11:13:14 -05:00
Vivian Phung 13fc92bf0e Merge pull request #128 from snowball-tools/ericlewis/prettify-select 2024-02-27 10:44:59 -05:00
Eric Lewis 8a8ee1d6ae fix centering 2024-02-27 10:40:54 -05:00
Vivian Phung 5680961964 Merge pull request #122 from snowball-tools/repo 2024-02-27 09:18:20 -05:00
Vivian Phung aa0ca8dd21 Merge pull request #121 from snowball-tools/search-area-plus 2024-02-27 09:18:03 -05:00
Wahyu Kurniawan 8280f4c7f7 [T-4902: feat] Update project card (#126)
* ️ feat: create wavy border component

* ️ feat: create project card component

* ️ feat: add new size in avatar

* 🎨 style: add default border radius when the shape is default on the button

* ️ feat: create some icons for project card component

* 🔧 chore: install and setup jetbrains mono font family

* ️ feat: create `getInitials` util function

* 🎨 style: add jetbrains mono font to the tailwind config

* ️ feat: add warning error icon

* ♻️ refactor: change `jetbrains-mono` to `mono`
2024-02-27 21:16:38 +07:00
nabarun ab72bbb03f Update template repo links (#124) 2024-02-27 14:16:36 +05:30
nabarun cc4e5fd627 Add deployer package for frontend app (#123)
* Add script and records for deploying frontend app

* Update readme with steps for new deployment

* Add troubleshooting section

* Move config env update

* Add step to commit updated records

* Bump record version
2024-02-27 11:52:05 +05:30
Vivian Phung 195471b888 Merge branch 'main' into repo 2024-02-27 00:07:22 -05:00
Wahyu Kurniawan c731dd308c [T-4840: feat] Dropdown/select component (#108)
* 🎨 style: adjust z index of the date picker popover

* 🎨 style: add new spacing and rename box shadow from calendar to dropdown

* 🐛 fix: console error becasue button inside button

* ♻️ refactor: rename shadow calendar to shador dropdown on calendar component

* 🚀 perf: remove vscode settings inside `packages/frontend`

* ️ feat: create check radio icon and chevron down icon component

* 🔧 chore: install `downshift`

* ️ feat: create select component

* 🎨 style: adjust the popover position based on the user screen

* ️ feat: separate select item to be a component

* ️ feat: separate select value to be a component

* ♻️ refactor: adjust style and refactor to a new component

* ️ feat: create a type for merge two interface but keep the last value

* 🐛 fix: forward ref the component to fix console error

* ️ feat: add `hideValues` prop to hide the values when on multiple

* 🐛 fix: no result not showing

* ️ feat: make the select to be controller component

* ♻️ refactor: remove console log

* ♻️ refactor: update pr review
2024-02-27 12:05:16 +07:00
Vivian Phung 8ce531274d center items in nav bar of git repos 2024-02-27 00:00:52 -05:00
Vivian Phung 6c38094093 github icon in search repo 2024-02-26 23:56:20 -05:00
Vivian Phung 95285d30dd private repo + repo 2024-02-26 23:50:42 -05:00
Vivian Phung 61c8846400 org slug 2024-02-26 23:24:19 -05:00
Eric Lewis d73c3c7daf fix: select issue 2024-02-26 22:57:05 -05:00
Vivian Phung effa0bbf14 Merge pull request #118 from snowball-tools/ericlewis/prettify-select
feat: prettify select in sidebar
2024-02-26 22:51:15 -05:00
Eric Lewis 10a875d6e8 feat: prettify select in sidebar 2024-02-26 22:46:42 -05:00
Vivian Phung 284eb1403a Merge pull request #116 from snowball-tools/ericlewis/search-bar
feat: prettier search area
2024-02-26 22:35:46 -05:00
Eric Lewis 538e89e3c9 Merge pull request #117 from snowball-tools/ericlewis/bg
feat: background depth
2024-02-26 22:35:37 -05:00
Eric Lewis b88752da69 softer line 2024-02-26 22:34:42 -05:00
Eric Lewis 48c569c371 feat: background depth 2024-02-26 22:31:03 -05:00
Eric Lewis 549dc5af10 feat: prettier search area 2024-02-26 22:24:15 -05:00
Vivian Phung 012beaad57 metadata (#112) 2024-02-26 21:10:04 -05:00
Vivian Phung 81f27442fb github page (#114)
* ss

* comment out connect account tab model for now
2024-02-26 21:08:39 -05:00
Eric Lewis bdb948f69e Merge pull request #113 from snowball-tools/ericlewis/sidebar-tabs
feat: side bar tabs + icons
2024-02-26 18:58:14 -05:00
Eric Lewis 2442e92395 feat: side bar tabs + icons 2024-02-26 18:35:45 -05:00
Vivian Phung dcf271a01b Build webapp script fix (#111)
* build-webapp-script-fix

* build-webapp-script-fix
2024-02-26 18:06:24 -05:00
Vivian Phung 48fd953c60 Merge pull request #107 from snowball-tools/designsystem
merge design system into main
2024-02-26 17:50:01 -05:00
Vivian Phung 5eff273abb Merge branch 'designsystem' of github.com:snowball-tools/snowballtools-base into designsystem 2024-02-26 17:46:28 -05:00
Vivian Phung f9af066333 merge conflict and env.example 2024-02-26 17:45:51 -05:00
Eric Lewis 04460a24ac Merge branch 'designsystem' of https://github.com/snowball-tools/snowballtools-base into designsystem 2024-02-26 17:45:38 -05:00
Eric Lewis ccc4589033 partial sidebar style 2024-02-26 17:45:04 -05:00
Vivian Phung cf2f9a88e1 Merge branch 'main' of github.com:snowball-tools/snowballtools-base into designsystem 2024-02-26 17:44:42 -05:00
Vivian Phung 50749055bf ignore backend env file and readme 2024-02-26 17:33:44 -05:00
Vivian Phung ef7c673698 ignore environments backend folder 2024-02-26 17:22:47 -05:00
Vivian Phung 7f11d8818d update eslint with react version 2024-02-26 17:08:58 -05:00
Vivian Phung ece9a2ecec update readme 2024-02-26 17:08:22 -05:00
Eric Lewis cb614c8d8f create project uses button 2024-02-26 17:02:53 -05:00
Vivian Phung dbb928cd2f cleanup gitignore example 2024-02-26 17:02:42 -05:00
Vivian Phung cfd129fb96 frontend ignore .env and change image 2024-02-26 16:57:49 -05:00
Eric Lewis efc10d3d7e fix walletconnect 2024-02-26 15:11:18 -05:00
Eric Lewis 042aff2f87 temp (?) fix build
revert this if needed
2024-02-26 11:56:51 -05:00
nabarunandneeraj b48afed24f Add project create template for image upload PWA (#109)
* Use image upload PWA template

* Set template URL from env

* Handle review changes

* Update readme and build app script

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-26 17:54:35 +05:30
Vivian Phung 0dfecd024d merge 2024-02-24 00:01:06 -05:00
telackeyandVivian Phung 6126c03eee Restore https://github.com/snowball-tools/snowballtools-base/pull/103 (#106)
* Update logo.svg

* Update logo.svg

* Delete packages/frontend/public/favicon.ico

* Delete packages/frontend/public/logo192.png

* Delete packages/frontend/public/logo512.png

* Add files via upload

* Add files via upload

* Add files via upload

* Update site.webmanifest

* Update index.html

---------

Co-authored-by: Vivian Phung <dev+github@vivianphung.com>
2024-02-23 16:09:45 -06:00
telackeyandVivian Phung 35be62b28e readme formatting (#105)
Co-authored-by: Vivian Phung <dev+github@vivianphung.com>
2024-02-23 15:57:34 -06:00
Andre Hadianto 9db87e6be3 Merge pull request #98 from snowball-tools/andrehadianto/T-4868-tags
[T-4868] Tags component
2024-02-24 03:41:35 +08:00
nabarunandneeraj 353fd0f621 Set authority for record name from backend config (#104)
* Fix format of eth address to display

* Set authority for record name from backend config

* Add functionality to open repo button

* Use commit author image and commit url in activity card

* Handle review changes

* Fix commit author as null

* Add timeout to move to next page on project creation

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-23 14:47:29 +05:30
Andre H 5a5756bcf2 🔧 chore: add cloneIcon to dep list 2024-02-23 10:47:50 +07:00
Andre Hadianto 22233d95f8 Merge branch 'andrehadianto/design-system-components' into andrehadianto/T-4868-tags 2024-02-23 11:44:29 +08:00
Wahyu Kurniawan 12fba5ee0c [T-4845: feat] Date picker component (#97)
* ️ feat: create calendar icon for date picker

* ️ feat: create date picker component

* 📝 docs: add date picker to the example page

* 🔧 chore: install `@radix-ui/react-popover`
2024-02-23 10:22:33 +07:00
Wahyu Kurniawan 9b6c777f5f [T-4865: feat] Segmented controls component (#91)
* ️ feat: create segmented controls component

* 📝 docs: add segmented controls component to example page

* ♻️ refactor: put the icon size to icon theme

* 🐛 fix: remove `value` from `useCallback` dependency
2024-02-23 10:20:08 +07:00
Andre Hadianto 3718e260f5 Merge pull request #95 from snowball-tools/andrehadianto/T-4869-toast
[T-4869] implement toast component
2024-02-22 23:48:45 +08:00
Andre H 05e1ac6b02 ♻️ refactor: change tags to tag for rencer page 2024-02-22 22:48:31 +07:00
Andre H 55848b1dd1 ♻️ refactor: rename tags to tag 2024-02-22 22:46:10 +07:00
Andre Hadianto 42bf1534fb Merge branch 'andrehadianto/design-system-components' into andrehadianto/T-4869-toast 2024-02-22 23:42:47 +08:00
nabarunandneeraj 9acb9daacc Update README after implementation of authentication (#101)
* Update README

* Add tooltip to display ethereum address

* Update README for production deployment

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-22 19:24:06 +05:30
nabarun f7c45ca045 Unset cookie domain to localhost by default (#100) 2024-02-22 17:52:49 +05:30
nabarunandneeraj ef0eac8293 Implement authentication with SIWE (#99)
* Create web3 modal provider with SIWE

* Add auth router to handle SIWE authentication

* Use axios instance to make request

* Add button for SIWE authentication

* Add changes to access session in web-app GQL requests

* Add auth check in GQL context and load/create user

* Use authenticated user from context

* Redirect to sign in page if unauthenticated and logout button

* Change sign-in route to login

* Get project domain from config file

* Set user ethAddress column as unique

* Use formatted user name

* Get session secret and origin url from config file

* Add unique constraint for eth address

* Get secure and samesite from origin url

* Get wallet connect id and backend url from env file

* Format user email in member tab panel

* Add backend config isProduction to set trust proxy

* Use only one server url config

* Add tool tip for displaying email

* Add trustProxy and domain in server.session config

* Add SERVER_GQL_PATH constant in frontend

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-22 17:26:26 +05:30
Wahyu Kurniawan 7d1810ebd9 [T-4866: feat] Switch component (#92)
* ️ feat: create switch component

* 📝 docs: add switch to the example page

* 🔧 chore: install `@radix-ui/react-switch`

* 🎨 style: add inset shadow

* 🎨 style: addjust input outline when error and focus
2024-02-22 17:42:13 +07:00
Wahyu Kurniawan d2ca4df35a [T-4864: feat] Radio component (#90)
* 🎨 style: make the cursor of the tab trigger wrapper to default

* ️ feat: create radio component

* 📝 docs: add radio component to the example page

* 🔧 chore: install `@radix-ui/react-radio-group`
2024-02-22 17:30:33 +07:00
Zachery 30bbe4d766 [T-4870] Tooltip component (#96)
* fix: button forwardRef

* feat: tooltip component
2024-02-22 18:25:04 +08:00
Andre H 0e7343da6f 🔧 chore: gqlclientprovider import 2024-02-22 17:11:03 +07:00
Andre H 8952393b31 🔧 chore: remove not working swipe down gesture 2024-02-22 17:08:05 +07:00
Andre H 32918b7930 🔧 chore: index import 2024-02-22 17:07:15 +07:00
Andre H 596889b5e2 🔧 chore: add render tags component 2024-02-22 17:01:58 +07:00
Andre H fb932eeb04 ️ feat: implement tags component 2024-02-22 17:01:36 +07:00
Andre H 200ea3ca7b 🐛 fix: aria-hidden naming on input 2024-02-22 16:14:27 +07:00
Andre H ba87c6f22a 🔧 chore: replace react-hot-toast with custom toast 2024-02-22 16:11:37 +07:00
Andre H 4d646e1ac0 🔧 chore: extend to buttonOrLinkProps 2024-02-22 15:03:21 +07:00
Andre H 5be29a9745 🔧 chore: add id to allow singular toast deletion 2024-02-22 14:59:12 +07:00
Andre H fffc370d40 🔧 chore: rework usememo logic 2024-02-22 14:58:57 +07:00
Andre H 1cd60d84dd 🔧 chore: use framer-motion 2024-02-22 14:22:29 +07:00
nabarunandneeraj a846531e43 Set DNS in application deployment request (#94)
Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-22 12:50:35 +05:30
Andre H c8a153ad27 🔧 chore: usememo dependencies 2024-02-22 14:20:26 +07:00
Andre H bb7a88f7e9 🔧 chore: add toast render page 2024-02-22 14:17:02 +07:00
Andre H 675112079c ️ feat: implement toast component 2024-02-22 14:16:47 +07:00
Andre H cb928c3360 🔧 chore: use buttonbaseprops, duration 2000 default 2024-02-22 14:16:21 +07:00
Andre H 5836779403 🔧 chore: export buttonBaseProps 2024-02-22 14:13:47 +07:00
Andre H 028dd35db1 🔧 chore: add icons 2024-02-22 14:13:27 +07:00
Andre H ef9f4df28a 🔧 chore: ghost button is white, not transparent 2024-02-22 14:13:10 +07:00
nabarunandneeraj 6b17dce2ae UI fixes in Snowball frontend app (#93)
* Fix alignment of deployment status chip

* Use template name from env

* Use env for git template link

* Add loading spinner for create project

* Display user name

* Format the displayed user name

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-22 11:25:17 +05:30
Wahyu Kurniawan 496404195c Merge branch 'main' of https://github.com/snowball-tools/snowballtools-base into andrehadianto/design-system-components 2024-02-22 12:45:17 +07:00
Wahyu Kurniawan 9dec48d67c ♻️ refactor: add disabled button example page 2024-02-22 12:24:25 +07:00
Andre H 267b52a352 Merge remote-tracking branch 'origin/andrehadianto/design-system-components' into andrehadianto/T-4869-toast 2024-02-22 11:49:11 +07:00
Andre H fa96b6e734 🔧 chore: add to components render page 2024-02-22 11:46:39 +07:00
Andre H 22c581dd33 🔧 chore: add icons 2024-02-22 11:46:17 +07:00
Andre H d3013719e6 🔧 chore: wip simple toast render 2024-02-22 11:45:55 +07:00
Andre H 98a4d7be07 ️ feat: implement toast contexts 2024-02-22 11:45:35 +07:00
Andre H 3928e2610f 🔧 chore: add zIndex toast token 2024-02-22 11:45:01 +07:00
Andre H 075cc05db5 ⚒️ build: add @radix-ui/react-toast 2024-02-22 11:43:31 +07:00
nabarunandneeraj e816c596ca Publish ApplicationRecord and ApplicationDeploymentRequest on creating new deployments (#89)
* Create application deployment request on new deployment

* Save application deployment request datas in deployment entity

* Add project as dependency

* Use project repo name as record app name

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-22 10:04:33 +05:30
Wahyu Kurniawan 6df094bf2e [T-4861: feat] Inline notification component (#86)
* ️ feat: create info square icon component

* ️ feat: create inline notification component

* 📝 docs: add js doc comment and add inline notification component to the example page

* 🐛 fix: use the right method of `useCallback
2024-02-22 10:45:19 +07:00
telackey 4d0f2ca893 SO webapp-ify the frontend (#82)
* webapp-ify

* Tweak values

* Self-contained
2024-02-21 13:53:05 -06:00
nabarunandneeraj 8ca55cd888 Implement polling for project deployment updates (#87)
* Populate organization and user if db is empty

* Use hardcoded user id from fixtures

* Implement polling for get deployments query

* Handle review changes

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-21 15:34:33 +05:30
telackey fc240c93d8 SO deployable config for the backend. (#83)
* webapp-ify

* SO deployable config for the backend.

* Use process.env

* Detangle
2024-02-21 00:05:45 -06:00
nabarunandneeraj a45fb4c617 Fix deployments visit URL and date filter (#78)
* Add method to update multiple deployments in single query

* Fix deployments URL visit and date filter

* Clean fixtures data

* Update fixtures data

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-20 10:53:42 +05:30
nabarunandneeraj c3d1b4f3eb Use branches from GitHub API in edit domain dialog (#74)
* Use branches from GitHub API

* Disable git branch input if repo not found

* Disable git branch if branches is empty

* Use async select for accounts dropdown

* Log actual HTTP error in console

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-19 16:55:07 +05:30
nabarunandneeraj ce55fe62d8 Check for ApplicationDeploymentRecord and update deployments (#73)
* Add skeleton and TODOs for polling deployment records

* Add method implementations for fetching deployment records

* Handle if deployment url is not set

* Add logs after getting ApplicationDeploymentRecord

* Add application deployment record to deployment entity

* Change type of application deployment record data

* Fetch delay to check deployment records from config

* Update isCurrent after deployment record received

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-19 13:43:29 +05:30
nabarunandneeraj b007286f4a Add script to publish dummy ApplicationDeploymentRecord (#72)
* Add script to publish application deployment record

* Refactor publish deploy records script

* Assert if name doesn't exist in package.json

* Disable import environment variables button

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-16 15:05:52 +05:30
603 changed files with 35084 additions and 12062 deletions
+2
View File
@@ -19,5 +19,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: yarn
- name: Build libs
run: yarn workspace gql-client run build
- name: Linter check
run: yarn lint
@@ -0,0 +1,39 @@
name: Test webapp deployment
on:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
jobs:
test_app_deployment:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: yarn
- name: Test webapp deployment
run: ./packages/deployer/test/test-webapp-deployment-undeployment.sh
- name: Notify Vulcanize Slack on CI failure
if: ${{ always() && github.ref_name == 'main' }}
uses: ravsamhq/notify-slack-action@v2
with:
status: ${{ job.status }}
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.VULCANIZE_SLACK_CI_ALERTS_WEBHOOK }}
- name: Notify DeepStack Slack on CI failure
if: ${{ always() && github.ref_name == 'main' }}
uses: ravsamhq/notify-slack-action@v2
with:
status: ${{ job.status }}
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.DEEPSTACK_SLACK_CI_ALERTS_WEBHOOK }}
+11
View File
@@ -1 +1,12 @@
node_modules/
yarn-error.log
.yarnrc.yml
.yarn/
.yarnrc
packages/backend/environments/local.toml
packages/backend/dev/
packages/frontend/dist/
# ignore all .DS_Store files
**/.DS_Store
+1
View File
@@ -0,0 +1 @@
v20.12.1
+2 -1
View File
@@ -1,6 +1,7 @@
{
// IntelliSense for taiwind variants
"tailwindCSS.experimental.classRegex": [
["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
"tv\\('([^)]*)\\')",
"(?:'|\"|`)([^\"'`]*)(?:'|\"|`)"
]
}
+14 -141
View File
@@ -1,150 +1,23 @@
# snowballtools
# snowballtools-base
## Setup
This is a [yarn workspace](https://yarnpkg.com/features/workspaces) monorepo for the dashboard.
- Clone the `snowballtools` repo
## Getting Started
```bash
git clone git@github.com:snowball-tools/snowballtools-base.git
```
### Install dependencies
- In root of the repo, install depedencies
In the root of the project, run:
```bash
yarn
```
```zsh
yarn
```
- Build packages
### Build backend
```bash
yarn build --ignore frontend
```
```zsh
yarn build --ignore frontend
```
## Backend
- Change directory to `packages/backend`
```bash
cd packages/backend
```
- Load fixtures in database
```bash
yarn test:db:load:fixtures
```
- Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml)
- Client ID and secret will be available after creating Github OAuth app
- https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app
- In "Homepage URL", type `http://localhost:3000`
- In "Authorization callback URL", type `http://localhost:3000/organization/projects/create`
- Generate a new client secret after app is created
- 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 --stack fixturenet-laconic-loaded deploy 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
```
- Get the REST and GQL endpoint ports of Laconicd and replace the ports for `registryConfig.restEndpoint` and `registryConfig.gqlEndpoint` in backend [config file](packages/backend/environments/local.toml)
```bash
# For registryConfig.restEndpoint
laconic-so --stack fixturenet-laconic-loaded deploy port laconicd 1317
# 0.0.0.0:32777
# For registryConfig.gqlEndpoint
laconic-so --stack fixturenet-laconic-loaded deploy port laconicd 9473
# 0.0.0.0:32771
```
- 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
- 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
```
...
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"
...
```
- Start the server in `packages/backend`
```bash
yarn start
```
## Frontend
- Change directory to `packages/frontend` in a new terminal
```bash
cd packages/frontend
```
- Copy the graphQL endpoint from terminal and add the endpoint in the [.env](packages/frontend/.env) file present in `packages/frontend`
```env
REACT_APP_GQL_SERVER_URL = 'http://localhost:8000/graphql'
```
- Copy the GitHub OAuth app client ID from previous steps and set it in frontend [.env](packages/frontend/.env) file
```env
REACT_APP_GITHUB_CLIENT_ID = <CLIENT_ID>
```
- Set `REACT_APP_GITHUB_TEMPLATE_REPO` in [.env](packages/frontend/.env) file
```env
REACT_APP_GITHUB_TEMPLATE_REPO = cerc-io/test-progressive-web-app
```
### Development
- Start the React application
```bash
yarn start
```
- The React application will be running in `http://localhost:3000/`
### Production
- Build the React application
```bash
yarn build
```
- Use a web server for hosting static built files
```bash
python3 -m http.server -d build 3000
```
### Environment variables, running the development server, and deployment
Follow the instructions in the README.md files of the [backend](packages/backend/README.md) and [frontend](packages/frontend/README.md) packages.
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
PKG_DIR="./packages/frontend"
OUTPUT_DIR="${PKG_DIR}/dist"
DEST_DIR=${1:-/data}
if [[ -d "$DEST_DIR" ]]; then
echo "${DEST_DIR} already exists." 1>&2
exit 1
fi
cat > $PKG_DIR/.env <<EOF
VITE_SERVER_URL = 'LACONIC_HOSTED_CONFIG_server_url'
VITE_GITHUB_CLIENT_ID = 'LACONIC_HOSTED_CONFIG_github_clientid'
VITE_GITHUB_PWA_TEMPLATE_REPO = 'LACONIC_HOSTED_CONFIG_github_pwa_templaterepo'
VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO = 'LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo'
VITE_WALLET_CONNECT_ID = 'LACONIC_HOSTED_CONFIG_wallet_connect_id'
VITE_LIT_RELAY_API_KEY = 'LACONIC_HOSTED_CONFIG_lit_relay_api_key'
VITE_ALCHEMY_API_KEY = 'LACONIC_HOSTED_CONFIG_aplchemy_api_key'
VITE_BUGSNAG_API_KEY = 'LACONIC_HOSTED_CONFIG_bugsnag_api_key'
VITE_PASSKEY_WALLET_RPID = 'LACONIC_HOSTED_CONFIG_passkey_wallet_rpid'
VITE_TURNKEY_API_BASE_URL = 'LACONIC_HOSTED_CONFIG_turnkey_api_base_url'
VITE_TURNKEY_ORGANIZATION_ID = 'LACONIC_HOSTED_CONFIG_turnkey_organization_id'
EOF
yarn || exit 1
yarn build || exit 1
if [[ ! -d "$OUTPUT_DIR" ]]; then
echo "Missing output directory: $OUTPUT_DIR" 1>&2
exit 1
fi
mv "$OUTPUT_DIR" "$DEST_DIR"
+4 -4
View File
@@ -4,15 +4,15 @@
"workspaces": [
"packages/*"
],
"dependencies": {},
"devDependencies": {
"depcheck": "^1.4.2",
"husky": "^8.0.3",
"lerna": "^8.0.0",
"depcheck": "^1.4.2"
"patch-package": "^8.0.0"
},
"scripts": {
"prepare": "husky install",
"build": "lerna run build --stream",
"lint": "lerna run lint --stream -- --max-warnings=0"
"lint": "lerna run lint --stream"
}
}
}
+4 -1
View File
@@ -25,6 +25,9 @@
"allowArgumentsExplicitlyTypedAsAny": true
}
],
"@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }]
"@typescript-eslint/no-unused-vars": [
"error",
{ "ignoreRestSiblings": true }
]
}
}
+1
View File
@@ -1,2 +1,3 @@
db
dist
environments/local.toml
+1
View File
@@ -0,0 +1 @@
v20.12.1
+76 -1
View File
@@ -1 +1,76 @@
# Backend for Snowball Tools
# backend
This backend is a [node.js](https://nodejs.org/) [express.js](https://expressjs.com/) [apollo server](https://www.apollographql.com/docs/apollo-server/) project in a [yarn workspace](https://yarnpkg.com/features/workspaces).
## Getting Started
### Install dependencies
In the root of the project, run:
```zsh
yarn
```
### Build backend
```zsh
yarn build --ignore frontend
```
### Environment variables
#### Local
Copy the `envionments/local.toml.example` file to `envionments/local.toml`:
```zsh
cp envionments/local.toml.example envionments/local.toml
```
#### Staging environment variables
In the deployment repository, update staging [staging/configmaps/config/prod.toml](https://git.vdb.to/cerc-io/snowballtools-base-api-deployments/src/commit/318c2bc09f334dca79c3501838512749f9431bf1/deployments/staging/configmaps/config/prod.toml)
#### Production environment variables
In the deployment repository, update production [production/configmaps/config/prod.toml](https://git.vdb.to/cerc-io/snowballtools-base-api-deployments/src/commit/318c2bc09f334dca79c3501838512749f9431bf1/deployments/production/configmaps/config/prod.toml)
### Run development server
```zsh
yarn start
```
## Deployment
Clone the deployer repository:
```zsh
git clone git@git.vdb.to:cerc-io/snowballtools-base-api-deployments.git
```
### Staging
```zsh
echo trigger >> .gitea/workflows/triggers/staging-deploy
git commit -a -m "Deploy v0.0.8" # replace with version number
git push
```
### Production
```zsh
echo trigger >> .gitea/workflows/triggers/production-deploy
git commit -a -m "Deploy v0.0.8" # replace with version number
git push
```
### Deployment status
Dumb for now
- [Staging](https://snowballtools-base-api.staging.apps.snowballtools.com/staging/version)
- [Production](https://snowballtools-base-api.apps.snowballtools.com/staging/version)
Update version number manually in [routes/staging.ts](/packages/backend/src/routes/staging.ts)
@@ -2,6 +2,11 @@
host = "127.0.0.1"
port = 8000
gqlPath = "/graphql"
[server.session]
secret = "p4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi"
appOriginUrl = "http://localhost:3000"
trustProxy = false
domain = "localhost"
[database]
dbPath = "db/snowball"
@@ -19,7 +24,11 @@
chainId = "laconic_9000-1"
privateKey = ""
bondId = ""
authority = ""
[registryConfig.fee]
amount = "200000"
denom = "aphoton"
gas = "550000"
gas = "750000"
[misc]
projectDomain = "apps.snowballtools.com"
+12 -13
View File
@@ -1,19 +1,26 @@
{
"name": "backend",
"license": "UNLICENSED",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@cerc-io/laconic-sdk": "^0.1.14",
"@graphql-tools/schema": "^10.0.2",
"@graphql-tools/utils": "^10.0.12",
"@octokit/oauth-app": "^6.1.0",
"@snowballtools/laconic-sdk": "^0.1.17",
"@turnkey/sdk-server": "^0.1.0",
"@types/debug": "^4.1.5",
"@types/express": "^4.17.21",
"@types/node": "^20.11.0",
"@types/semver": "^7.5.8",
"apollo-server-core": "^3.13.0",
"apollo-server-express": "^3.13.0",
"cookie-session": "^2.1.0",
"cors": "^2.8.5",
"debug": "^4.3.1",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"express-session": "^1.18.0",
"fs-extra": "^11.2.0",
"graphql": "^16.8.1",
"luxon": "^3.4.4",
@@ -33,29 +40,21 @@
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
"clean": "rm -rf ./dist",
"build": "yarn clean && tsc && yarn copy-assets",
"lint": "eslint .",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "tsc --noEmit",
"test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts",
"test:registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts",
"test:registry:publish-deployment-removal-records": "DEBUG=snowball:* ts-node ./test/publish-deployment-removal-records.ts",
"test:db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts",
"test:db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts"
},
"devDependencies": {
"@types/cookie-session": "^2.0.49",
"@types/express-session": "^1.17.10",
"@types/fs-extra": "^11.0.4",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"better-sqlite3": "^9.2.2",
"copyfiles": "^2.4.1",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-semistandard": "^15.0.1",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0",
"prettier": "^3.1.1",
"workspace": "^0.0.1-preview.1"
}
+25 -2
View File
@@ -1,7 +1,18 @@
export interface SessionConfig {
secret: string;
appOriginUrl: string;
trustProxy: boolean;
domain: string;
}
export interface ServerConfig {
host: string;
port: number;
gqlPath?: string;
sessionSecret: string;
appOriginUrl: string;
isProduction: boolean;
session: SessionConfig;
}
export interface DatabaseConfig {
@@ -13,7 +24,7 @@ export interface GitHubConfig {
oAuth: {
clientId: string;
clientSecret: string;
}
};
}
export interface RegistryConfig {
@@ -23,11 +34,16 @@ export interface RegistryConfig {
privateKey: string;
bondId: string;
fetchDeploymentRecordDelay: number;
authority: string;
fee: {
amount: string;
denom: string;
gas: string;
}
};
}
export interface MiscConfig {
projectDomain: string;
}
export interface Config {
@@ -35,4 +51,11 @@ export interface Config {
database: DatabaseConfig;
gitHub: GitHubConfig;
registryConfig: RegistryConfig;
misc: MiscConfig;
turnkey: {
apiBaseUrl: string;
apiPublicKey: string;
apiPrivateKey: string;
defaultOrganizationId: string;
};
}
+4 -6
View File
@@ -1,8 +1,6 @@
export const DEFAULT_CONFIG_FILE_PATH = 'environments/local.toml';
import process from 'process';
export const DEFAULT_CONFIG_FILE_PATH =
process.env.SNOWBALL_BACKEND_CONFIG_FILE_PATH || 'environments/local.toml';
export const DEFAULT_GQL_PATH = '/graphql';
// Note: temporary hardcoded user, later to be derived from auth token
export const USER_ID = '59f4355d-9549-4aac-9b54-eeefceeabef0';
export const PROJECT_DOMAIN = 'snowball.xyz';
+186 -56
View File
@@ -1,11 +1,17 @@
import { DataSource, DeepPartial, FindManyOptions, FindOneOptions, FindOptionsWhere } from 'typeorm';
import {
DataSource,
DeepPartial,
FindManyOptions,
FindOneOptions,
FindOptionsWhere
} from 'typeorm';
import path from 'path';
import debug from 'debug';
import assert from 'assert';
import { customAlphabet } from 'nanoid';
import { lowercase, numbers } from 'nanoid-dictionary';
import { DatabaseConfig } from './config';
import { DatabaseConfig, MiscConfig } from './config';
import { User } from './entity/User';
import { Organization } from './entity/Organization';
import { Project } from './entity/Project';
@@ -13,7 +19,10 @@ import { Deployment } from './entity/Deployment';
import { ProjectMember } from './entity/ProjectMember';
import { EnvironmentVariable } from './entity/EnvironmentVariable';
import { Domain } from './entity/Domain';
import { PROJECT_DOMAIN } from './constants';
import { getEntities, loadAndSaveData } from './utils';
import { UserOrganization } from './entity/UserOrganization';
const ORGANIZATION_DATA_PATH = '../test/fixtures/organizations.json';
const log = debug('snowball:database');
@@ -22,8 +31,9 @@ const nanoid = customAlphabet(lowercase + numbers, 8);
// TODO: Fix order of methods
export class Database {
private dataSource: DataSource;
private projectDomain: string;
constructor ({ dbPath }: DatabaseConfig) {
constructor ({ dbPath } : DatabaseConfig, { projectDomain } : MiscConfig) {
this.dataSource = new DataSource({
type: 'better-sqlite3',
database: dbPath,
@@ -31,11 +41,21 @@ export class Database {
synchronize: true,
logging: false
});
this.projectDomain = projectDomain;
}
async init (): Promise<void> {
await this.dataSource.initialize();
log('database initialized');
const organizations = await this.getOrganizations({});
// Load an organization if none exist
if (!organizations.length) {
const orgEntities = await getEntities(path.resolve(__dirname, ORGANIZATION_DATA_PATH));
await loadAndSaveData(Organization, this.dataSource, [orgEntities[0]]);
}
}
async getUser (options: FindOneOptions<User>): Promise<User | null> {
@@ -52,15 +72,26 @@ export class Database {
return user;
}
async updateUser (userId: string, data: DeepPartial<User>): Promise<boolean> {
async updateUser (user: User, data: DeepPartial<User>): Promise<boolean> {
const userRepository = this.dataSource.getRepository(User);
const updateResult = await userRepository.update({ id: userId }, data);
const updateResult = await userRepository.update({ id: user.id }, data);
assert(updateResult.affected);
return updateResult.affected > 0;
}
async getOrganization (options: FindOneOptions<Organization>): Promise<Organization | null> {
async getOrganizations (
options: FindManyOptions<Organization>
): Promise<Organization[]> {
const organizationRepository = this.dataSource.getRepository(Organization);
const organizations = await organizationRepository.find(options);
return organizations;
}
async getOrganization (
options: FindOneOptions<Organization>
): Promise<Organization | null> {
const organizationRepository = this.dataSource.getRepository(Organization);
const organization = await organizationRepository.findOne(options);
@@ -83,6 +114,13 @@ export class Database {
return userOrgs;
}
async addUserOrganization (data: DeepPartial<UserOrganization>): Promise<UserOrganization> {
const userOrganizationRepository = this.dataSource.getRepository(UserOrganization);
const newUserOrganization = await userOrganizationRepository.save(data);
return newUserOrganization;
}
async getProjects (options: FindManyOptions<Project>): Promise<Project[]> {
const projectRepository = this.dataSource.getRepository(Project);
const projects = await projectRepository.find(options);
@@ -95,7 +133,11 @@ export class Database {
const project = await projectRepository
.createQueryBuilder('project')
.leftJoinAndSelect('project.deployments', 'deployments', 'deployments.isCurrent = true')
.leftJoinAndSelect(
'project.deployments',
'deployments',
'deployments.isCurrent = true'
)
.leftJoinAndSelect('deployments.createdBy', 'user')
.leftJoinAndSelect('deployments.domain', 'domain')
.leftJoinAndSelect('project.owner', 'owner')
@@ -108,19 +150,29 @@ export class Database {
return project;
}
async getProjectsInOrganization (userId: string, organizationSlug: string): Promise<Project[]> {
async getProjectsInOrganization (
userId: string,
organizationSlug: string
): Promise<Project[]> {
const projectRepository = this.dataSource.getRepository(Project);
const projects = await projectRepository
.createQueryBuilder('project')
.leftJoinAndSelect('project.deployments', 'deployments', 'deployments.isCurrent = true')
.leftJoinAndSelect(
'project.deployments',
'deployments',
'deployments.isCurrent = true'
)
.leftJoinAndSelect('deployments.domain', 'domain')
.leftJoin('project.projectMembers', 'projectMembers')
.leftJoin('project.organization', 'organization')
.where('(project.ownerId = :userId OR projectMembers.userId = :userId) AND organization.slug = :organizationSlug', {
userId,
organizationSlug
})
.where(
'(project.ownerId = :userId OR projectMembers.userId = :userId) AND organization.slug = :organizationSlug',
{
userId,
organizationSlug
}
)
.getMany();
return projects;
@@ -129,7 +181,9 @@ export class Database {
/**
* Get deployments with specified filter
*/
async getDeployments (options: FindManyOptions<Deployment>): Promise<Deployment[]> {
async getDeployments (
options: FindManyOptions<Deployment>
): Promise<Deployment[]> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const deployments = await deploymentRepository.find(options);
@@ -154,7 +208,9 @@ export class Database {
});
}
async getDeployment (options: FindOneOptions<Deployment>): Promise<Deployment | null> {
async getDeployment (
options: FindOneOptions<Deployment>
): Promise<Deployment | null> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const deployment = await deploymentRepository.findOne(options);
@@ -182,8 +238,11 @@ export class Database {
return deployment;
}
async getProjectMembersByProjectId (projectId: string): Promise<ProjectMember[]> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
async getProjectMembersByProjectId (
projectId: string
): Promise<ProjectMember[]> {
const projectMemberRepository =
this.dataSource.getRepository(ProjectMember);
const projectMembers = await projectMemberRepository.find({
relations: {
@@ -200,8 +259,12 @@ export class Database {
return projectMembers;
}
async getEnvironmentVariablesByProjectId (projectId: string, filter?: FindOptionsWhere<EnvironmentVariable>): Promise<EnvironmentVariable[]> {
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
async getEnvironmentVariablesByProjectId (
projectId: string,
filter?: FindOptionsWhere<EnvironmentVariable>
): Promise<EnvironmentVariable[]> {
const environmentVariableRepository =
this.dataSource.getRepository(EnvironmentVariable);
const environmentVariables = await environmentVariableRepository.find({
where: {
@@ -216,9 +279,12 @@ export class Database {
}
async removeProjectMemberById (projectMemberId: string): Promise<boolean> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
const projectMemberRepository =
this.dataSource.getRepository(ProjectMember);
const deleteResult = await projectMemberRepository.delete({ id: projectMemberId });
const deleteResult = await projectMemberRepository.delete({
id: projectMemberId
});
if (deleteResult.affected) {
return deleteResult.affected > 0;
@@ -227,37 +293,63 @@ export class Database {
}
}
async updateProjectMemberById (projectMemberId: string, data: DeepPartial<ProjectMember>): Promise<boolean> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
const updateResult = await projectMemberRepository.update({ id: projectMemberId }, data);
async updateProjectMemberById (
projectMemberId: string,
data: DeepPartial<ProjectMember>
): Promise<boolean> {
const projectMemberRepository =
this.dataSource.getRepository(ProjectMember);
const updateResult = await projectMemberRepository.update(
{ id: projectMemberId },
data
);
return Boolean(updateResult.affected);
}
async addProjectMember (data: DeepPartial<ProjectMember>): Promise<ProjectMember> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
async addProjectMember (
data: DeepPartial<ProjectMember>
): Promise<ProjectMember> {
const projectMemberRepository =
this.dataSource.getRepository(ProjectMember);
const newProjectMember = await projectMemberRepository.save(data);
return newProjectMember;
}
async addEnvironmentVariables (data: DeepPartial<EnvironmentVariable>[]): Promise<EnvironmentVariable[]> {
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
const savedEnvironmentVariables = await environmentVariableRepository.save(data);
async addEnvironmentVariables (
data: DeepPartial<EnvironmentVariable>[]
): Promise<EnvironmentVariable[]> {
const environmentVariableRepository =
this.dataSource.getRepository(EnvironmentVariable);
const savedEnvironmentVariables =
await environmentVariableRepository.save(data);
return savedEnvironmentVariables;
}
async updateEnvironmentVariable (environmentVariableId: string, data: DeepPartial<EnvironmentVariable>): Promise<boolean> {
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
const updateResult = await environmentVariableRepository.update({ id: environmentVariableId }, data);
async updateEnvironmentVariable (
environmentVariableId: string,
data: DeepPartial<EnvironmentVariable>
): Promise<boolean> {
const environmentVariableRepository =
this.dataSource.getRepository(EnvironmentVariable);
const updateResult = await environmentVariableRepository.update(
{ id: environmentVariableId },
data
);
return Boolean(updateResult.affected);
}
async deleteEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
const deleteResult = await environmentVariableRepository.delete({ id: environmentVariableId });
async deleteEnvironmentVariable (
environmentVariableId: string
): Promise<boolean> {
const environmentVariableRepository =
this.dataSource.getRepository(EnvironmentVariable);
const deleteResult = await environmentVariableRepository.delete({
id: environmentVariableId
});
if (deleteResult.affected) {
return deleteResult.affected > 0;
@@ -267,7 +359,8 @@ export class Database {
}
async getProjectMemberById (projectMemberId: string): Promise<ProjectMember> {
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
const projectMemberRepository =
this.dataSource.getRepository(ProjectMember);
const projectMemberWithProject = await projectMemberRepository.find({
relations: {
@@ -279,8 +372,7 @@ export class Database {
where: {
id: projectMemberId
}
}
);
});
if (projectMemberWithProject.length === 0) {
throw new Error('Member does not exist');
@@ -289,34 +381,49 @@ export class Database {
return projectMemberWithProject[0];
}
async getProjectsBySearchText (userId: string, searchText: string): Promise<Project[]> {
async getProjectsBySearchText (
userId: string,
searchText: string
): Promise<Project[]> {
const projectRepository = this.dataSource.getRepository(Project);
const projects = await projectRepository
.createQueryBuilder('project')
.leftJoinAndSelect('project.organization', 'organization')
.leftJoin('project.projectMembers', 'projectMembers')
.where('(project.owner = :userId OR projectMembers.member.id = :userId) AND project.name LIKE :searchText', {
userId,
searchText: `%${searchText}%`
})
.where(
'(project.owner = :userId OR projectMembers.member.id = :userId) AND project.name LIKE :searchText',
{
userId,
searchText: `%${searchText}%`
}
)
.getMany();
return projects;
}
async updateDeploymentById (deploymentId: string, data: DeepPartial<Deployment>): Promise<boolean> {
async updateDeploymentById (
deploymentId: string,
data: DeepPartial<Deployment>
): Promise<boolean> {
return this.updateDeployment({ id: deploymentId }, data);
}
async updateDeployment (criteria: FindOptionsWhere<Deployment>, data: DeepPartial<Deployment>): Promise<boolean> {
async updateDeployment (
criteria: FindOptionsWhere<Deployment>,
data: DeepPartial<Deployment>
): Promise<boolean> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const updateResult = await deploymentRepository.update(criteria, data);
return Boolean(updateResult.affected);
}
async updateDeploymentsByProjectIds (projectIds: string[], data: DeepPartial<Deployment>): Promise<boolean> {
async updateDeploymentsByProjectIds (
projectIds: string[],
data: DeepPartial<Deployment>
): Promise<boolean> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const updateResult = await deploymentRepository
@@ -329,7 +436,20 @@ export class Database {
return Boolean(updateResult.affected);
}
async addProject (userId: string, organizationId: string, data: DeepPartial<Project>): Promise<Project> {
async deleteDeploymentById (deploymentId: string): Promise<boolean> {
const deploymentRepository = this.dataSource.getRepository(Deployment);
const deployment = await deploymentRepository.findOneOrFail({
where: {
id: deploymentId
}
});
const deleteResult = await deploymentRepository.softRemove(deployment);
return Boolean(deleteResult);
}
async addProject (user: User, organizationId: string, data: DeepPartial<Project>): Promise<Project> {
const projectRepository = this.dataSource.getRepository(Project);
// TODO: Check if organization exists
@@ -339,22 +459,26 @@ export class Database {
// TODO: Set icon according to framework
newProject.icon = '';
newProject.owner = Object.assign(new User(), {
id: userId
});
newProject.owner = user;
newProject.organization = Object.assign(new Organization(), {
id: organizationId
});
newProject.subDomain = `${newProject.name}.${PROJECT_DOMAIN}`;
newProject.subDomain = `${newProject.name}.${this.projectDomain}`;
return projectRepository.save(newProject);
}
async updateProjectById (projectId: string, data: DeepPartial<Project>): Promise<boolean> {
async updateProjectById (
projectId: string,
data: DeepPartial<Project>
): Promise<boolean> {
const projectRepository = this.dataSource.getRepository(Project);
const updateResult = await projectRepository.update({ id: projectId }, data);
const updateResult = await projectRepository.update(
{ id: projectId },
data
);
return Boolean(updateResult.affected);
}
@@ -401,14 +525,20 @@ export class Database {
return domain;
}
async updateDomainById (domainId: string, data: DeepPartial<Domain>): Promise<boolean> {
async updateDomainById (
domainId: string,
data: DeepPartial<Domain>
): Promise<boolean> {
const domainRepository = this.dataSource.getRepository(Domain);
const updateResult = await domainRepository.update({ id: domainId }, data);
return Boolean(updateResult.affected);
}
async getDomainsByProjectId (projectId: string, filter?: FindOptionsWhere<Domain>): Promise<Domain[]> {
async getDomainsByProjectId (
projectId: string,
filter?: FindOptionsWhere<Domain>
): Promise<Domain[]> {
const domainRepository = this.dataSource.getRepository(Domain);
const domains = await domainRepository.find({
+56 -12
View File
@@ -6,13 +6,14 @@ import {
UpdateDateColumn,
ManyToOne,
OneToOne,
JoinColumn
JoinColumn,
DeleteDateColumn
} from 'typeorm';
import { Project } from './Project';
import { Domain } from './Domain';
import { User } from './User';
import { AppDeploymentRecordAttributes } from '../types';
import { AppDeploymentRecordAttributes, AppDeploymentRemovalRecordAttributes } from '../types';
export enum Environment {
Production = 'Production',
@@ -24,20 +25,42 @@ export enum DeploymentStatus {
Building = 'Building',
Ready = 'Ready',
Error = 'Error',
Deleting = 'Deleting',
}
export interface ApplicationDeploymentRequest {
type: string;
version: string;
name: string;
application: string;
config: string;
meta: string;
}
export interface ApplicationDeploymentRemovalRequest {
type: string;
version: string;
deployment: string;
}
export interface ApplicationDeploymentRemovalRequest {
type: string;
version: string;
deployment: string;
}
export interface ApplicationRecord {
type: string;
version:string
name: string
description?: string
homepage?: string
license?: string
author?: string
repository?: string[],
app_version?: string
repository_ref: string
app_type: string
version: string;
name: string;
description?: string;
homepage?: string;
license?: string;
author?: string;
repository?: string[];
app_version?: string;
repository_ref: string;
app_type: string;
}
@Entity()
@@ -78,11 +101,29 @@ export class Deployment {
@Column('simple-json')
applicationRecordData!: ApplicationRecord;
@Column('varchar', { nullable: true })
applicationDeploymentRequestId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRequestData!: ApplicationDeploymentRequest | null;
@Column('varchar', { nullable: true })
applicationDeploymentRecordId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRecordData!: AppDeploymentRecordAttributes | null;
@Column('varchar', { nullable: true })
applicationDeploymentRemovalRequestId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRemovalRequestData!: ApplicationDeploymentRemovalRequest | null;
@Column('varchar', { nullable: true })
applicationDeploymentRemovalRecordId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRemovalRecordData!: AppDeploymentRemovalRecordAttributes | null;
@Column({
enum: Environment
@@ -106,4 +147,7 @@ export class Deployment {
@UpdateDateColumn()
updatedAt!: Date;
@DeleteDateColumn()
deletedAt!: Date | null;
}
+1 -1
View File
@@ -39,7 +39,7 @@ export class Domain {
@ManyToOne(() => Domain)
@JoinColumn({ name: 'redirectToId' })
// eslint-disable-next-line no-use-before-define
// eslint-disable-next-line no-use-before-define
redirectTo!: Domain | null;
@Column({
+7 -3
View File
@@ -27,8 +27,12 @@ export class Organization {
@UpdateDateColumn()
updatedAt!: Date;
@OneToMany(() => UserOrganization, userOrganization => userOrganization.organization, {
cascade: ['soft-remove']
})
@OneToMany(
() => UserOrganization,
(userOrganization) => userOrganization.organization,
{
cascade: ['soft-remove']
}
)
userOrganizations!: UserOrganization[];
}
+1 -16
View File
@@ -15,15 +15,6 @@ import { Organization } from './Organization';
import { ProjectMember } from './ProjectMember';
import { Deployment } from './Deployment';
export interface ApplicationDeploymentRequest {
type: string
version: string
name: string
application: string
config: string,
meta: string
}
@Entity()
export class Project {
@PrimaryGeneratedColumn('uuid')
@@ -52,12 +43,6 @@ export class Project {
@Column('varchar', { length: 255, default: 'main' })
prodBranch!: string;
@Column('varchar', { nullable: true })
applicationDeploymentRequestId!: string | null;
@Column('simple-json', { nullable: true })
applicationDeploymentRequestData!: ApplicationDeploymentRequest | null;
@Column('text', { default: '' })
description!: string;
@@ -91,7 +76,7 @@ export class Project {
@OneToMany(() => Deployment, (deployment) => deployment.project)
deployments!: Deployment[];
@OneToMany(() => ProjectMember, projectMember => projectMember.project, {
@OneToMany(() => ProjectMember, (projectMember) => projectMember.project, {
cascade: ['soft-remove']
})
projectMembers!: ProjectMember[];
+1 -1
View File
@@ -15,7 +15,7 @@ import { User } from './User';
export enum Permission {
View = 'View',
Edit = 'Edit'
Edit = 'Edit',
}
@Entity()
+19 -4
View File
@@ -12,10 +12,15 @@ import { UserOrganization } from './UserOrganization';
@Entity()
@Unique(['email'])
@Unique(['ethAddress'])
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string;
// TODO: Set ethAddress as ID
@Column()
ethAddress!: string;
@Column('varchar', { length: 255, nullable: true })
name!: string | null;
@@ -34,13 +39,23 @@ export class User {
@CreateDateColumn()
updatedAt!: Date;
@OneToMany(() => ProjectMember, projectMember => projectMember.project, {
@Column()
subOrgId!: string;
@Column()
turnkeyWalletId!: string;
@OneToMany(() => ProjectMember, (projectMember) => projectMember.project, {
cascade: ['soft-remove']
})
projectMembers!: ProjectMember[];
@OneToMany(() => UserOrganization, UserOrganization => UserOrganization.member, {
cascade: ['soft-remove']
})
@OneToMany(
() => UserOrganization,
(UserOrganization) => UserOrganization.member,
{
cascade: ['soft-remove']
}
)
userOrganizations!: UserOrganization[];
}
@@ -12,7 +12,7 @@ import {
import { User } from './User';
import { Organization } from './Organization';
enum Role {
export enum Role {
Owner = 'Owner',
Maintainer = 'Maintainer',
Reader = 'Reader',
+13 -8
View File
@@ -1,3 +1,4 @@
import 'express-async-errors';
import 'reflect-metadata';
import debug from 'debug';
import fs from 'fs';
@@ -9,8 +10,6 @@ import { Database } from './database';
import { createAndStartServer } from './server';
import { createResolvers } from './resolvers';
import { getConfig } from './utils';
import { Config } from './config';
import { DEFAULT_CONFIG_FILE_PATH } from './constants';
import { Service } from './service';
import { Registry } from './registry';
@@ -18,22 +17,28 @@ const log = debug('snowball:server');
const OAUTH_CLIENT_TYPE = 'oauth-app';
export const main = async (): Promise<void> => {
// TODO: get config path using cli
const { server, database, gitHub, registryConfig } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
const { server, database, gitHub, registryConfig, misc } = await getConfig();
const app = new OAuthApp({
clientType: OAUTH_CLIENT_TYPE,
clientId: gitHub.oAuth.clientId,
clientSecret: gitHub.oAuth.clientSecret
clientSecret: gitHub.oAuth.clientSecret,
});
const db = new Database(database);
const db = new Database(database, misc);
await db.init();
const registry = new Registry(registryConfig);
const service = new Service({ gitHubConfig: gitHub, registryConfig }, db, app, registry);
const service = new Service(
{ gitHubConfig: gitHub, registryConfig },
db,
app,
registry,
);
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString();
const typeDefs = fs
.readFileSync(path.join(__dirname, 'schema.gql'))
.toString();
const resolvers = await createResolvers(service);
await createAndStartServer(server, typeDefs, resolvers, service);
+182 -47
View File
@@ -3,53 +3,80 @@ import assert from 'assert';
import { inc as semverInc } from 'semver';
import { DateTime } from 'luxon';
import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk';
import { Registry as LaconicRegistry } from '@snowballtools/laconic-sdk';
import { RegistryConfig } from './config';
import { ApplicationDeploymentRequest } from './entity/Project';
import { ApplicationRecord, Deployment } from './entity/Deployment';
import { AppDeploymentRecord, PackageJSON } from './types';
import {
ApplicationRecord,
Deployment,
ApplicationDeploymentRequest,
ApplicationDeploymentRemovalRequest
} from './entity/Deployment';
import { AppDeploymentRecord, AppDeploymentRemovalRecord, PackageJSON } from './types';
import { sleep } from './utils';
const log = debug('snowball:registry');
const APP_RECORD_TYPE = 'ApplicationRecord';
const APP_DEPLOYMENT_REQUEST_TYPE = 'ApplicationDeploymentRequest';
const APP_DEPLOYMENT_REMOVAL_REQUEST_TYPE = 'ApplicationDeploymentRemovalRequest';
const APP_DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRecord';
const APP_DEPLOYMENT_REMOVAL_RECORD_TYPE = 'ApplicationDeploymentRemovalRecord';
const SLEEP_DURATION = 1000;
// TODO: Move registry code to laconic-sdk/watcher-ts
export class Registry {
private registry: LaconicRegistry;
private registryConfig: RegistryConfig;
constructor (registryConfig : RegistryConfig) {
constructor (registryConfig: RegistryConfig) {
this.registryConfig = registryConfig;
this.registry = new LaconicRegistry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
this.registry = new LaconicRegistry(
registryConfig.gqlEndpoint,
registryConfig.restEndpoint,
registryConfig.chainId
);
}
async createApplicationRecord ({
appName,
packageJSON,
commitHash,
appType,
repoUrl
}: {
packageJSON: PackageJSON
commitHash: string,
appType: string,
repoUrl: string
}): Promise<{applicationRecordId: string, applicationRecordData: ApplicationRecord}> {
assert(packageJSON.name, "name field doesn't exist in package.json");
appName: string;
packageJSON: PackageJSON;
commitHash: string;
appType: string;
repoUrl: string;
}): Promise<{
applicationRecordId: string;
applicationRecordData: ApplicationRecord;
}> {
// Use laconic-sdk to publish record
// Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts/publish-app-record.sh
// Fetch previous records
const records = await this.registry.queryRecords({
type: APP_RECORD_TYPE,
name: packageJSON.name
}, true);
const records = await this.registry.queryRecords(
{
type: APP_RECORD_TYPE,
name: packageJSON.name
},
true
);
// Get next version of record
const bondRecords = records.filter((record: any) => record.bondId === this.registryConfig.bondId);
const [latestBondRecord] = bondRecords.sort((a: any, b: any) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime());
const nextVersion = semverInc(latestBondRecord?.attributes.version ?? '0.0.0', 'patch');
const bondRecords = records.filter(
(record: any) => record.bondId === this.registryConfig.bondId
);
const [latestBondRecord] = bondRecords.sort(
(a: any, b: any) =>
new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
);
const nextVersion = semverInc(
latestBondRecord?.attributes.version ?? '0.0.0',
'patch'
);
assert(nextVersion, 'Application record version not valid');
@@ -60,11 +87,16 @@ export class Registry {
repository_ref: commitHash,
repository: [repoUrl],
app_type: appType,
name: packageJSON.name,
name: appName,
...(packageJSON.description && { description: packageJSON.description }),
...(packageJSON.homepage && { homepage: packageJSON.homepage }),
...(packageJSON.license && { license: packageJSON.license }),
...(packageJSON.author && { author: typeof packageJSON.author === 'object' ? JSON.stringify(packageJSON.author) : packageJSON.author }),
...(packageJSON.author && {
author:
typeof packageJSON.author === 'object'
? JSON.stringify(packageJSON.author)
: packageJSON.author
}),
...(packageJSON.version && { app_version: packageJSON.version })
};
@@ -81,24 +113,48 @@ export class Registry {
log('Application record data:', applicationRecord);
// TODO: Discuss computation of CRN
const crn = this.getCrn(packageJSON.name);
const crn = this.getCrn(appName);
log(`Setting name: ${crn} for record ID: ${result.data.id}`);
await this.registry.setName({ cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee);
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee);
await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.repository_ref}` }, this.registryConfig.privateKey, this.registryConfig.fee);
await sleep(SLEEP_DURATION);
await this.registry.setName(
{ cid: result.data.id, crn },
this.registryConfig.privateKey,
this.registryConfig.fee
);
return { applicationRecordId: result.data.id, applicationRecordData: applicationRecord };
await sleep(SLEEP_DURATION);
await this.registry.setName(
{ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` },
this.registryConfig.privateKey,
this.registryConfig.fee
);
await sleep(SLEEP_DURATION);
await this.registry.setName(
{
cid: result.data.id,
crn: `${crn}@${applicationRecord.repository_ref}`
},
this.registryConfig.privateKey,
this.registryConfig.fee
);
return {
applicationRecordId: result.data.id,
applicationRecordData: applicationRecord
};
}
async createApplicationDeploymentRequest (data: {
deployment: Deployment,
appName: string,
commitHash: string,
repository: string,
environmentVariables: { [key: string]: string }
environmentVariables: { [key: string]: string },
dns: string,
}): Promise<{
applicationDeploymentRequestId: string,
applicationDeploymentRequestData: ApplicationDeploymentRequest
applicationDeploymentRequestId: string;
applicationDeploymentRequestData: ApplicationDeploymentRequest;
}> {
const crn = this.getCrn(data.appName);
const records = await this.registry.resolveNames([crn]);
@@ -114,9 +170,9 @@ export class Registry {
version: '1.0.0',
name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`,
application: `${crn}@${applicationRecord.attributes.app_version}`,
dns: data.dns,
// TODO: Not set in test-progressive-web-app CI
// dns: '$CERC_REGISTRY_DEPLOYMENT_SHORT_HOSTNAME',
// deployment: '$CERC_REGISTRY_DEPLOYMENT_CRN',
// https://git.vdb.to/cerc-io/laconic-registry-cli/commit/129019105dfb93bebcea02fde0ed64d0f8e5983b
@@ -124,12 +180,15 @@ export class Registry {
env: data.environmentVariables
}),
meta: JSON.stringify({
note: `Added by Snowball @ ${DateTime.utc().toFormat('EEE LLL dd HH:mm:ss \'UTC\' yyyy')}`,
note: `Added by Snowball @ ${DateTime.utc().toFormat(
"EEE LLL dd HH:mm:ss 'UTC' yyyy"
)}`,
repository: data.repository,
repository_ref: data.commitHash
repository_ref: data.deployment.commitHash
})
};
await sleep(SLEEP_DURATION);
const result = await this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
@@ -142,31 +201,107 @@ export class Registry {
log(`Application deployment request record published: ${result.data.id}`);
log('Application deployment request data:', applicationDeploymentRequest);
return { applicationDeploymentRequestId: result.data.id, applicationDeploymentRequestData: applicationDeploymentRequest };
return {
applicationDeploymentRequestId: result.data.id,
applicationDeploymentRequestData: applicationDeploymentRequest
};
}
/**
* Fetch ApplicationDeploymentRecords for deployments
*/
async getDeploymentRecords (deployments: Deployment[]): Promise<AppDeploymentRecord[]> {
async getDeploymentRecords (
deployments: Deployment[]
): Promise<AppDeploymentRecord[]> {
// Fetch ApplicationDeploymentRecords for corresponding ApplicationRecord set in deployments
// TODO: Implement Laconicd GQL query to filter records by multiple values for an attribute
const records = await this.registry.queryRecords({
type: APP_DEPLOYMENT_RECORD_TYPE
}, true);
const records = await this.registry.queryRecords(
{
type: APP_DEPLOYMENT_RECORD_TYPE
},
true
);
// Filter records with ApplicationRecord ids
return records.filter((record: AppDeploymentRecord) => deployments.some(deployment => deployment.applicationRecordId === record.attributes.application));
// Filter records with ApplicationRecord ID and Deployment specific URL
return records.filter((record: AppDeploymentRecord) =>
deployments.some(
(deployment) =>
deployment.applicationRecordId === record.attributes.application &&
record.attributes.url.includes(deployment.id)
)
);
}
getCrn (packageJsonName: string): string {
const [arg1, arg2] = packageJsonName.split('/');
/**
* Fetch ApplicationDeploymentRecords by filter
*/
async getDeploymentRecordsByFilter (filter: { [key: string]: any }): Promise<AppDeploymentRecord[]> {
return this.registry.queryRecords(
{
type: APP_DEPLOYMENT_RECORD_TYPE,
...filter
},
true
);
}
if (arg2) {
const authority = arg1.replace('@', '');
return `crn://${authority}/applications/${arg2}`;
}
/**
* Fetch ApplicationDeploymentRemovalRecords for deployments
*/
async getDeploymentRemovalRecords (
deployments: Deployment[]
): Promise<AppDeploymentRemovalRecord[]> {
// Fetch ApplicationDeploymentRemovalRecords for corresponding ApplicationDeploymentRecord set in deployments
const records = await this.registry.queryRecords(
{
type: APP_DEPLOYMENT_REMOVAL_RECORD_TYPE
},
true
);
return `crn://${arg1}/applications/${arg1}`;
// Filter records with ApplicationDeploymentRecord and ApplicationDeploymentRemovalRequest IDs
return records.filter((record: AppDeploymentRemovalRecord) =>
deployments.some(
(deployment) =>
deployment.applicationDeploymentRemovalRequestId === record.attributes.request &&
deployment.applicationDeploymentRecordId === record.attributes.deployment
)
);
}
async createApplicationDeploymentRemovalRequest (data: {
deploymentId: string;
}): Promise<{
applicationDeploymentRemovalRequestId: string;
applicationDeploymentRemovalRequestData: ApplicationDeploymentRemovalRequest;
}> {
const applicationDeploymentRemovalRequest = {
type: APP_DEPLOYMENT_REMOVAL_REQUEST_TYPE,
version: '1.0.0',
deployment: data.deploymentId
};
const result = await this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
record: applicationDeploymentRemovalRequest,
bondId: this.registryConfig.bondId
},
'',
this.registryConfig.fee
);
log(`Application deployment removal request record published: ${result.data.id}`);
log('Application deployment removal request data:', applicationDeploymentRemovalRequest);
return {
applicationDeploymentRemovalRequestId: result.data.id,
applicationDeploymentRemovalRequestData: applicationDeploymentRemovalRequest
};
}
getCrn (appName: string): string {
assert(this.registryConfig.authority, "Authority doesn't exist");
return `crn://${this.registryConfig.authority}/applications/${appName}`;
}
}
+185 -46
View File
@@ -6,6 +6,7 @@ import { Permission } from './entity/ProjectMember';
import { Domain } from './entity/Domain';
import { Project } from './entity/Project';
import { EnvironmentVariable } from './entity/EnvironmentVariable';
import { AddProjectFromTemplateInput } from './types';
const log = debug('snowball:resolver');
@@ -14,26 +15,36 @@ export const createResolvers = async (service: Service): Promise<any> => {
Query: {
// TODO: add custom type for context
user: (_: any, __: any, context: any) => {
return service.getUser(context.userId);
return context.user;
},
organizations: async (_:any, __: any, context: any) => {
return service.getOrganizationsByUserId(context.userId);
organizations: async (_: any, __: any, context: any) => {
return service.getOrganizationsByUserId(context.user);
},
project: async (_: any, { projectId }: { projectId: string }) => {
return service.getProjectById(projectId);
},
projectsInOrganization: async (_: any, { organizationSlug }: {organizationSlug: string }, context: any) => {
return service.getProjectsInOrganization(context.userId, organizationSlug);
projectsInOrganization: async (
_: any,
{ organizationSlug }: { organizationSlug: string },
context: any,
) => {
return service.getProjectsInOrganization(
context.user,
organizationSlug,
);
},
deployments: async (_: any, { projectId }: { projectId: string }) => {
return service.getDeploymentsByProjectId(projectId);
},
environmentVariables: async (_: any, { projectId }: { projectId: string }) => {
environmentVariables: async (
_: any,
{ projectId }: { projectId: string },
) => {
return service.getEnvironmentVariablesByProjectId(projectId);
},
@@ -41,32 +52,55 @@ export const createResolvers = async (service: Service): Promise<any> => {
return service.getProjectMembersByProjectId(projectId);
},
searchProjects: async (_: any, { searchText }: { searchText: string }, context: any) => {
return service.searchProjects(context.userId, searchText);
searchProjects: async (
_: any,
{ searchText }: { searchText: string },
context: any,
) => {
return service.searchProjects(context.user, searchText);
},
domains: async (_:any, { projectId, filter }: { projectId: string, filter?: FindOptionsWhere<Domain> }) => {
domains: async (
_: any,
{
projectId,
filter,
}: { projectId: string; filter?: FindOptionsWhere<Domain> },
) => {
return service.getDomainsByProjectId(projectId, filter);
}
},
},
// TODO: Return error in GQL response
Mutation: {
removeProjectMember: async (_: any, { projectMemberId }: { projectMemberId: string }, context: any) => {
removeProjectMember: async (
_: any,
{ projectMemberId }: { projectMemberId: string },
context: any,
) => {
try {
return await service.removeProjectMember(context.userId, projectMemberId);
return await service.removeProjectMember(
context.user,
projectMemberId,
);
} catch (err) {
log(err);
return false;
}
},
updateProjectMember: async (_: any, { projectMemberId, data }: {
projectMemberId: string,
data: {
permissions: Permission[]
}
}) => {
updateProjectMember: async (
_: any,
{
projectMemberId,
data,
}: {
projectMemberId: string;
data: {
permissions: Permission[];
};
},
) => {
try {
return await service.updateProjectMember(projectMemberId, data);
} catch (err) {
@@ -75,13 +109,19 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
addProjectMember: async (_: any, { projectId, data }: {
projectId: string,
data: {
email: string,
permissions: Permission[]
}
}) => {
addProjectMember: async (
_: any,
{
projectId,
data,
}: {
projectId: string;
data: {
email: string;
permissions: Permission[];
};
},
) => {
try {
return Boolean(await service.addProjectMember(projectId, data));
} catch (err) {
@@ -90,25 +130,51 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
addEnvironmentVariables: async (_: any, { projectId, data }: { projectId: string, data: { environments: string[], key: string, value: string}[] }) => {
addEnvironmentVariables: async (
_: any,
{
projectId,
data,
}: {
projectId: string;
data: { environments: string[]; key: string; value: string }[];
},
) => {
try {
return Boolean(await service.addEnvironmentVariables(projectId, data));
return Boolean(
await service.addEnvironmentVariables(projectId, data),
);
} catch (err) {
log(err);
return false;
}
},
updateEnvironmentVariable: async (_: any, { environmentVariableId, data }: { environmentVariableId: string, data : DeepPartial<EnvironmentVariable>}) => {
updateEnvironmentVariable: async (
_: any,
{
environmentVariableId,
data,
}: {
environmentVariableId: string;
data: DeepPartial<EnvironmentVariable>;
},
) => {
try {
return await service.updateEnvironmentVariable(environmentVariableId, data);
return await service.updateEnvironmentVariable(
environmentVariableId,
data,
);
} catch (err) {
log(err);
return false;
}
},
removeEnvironmentVariable: async (_: any, { environmentVariableId }: { environmentVariableId: string}) => {
removeEnvironmentVariable: async (
_: any,
{ environmentVariableId }: { environmentVariableId: string },
) => {
try {
return await service.removeEnvironmentVariable(environmentVariableId);
} catch (err) {
@@ -117,25 +183,61 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
updateDeploymentToProd: async (
_: any,
{ deploymentId }: { deploymentId: string },
context: any,
) => {
try {
return Boolean(await service.updateDeploymentToProd(context.userId, deploymentId));
return Boolean(
await service.updateDeploymentToProd(context.user, deploymentId),
);
} catch (err) {
log(err);
return false;
}
},
addProject: async (_: any, { organizationSlug, data }: { organizationSlug: string, data: DeepPartial<Project> }, context: any) => {
addProjectFromTemplate: async (
_: any,
{
organizationSlug,
data,
}: { organizationSlug: string; data: AddProjectFromTemplateInput },
context: any,
) => {
try {
return await service.addProject(context.userId, organizationSlug, data);
return await service.addProjectFromTemplate(
context.user,
organizationSlug,
data,
);
} catch (err) {
log(err);
throw err;
}
},
updateProject: async (_: any, { projectId, data }: { projectId: string, data: DeepPartial<Project> }) => {
addProject: async (
_: any,
{
organizationSlug,
data,
}: { organizationSlug: string; data: DeepPartial<Project> },
context: any,
) => {
try {
return await service.addProject(context.user, organizationSlug, data);
} catch (err) {
log(err);
throw err;
}
},
updateProject: async (
_: any,
{ projectId, data }: { projectId: string; data: DeepPartial<Project> },
) => {
try {
return await service.updateProject(projectId, data);
} catch (err) {
@@ -144,9 +246,15 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
redeployToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
redeployToProd: async (
_: any,
{ deploymentId }: { deploymentId: string },
context: any,
) => {
try {
return Boolean(await service.redeployToProd(context.userId, deploymentId));
return Boolean(
await service.redeployToProd(context.user, deploymentId),
);
} catch (err) {
log(err);
return false;
@@ -157,7 +265,8 @@ export const createResolvers = async (service: Service): Promise<any> => {
try {
return await service.deleteProject(projectId);
} catch (err) {
log(err); return false;
log(err);
return false;
}
},
@@ -170,7 +279,13 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => {
rollbackDeployment: async (
_: any,
{
projectId,
deploymentId,
}: { deploymentId: string; projectId: string },
) => {
try {
return await service.rollbackDeployment(projectId, deploymentId);
} catch (err) {
@@ -179,7 +294,22 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
addDomain: async (_: any, { projectId, data }: { projectId: string, data: { name: string } }) => {
deleteDeployment: async (
_: any,
{ deploymentId }: { deploymentId: string },
) => {
try {
return await service.deleteDeployment(deploymentId);
} catch (err) {
log(err);
return false;
}
},
addDomain: async (
_: any,
{ projectId, data }: { projectId: string; data: { name: string } },
) => {
try {
return Boolean(await service.addDomain(projectId, data));
} catch (err) {
@@ -188,7 +318,10 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
updateDomain: async (_: any, { domainId, data }: { domainId: string, data: DeepPartial<Domain>}) => {
updateDomain: async (
_: any,
{ domainId, data }: { domainId: string; data: DeepPartial<Domain> },
) => {
try {
return await service.updateDomain(domainId, data);
} catch (err) {
@@ -197,9 +330,13 @@ export const createResolvers = async (service: Service): Promise<any> => {
}
},
authenticateGitHub: async (_: any, { code }: { code: string }, context: any) => {
authenticateGitHub: async (
_: any,
{ code }: { code: string },
context: any,
) => {
try {
return await service.authenticateGitHub(code, context.userId);
return await service.authenticateGitHub(code, context.user);
} catch (err) {
log(err);
return false;
@@ -208,12 +345,14 @@ export const createResolvers = async (service: Service): Promise<any> => {
unauthenticateGitHub: async (_: any, __: object, context: any) => {
try {
return service.unauthenticateGitHub(context.userId, { gitHubToken: null });
return service.unauthenticateGitHub(context.user, {
gitHubToken: null,
});
} catch (err) {
log(err);
return false;
}
}
}
},
},
};
};
+119
View File
@@ -0,0 +1,119 @@
import { Router } from 'express';
import { SiweMessage } from 'siwe';
import { Service } from '../service';
import { authenticateUser, createUser } from '../turnkey-backend';
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
//
router.get('/registration/:email', async (req, res) => {
const service: Service = req.app.get('service');
const user = await service.getUserByEmail(req.params.email);
if (user) {
return res.send({ subOrganizationId: user?.subOrgId });
} else {
return res.sendStatus(204);
}
});
router.post('/register', async (req, res) => {
console.log('Register', req.body);
const { email, challenge, attestation } = req.body;
const user = await createUser(req.app.get('service'), {
challenge,
attestation,
userEmail: email,
userName: email.split('@')[0],
});
req.session.userId = user.id;
res.sendStatus(200);
});
router.post('/authenticate', async (req, res) => {
console.log('Authenticate', req.body);
const { signedWhoamiRequest } = req.body;
const user = await authenticateUser(
req.app.get('service'),
signedWhoamiRequest,
);
if (user) {
req.session.userId = user.id;
res.sendStatus(200);
} else {
res.sendStatus(401);
}
});
//
// Lit
//
router.post('/validate', async (req, res) => {
const { message, signature, action } = req.body;
const { success, data } = await new SiweMessage(message).verify({
signature,
});
if (!success) {
return res.send({ success });
}
const service: Service = req.app.get('service');
const user = await service.getUserByEthAddress(data.address);
if (action === 'signup') {
if (user) {
return res.send({ success: false, error: 'user_already_exists' });
}
const newUser = await service.createUser({
ethAddress: data.address,
email: '',
name: '',
subOrgId: '',
turnkeyWalletId: '',
});
req.session.userId = newUser.id;
} else if (action === 'login') {
if (!user) {
return res.send({ success: false, error: 'user_not_found' });
}
req.session.userId = user.id;
}
res.send({ success });
});
//
// General
//
router.get('/session', (req, res) => {
if (req.session.userId) {
res.send({
userId: req.session.userId,
});
} else {
res.status(401).send({ error: 'Unauthorized: No active session' });
}
});
router.post('/logout', (req, res) => {
// This is how you clear cookie-session
(req as any).session = null;
res.send({ success: true });
});
export default router;
+9
View File
@@ -0,0 +1,9 @@
import { Router } from 'express';
const router = Router();
router.get('/version', async (req, res) => {
return res.send({ version: '0.0.8' });
});
export default router;
+26 -3
View File
@@ -19,6 +19,7 @@ enum DeploymentStatus {
Building
Ready
Error
Deleting
}
enum DomainStatus {
@@ -129,6 +130,14 @@ input AddEnvironmentVariableInput {
value: String!
}
input AddProjectFromTemplateInput {
templateOwner: String!
templateRepo: String!
owner: String!
name: String!
isPrivate: Boolean!
}
input AddProjectInput {
name: String!
repository: String!
@@ -188,18 +197,32 @@ type Query {
type Mutation {
addProjectMember(projectId: String!, data: AddProjectMemberInput): Boolean!
updateProjectMember(projectMemberId: String!, data: UpdateProjectMemberInput): Boolean!
updateProjectMember(
projectMemberId: String!
data: UpdateProjectMemberInput
): Boolean!
removeProjectMember(projectMemberId: String!): Boolean!
addEnvironmentVariables(projectId: String!, data: [AddEnvironmentVariableInput!]): Boolean!
updateEnvironmentVariable(environmentVariableId: String!, data: UpdateEnvironmentVariableInput!): Boolean!
addEnvironmentVariables(
projectId: String!
data: [AddEnvironmentVariableInput!]
): Boolean!
updateEnvironmentVariable(
environmentVariableId: String!
data: UpdateEnvironmentVariableInput!
): Boolean!
removeEnvironmentVariable(environmentVariableId: String!): Boolean!
updateDeploymentToProd(deploymentId: String!): Boolean!
addProjectFromTemplate(
organizationSlug: String!
data: AddProjectFromTemplateInput
): Project!
addProject(organizationSlug: String!, data: AddProjectInput): Project!
updateProject(projectId: String!, data: UpdateProjectInput): Boolean!
redeployToProd(deploymentId: String!): Boolean!
deleteProject(projectId: String!): Boolean!
deleteDomain(domainId: String!): Boolean!
rollbackDeployment(projectId: String!, deploymentId: String!): Boolean!
deleteDeployment(deploymentId: String!): Boolean!
addDomain(projectId: String!, data: AddDomainInput!): Boolean!
updateDomain(domainId: String!, data: UpdateDomainInput!): Boolean!
authenticateGitHub(code: String!): AuthResult!
+67 -11
View File
@@ -1,29 +1,41 @@
import debug from 'debug';
import express from 'express';
import cors from 'cors';
import { ApolloServer } from 'apollo-server-express';
import { createServer } from 'http';
import {
ApolloServerPluginDrainHttpServer,
ApolloServerPluginLandingPageLocalDefault
ApolloServerPluginLandingPageLocalDefault,
AuthenticationError,
} from 'apollo-server-core';
import cookieSession from 'cookie-session';
import { TypeSource } from '@graphql-tools/utils';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { ServerConfig } from './config';
import { DEFAULT_GQL_PATH, USER_ID } from './constants';
import { DEFAULT_GQL_PATH } from './constants';
import githubRouter from './routes/github';
import authRouter from './routes/auth';
import stagingRouter from './routes/staging';
import { Service } from './service';
const log = debug('snowball:server');
declare module 'express-session' {
interface SessionData {
userId: string;
}
}
export const createAndStartServer = async (
serverConfig: ServerConfig,
typeDefs: TypeSource,
resolvers: any,
service: Service
service: Service,
): Promise<ApolloServer> => {
const { host, port, gqlPath = DEFAULT_GQL_PATH } = serverConfig;
const { appOriginUrl, secret, domain, trustProxy } = serverConfig.session;
const app = express();
@@ -33,33 +45,77 @@ export const createAndStartServer = async (
// Create the schema
const schema = makeExecutableSchema({
typeDefs,
resolvers
resolvers,
});
const server = new ApolloServer({
schema,
csrfPrevention: true,
context: () => {
// TODO: Use userId derived from auth token
return { userId: USER_ID };
context: async ({ req }) => {
// https://www.apollographql.com/docs/apollo-server/v3/security/authentication#api-wide-authorization
const { userId } = req.session;
if (!userId) {
throw new AuthenticationError('Unauthorized: No active session');
}
const user = await service.getUser(userId);
return { user };
},
plugins: [
// Proper shutdown for the HTTP server
ApolloServerPluginDrainHttpServer({ httpServer }),
ApolloServerPluginLandingPageLocalDefault({ embed: true })
]
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});
await server.start();
app.use(
cors({
origin: appOriginUrl,
credentials: true,
}),
);
if (trustProxy) {
// trust first proxy
app.set('trust proxy', 1);
}
app.use(
cookieSession({
secret: secret,
secure: new URL(appOriginUrl).protocol === 'https:',
// 23 hours (less than 24 hours to avoid sessionSigs expiration issues)
maxAge: 23 * 60 * 60 * 1000,
sameSite: new URL(appOriginUrl).protocol === 'https:' ? 'none' : 'lax',
domain: domain || undefined,
}),
);
server.applyMiddleware({
app,
path: gqlPath
path: gqlPath,
cors: {
origin: [appOriginUrl],
credentials: true,
},
});
app.set('service', service);
app.use(express.json());
app.set('service', service);
app.use('/auth', authRouter);
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, () => {
log(`Server is listening on ${host}:${port}${server.graphqlPath}`);
File diff suppressed because it is too large Load Diff
+130
View File
@@ -0,0 +1,130 @@
import { Turnkey, TurnkeyApiTypes } from '@turnkey/sdk-server';
// Default path for the first Ethereum address in a new HD wallet.
// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki, paths are in the form:
// m / purpose' / coin_type' / account' / change / address_index
// - Purpose is a constant set to 44' following the BIP43 recommendation.
// - Coin type is set to 60 (ETH) -- see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
// - Account, Change, and Address Index are set to 0
import { DEFAULT_ETHEREUM_ACCOUNTS } from '@turnkey/sdk-server';
import { getConfig } from './utils';
import { Service } from './service';
type TAttestation = TurnkeyApiTypes['v1Attestation'];
type CreateUserParams = {
userName: string;
userEmail: string;
challenge: string;
attestation: TAttestation;
};
export async function createUser(
service: Service,
{ userName, userEmail, challenge, attestation }: CreateUserParams,
) {
try {
if (await service.getUserByEmail(userEmail)) {
throw new Error(`User already exists: ${userEmail}`);
}
const config = await getConfig();
const turnkey = new Turnkey(config.turnkey);
const apiClient = turnkey.api();
const walletName = `Default ETH Wallet`;
const createSubOrgResponse = await apiClient.createSubOrganization({
subOrganizationName: `Default SubOrg for ${userEmail}`,
rootQuorumThreshold: 1,
rootUsers: [
{
userName,
userEmail,
apiKeys: [],
authenticators: [
{
authenticatorName: 'Passkey',
challenge,
attestation,
},
],
},
],
wallet: {
walletName: walletName,
accounts: DEFAULT_ETHEREUM_ACCOUNTS,
},
});
const subOrgId = refineNonNull(createSubOrgResponse.subOrganizationId);
const wallet = refineNonNull(createSubOrgResponse.wallet);
const result = {
id: wallet.walletId,
address: wallet.addresses[0],
subOrgId: subOrgId,
};
console.log('Turnkey success', result);
const user = await service.createUser({
name: userName,
email: userEmail,
subOrgId,
ethAddress: wallet.addresses[0],
turnkeyWalletId: wallet.walletId,
});
console.log('New user', user);
return user;
} catch (e) {
console.error('Failed to create user:', e);
throw e;
}
}
export async function authenticateUser(
service: Service,
signedWhoamiRequest: {
url: string;
body: any;
stamp: {
stampHeaderName: string;
stampHeaderValue: string;
};
},
) {
try {
const tkRes = await fetch(signedWhoamiRequest.url, {
method: 'POST',
body: signedWhoamiRequest.body,
headers: {
[signedWhoamiRequest.stamp.stampHeaderName]:
signedWhoamiRequest.stamp.stampHeaderValue,
},
});
console.log('AUTH RESULT', tkRes.status);
if (tkRes.status !== 200) {
console.log(await tkRes.text());
return null;
}
const orgId = (await tkRes.json()).organizationId;
const user = await service.getUserBySubOrgId(orgId);
return user;
} catch (e) {
console.error('Failed to authenticate:', e);
throw e;
}
}
function refineNonNull<T>(
input: T | null | undefined,
errorMessage?: string,
): T {
if (input == null) {
throw new Error(errorMessage ?? `Unexpected ${JSON.stringify(input)}`);
}
return input;
}
+23 -3
View File
@@ -1,6 +1,6 @@
export interface PackageJSON {
name?: string;
version?: string;
name: string;
version: string;
author?: string;
description?: string;
homepage?: string;
@@ -24,6 +24,7 @@ export interface GitPushEventPayload {
id: string;
message: string;
};
deleted: boolean;
}
export interface AppDeploymentRecordAttributes {
@@ -37,6 +38,13 @@ export interface AppDeploymentRecordAttributes {
version: string;
}
export interface AppDeploymentRemovalRecordAttributes {
deployment: string;
request: string;
type: 'ApplicationDeploymentRemovalRecord';
version: string;
}
interface RegistryRecord {
id: string;
names: string[] | null;
@@ -47,5 +55,17 @@ interface RegistryRecord {
}
export interface AppDeploymentRecord extends RegistryRecord {
attributes: AppDeploymentRecordAttributes
attributes: AppDeploymentRecordAttributes;
}
export interface AppDeploymentRemovalRecord extends RegistryRecord {
attributes: AppDeploymentRemovalRecordAttributes;
}
export interface AddProjectFromTemplateInput {
templateOwner: string;
templateRepo: string;
owner: string;
name: string;
isPrivate: boolean;
}
+59 -2
View File
@@ -2,11 +2,19 @@ import fs from 'fs-extra';
import path from 'path';
import toml from 'toml';
import debug from 'debug';
import { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm';
import { Config } from './config';
import { DEFAULT_CONFIG_FILE_PATH } from './constants';
const log = debug('snowball:utils');
export const getConfig = async <ConfigType>(
configFile: string
export async function getConfig() {
// TODO: get config path using cli
return await _getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
}
const _getConfig = async <ConfigType>(
configFile: string,
): Promise<ConfigType> => {
const configFilePath = path.resolve(configFile);
const fileExists = await fs.pathExists(configFilePath);
@@ -19,3 +27,52 @@ export const getConfig = async <ConfigType>(
return config;
};
export const checkFileExists = async (filePath: string): Promise<boolean> => {
try {
await fs.access(filePath, fs.constants.F_OK);
return true;
} catch (err) {
log(err);
return false;
}
};
export const getEntities = async (filePath: string): Promise<any> => {
const entitiesData = await fs.readFile(filePath, 'utf-8');
const entities = JSON.parse(entitiesData);
return entities;
};
export const loadAndSaveData = async <Entity extends ObjectLiteral>(
entityType: EntityTarget<Entity>,
dataSource: DataSource,
entities: any,
relations?: any | undefined,
): Promise<Entity[]> => {
const entityRepository = dataSource.getRepository(entityType);
const savedEntity: Entity[] = [];
for (const entityData of entities) {
let entity = entityRepository.create(entityData as DeepPartial<Entity>);
if (relations) {
for (const field in relations) {
const valueIndex = String(field + 'Index');
entity = {
...entity,
[field]: relations[field][entityData[valueIndex]],
};
}
}
const dbEntity = await entityRepository.save(entity);
savedEntity.push(dbEntity);
}
return savedEntity;
};
export const sleep = async (ms: number): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, ms));
+1 -1
View File
@@ -18,4 +18,4 @@ const main = async () => {
deleteFile(config.database.dbPath);
};
main().catch(err => log(err));
main().catch((err) => log(err));
+42 -20
View File
@@ -1,14 +1,16 @@
[
{
"projectIndex": 0,
"domainIndex":0,
"domainIndex": 0,
"createdByIndex": 0,
"id":"ffhae3zq",
"id": "ffhae3zq",
"status": "Ready",
"environment": "Production",
"isCurrent": true,
"applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "xqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -16,14 +18,16 @@
},
{
"projectIndex": 0,
"domainIndex":1,
"domainIndex": 1,
"createdByIndex": 0,
"id":"vehagei8",
"id": "vehagei8",
"status": "Ready",
"environment": "Preview",
"isCurrent": false,
"applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "wqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -31,14 +35,16 @@
},
{
"projectIndex": 0,
"domainIndex":2,
"domainIndex": 2,
"createdByIndex": 0,
"id":"qmgekyte",
"id": "qmgekyte",
"status": "Ready",
"environment": "Development",
"isCurrent": false,
"applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "kqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -48,12 +54,14 @@
"projectIndex": 0,
"domainIndex": null,
"createdByIndex": 0,
"id":"f8wsyim6",
"id": "f8wsyim6",
"status": "Ready",
"environment": "Production",
"isCurrent": false,
"applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "yqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "prod",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -61,14 +69,16 @@
},
{
"projectIndex": 1,
"domainIndex":3,
"domainIndex": 3,
"createdByIndex": 1,
"id":"eO8cckxk",
"id": "eO8cckxk",
"status": "Ready",
"environment": "Production",
"isCurrent": true,
"applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -76,14 +86,16 @@
},
{
"projectIndex": 1,
"domainIndex":4,
"domainIndex": 4,
"createdByIndex": 1,
"id":"yaq0t5yw",
"id": "yaq0t5yw",
"status": "Ready",
"environment": "Preview",
"isCurrent": false,
"applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "tqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -91,14 +103,16 @@
},
{
"projectIndex": 1,
"domainIndex":5,
"domainIndex": 5,
"createdByIndex": 1,
"id":"hwwr6sbx",
"id": "hwwr6sbx",
"status": "Ready",
"environment": "Development",
"isCurrent": false,
"applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "eqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -106,14 +120,16 @@
},
{
"projectIndex": 2,
"domainIndex":9,
"domainIndex": 9,
"createdByIndex": 2,
"id":"ndxje48a",
"id": "ndxje48a",
"status": "Ready",
"environment": "Production",
"isCurrent": true,
"applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "dqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "main",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -121,14 +137,16 @@
},
{
"projectIndex": 2,
"domainIndex":7,
"domainIndex": 7,
"createdByIndex": 2,
"id":"gtgpgvei",
"id": "gtgpgvei",
"status": "Ready",
"environment": "Preview",
"isCurrent": false,
"applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationRecordData": {},
"applicationDeploymentRequestId": "aqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -136,14 +154,16 @@
},
{
"projectIndex": 2,
"domainIndex":8,
"domainIndex": 8,
"createdByIndex": 2,
"id":"b4bpthjr",
"id": "b4bpthjr",
"status": "Ready",
"environment": "Development",
"isCurrent": false,
"applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
"applicationRecordData": {},
"applicationDeploymentRequestId": "uqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
@@ -153,12 +173,14 @@
"projectIndex": 3,
"domainIndex": 6,
"createdByIndex": 2,
"id":"b4bpthjr",
"id": "b4bpthjr",
"status": "Ready",
"environment": "Production",
"isCurrent": true,
"applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
"applicationRecordData": {},
"applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"branch": "test",
"commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00",
"commitMessage": "subscription added",
+9 -31
View File
@@ -2,77 +2,55 @@
{
"memberIndex": 1,
"projectIndex": 0,
"permissions": [
"View"
],
"permissions": ["View"],
"isPending": false
},
{
"memberIndex": 2,
"projectIndex": 0,
"permissions": [
"View",
"Edit"
],
"permissions": ["View", "Edit"],
"isPending": false
},
{
"memberIndex": 2,
"projectIndex": 1,
"permissions": [
"View"
],
"permissions": ["View"],
"isPending": false
},
{
"memberIndex": 0,
"projectIndex": 2,
"permissions": [
"View"
],
"permissions": ["View"],
"isPending": false
},
{
"memberIndex": 1,
"projectIndex": 2,
"permissions": [
"View",
"Edit"
],
"permissions": ["View", "Edit"],
"isPending": false
},
{
"memberIndex": 0,
"projectIndex": 3,
"permissions": [
"View"
],
"permissions": ["View"],
"isPending": false
},
{
"memberIndex": 2,
"projectIndex": 3,
"permissions": [
"View",
"Edit"
],
"permissions": ["View", "Edit"],
"isPending": false
},
{
"memberIndex": 1,
"projectIndex": 4,
"permissions": [
"View"
],
"permissions": ["View"],
"isPending": false
},
{
"memberIndex": 2,
"projectIndex": 4,
"permissions": [
"View",
"Edit"
],
"permissions": ["View", "Edit"],
"isPending": false
}
]
-10
View File
@@ -10,8 +10,6 @@
"framework": "test",
"webhooks": [],
"icon": "",
"applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"subDomain": "testProject.snowball.xyz"
},
{
@@ -25,8 +23,6 @@
"framework": "test-2",
"webhooks": [],
"icon": "",
"applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"subDomain": "testProject-2.snowball.xyz"
},
{
@@ -40,8 +36,6 @@
"framework": "test-3",
"webhooks": [],
"icon": "",
"applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"subDomain": "iglootools.snowball.xyz"
},
{
@@ -55,8 +49,6 @@
"framework": "test-4",
"webhooks": [],
"icon": "",
"applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"subDomain": "iglootools-2.snowball.xyz"
},
{
@@ -70,8 +62,6 @@
"framework": "test-5",
"webhooks": [],
"icon": "",
"applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"applicationDeploymentRequestData": {},
"subDomain": "snowball-2.snowball.xyz"
}
]
+9 -6
View File
@@ -1,20 +1,23 @@
[
{
"id": "59f4355d-9549-4aac-9b54-eeefceeabef0",
"name": "Snowball",
"name": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"email": "snowball@snowballtools.xyz",
"isVerified": true
"isVerified": true,
"ethAddress": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
},
{
"id": "e505b212-8da6-48b2-9614-098225dab34b",
"name": "Alice Anderson",
"name": "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8",
"email": "alice@snowballtools.xyz",
"isVerified": true
"isVerified": true,
"ethAddress": "0xbe0eb53f46cd790cd13851d5eff43d12404d33e8"
},
{
"id": "cd892fad-9138-4aa2-a62c-414a32776ea7",
"name": "Bob Banner",
"name": "0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a",
"email": "bob@snowballtools.xyz",
"isVerified": true
"isVerified": true,
"ethAddress": "0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a"
}
]
+83 -49
View File
@@ -1,5 +1,4 @@
import { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm';
import * as fs from 'fs/promises';
import { DataSource } from 'typeorm';
import debug from 'debug';
import path from 'path';
@@ -11,7 +10,12 @@ import { EnvironmentVariable } from '../src/entity/EnvironmentVariable';
import { Domain } from '../src/entity/Domain';
import { ProjectMember } from '../src/entity/ProjectMember';
import { Deployment } from '../src/entity/Deployment';
import { getConfig } from '../src/utils';
import {
checkFileExists,
getConfig,
getEntities,
loadAndSaveData
} from '../src/utils';
import { Config } from '../src/config';
import { DEFAULT_CONFIG_FILE_PATH } from '../src/constants';
@@ -27,43 +31,35 @@ const DEPLOYMENT_DATA_PATH = './fixtures/deployments.json';
const ENVIRONMENT_VARIABLE_DATA_PATH = './fixtures/environment-variables.json';
const REDIRECTED_DOMAIN_DATA_PATH = './fixtures/redirected-domains.json';
const loadAndSaveData = async <Entity extends ObjectLiteral>(entityType: EntityTarget<Entity>, dataSource: DataSource, filePath: string, relations?: any | undefined) => {
const entitiesData = await fs.readFile(filePath, 'utf-8');
const entities = JSON.parse(entitiesData);
const entityRepository = dataSource.getRepository(entityType);
const savedEntity:Entity[] = [];
for (const entityData of entities) {
let entity = entityRepository.create(entityData as DeepPartial<Entity>);
if (relations) {
for (const field in relations) {
const valueIndex = String(field + 'Index');
entity = {
...entity,
[field]: relations[field][entityData[valueIndex]]
};
}
}
const dbEntity = await entityRepository.save(entity);
savedEntity.push(dbEntity);
}
return savedEntity;
};
const generateTestData = async (dataSource: DataSource) => {
const savedUsers = await loadAndSaveData(User, dataSource, path.resolve(__dirname, USER_DATA_PATH));
const savedOrgs = await loadAndSaveData(Organization, dataSource, path.resolve(__dirname, ORGANIZATION_DATA_PATH));
const userEntities = await getEntities(
path.resolve(__dirname, USER_DATA_PATH)
);
const savedUsers = await loadAndSaveData(User, dataSource, userEntities);
const orgEntities = await getEntities(
path.resolve(__dirname, ORGANIZATION_DATA_PATH)
);
const savedOrgs = await loadAndSaveData(
Organization,
dataSource,
orgEntities
);
const projectRelations = {
owner: savedUsers,
organization: savedOrgs
};
const savedProjects = await loadAndSaveData(Project, dataSource, path.resolve(__dirname, PROJECT_DATA_PATH), projectRelations);
const projectEntities = await getEntities(
path.resolve(__dirname, PROJECT_DATA_PATH)
);
const savedProjects = await loadAndSaveData(
Project,
dataSource,
projectEntities,
projectRelations
);
const domainRepository = dataSource.getRepository(Domain);
@@ -71,14 +67,30 @@ const generateTestData = async (dataSource: DataSource) => {
project: savedProjects
};
const savedPrimaryDomains = await loadAndSaveData(Domain, dataSource, path.resolve(__dirname, PRIMARY_DOMAIN_DATA_PATH), domainPrimaryRelations);
const primaryDomainsEntities = await getEntities(
path.resolve(__dirname, PRIMARY_DOMAIN_DATA_PATH)
);
const savedPrimaryDomains = await loadAndSaveData(
Domain,
dataSource,
primaryDomainsEntities,
domainPrimaryRelations
);
const domainRedirectedRelations = {
project: savedProjects,
redirectTo: savedPrimaryDomains
};
await loadAndSaveData(Domain, dataSource, path.resolve(__dirname, REDIRECTED_DOMAIN_DATA_PATH), domainRedirectedRelations);
const redirectDomainsEntities = await getEntities(
path.resolve(__dirname, REDIRECTED_DOMAIN_DATA_PATH)
);
await loadAndSaveData(
Domain,
dataSource,
redirectDomainsEntities,
domainRedirectedRelations
);
const savedDomains = await domainRepository.find();
@@ -87,14 +99,30 @@ const generateTestData = async (dataSource: DataSource) => {
organization: savedOrgs
};
await loadAndSaveData(UserOrganization, dataSource, path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH), userOrganizationRelations);
const userOrganizationsEntities = await getEntities(
path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH)
);
await loadAndSaveData(
UserOrganization,
dataSource,
userOrganizationsEntities,
userOrganizationRelations
);
const projectMemberRelations = {
member: savedUsers,
project: savedProjects
};
await loadAndSaveData(ProjectMember, dataSource, path.resolve(__dirname, PROJECT_MEMBER_DATA_PATH), projectMemberRelations);
const projectMembersEntities = await getEntities(
path.resolve(__dirname, PROJECT_MEMBER_DATA_PATH)
);
await loadAndSaveData(
ProjectMember,
dataSource,
projectMembersEntities,
projectMemberRelations
);
const deploymentRelations = {
project: savedProjects,
@@ -102,23 +130,29 @@ const generateTestData = async (dataSource: DataSource) => {
createdBy: savedUsers
};
await loadAndSaveData(Deployment, dataSource, path.resolve(__dirname, DEPLOYMENT_DATA_PATH), deploymentRelations);
const deploymentsEntities = await getEntities(
path.resolve(__dirname, DEPLOYMENT_DATA_PATH)
);
await loadAndSaveData(
Deployment,
dataSource,
deploymentsEntities,
deploymentRelations
);
const environmentVariableRelations = {
project: savedProjects
};
await loadAndSaveData(EnvironmentVariable, dataSource, path.resolve(__dirname, ENVIRONMENT_VARIABLE_DATA_PATH), environmentVariableRelations);
};
const checkFileExists = async (filePath: string) => {
try {
await fs.access(filePath, fs.constants.F_OK);
return true;
} catch (err) {
log(err);
return false;
}
const environmentVariablesEntities = await getEntities(
path.resolve(__dirname, ENVIRONMENT_VARIABLE_DATA_PATH)
);
await loadAndSaveData(
EnvironmentVariable,
dataSource,
environmentVariablesEntities,
environmentVariableRelations
);
};
const main = async () => {
+15 -7
View File
@@ -1,6 +1,6 @@
import debug from 'debug';
import { Registry } from '@cerc-io/laconic-sdk';
import { Registry } from '@snowballtools/laconic-sdk';
import { DEFAULT_CONFIG_FILE_PATH } from '../src/constants';
import { Config } from '../src/config';
@@ -11,22 +11,30 @@ const log = debug('snowball:initialize-registry');
const DENOM = 'aphoton';
const BOND_AMOUNT = '1000000000';
// TODO: Get authority names from args
const AUTHORITY_NAMES = ['snowballtools', 'cerc-io'];
async function main () {
const { registryConfig } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
// TODO: Get authority names from args
const authorityNames = ['snowballtools', registryConfig.authority];
const registry = new Registry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
const bondId = await registry.getNextBondId(registryConfig.privateKey);
log('bondId:', bondId);
await registry.createBond({ denom: DENOM, amount: BOND_AMOUNT }, registryConfig.privateKey, registryConfig.fee);
await registry.createBond(
{ denom: DENOM, amount: BOND_AMOUNT },
registryConfig.privateKey,
registryConfig.fee
);
for await (const name of AUTHORITY_NAMES) {
for await (const name of authorityNames) {
await registry.reserveAuthority({ name }, registryConfig.privateKey, registryConfig.fee);
log('Reserved authority name:', name);
await registry.setAuthorityBond({ name, bondId }, registryConfig.privateKey, registryConfig.fee);
await registry.setAuthorityBond(
{ name, bondId },
registryConfig.privateKey,
registryConfig.fee
);
log(`Bond ${bondId} set for authority ${name}`);
}
}
@@ -2,19 +2,23 @@ import debug from 'debug';
import { DataSource } from 'typeorm';
import path from 'path';
import { Registry } from '@cerc-io/laconic-sdk';
import { Registry } from '@snowballtools/laconic-sdk';
import { Config } from '../src/config';
import { DEFAULT_CONFIG_FILE_PATH, PROJECT_DOMAIN } from '../src/constants';
import { DEFAULT_CONFIG_FILE_PATH } from '../src/constants';
import { getConfig } from '../src/utils';
import { Deployment, DeploymentStatus } from '../src/entity/Deployment';
import { Deployment, DeploymentStatus, Environment } from '../src/entity/Deployment';
const log = debug('snowball:publish-deploy-records');
async function main () {
const { registryConfig, database } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
const { registryConfig, database, misc } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
const registry = new Registry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
const registry = new Registry(
registryConfig.gqlEndpoint,
registryConfig.restEndpoint,
registryConfig.chainId
);
const dataSource = new DataSource({
type: 'better-sqlite3',
@@ -36,7 +40,7 @@ async function main () {
});
for await (const deployment of deployments) {
const url = `${deployment.project.name}-${deployment.id}.${PROJECT_DOMAIN}`;
const url = `https://${deployment.project.name}-${deployment.id}.${misc.projectDomain}`;
const applicationDeploymentRecord = {
type: 'ApplicationDeploymentRecord',
@@ -53,7 +57,7 @@ async function main () {
so: '66fcfa49a1664d4cb4ce4f72c1c0e151'
}),
request: deployment.project.applicationDeploymentRequestId,
request: deployment.applicationDeploymentRequestId,
url
};
@@ -67,6 +71,21 @@ async function main () {
registryConfig.fee
);
// Remove deployment for project subdomain if deployment is for production environment
if (deployment.environment === Environment.Production) {
applicationDeploymentRecord.url = `https://${deployment.project.subDomain}`
await registry.setRecord(
{
privateKey: registryConfig.privateKey,
record: applicationDeploymentRecord,
bondId: registryConfig.bondId
},
'',
registryConfig.fee
);
}
log('Application deployment record data:', applicationDeploymentRecord);
log(`Application deployment record published: ${result.data.id}`);
}
@@ -0,0 +1,67 @@
import debug from 'debug';
import { DataSource } from 'typeorm';
import path from 'path';
import { Registry } from '@cerc-io/laconic-sdk';
import { Config } from '../src/config';
import { DEFAULT_CONFIG_FILE_PATH } from '../src/constants';
import { getConfig } from '../src/utils';
import { Deployment, DeploymentStatus } from '../src/entity/Deployment';
const log = debug('snowball:publish-deployment-removal-records');
async function main () {
const { registryConfig, database, misc } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH);
const registry = new Registry(
registryConfig.gqlEndpoint,
registryConfig.restEndpoint,
registryConfig.chainId
);
const dataSource = new DataSource({
type: 'better-sqlite3',
database: database.dbPath,
synchronize: true,
entities: [path.join(__dirname, '../src/entity/*')]
});
await dataSource.initialize();
const deploymentRepository = dataSource.getRepository(Deployment);
const deployments = await deploymentRepository.find({
relations: {
project: true
},
where: {
status: DeploymentStatus.Deleting
}
});
for await (const deployment of deployments) {
const applicationDeploymentRemovalRecord = {
type: "ApplicationDeploymentRemovalRecord",
version: "1.0.0",
deployment: deployment.applicationDeploymentRecordId,
request: deployment.applicationDeploymentRemovalRequestId,
}
const result = await registry.setRecord(
{
privateKey: registryConfig.privateKey,
record: applicationDeploymentRemovalRecord,
bondId: registryConfig.bondId
},
'',
registryConfig.fee
);
log('Application deployment removal record data:', applicationDeploymentRemovalRecord);
log(`Application deployment removal record published: ${result.data.id}`);
}
}
main().catch((err) => {
log(err);
});
+64
View File
@@ -0,0 +1,64 @@
# deployer
- Install dependencies
```bash
yarn
```
```bash
brew install jq # if you do not have jq installed already
```
- Run script to deploy app
- To deploy frontend app to `dashboard.staging.apps.snowballtools.com`
```bash
./deploy-frontend.staging.sh
```
- To deploy frontend app to `dashboard.apps.snowballtools.com`
```bash
./deploy-frontend.sh
```
- Commit the updated [ApplicationRecord](records/application-record.yml) and [ApplicationDeploymentRequest](records/application-deployment-request.yml) files to the repository
## Notes
- Any config env can be updated in [records/application-deployment-request.yml](records/application-deployment-request.yml)
```yml
record:
...
config:
env:
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
## Troubleshoot
- Check deployment status in [web-app deployer](https://console.laconic.com/deployer).
- Check records in [registry console app](https://console.laconic.com/#/registry).
- If deployment fails due to low bond balance
- Check balances
```bash
# Account balance
yarn laconic cns account get
# Bond balance
yarn laconic cns bond get --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32
```
- Command to refill bond
```bash
yarn laconic cns bond refill --id 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32 --type aphoton --quantity 10000000
```
+9
View File
@@ -0,0 +1,9 @@
services:
cns:
restEndpoint: 'http://console.laconic.com:1317'
gqlEndpoint: 'http://console.laconic.com:9473/api'
userKey: 87d00f66a73e2ca428adeb49ba9164d0ad9a87edc60e33d46ad3031b9c5701fe
bondId: 89c75c7bc5759861d10285aff6f9e7227d6855e446b77ad5d8324822dfec7deb
chainId: laconic_9000-1
gas: 1200000
fees: 200000aphoton
+9
View File
@@ -0,0 +1,9 @@
services:
cns:
restEndpoint: http://console.laconic.com:1317
gqlEndpoint: http://console.laconic.com:9473/api
userKey: 489c9dd3931c2a2d4dd77973302dc5eb01e2a49552f9d932c58d9da823512311
bondId: 99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32
chainId: laconic_9000-1
gas: 1200000
fees: 200000aphoton
+127
View File
@@ -0,0 +1,127 @@
#!/bin/bash
# Repository URL
REPO_URL="https://git.vdb.to/cerc-io/snowballtools-base"
# Get the latest commit hash from the repository
LATEST_HASH=$(git ls-remote $REPO_URL HEAD | awk '{print $1}')
# Extract version from ../frontend/package.json
PACKAGE_VERSION=$(jq -r '.version' ../frontend/package.json)
# Current date and time for note
CURRENT_DATE_TIME=$(date -u)
CONFIG_FILE=config.yml
REGISTRY_BOND_ID="99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32"
# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts
# Get latest version from registry and increment application-record version
NEW_APPLICATION_VERSION=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "snowballtools-base-frontend" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}')
if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; then
# Set application-record version if no previous records were found
NEW_APPLICATION_VERSION=0.0.1
fi
# Generate application-deployment-request.yml
cat >./records/application-deployment-request.yml <<EOF
record:
type: ApplicationDeploymentRequest
version: '1.0.0'
name: snowballtools-base-frontend@$PACKAGE_VERSION
application: crn://snowballtools/applications/snowballtools-base-frontend@$PACKAGE_VERSION
dns: dashboard
config:
env:
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api-001.apps.snowballtools.com
LACONIC_HOSTED_CONFIG_github_clientid: b7c63b235ca1dd5639ab
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_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
meta:
note: Added by Snowball @ $CURRENT_DATE_TIME
repository: "$REPO_URL"
repository_ref: $LATEST_HASH
EOF
# Generate application-record.yml with incremented version
cat >./records/application-record.yml <<EOF
record:
type: ApplicationRecord
version: $NEW_APPLICATION_VERSION
repository_ref: $LATEST_HASH
repository: ["$REPO_URL"]
app_type: webapp
name: snowballtools-base-frontend
app_version: $PACKAGE_VERSION
EOF
echo "Files generated successfully."
RECORD_FILE=records/application-record.yml
# Publish ApplicationRecord
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
rc=$?
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to publish record"
exit $rc
fi
RECORD_ID=$(echo $publish_response | jq -r '.id')
echo "ApplicationRecord published"
echo $RECORD_ID
# Set name to record
REGISTRY_APP_CRN="crn://snowballtools/applications/snowballtools-base-frontend"
sleep 2
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
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
# Set name if latest release
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"
# Check if record found for REGISTRY_APP_CRN
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN")
rc=$?
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to query name"
exit $rc
fi
APP_RECORD=$(echo $query_response | jq '.[0]')
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
echo "No record found for $REGISTRY_APP_CRN."
exit 1
fi
RECORD_FILE=records/application-deployment-request.yml
sleep 2
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to query deployment request"
exit $rc
fi
DEPLOYMENT_REQUEST_ID=$(echo $deployment_response | jq -r '.id')
echo "ApplicationDeploymentRequest published"
echo $DEPLOYMENT_REQUEST_ID
+133
View File
@@ -0,0 +1,133 @@
#!/bin/bash
# Repository URL
REPO_URL="https://git.vdb.to/cerc-io/snowballtools-base"
# Get the latest commit hash from the repository
LATEST_HASH=$(git ls-remote $REPO_URL HEAD | awk '{print $1}')
# Extract version from ../frontend/package.json
PACKAGE_VERSION=$(jq -r '.version' ../frontend/package.json)
# Current date and time for note
CURRENT_DATE_TIME=$(date -u)
CONFIG_FILE=config.staging.yml
REGISTRY_BOND_ID="098c906850b87412f02200e41f449bc79e055eab77acfef32c0b22443bb46661"
# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts
# Get latest version from registry and increment application-record version
NEW_APPLICATION_VERSION=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "staging-snowballtools-base-frontend" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}')
if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; then
# Set application-record version if no previous records were found
NEW_APPLICATION_VERSION=0.0.1
fi
# Generate application-deployment-request.yml
cat >./staging-records/application-deployment-request.yml <<EOF
record:
type: ApplicationDeploymentRequest
version: '1.0.0'
name: staging-snowballtools-base-frontend@$PACKAGE_VERSION
application: crn://staging-snowballtools/applications/staging-snowballtools-base-frontend@$PACKAGE_VERSION
dns: dashboard.staging.apps.snowballtools.com
config:
env:
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
LACONIC_HOSTED_CONFIG_github_clientid: Ov23liOaoahRTYd4nSCV
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_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
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_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
meta:
note: Added by Snowball @ $CURRENT_DATE_TIME
repository: "$REPO_URL"
repository_ref: $LATEST_HASH
EOF
# Generate application-record.yml with incremented version
cat >./staging-records/application-record.yml <<EOF
record:
type: ApplicationRecord
version: $NEW_APPLICATION_VERSION
repository_ref: $LATEST_HASH
repository: ["$REPO_URL"]
app_type: webapp
name: staging-snowballtools-base-frontend
app_version: $PACKAGE_VERSION
EOF
echo "Files generated successfully."
RECORD_FILE=staging-records/application-record.yml
# Publish ApplicationRecord
publish_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
rc=$?
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to publish record"
exit $rc
fi
RECORD_ID=$(echo $publish_response | jq -r '.id')
echo "ApplicationRecord published"
echo $RECORD_ID
# Set name to record
REGISTRY_APP_CRN="crn://staging-snowballtools/applications/staging-snowballtools-base-frontend"
sleep 2
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
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
# Set name if latest release
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"
# Check if record found for REGISTRY_APP_CRN
query_response=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN")
rc=$?
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to query name"
exit $rc
fi
APP_RECORD=$(echo $query_response | jq '.[0]')
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
echo "No record found for $REGISTRY_APP_CRN."
exit 1
fi
RECORD_FILE=staging-records/application-deployment-request.yml
sleep 2
deployment_response=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE)
if [ $rc -ne 0 ]; then
echo "FATAL: Failed to query deployment request"
exit $rc
fi
DEPLOYMENT_REQUEST_ID=$(echo $deployment_response | jq -r '.id')
echo "ApplicationDeploymentRequest published"
echo $DEPLOYMENT_REQUEST_ID
+9
View File
@@ -0,0 +1,9 @@
{
"name": "deployer",
"version": "1.0.0",
"main": "index.js",
"private": true,
"devDependencies": {
"@snowballtools/laconic-registry-cli": "^0.1.13"
}
}
@@ -0,0 +1,18 @@
record:
type: ApplicationDeploymentRequest
version: "1.0.0"
name: snowballtools-base-frontend@0.1.8
application: crn://snowballtools/applications/snowballtools-base-frontend@0.1.8
dns: dashboard
config:
env:
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_templaterepo: snowball-tools/test-progressive-web-app
LACONIC_HOSTED_CONFIG_app_github_pwa_templaterepo: snowball-tools/test-progressive-web-app
LACONIC_HOSTED_CONFIG_app_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_app_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
meta:
note: Added by Snowball @ Thu Apr 4 14:49:41 UTC 2024
repository: "https://git.vdb.to/cerc-io/snowballtools-base"
repository_ref: 351db16336eacc3e1f9119ceb8d1282b8e27a27e
@@ -0,0 +1,8 @@
record:
type: ApplicationRecord
version: 0.0.2
repository_ref: 351db16336eacc3e1f9119ceb8d1282b8e27a27e
repository: ["https://git.vdb.to/cerc-io/snowballtools-base"]
app_type: webapp
name: snowballtools-base-frontend
app_version: 0.1.8
@@ -0,0 +1,24 @@
record:
type: ApplicationDeploymentRequest
version: '1.0.0'
name: staging-snowballtools-base-frontend@0.0.0
application: crn://staging-snowballtools/applications/staging-snowballtools-base-frontend@0.0.0
dns: dashboard.staging.apps.snowballtools.com
config:
env:
LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com
LACONIC_HOSTED_CONFIG_github_clientid: Ov23liOaoahRTYd4nSCV
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_image_upload_templaterepo: snowball-tools/image-upload-pwa-example
LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2
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_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073
LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com
LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com
LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591
meta:
note: Added by Snowball @ Sat Jun 22 02:49:45 UTC 2024
repository: "https://git.vdb.to/cerc-io/snowballtools-base"
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601
@@ -0,0 +1,8 @@
record:
type: ApplicationRecord
version: 0.0.1
repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601
repository: ["https://git.vdb.to/cerc-io/snowballtools-base"]
app_type: webapp
name: staging-snowballtools-base-frontend
app_version: 0.0.0
+23
View File
@@ -0,0 +1,23 @@
# deployer test
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)
- Config used: [../config.yml](../config.yml)
- 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
- Fetch the latest version of `deployment-test-app` from registry and increment `ApplicationRecord` version
- Publish the resulting `ApplicationRecord` record
- Set names to the record and check name resolution
- Publish the `ApplicationDeploymentRequest` record
- Check that the deployment occurs
- Check that a `ApplicationDeploymentRecord` is created
- Check that the deployment record has correct `ApplicationRecord` id
- Check that the URL present in deployment record is active
- Create and publish a [`ApplicationDeploymentRemovalRequest`](./records/application-deployment-removal-request.yml) record
- Check that the deployment is removed
- Check that a `ApplicationDeploymentRemovalRecord` is created
- Check that the deployment URL goes down
- The test script is run in a GitHub CI [workflow](../../../.github/workflows/test-app-deployment.yaml) that:
- Is scheduled to run everyday on the default (`main`) branch or can be triggered manually
- Sends Slack alerts to configured channels on failure
@@ -0,0 +1,4 @@
record:
deployment: <APPLICATION_DEPLOYMENT_RECORD_ID>
type: ApplicationDeploymentRemovalRequest
version: 1.0.0
@@ -0,0 +1,15 @@
record:
type: ApplicationDeploymentRequest
version: "1.0.0"
name: deployment-test-app@0.1.24
application: crn://snowballtools/applications/deployment-test-app@0.1.24
dns: deployment-ci-test
config:
env:
CERC_TEST_WEBAPP_CONFIG1: "deployment test config 1"
CERC_TEST_WEBAPP_CONFIG2: "deployment test config 2"
CERC_WEBAPP_DEBUG: 0
meta:
note: Deployment test @ Thu 11 Apr 2024 07:29:19 AM UTC
repository: "https://github.com/snowball-tools/test-progressive-web-app"
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
@@ -0,0 +1,8 @@
record:
type: ApplicationRecord
version: 0.0.1
repository_ref: 05819619487a0d2dbc5453b6d1ccff3044c0dd26
repository: ["https://github.com/snowball-tools/test-progressive-web-app"]
app_type: webapp
name: deployment-test-app
app_version: 0.1.24
@@ -0,0 +1,225 @@
#!/bin/bash
# Repository URL
REPO_URL="https://github.com/snowball-tools/test-progressive-web-app"
# Get the latest commit hash from the repository
LATEST_HASH=$(git ls-remote $REPO_URL HEAD | awk '{print $1}')
# Fetch the package.json file content
# Extract version from package.json content
package_json=$(wget -qO- "$REPO_URL/raw/$LATEST_HASH/package.json")
PACKAGE_VERSION=$(echo "$package_json" | jq -r '.version')
# Current date and time for note
CURRENT_DATE_TIME=$(date -u)
CONFIG_FILE=packages/deployer/config.yml
REGISTRY_BOND_ID="99c0e9aec0ac1b8187faa579be3b54f93fafb6060ac1fd29170b860df605be32"
# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts
APP_NAME=deployment-test-app
# Get latest version from registry and increment application-record version
NEW_APPLICATION_VERSION=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "$APP_NAME" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}')
if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; then
# Set application-record version if no previous records were found
NEW_APPLICATION_VERSION=0.0.1
fi
# Generate application-record.yml with incremented version
RECORD_FILE=packages/deployer/test/records/application-record.yml
cat >$RECORD_FILE <<EOF
record:
type: ApplicationRecord
version: $NEW_APPLICATION_VERSION
repository_ref: $LATEST_HASH
repository: ["$REPO_URL"]
app_type: webapp
name: $APP_NAME
app_version: $PACKAGE_VERSION
EOF
# Generate application-deployment-request.yml
REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-request.yml
cat >$REQUEST_RECORD_FILE <<EOF
record:
type: ApplicationDeploymentRequest
version: '1.0.0'
name: $APP_NAME@$PACKAGE_VERSION
application: crn://snowballtools/applications/$APP_NAME@$PACKAGE_VERSION
dns: deployment-ci-test
config:
env:
CERC_TEST_WEBAPP_CONFIG1: "deployment test config 1"
CERC_TEST_WEBAPP_CONFIG2: "deployment test config 2"
CERC_WEBAPP_DEBUG: 0
meta:
note: Deployment test @ $CURRENT_DATE_TIME
repository: "$REPO_URL"
repository_ref: $LATEST_HASH
EOF
echo "Record files generated successfully."
# Publish ApplicationRecord
RECORD_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE | jq -r '.id')
echo "ApplicationRecord published"
echo $RECORD_ID
# Set name to record
REGISTRY_APP_CRN="crn://snowballtools/applications/$APP_NAME"
sleep 2
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${PACKAGE_VERSION}" "$RECORD_ID"
sleep 2
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN@${LATEST_HASH}" "$RECORD_ID"
sleep 2
# Set name if latest release
yarn --silent laconic -c $CONFIG_FILE cns name set "$REGISTRY_APP_CRN" "$RECORD_ID"
echo "$REGISTRY_APP_CRN set for ApplicationRecord"
# Check if record exists for REGISTRY_APP_CRN
APP_RECORD=$(yarn --silent laconic -c $CONFIG_FILE cns name resolve "$REGISTRY_APP_CRN" | jq '.[0]')
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
echo "No record found for $REGISTRY_APP_CRN."
exit 1
fi
sleep 2
DEPLOYMENT_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $REQUEST_RECORD_FILE | jq -r '.id')
echo "ApplicationDeploymentRequest published"
echo $DEPLOYMENT_REQUEST_ID
# Deployment checks
RETRY_INTERVAL=30
MAX_RETRIES=20
# Check that a ApplicationDeploymentRecord is published
retry_count=0
while true; do
deployment_records_response=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationDeploymentRecord --all --name "$APP_NAME" request $DEPLOYMENT_REQUEST_ID)
len_deployment_records=$(echo $deployment_records_response | jq 'length')
# Check if number of records returned is 0
if [ $len_deployment_records -eq 0 ]; then
# Check if retries are exhausted
if [ $retry_count -eq $MAX_RETRIES ]; then
echo "Retries exhausted"
echo "ApplicationDeploymentRecord for deployment request $DEPLOYMENT_REQUEST_ID not found, exiting"
exit 1
else
echo "ApplicationDeploymentRecord not found, retrying in $RETRY_INTERVAL sec..."
sleep $RETRY_INTERVAL
retry_count=$((retry_count + 1))
fi
else
echo "ApplicationDeploymentRecord found"
break
fi
done
DEPLOYMENT_RECORD_ID=$(echo $deployment_records_response | jq -r '.[0].id')
echo $DEPLOYMENT_RECORD_ID
# Check if ApplicationDeploymentRecord has the correct record id
fetched_application_record_id=$(echo $deployment_records_response | jq -r '.[0].attributes.application')
if [ "$fetched_application_record_id" = "$RECORD_ID" ]; then
echo "ApplicationRecord id matched"
else
echo "ApplicationRecord id does not match, expected: $RECORD_ID, received: $fetched_application_record_id"
exit 1
fi
# Check if the url present in ApplicationDeploymentRecord is active
fetched_url=$(echo $deployment_records_response | jq -r '.[0].attributes.url')
retry_count=0
max_retries=10
retry_interval=5
while true; do
url_response=$(curl -s -o /dev/null -I -w "%{http_code}" $fetched_url)
if [ "$url_response" = "200" ]; then
echo "Deployment URL $fetched_url is active"
break
else
if [ $retry_count -eq $max_retries ]; then
echo "Retries exhausted"
echo "Deployment URL $fetched_url is not active, exiting"
exit 1
else
echo "Deployment URL $fetched_url is not active, received code $url_response, retrying in $retry_interval sec..."
sleep $retry_interval
retry_count=$((retry_count + 1))
fi
fi
done
# Generate application-deployment-removal-request.yml
REMOVAL_REQUEST_RECORD_FILE=packages/deployer/test/records/application-deployment-removal-request.yml
cat >$REMOVAL_REQUEST_RECORD_FILE <<EOF
record:
deployment: $DEPLOYMENT_RECORD_ID
type: ApplicationDeploymentRemovalRequest
version: 1.0.0
EOF
sleep 2
REMOVAL_REQUEST_ID=$(yarn --silent laconic -c $CONFIG_FILE cns record publish --filename $REMOVAL_REQUEST_RECORD_FILE | jq -r '.id')
echo "ApplicationDeploymentRemovalRequest published"
echo $REMOVAL_REQUEST_ID
# Check that an ApplicationDeploymentRemovalRecord is published
retry_count=0
while true; do
removal_records_response=$(yarn --silent laconic -c $CONFIG_FILE cns record list --type ApplicationDeploymentRemovalRecord --all request $REMOVAL_REQUEST_ID)
len_removal_records=$(echo $removal_records_response | jq 'length')
# Check if number of records returned is 0
if [ $len_removal_records -eq 0 ]; then
# Check if retries are exhausted
if [ $retry_count -eq $MAX_RETRIES ]; then
echo "Retries exhausted"
echo "ApplicationDeploymentRemovalRecord for deployment removal request $REMOVAL_REQUEST_ID not found"
exit 1
else
echo "ApplicationDeploymentRemovalRecord not found, retrying in $RETRY_INTERVAL sec..."
sleep $RETRY_INTERVAL
retry_count=$((retry_count + 1))
fi
else
echo "ApplicationDeploymentRemovalRecord found"
REMOVAL_RECORD_ID=$(echo $removal_records_response | jq -r '.[0].id')
echo $REMOVAL_RECORD_ID
break
fi
done
# Check if the application url is down after deployment removal
retry_count=0
max_retries=10
retry_interval=5
while true; do
url_response=$(curl -s -o /dev/null -I -w "%{http_code}" $fetched_url)
if [ "$url_response" = "404" ]; then
echo "Deployment URL $fetched_url is down"
break
else
if [ $retry_count -eq $max_retries ]; then
echo "Retries exhausted"
echo "Deployment URL $fetched_url is still active, exiting"
exit 1
else
echo "Deployment URL $fetched_url is still active, received code $url_response, retrying in $retry_interval sec..."
sleep $retry_interval
retry_count=$((retry_count + 1))
fi
fi
done
echo "Test successful"
-4
View File
@@ -1,4 +0,0 @@
REACT_APP_GQL_SERVER_URL = 'http://localhost:8000/graphql'
REACT_APP_GITHUB_CLIENT_ID =
REACT_APP_GITHUB_TEMPLATE_REPO =
+17
View File
@@ -0,0 +1,17 @@
VITE_SERVER_URL='http://localhost:8000'
VITE_GITHUB_CLIENT_ID=
VITE_GITHUB_PWA_TEMPLATE_REPO="snowball-tools/test-progressive-web-app"
VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO="snowball-tools/image-upload-pwa-example"
VITE_WALLET_CONNECT_ID=
VITE_LIT_RELAY_API_KEY=
VITE_ALCHEMY_API_KEY=
VITE_BUGSNAG_API_KEY=
VITE_PASSKEY_WALLET_RPID=
VITE_TURNKEY_API_BASE_URL=
VITE_TURNKEY_ORGANIZATION_ID=
-1
View File
@@ -1 +0,0 @@
build
+19
View File
@@ -0,0 +1,19 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
};
-20
View File
@@ -1,20 +0,0 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 13,
"sourceType": "module"
},
"env": {
"browser": true,
"es2021": true
},
"plugins": ["react", "@typescript-eslint"],
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
]
}
+2
View File
@@ -13,6 +13,7 @@
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
@@ -21,3 +22,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*storybook.log
+1
View File
@@ -0,0 +1 @@
v20.12.1
+1 -3
View File
@@ -1,3 +1 @@
# artifacts
build
coverage
dist/
+33
View File
@@ -0,0 +1,33 @@
import type { StorybookConfig } from '@storybook/react-vite';
import { join, dirname } from 'path';
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, 'package.json')));
}
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
getAbsolutePath('@storybook/addon-onboarding'),
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@chromatic-com/storybook'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('storybook-addon-remix-react-router'),
],
framework: {
name: getAbsolutePath('@storybook/react-vite'),
options: {},
},
docs: {
autodocs: 'tag',
},
staticDirs: ['../public'],
};
export default config;
+16
View File
@@ -0,0 +1,16 @@
import type { Preview } from '@storybook/react';
import '../src/index.css';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
-39
View File
@@ -1,39 +0,0 @@
{
// eslint extension options
"eslint.enable": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"css.customData": [".vscode/tailwind.json"],
// prettier extension setting
"editor.formatOnSave": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.rulers": [80],
"editor.codeActionsOnSave": [
"source.addMissingImports",
"source.fixAll",
"source.organizeImports"
],
// Show in vscode "Problems" tab when there are errors
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
// Use absolute import for typescript files
"typescript.preferences.importModuleSpecifier": "non-relative",
// IntelliSense for taiwind variants
"tailwindCSS.experimental.classRegex": [
["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}
+43 -26
View File
@@ -1,46 +1,63 @@
# Getting Started with Create React App
# frontend
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app), using [typescript-tailwindcss-eslint-prettier](https://github.com/cufarvid/cra-templates) template.
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).
## Available Scripts
## Getting Started
In the project directory, you can run:
### Install dependencies
### `yarn start`
In the root of the project, run:
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
```zsh
yarn
```
The page will reload if you make edits.\
You will also see any lint errors in the console.
### Build backend
### `yarn test`
```zsh
yarn build --ignore frontend
```
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### Environment variables
### `yarn build`
#### Local
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
Copy the `.env.example` file to `.env`:
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
```zsh
cp .env.example .env
```
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
#### Staging environment variables
### `yarn eject`
Change in [deployer/deploy-frontend.staging.sh](/packages/deployer/deploy-frontend.staging.sh)
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
#### Production environment variables
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Change in [deployer/deploy-frontend.sh](/packages/deployer/deploy-frontend.sh)
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
### Run development server
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
```zsh
yarn dev
```
## Learn More
## Deployment
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
From the root of the project, run:
To learn React, check out the [React documentation](https://reactjs.org/).
### 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)
+4
View File
@@ -0,0 +1,4 @@
{
"projectId": "Project:663d04870db27ed66a48e466",
"zip": true
}
+22
View File
@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="snowball tools dashboard" />
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="msapplication-TileColor" content="#2d89ef" />
<meta name="theme-color" content="#ffffff" />
<link rel="manifest" href="/manifest.json" />
<title>Snowball</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
+69 -47
View File
@@ -1,26 +1,58 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"build": "vite build",
"lint": "tsc --noEmit",
"preview": "vite preview",
"format": "prettier --write .",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@bugsnag/browser-performance": "^2.4.1",
"@bugsnag/js": "^7.22.7",
"@bugsnag/plugin-react": "^7.22.7",
"@fontsource-variable/jetbrains-mono": "^5.0.19",
"@fontsource/inter": "^5.0.16",
"@material-tailwind/react": "^2.1.7",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.7",
"@snowballtools/auth": "^0.2.0",
"@snowballtools/auth-lit": "^0.2.0",
"@snowballtools/js-sdk": "^0.1.1",
"@snowballtools/link-lit-alchemy-light": "^0.2.0",
"@snowballtools/material-tailwind-react-fork": "^2.1.10",
"@snowballtools/smartwallet-alchemy-light": "^0.2.0",
"@snowballtools/types": "^0.2.0",
"@snowballtools/utils": "^0.1.1",
"@tanstack/react-query": "^5.22.2",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.68",
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"@turnkey/http": "^2.10.0",
"@turnkey/sdk-react": "^0.1.0",
"@turnkey/webauthn-stamper": "^0.5.0",
"@walletconnect/ethereum-provider": "^2.12.2",
"@web3modal/siwe": "^4.0.5",
"@web3modal/wagmi": "^4.0.5",
"assert": "^2.1.0",
"axios": "^1.6.7",
"clsx": "^2.1.0",
"date-fns": "^3.3.1",
"downshift": "^8.2.3",
"eslint-config-react-app": "^7.0.1",
"downshift": "^8.3.2",
"framer-motion": "^11.0.8",
"gql-client": "^1.0.0",
"lottie-react": "^2.4.0",
"luxon": "^3.4.4",
"octokit": "^3.1.2",
"react": "^18.2.0",
@@ -33,51 +65,41 @@
"react-hot-toast": "^2.4.1",
"react-oauth-popup": "^1.0.5",
"react-router-dom": "^6.20.1",
"react-scripts": "5.0.1",
"react-timer-hook": "^3.0.7",
"siwe": "^2.1.4",
"tailwind-variants": "^0.2.0",
"typescript": "^4.9.5",
"usehooks-ts": "^2.10.0",
"vertical-stepper-nav": "^1.0.2",
"usehooks-ts": "^2.15.1",
"uuid": "^9.0.1",
"viem": "^2.7.11",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint ."
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@chromatic-com/storybook": "^1.3.3",
"@storybook/addon-essentials": "^8.0.10",
"@storybook/addon-interactions": "^8.0.10",
"@storybook/addon-links": "^8.0.10",
"@storybook/addon-onboarding": "^8.0.10",
"@storybook/blocks": "^8.0.10",
"@storybook/react": "^8.0.10",
"@storybook/react-vite": "^8.0.10",
"@storybook/test": "^8.0.10",
"@types/jest": "^27.5.2",
"@types/lodash": "^4.17.0",
"@types/luxon": "^3.3.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-react": "^7.33.2",
"@types/node": "^16.18.68",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/uuid": "^9.0.8",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"chromatic": "^11.3.2",
"eslint-plugin-storybook": "^0.8.0",
"postcss": "^8.4.38",
"prettier": "^3.1.0",
"tailwindcss": "^3.4.1"
"storybook": "^8.0.10",
"storybook-addon-remix-react-router": "^3.0.0",
"tailwindcss": "^3.4.3",
"typescript": "^5.3.3",
"vite": "^5.2.0"
}
}
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#2d89ef</TileColor>
</tile>
</msapplication>
</browserconfig>
@@ -0,0 +1,3 @@
<svg width="197" height="2" viewBox="0 0 197 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0.5" y1="1.19141" x2="197" y2="1.19141" stroke="#94A7B8" stroke-dasharray="1 12"/>
</svg>

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

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