├── .DS_Store ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── scripts │ ├── create_release_manifest.py │ ├── get_released_apps.py │ ├── update_helm_chart.py │ └── update_package_json.py └── workflows │ ├── build_and_push.yaml │ └── release.yaml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── PR_TEMPLATE.md ├── README.md ├── agnost.svg ├── k8s ├── monitor.yaml ├── package-lock.json ├── platform.yaml ├── registry.yaml ├── skaffold.yaml ├── sync.yaml └── webhook.yaml ├── monitor ├── .dockerignore ├── Dockerfile ├── Dockerfile.dev ├── config │ ├── default.json │ ├── development.json │ ├── errorCodes.js │ └── production.json ├── eslint.config.js ├── handler │ ├── deleteUnusedImagesInRegistry.js │ ├── monitorAccessTokens.js │ ├── monitorContainers.js │ └── watchTaskRuns.js ├── init │ ├── db.js │ └── logger.js ├── middlewares │ ├── logRequest.js │ └── undefinedPaths.js ├── package-lock.json ├── package.json ├── routes │ └── system.js ├── server.js └── util │ └── helper.js ├── platform ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Dockerfile.dev ├── config │ ├── clusterContainers.js │ ├── constants.js │ ├── default.json │ ├── development.json │ ├── errorCodes.js │ ├── production.json │ └── timezones.js ├── controllers │ ├── audit.js │ ├── auth.js │ ├── base.js │ ├── cluster.js │ ├── container.js │ ├── domain.js │ ├── environment.js │ ├── gitProvider.js │ ├── organization.js │ ├── organizationInvitation.js │ ├── organizationMember.js │ ├── project.js │ ├── projectInvitation.js │ ├── registry.js │ ├── tcpProxyPort.js │ └── user.js ├── eslint.config.js ├── handlers │ ├── certificate.js │ ├── cluster.js │ ├── cronjob.js │ ├── deployment.js │ ├── git.js │ ├── hpa.js │ ├── ingress.js │ ├── k8s.js │ ├── manifests │ │ ├── bitbucket-pipeline.yaml │ │ ├── cronjob.yaml │ │ ├── deployment.yaml │ │ ├── github-pipeline.yaml │ │ ├── gitlab-pipeline.yaml │ │ ├── hpa.yaml │ │ ├── namespace.yaml │ │ ├── pvc.yaml │ │ ├── service.yaml │ │ └── statefulset.yaml │ ├── ns.js │ ├── pvc.js │ ├── service.js │ ├── statefulset.js │ ├── status.js │ ├── tcpproxy.js │ ├── tekton.js │ ├── templates │ │ ├── index.js │ │ ├── manifests │ │ │ ├── mariadbv1.0.yaml │ │ │ ├── memcachedv1.0.yaml │ │ │ ├── miniov1.0.yaml │ │ │ ├── mongodbv1.0.yaml │ │ │ ├── mysqlv1.0.yaml │ │ │ ├── postgresqlv1.0.yaml │ │ │ └── redisv1.0.yaml │ │ └── middlewares.js │ ├── usage.js │ └── util.js ├── init │ ├── cache.js │ ├── db.js │ ├── logger.js │ ├── storage.js │ └── sync.js ├── middlewares │ ├── authMasterToken.js │ ├── authSession.js │ ├── authorizeOrgAction.js │ ├── authorizeProjectAction.js │ ├── checkClusterSetupStatus.js │ ├── contentType.js │ ├── handleFile.js │ ├── logRequest.js │ ├── rateLimiter.js │ ├── undefinedPaths.js │ ├── validate.js │ ├── validateCluster.js │ ├── validateClusterIPs.js │ ├── validateContainer.js │ ├── validateEnvironment.js │ ├── validateGitProvider.js │ ├── validateOrg.js │ └── validateProject.js ├── package-lock.json ├── package.json ├── routes │ ├── auth.js │ ├── cluster.js │ ├── container.js │ ├── environment.js │ ├── git.js │ ├── log.js │ ├── organization.js │ ├── organizationInvites.js │ ├── organizationTeam.js │ ├── project.js │ ├── projectInvites.js │ ├── projectTeam.js │ ├── registry.js │ ├── storage.js │ ├── system.js │ ├── telemetry.js │ ├── types.js │ └── user.js ├── schemas │ ├── audit.js │ ├── cluster.js │ ├── container.js │ ├── domain.js │ ├── environment.js │ ├── gitProvider.js │ ├── organization.js │ ├── organizationInvitation.js │ ├── organizationMember.js │ ├── project.js │ ├── projectInvitation.js │ ├── registry.js │ ├── rules │ │ ├── checks.js │ │ ├── cronJob.js │ │ ├── deployment.js │ │ └── statefulSet.js │ ├── tcpProxyPort.js │ └── user.js ├── server.js └── util │ └── helper.js ├── releases ├── .gitkeep ├── latest.json ├── v0.0.10.json ├── v0.0.11.json ├── v0.0.12.json ├── v0.0.13.json ├── v0.0.14.json ├── v0.0.15.json ├── v0.0.16.json ├── v0.0.17.json ├── v0.0.18.json ├── v0.0.19.json ├── v0.0.2.json ├── v0.0.20.json ├── v0.0.21.json ├── v0.0.22.json ├── v0.0.23.json ├── v0.0.24.json ├── v0.0.25.json ├── v0.0.26.json ├── v0.0.27.json ├── v0.0.28.json ├── v0.0.29.json ├── v0.0.3.json ├── v0.0.30.json ├── v0.0.31.json ├── v0.0.32.json ├── v0.0.33.json ├── v0.0.34.json ├── v0.0.35.json ├── v0.0.36.json ├── v0.0.37.json ├── v0.0.38.json ├── v0.0.39.json ├── v0.0.4.json ├── v0.0.40.json ├── v0.0.41.json ├── v0.0.42.json ├── v0.0.43.json ├── v0.0.44.json ├── v0.0.45.json ├── v0.0.46.json ├── v0.0.47.json ├── v0.0.48.json ├── v0.0.5.json ├── v0.0.6.json ├── v0.0.7.json ├── v0.0.8.json ├── v0.0.9.json ├── v1.0.0.json ├── v1.0.1.json ├── v1.0.10.json ├── v1.0.2.json ├── v1.0.3.json ├── v1.0.4.json ├── v1.0.5.json ├── v1.0.6.json ├── v1.0.7.json ├── v1.0.8.json └── v1.0.9.json ├── studio ├── .Dockerignore ├── .gitignore ├── .prettierrc ├── Dockerfile ├── Dockerfile.dev ├── biome.json ├── index.html ├── nginx │ └── default.conf ├── package-lock.json ├── package.json ├── postcss.config.js ├── src │ ├── App.tsx │ ├── assets │ │ ├── browserconfig.xml │ │ ├── images │ │ │ ├── agnost-dark-bg-logo.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── ms-icon-310x310.png │ │ │ ├── mstile-150x150.png │ │ │ └── safari-pinned-tab.svg │ │ ├── manifest.json │ │ └── site.webmanifest │ ├── components │ │ ├── Accordion │ │ │ ├── Accordion.tsx │ │ │ └── index.ts │ │ ├── ActionsCell │ │ │ ├── ActionsCell.tsx │ │ │ └── index.ts │ │ ├── Alert │ │ │ ├── Alert.tsx │ │ │ ├── Feedback.tsx │ │ │ ├── alert.scss │ │ │ └── index.ts │ │ ├── AuthUserAvatar │ │ │ ├── AuthUserAvatar.tsx │ │ │ └── index.ts │ │ ├── Avatar │ │ │ ├── Avatar.tsx │ │ │ ├── avatar.scss │ │ │ └── index.ts │ │ ├── Badge │ │ │ ├── Badge.tsx │ │ │ ├── badge.scss │ │ │ └── index.ts │ │ ├── Button │ │ │ ├── Button.scss │ │ │ ├── Button.tsx │ │ │ ├── ButtonGroup.tsx │ │ │ └── index.ts │ │ ├── ChangeAvatar │ │ │ ├── ChangeAvatar.tsx │ │ │ ├── changeAvatar.scss │ │ │ └── index.ts │ │ ├── ChangeNameForm │ │ │ ├── ChangeNameForm.tsx │ │ │ └── index.ts │ │ ├── Checkbox │ │ │ ├── Checkbox.tsx │ │ │ ├── checkbox.scss │ │ │ └── index.ts │ │ ├── ConfirmationModal │ │ │ ├── ConfirmationModal.tsx │ │ │ ├── confirmationModal.scss │ │ │ └── index.ts │ │ ├── CopyButton │ │ │ ├── CopyButton.tsx │ │ │ └── index.ts │ │ ├── CopyInput │ │ │ ├── CopyInput.tsx │ │ │ ├── copyInput.scss │ │ │ └── index.ts │ │ ├── DataTable │ │ │ ├── DataTable.tsx │ │ │ ├── SortButton.tsx │ │ │ ├── index.ts │ │ │ └── sortButton.scss │ │ ├── DateRangePicker │ │ │ ├── DateRangePicker.tsx │ │ │ └── index.ts │ │ ├── DateText │ │ │ ├── DateText.tsx │ │ │ └── index.ts │ │ ├── Description │ │ │ ├── Description.scss │ │ │ ├── Description.tsx │ │ │ └── index.ts │ │ ├── Dialog │ │ │ ├── Dialog.tsx │ │ │ ├── dialog.scss │ │ │ └── index.ts │ │ ├── Drawer │ │ │ ├── Drawer.tsx │ │ │ ├── drawer.scss │ │ │ └── index.ts │ │ ├── Dropdown │ │ │ ├── Dropdown.tsx │ │ │ ├── dropdown.scss │ │ │ └── index.ts │ │ ├── EmptyState │ │ │ ├── EmptyState.tsx │ │ │ └── index.ts │ │ ├── Error │ │ │ ├── Error.tsx │ │ │ ├── NotFound.tsx │ │ │ └── index.ts │ │ ├── Form │ │ │ ├── Form.tsx │ │ │ ├── form.scss │ │ │ └── index.ts │ │ ├── Header │ │ │ ├── Feedback.tsx │ │ │ ├── Header.tsx │ │ │ ├── header.scss │ │ │ └── index.ts │ │ ├── InfoModal │ │ │ ├── InfoModal.tsx │ │ │ ├── index.ts │ │ │ └── infoModal.scss │ │ ├── Input │ │ │ ├── Input.tsx │ │ │ ├── Textarea.tsx │ │ │ ├── index.ts │ │ │ └── input.scss │ │ ├── InviteMemberForm │ │ │ ├── InviteMemberForm.tsx │ │ │ ├── index.ts │ │ │ └── inviteMemberForm.scss │ │ ├── Label │ │ │ ├── Label.tsx │ │ │ ├── index.ts │ │ │ └── label.scss │ │ ├── Loading │ │ │ ├── Loading.tsx │ │ │ └── index.ts │ │ ├── LogViewer │ │ │ ├── LogViewer.tsx │ │ │ └── index.ts │ │ ├── PasswordInput │ │ │ ├── PasswordInput.tsx │ │ │ ├── index.ts │ │ │ └── password-input.scss │ │ ├── Popover │ │ │ ├── Popover.tsx │ │ │ ├── index.ts │ │ │ └── popover.scss │ │ ├── RadioGroup │ │ │ ├── RadioGroup.tsx │ │ │ └── index.ts │ │ ├── RoleDropdown │ │ │ ├── RoleDropdown.tsx │ │ │ ├── RoleSelect.tsx │ │ │ └── index.ts │ │ ├── SearchInput │ │ │ ├── SearchInput.tsx │ │ │ ├── index.ts │ │ │ └── searchInput.scss │ │ ├── Select │ │ │ ├── Select.tsx │ │ │ ├── index.ts │ │ │ └── select.scss │ │ ├── SelectionDropdown │ │ │ ├── SelectionDropdown.tsx │ │ │ └── index.ts │ │ ├── Separator │ │ │ ├── Separator.tsx │ │ │ └── index.ts │ │ ├── SettingsFormItem │ │ │ ├── SettingsFormItem.tsx │ │ │ ├── index.ts │ │ │ └── settingsFormItem.scss │ │ ├── Switch │ │ │ ├── Switch.tsx │ │ │ ├── index.ts │ │ │ └── switch.scss │ │ ├── Table │ │ │ ├── SelectedRowButton.tsx │ │ │ ├── Table.tsx │ │ │ ├── TableConfirmation.tsx │ │ │ ├── index.ts │ │ │ └── table.scss │ │ ├── Toast │ │ │ ├── Toast.tsx │ │ │ ├── Toaster.tsx │ │ │ └── index.ts │ │ ├── Tooltip │ │ │ ├── Tooltip.tsx │ │ │ └── index.ts │ │ ├── TransferOwnership │ │ │ ├── TransferOwnership.tsx │ │ │ └── index.ts │ │ └── icons │ │ │ ├── 401.tsx │ │ │ ├── 404.tsx │ │ │ ├── Agnost.tsx │ │ │ ├── Awss3.tsx │ │ │ ├── AzureBlobStorage.tsx │ │ │ ├── Bitbucket.tsx │ │ │ ├── Docker.tsx │ │ │ ├── Error.tsx │ │ │ ├── ErrorPage.tsx │ │ │ ├── GcpStorage.tsx │ │ │ ├── GitLab.tsx │ │ │ ├── Github.tsx │ │ │ ├── Kafka.tsx │ │ │ ├── Kubernetes.tsx │ │ │ ├── Logo.tsx │ │ │ ├── MariaDb.tsx │ │ │ ├── Memcached.tsx │ │ │ ├── MinIo.tsx │ │ │ ├── MongoDb.tsx │ │ │ ├── MySql.tsx │ │ │ ├── Nodejs.tsx │ │ │ ├── PostgreSql.tsx │ │ │ ├── RabbitMq.tsx │ │ │ ├── React.tsx │ │ │ ├── Redis.tsx │ │ │ ├── Resource.tsx │ │ │ ├── SocketIo.tsx │ │ │ ├── SuccessCheck.tsx │ │ │ ├── Warning.tsx │ │ │ └── index.ts │ ├── constants │ │ ├── constants.ts │ │ ├── index.ts │ │ └── stateList.ts │ ├── features │ │ ├── auth │ │ │ ├── AcceptInvitation │ │ │ │ ├── AcceptInvitation.tsx │ │ │ │ └── index.ts │ │ │ ├── AuthUserDropdown │ │ │ │ ├── AuthUserDropdown.tsx │ │ │ │ └── index.ts │ │ │ ├── ChangeAvatar │ │ │ │ ├── ChangeAvatar.tsx │ │ │ │ └── index.ts │ │ │ ├── ChangeName │ │ │ │ ├── ChangeName.tsx │ │ │ │ ├── changeName.sass │ │ │ │ └── index.ts │ │ │ ├── DeleteAccount │ │ │ │ ├── DeleteAccount.tsx │ │ │ │ ├── deleteAccount.scss │ │ │ │ └── index.ts │ │ │ ├── Notifications │ │ │ │ ├── Filters │ │ │ │ │ ├── ActionFilter.tsx │ │ │ │ │ ├── DateFilter.tsx │ │ │ │ │ ├── EnvironmentFilter.tsx │ │ │ │ │ ├── OrganizationsFilter.tsx │ │ │ │ │ ├── ProjectFilter.tsx │ │ │ │ │ └── TeamMemberFilter.tsx │ │ │ │ ├── Notification.tsx │ │ │ │ ├── NotificationDropdown.tsx │ │ │ │ ├── NotificationFilter.tsx │ │ │ │ ├── NotificationItem.tsx │ │ │ │ ├── Notifications.tsx │ │ │ │ ├── index.ts │ │ │ │ └── notifications.scss │ │ │ ├── ProfileSettings │ │ │ │ ├── ProfileSettings.tsx │ │ │ │ └── index.ts │ │ │ ├── Providers │ │ │ │ └── Providers.tsx │ │ │ └── UserProviders │ │ │ │ ├── UserProviders.tsx │ │ │ │ └── index.ts │ │ ├── cluster │ │ │ ├── CustomDomain │ │ │ │ ├── CustomDomainForm.tsx │ │ │ │ ├── CustomDomains.tsx │ │ │ │ ├── DnsSettings.tsx │ │ │ │ ├── DomainList.tsx │ │ │ │ └── ReverseProxyURL.tsx │ │ │ ├── ReleaseColumns.tsx │ │ │ ├── ReleaseDropdown.tsx │ │ │ ├── ReleaseHistory.tsx │ │ │ ├── ReleaseHistoryColumns.tsx │ │ │ └── index.ts │ │ ├── container │ │ │ ├── ContainerColumns.tsx │ │ │ ├── CreateContainerButton.tsx │ │ │ ├── CreateContainerDrawer.tsx │ │ │ ├── CreateForms │ │ │ │ ├── CronJobFrom.tsx │ │ │ │ ├── DeploymentForm.tsx │ │ │ │ └── StatefulForm.tsx │ │ │ ├── DeleteContainer.tsx │ │ │ ├── EditContainer.tsx │ │ │ └── config │ │ │ │ ├── AutoScaleConfig.tsx │ │ │ │ ├── BuildLogs.tsx │ │ │ │ ├── Builds.tsx │ │ │ │ ├── ContainerFormLayout.tsx │ │ │ │ ├── CronExamples.tsx │ │ │ │ ├── Events.tsx │ │ │ │ ├── Logs.tsx │ │ │ │ ├── Networking.tsx │ │ │ │ ├── PodConfiguration.tsx │ │ │ │ ├── Pods.tsx │ │ │ │ ├── Probes.tsx │ │ │ │ ├── SourceConfig.tsx │ │ │ │ ├── StorageConfig.tsx │ │ │ │ ├── Variables.tsx │ │ │ │ └── index.ts │ │ ├── organization │ │ │ ├── OrganizationCreateButton.tsx │ │ │ ├── OrganizationCreateModal.tsx │ │ │ ├── OrganizationDropdown.tsx │ │ │ ├── OrganizationSettings.tsx │ │ │ ├── Settings │ │ │ │ ├── ChangeOrganizationAvatar.tsx │ │ │ │ ├── ChangeOrganizationName.tsx │ │ │ │ ├── DeleteOrganization.tsx │ │ │ │ ├── Members │ │ │ │ │ ├── InviteOrganization.tsx │ │ │ │ │ ├── OrganizationInvitationDrawer.tsx │ │ │ │ │ ├── OrganizationInvitations.tsx │ │ │ │ │ ├── OrganizationInvitationsColumns.tsx │ │ │ │ │ ├── OrganizationMembers.tsx │ │ │ │ │ ├── OrganizationMembersColumns.tsx │ │ │ │ │ └── OrganizationMembersTableHeader.tsx │ │ │ │ └── TransferOrganization.tsx │ │ │ ├── index.ts │ │ │ ├── navbar │ │ │ │ ├── OrganizationMenuItem.tsx │ │ │ │ └── organizationMenu.scss │ │ │ └── organization.scss │ │ ├── profile │ │ │ ├── ClusterManagement.tsx │ │ │ ├── ClusterManagementGeneral.tsx │ │ │ ├── ClusterManagementUsage.tsx │ │ │ └── TransferClusterOwnership.tsx │ │ └── projects │ │ │ ├── CreateEnvironment.tsx │ │ │ ├── CreateProject.tsx │ │ │ ├── DeleteProject.tsx │ │ │ ├── EditProject.tsx │ │ │ ├── EnvironmentDropdown.tsx │ │ │ ├── Environments.tsx │ │ │ ├── EnvironmentsColumns.tsx │ │ │ ├── ProjectActions.tsx │ │ │ ├── ProjectCard.tsx │ │ │ ├── ProjectColumns.tsx │ │ │ ├── ProjectGeneralSettings.tsx │ │ │ ├── ProjectInvitationFilter.tsx │ │ │ ├── ProjectInvitations.tsx │ │ │ ├── ProjectInviteMember.tsx │ │ │ ├── ProjectMembers.tsx │ │ │ ├── ProjectMembersColumns.tsx │ │ │ ├── ProjectSelectDropdown.tsx │ │ │ ├── ProjectSettings.tsx │ │ │ ├── ProjectTeam.tsx │ │ │ └── Settings │ │ │ ├── ChangeProjectAvatar.tsx │ │ │ ├── ChangeProjectName.tsx │ │ │ ├── ProjectInvitationsColumns.tsx │ │ │ └── TransferProject.tsx │ ├── helpers │ │ ├── axios.ts │ │ ├── componentLoader.ts │ │ ├── index.ts │ │ ├── realtime │ │ │ ├── Cluster.ts │ │ │ ├── Container.ts │ │ │ ├── Environment.ts │ │ │ ├── OrgMember.ts │ │ │ ├── Organization.ts │ │ │ ├── Project.ts │ │ │ ├── ProjectTeam.ts │ │ │ ├── RealtimeActions.ts │ │ │ ├── User.ts │ │ │ └── index.ts │ │ └── socket.ts │ ├── hooks │ │ ├── index.ts │ │ ├── useAuthorizeOrg.tsx │ │ ├── useAuthorizeProject.tsx │ │ ├── useDebounce.tsx │ │ ├── useEnvironmentDropdown.tsx │ │ ├── useRealtime.tsx │ │ ├── useSearch.tsx │ │ ├── useTable.tsx │ │ ├── useToast.tsx │ │ └── useUpdateEffect.tsx │ ├── i18n │ │ ├── config.ts │ │ └── en │ │ │ ├── cluster.json │ │ │ ├── container.json │ │ │ ├── forms.json │ │ │ ├── general.json │ │ │ ├── index.ts │ │ │ ├── login.json │ │ │ ├── onboarding.json │ │ │ ├── organization.json │ │ │ ├── profileSettings.json │ │ │ └── project.json │ ├── index.scss │ ├── layouts │ │ ├── AuthLayout │ │ │ ├── AuthLayout.tsx │ │ │ └── index.ts │ │ └── Layout │ │ │ ├── Layout.tsx │ │ │ └── index.ts │ ├── main.tsx │ ├── pages │ │ ├── auth │ │ │ ├── Login.tsx │ │ │ ├── OrgAcceptInvitation.tsx │ │ │ └── ProjectAcceptInvitation.tsx │ │ ├── environment │ │ │ ├── Environment.tsx │ │ │ └── EnvironmentContainers.tsx │ │ ├── errors │ │ │ ├── 401.tsx │ │ │ ├── 404.tsx │ │ │ └── ErrorBoundary.tsx │ │ ├── home │ │ │ ├── Health.tsx │ │ │ └── Home.tsx │ │ ├── notifications │ │ │ └── Notifications.tsx │ │ ├── onboarding │ │ │ ├── AccountSetup.tsx │ │ │ ├── Onboarding.tsx │ │ │ └── Register.tsx │ │ ├── organization │ │ │ ├── Organization.tsx │ │ │ ├── OrganizationDetails.tsx │ │ │ ├── OrganizationProjects.tsx │ │ │ ├── OrganizationSelect.tsx │ │ │ └── organization.scss │ │ ├── redirect-handle │ │ │ └── RedirectHandle.tsx │ │ └── root │ │ │ ├── Root.tsx │ │ │ └── index.ts │ ├── router │ │ ├── index.ts │ │ ├── loader │ │ │ ├── AuthLoader.ts │ │ │ ├── HomeLoader.ts │ │ │ └── OnboardingLoader.ts │ │ └── router.tsx │ ├── services │ │ ├── AuthService.ts │ │ ├── ClusterService.ts │ │ ├── ContainerService.ts │ │ ├── EnvironmentService.ts │ │ ├── NotificationService.ts │ │ ├── OrganizationService.ts │ │ ├── ProjectService.ts │ │ ├── TypesService.ts │ │ ├── UserService.ts │ │ └── index.ts │ ├── store │ │ ├── auth │ │ │ └── authStore.ts │ │ ├── cluster │ │ │ └── clusterStore.ts │ │ ├── container │ │ │ └── containerStore.ts │ │ ├── environment │ │ │ └── environmentStore.ts │ │ ├── notification │ │ │ └── notificationStore.ts │ │ ├── organization │ │ │ └── organizationStore.ts │ │ ├── project │ │ │ └── projectStore.ts │ │ ├── theme │ │ │ └── themeStore.ts │ │ └── types │ │ │ └── typeStore.ts │ ├── types │ │ ├── cluster.ts │ │ ├── container.ts │ │ ├── environment.ts │ │ ├── index.ts │ │ ├── organization.ts │ │ ├── project.ts │ │ ├── schema.ts │ │ └── type.ts │ ├── utils │ │ ├── index.ts │ │ ├── redirect.ts │ │ ├── time.ts │ │ └── utils.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── sync ├── .dockerignore ├── Dockerfile ├── Dockerfile.dev ├── config │ ├── default.json │ ├── development.json │ ├── errorCodes.js │ └── production.json ├── eslint.config.js ├── init │ ├── cache.js │ ├── logger.js │ └── sync.js ├── middlewares │ ├── logRequest.js │ ├── rateLimiter.js │ └── undefinedPaths.js ├── package-lock.json ├── package.json ├── routes │ └── system.js ├── server.js └── util │ └── helper.js └── webhook ├── Dockerfile ├── Dockerfile.dev ├── README.md ├── agnost.go ├── go.mod ├── go.sum ├── main.go └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/.DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, could you add screenshots to help explain your problem? 25 | 26 | **Suggested Fixes** 27 | "If you know of any possible way to resolve the issue, please let us know." 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. iOS] 31 | - Browser [e.g., chrome, safari] 32 | - Version [e.g. 22] 33 | 34 | **Smartphone (please complete the following information):** 35 | - Device: [e.g. iPhone6] 36 | - OS: [e.g. iOS8.1] 37 | - Browser [e.g. stock browser, safari] 38 | - Version [e.g. 22] 39 | 40 | **Additional context** 41 | Please feel free to add any other context about the problem here. 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/scripts/create_release_manifest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import os 4 | 5 | release_number = os.environ['RELEASE_NUMBER'] 6 | release_file = os.path.join('releases', release_number + '.json') 7 | latest_file = os.path.join('releases', 'latest.json') 8 | applications = ['monitor', 'platform', 'sync', 'webhook', 'studio'] 9 | 10 | new_release_dict = {} 11 | modules_dict = {} 12 | 13 | for app in applications: 14 | package_file = os.path.join(app, 'package.json') 15 | with open(package_file) as fp: 16 | package_info = json.load(fp) 17 | version = package_info['version'] 18 | app_name = app.replace('/', '-') 19 | modules_dict[app_name] = version 20 | 21 | new_release_dict["release"] = release_number 22 | new_release_dict["modules"] = modules_dict 23 | 24 | with open(release_file, 'w') as fp: 25 | fp.write(json.dumps(new_release_dict, indent=3)) 26 | fp.close() 27 | 28 | with open(latest_file, 'w') as fp: 29 | fp.write(json.dumps(new_release_dict, indent=3)) 30 | fp.close() 31 | -------------------------------------------------------------------------------- /.github/scripts/update_helm_chart.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import os 4 | from ruamel.yaml import YAML 5 | 6 | yaml=YAML() 7 | yaml.preserve_quotes = True 8 | values_yaml = "base/values.yaml" 9 | chart_yaml = "base/Chart.yaml" 10 | applications = json.loads(sys.argv[1]) 11 | 12 | ## Update values.yaml with the new image tags 13 | values_data = yaml.load(open(values_yaml).read()) 14 | 15 | for app in applications: 16 | app_name = app['application'] 17 | if app_name == 'webhook': 18 | values_data['agnost-webhook']['image']['tag'] = app['version'] 19 | else: 20 | values_data[app_name]['tag'] = app['version'] 21 | 22 | with open(values_yaml, 'w') as outfile: 23 | yaml.dump(values_data, outfile) 24 | 25 | ## Update Chart.yaml with a new version 26 | chart_data = yaml.load(open(chart_yaml).read()) 27 | command = 'semver next ' + os.environ['RELEASE_TYPE'] + ' ' + chart_data['version'] 28 | chart_data['version'] = os.popen(command).read().strip() 29 | 30 | chart_data['appVersion'] = os.environ['RELEASE_NUMBER'] 31 | 32 | with open(chart_yaml, 'w') as outfile: 33 | yaml.dump(chart_data, outfile) 34 | -------------------------------------------------------------------------------- /.github/scripts/update_package_json.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | import os 4 | 5 | applications = json.loads(sys.argv[1]) 6 | 7 | for app in applications: 8 | package_json_file = os.path.join('.', app['application'], 'package.json') 9 | with open(package_json_file) as fp: 10 | package_json = json.load(fp) 11 | 12 | package_json['version'] = app['version'] 13 | 14 | with open(package_json_file, 'w', encoding='utf8') as fp: 15 | fp.write(json.dumps(package_json, indent=3, ensure_ascii=False)) 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "all", 7 | "jsxSingleQuote": true, 8 | "bracketSpacing": true, 9 | "useTabs": true 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /PR_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | ## Description 4 | 5 | Please include a summary of the change and which issue is fixed. Include relevant motivation and context. List any dependencies that are required for this change. 6 | 7 | Fixes # (issue) 8 | 9 | ## Type of change 10 | 11 | Please delete options that are not relevant. 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] This change requires a documentation update 17 | 18 | ## How Has This Been Tested? 19 | 20 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. 21 | 22 | - [ ] Test A 23 | - [ ] Test B 24 | 25 | ## Checklist: 26 | 27 | - [ ] My code follows the style guidelines of this project 28 | - [ ] I have performed a self-review of my own code 29 | - [ ] I have commented my code, particularly in hard-to-understand areas 30 | - [ ] I have made corresponding changes to the documentation 31 | - [ ] My changes generate no new warnings 32 | - [ ] I have added tests that prove my fix is effective or that my feature works 33 | - [ ] New and existing unit tests pass locally with my changes 34 | - [ ] Any dependent changes have been merged and published in downstream modules 35 | -------------------------------------------------------------------------------- /k8s/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "k8s", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /k8s/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta14 2 | kind: Config 3 | build: 4 | artifacts: 5 | - image: cloudagnost/platform 6 | context: ../platform 7 | sync: 8 | infer: 9 | - '**/*.js' 10 | - '**/*.json' 11 | - '**/*.yaml' 12 | docker: 13 | dockerfile: Dockerfile.dev 14 | - image: cloudagnost/sync 15 | context: ../sync 16 | sync: 17 | infer: 18 | - '**/*.js' 19 | - '**/*.json' 20 | docker: 21 | dockerfile: Dockerfile.dev 22 | - image: cloudagnost/monitor 23 | context: ../monitor 24 | sync: 25 | infer: 26 | - '**/*.js' 27 | - '**/*.json' 28 | docker: 29 | dockerfile: Dockerfile.dev 30 | - image: cloudagnost/webhook 31 | context: ../webhook 32 | sync: 33 | infer: 34 | - '**/*.go' 35 | docker: 36 | dockerfile: Dockerfile.dev 37 | deploy: 38 | statusCheckDeadlineSeconds: 300 39 | kubectl: 40 | defaultNamespace: agnost 41 | manifests: 42 | - platform.yaml 43 | - monitor.yaml 44 | - sync.yaml 45 | - webhook.yaml 46 | 47 | -------------------------------------------------------------------------------- /k8s/sync.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sync 5 | namespace: agnost 6 | labels: 7 | app: sync 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: sync 13 | template: 14 | metadata: 15 | labels: 16 | app: sync 17 | spec: 18 | containers: 19 | - name: sync 20 | image: cloudagnost/sync:v4 21 | # We will be using the minikube docker daemon, since the actual docker daemon on local machine are different 22 | # Prevent minikube docker daemon to pull images from central docker hub set imagePullPolicy to Never, so that 23 | imagePullPolicy: Never 24 | ports: 25 | - containerPort: 4000 26 | env: 27 | - name: CACHE_HOSTNAME 28 | valueFrom: 29 | secretKeyRef: 30 | name: redis 31 | key: hostname 32 | - name: CACHE_PWD 33 | valueFrom: 34 | secretKeyRef: 35 | name: redis 36 | key: password 37 | - name: NAMESPACE 38 | value: agnost 39 | - name: RELEASE_NUMBER 40 | value: "v0.0.2" -------------------------------------------------------------------------------- /monitor/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | Dockerfile.dev 3 | package-lock.json 4 | eslint.config.js 5 | node_modules -------------------------------------------------------------------------------- /monitor/Dockerfile: -------------------------------------------------------------------------------- 1 | # Get the base node image from docker hub 2 | FROM node:20-alpine 3 | # Set the working directory in our docker image, anything that is copied from now on 4 | # from local machine to the image will be moved under '/app' directory in docker image 5 | WORKDIR '/app' 6 | # Copy package.json file from local machine to the image under '/app' directory 7 | COPY ./package.json ./ 8 | # Set the node environment variable 9 | ENV NODE_ENV production 10 | # Run npm install to install dependent modules 11 | RUN npm install --omit=dev 12 | # Copy everything to the image under '/app' directory 13 | COPY ./ ./ 14 | # This will be the start up command of the docker container, run the development react server 15 | CMD ["node", "--expose-gc", "server"] 16 | -------------------------------------------------------------------------------- /monitor/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Get the base node image from docker hub 2 | FROM node:20-alpine 3 | # Set the working directory in our docker image, anything that is copied from now on 4 | # from local machine to the image will be moved under '/app' directory in docker image 5 | WORKDIR '/app' 6 | # Copy package.json file from local machine to the image under '/app' directory 7 | COPY ./package.json ./ 8 | # Set the node environment variable 9 | ENV NODE_ENV development 10 | # Run npm install to install dependent modules 11 | RUN npm install -g nodemon 12 | RUN npm install --omit=dev 13 | # Copy everything to the image under '/app' directory 14 | COPY ./ ./ 15 | # This will be the start up command of the docker container, run the development react server 16 | CMD ["nodemon", "--expose-gc", "server"] 17 | 18 | -------------------------------------------------------------------------------- /monitor/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "host": "localhost", 4 | "port": 4000, 5 | "timeout": 3600000 6 | }, 7 | "general": { 8 | "monitoringInterval": 3000, 9 | "monitoringIntervalTokens": 600000, 10 | "monitoringIntervalRepositories": 3600000, 11 | "resourcePaginationSize": 100, 12 | "containerPaginationSize": 100, 13 | "gitProviderPaginationSize": 100, 14 | "gcSeconds": 30, 15 | "registry": { 16 | "maxImages": 5 17 | } 18 | }, 19 | "database": { 20 | "maxPoolSize": 3 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /monitor/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /monitor/config/errorCodes.js: -------------------------------------------------------------------------------- 1 | const ERROR_CODES = { 2 | internalServerError: "internal_server_error", 3 | missingAccessToken: "missing_access_token", 4 | invalidCredentials: "invalid_credentials", 5 | invalidContentType: "invalid_content_type", 6 | invalidRequestBody: "invalid_request_body", 7 | rateLimitExceeded: "rate_limit_exceeded", 8 | resourceNotFound: "resource_not_found", 9 | }; 10 | export default ERROR_CODES; 11 | -------------------------------------------------------------------------------- /monitor/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /monitor/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | 4 | export default [ 5 | { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, 6 | pluginJs.configs.recommended, 7 | ]; 8 | -------------------------------------------------------------------------------- /monitor/init/db.js: -------------------------------------------------------------------------------- 1 | import config from "config"; 2 | import mongo from "mongodb"; 3 | 4 | //MongoDB client 5 | var client; 6 | 7 | export const connectToDatabase = async () => { 8 | try { 9 | client = new mongo.MongoClient(process.env.CLUSTER_DB_URI, { 10 | maxPoolSize: config.get("database.maxPoolSize"), 11 | auth: { 12 | username: process.env.CLUSTER_DB_USER, 13 | password: process.env.CLUSTER_DB_PWD, 14 | }, 15 | }); 16 | //Connect to the database of the application 17 | await client.connect(); 18 | console.info(`Connected to the database ${process.env.CLUSTER_DB_URI}`); 19 | } catch (err) { 20 | console.error(`Cannot connect to the database. ${err}`); 21 | process.exit(1); 22 | } 23 | }; 24 | 25 | export const disconnectFromDatabase = async () => { 26 | await client.close(); 27 | console.info("Disconnected from the database"); 28 | }; 29 | 30 | export const getDBClient = () => { 31 | return client; 32 | }; 33 | -------------------------------------------------------------------------------- /monitor/middlewares/logRequest.js: -------------------------------------------------------------------------------- 1 | // Log requests to console 2 | export const logRequest = (req, res, time) => { 3 | if (req.originalUrl !== "/health") 4 | console.info( 5 | `${req.method} (${res.statusCode}) ${Math.round(time * 10) / 10}ms ${ 6 | req.originalUrl 7 | }` 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /monitor/middlewares/undefinedPaths.js: -------------------------------------------------------------------------------- 1 | import ERROR_CODES from "../config/errorCodes.js"; 2 | 3 | // Middleware to handle undefined paths or posts 4 | export const handleUndefinedPaths = (req, res) => { 5 | return res.status(404).json({ 6 | error: "Not Found", 7 | details: "The server can not find the requested resource.", 8 | code: ERROR_CODES.resourceNotFound, 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /monitor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "engine-core", 3 | "version": "v1.0.0", 4 | "description": "Engine core api server", 5 | "main": "server.js", 6 | "type": "module", 7 | "scripts": { 8 | "prod": "NODE_ENV=production node server", 9 | "dev": "NODE_ENV=development nodemon server" 10 | }, 11 | "author": "Ümit Çakmak", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@kubernetes/client-node": "0.18.1", 15 | "axios": "1.3.3", 16 | "config": "3.3.9", 17 | "crypto-js": "4.1.1", 18 | "express": "4.18.2", 19 | "mongodb": "5.1.0", 20 | "response-time": "2.3.2", 21 | "winston": "3.8.2", 22 | "winston-transport": "4.5.0" 23 | }, 24 | "devDependencies": { 25 | "@eslint/js": "^9.4.0", 26 | "eslint": "^9.4.0", 27 | "globals": "^15.3.0", 28 | "nodemon": "2.0.20" 29 | } 30 | } -------------------------------------------------------------------------------- /monitor/routes/system.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | 3 | const router = express.Router({ mergeParams: true }); 4 | 5 | /* 6 | @route /health 7 | @method GET 8 | @desc Checks liveliness of monitoring server 9 | @access public 10 | */ 11 | router.get("/health", (req, res) => { 12 | res 13 | .status(200) 14 | .send( 15 | new Date().toISOString() + 16 | " - Healthy monitoring server" + 17 | " - " + 18 | process.env.RELEASE_NUMBER 19 | ); 20 | }); 21 | 22 | /* 23 | @route /ping 24 | @method GET 25 | @desc Checks liveliness of monitoring server 26 | @access public 27 | */ 28 | router.get("/ping", (req, res) => { 29 | res.status(200).send(new Date().toISOString() + " - Pong!"); 30 | }); 31 | 32 | export default router; 33 | -------------------------------------------------------------------------------- /monitor/util/helper.js: -------------------------------------------------------------------------------- 1 | import cyripto from "crypto-js"; 2 | 3 | /** 4 | * Returns the platform URL based on the current environment. 5 | * @returns {string} The platform URL. 6 | */ 7 | function getPlatformUrl() { 8 | return `http://platform.${process.env.NAMESPACE}.svc.cluster.local:4000`; 9 | } 10 | 11 | /** 12 | * Decrypts the encrypted text and returns the decrypted string value 13 | * @param {string} ciphertext The encrypted input text 14 | */ 15 | function decryptText(cipherText) { 16 | const bytes = cyripto.AES.decrypt(cipherText, process.env.PASSPHRASE); 17 | return bytes.toString(cyripto.enc.Utf8); 18 | } 19 | 20 | export default { 21 | getPlatformUrl, 22 | decryptText, 23 | }; 24 | -------------------------------------------------------------------------------- /platform/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | Dockerfile.dev 3 | package-lock.json 4 | eslint.config.js 5 | node_modules -------------------------------------------------------------------------------- /platform/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /platform/Dockerfile: -------------------------------------------------------------------------------- 1 | # Get the base node image from docker hub 2 | FROM node:20-alpine 3 | # Set the working directory in our docker image, anything that is copied from now on 4 | # from local machine to the image will be moved under '/app' directory in docker image 5 | WORKDIR '/app' 6 | # Copy package.json file from local machine to the image under '/app' directory 7 | COPY ./package.json ./ 8 | # Set the node environment variable 9 | ENV NODE_ENV production 10 | # Run npm install to install dependent modules 11 | RUN npm install --omit=dev 12 | # Copy everything to the image under '/app' directory 13 | COPY ./ ./ 14 | # This will be the start up command of the docker container, run the development react server 15 | CMD ["node", "server"] 16 | -------------------------------------------------------------------------------- /platform/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Get the base node image from docker hub 2 | FROM node:20-alpine 3 | # Set the working directory in our docker image, anything that is copied from now on 4 | # from local machine to the image will be moved under '/app' directory in docker image 5 | WORKDIR '/app' 6 | # Copy package.json file from local machine to the image under '/app' directory 7 | COPY ./package.json ./ 8 | # Set the node environment variable 9 | ENV NODE_ENV development 10 | # Run npm install to install dependent modules 11 | RUN npm install -g nodemon 12 | RUN npm install --omit=dev 13 | # Copy everything to the image under '/app' directory 14 | COPY ./ ./ 15 | # This will be the start up command of the docker container, run the development react server 16 | CMD ["nodemon", "--trace-warnings", "server"] 17 | -------------------------------------------------------------------------------- /platform/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "session": { 3 | "accessTokenExpiry": 31536000, 4 | "refreshTokenExpiry": 31536000, 5 | "refreshTokenDelete": 60 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /platform/config/errorCodes.js: -------------------------------------------------------------------------------- 1 | const ERROR_CODES = { 2 | // Platform specific error codes 3 | internalServerError: "internal_server_error", 4 | rateLimitExceeded: "rate_limit_exceeded", 5 | resourceNotFound: "resource_not_found", 6 | invalidRequestBody: "invalid_request_body", 7 | invalidContentType: "invalid_content_type", 8 | validationError: "validation_error", 9 | invalidCredentials: "invalid_credentials", 10 | missingAccessToken: "missing_access_token", 11 | invalidSession: "invalid_session", 12 | invalidAccessToken: "invalid_access_token", 13 | invalidUser: "invalid_user", 14 | notAllowed: "not_allowed", 15 | fileUploadError: "file_upload_error", 16 | unauthorized: "unauthorized", 17 | notFound: "not_found", 18 | fileSizeTooLarge: "file_size_too_large", 19 | badRequest: "bad_request", 20 | }; 21 | export default ERROR_CODES; 22 | -------------------------------------------------------------------------------- /platform/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /platform/controllers/container.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { ContainerModel } from "../schemas/container.js"; 3 | 4 | class ContainerController extends BaseController { 5 | constructor() { 6 | super(ContainerModel); 7 | } 8 | } 9 | 10 | export default new ContainerController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/domain.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { DomainModel } from "../schemas/domain.js"; 3 | 4 | class DomainController extends BaseController { 5 | constructor() { 6 | super(DomainModel); 7 | } 8 | } 9 | 10 | export default new DomainController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/environment.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { ProjectEnvModel } from "../schemas/environment.js"; 3 | import auditCtrl from "./audit.js"; 4 | import cntrCtrl from "./container.js"; 5 | 6 | class ProjectEnvController extends BaseController { 7 | constructor() { 8 | super(ProjectEnvModel); 9 | } 10 | 11 | /** 12 | * Delete all project environment related data 13 | * @param {Object} session The database session object 14 | * @param {Object} org The organization object 15 | * @param {Object} project The project object 16 | * @param {Object} environment The environment object that will be deleted 17 | */ 18 | async deleteEnvironment(session, org, project, environment) { 19 | await this.deleteOneById(environment._id, { 20 | session, 21 | cacheKey: environment._id, 22 | }); 23 | 24 | await cntrCtrl.deleteManyByQuery( 25 | { 26 | orgId: org._id, 27 | projectId: project._id, 28 | environmentId: environment._id, 29 | }, 30 | { session } 31 | ); 32 | 33 | await auditCtrl.deleteManyByQuery( 34 | { 35 | orgId: org._id, 36 | projectId: project._id, 37 | environmentId: environment._id, 38 | }, 39 | { session } 40 | ); 41 | } 42 | } 43 | 44 | export default new ProjectEnvController(); 45 | -------------------------------------------------------------------------------- /platform/controllers/gitProvider.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { GitProviderModel } from "../schemas/gitProvider.js"; 3 | 4 | class GitProviderController extends BaseController { 5 | constructor() { 6 | super(GitProviderModel); 7 | } 8 | } 9 | 10 | export default new GitProviderController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/organizationInvitation.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { OrgInvitationModel } from "../schemas/organizationInvitation.js"; 3 | 4 | class OrgInvitationController extends BaseController { 5 | constructor() { 6 | super(OrgInvitationModel); 7 | } 8 | 9 | /** 10 | * Updates the matching host name in all organization invitaions 11 | * @param {string} userId The user identifier 12 | * @param {string} name The name of the user 13 | */ 14 | async updateHostName(userId, name) { 15 | await this.updateMultiByQuery( 16 | { "host.userId": userId }, 17 | { "host.name": name }, 18 | {}, 19 | { writeConcern: { w: 0 } } 20 | ); 21 | } 22 | 23 | /** 24 | * Updates the matching host profile image in all organization invitaions 25 | * @param {string} userId The user identifier 26 | * @param {string} pictureUrl The url of the profile picture 27 | */ 28 | async updateHostPicture(userId, pictureUrl) { 29 | await this.updateMultiByQuery( 30 | { "host.userId": userId }, 31 | { "host.pictureUrl": pictureUrl }, 32 | {}, 33 | { writeConcern: { w: 0 } } 34 | ); 35 | } 36 | 37 | /** 38 | * Removes the matching host profile image in all organization invitaions 39 | * @param {string} userId The user identifier 40 | */ 41 | async removeHostPicture(userId) { 42 | await this.updateMultiByQuery( 43 | { "host.userId": userId }, 44 | {}, 45 | { "host.pictureUrl": 1 }, 46 | { writeConcern: { w: 0 } } 47 | ); 48 | } 49 | } 50 | 51 | export default new OrgInvitationController(); 52 | -------------------------------------------------------------------------------- /platform/controllers/organizationMember.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { OrganizationMemberModel } from "../schemas/organizationMember.js"; 3 | 4 | class OrganizationMemberController extends BaseController { 5 | constructor() { 6 | super(OrganizationMemberModel); 7 | } 8 | } 9 | 10 | export default new OrganizationMemberController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/projectInvitation.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { ProjectInvitationModel } from "../schemas/projectInvitation.js"; 3 | 4 | class ProjectInvitationController extends BaseController { 5 | constructor() { 6 | super(ProjectInvitationModel); 7 | } 8 | 9 | /** 10 | * Updates the matching host name in all app invitaions 11 | * @param {string} userId The user identifier 12 | * @param {string} name The name of the user 13 | */ 14 | async updateHostName(userId, name) { 15 | await this.updateMultiByQuery( 16 | { "host.userId": userId }, 17 | { "host.name": name }, 18 | {}, 19 | { writeConcern: { w: 0 } } 20 | ); 21 | } 22 | 23 | /** 24 | * Updates the matching host profile image in all app invitaions 25 | * @param {string} userId The user identifier 26 | * @param {string} pictureUrl The url of the profile picture 27 | */ 28 | async updateHostPicture(userId, pictureUrl) { 29 | await this.updateMultiByQuery( 30 | { "host.userId": userId }, 31 | { "host.pictureUrl": pictureUrl }, 32 | {}, 33 | { writeConcern: { w: 0 } } 34 | ); 35 | } 36 | 37 | /** 38 | * Removes the matching host profile image in all app invitaions 39 | * @param {string} userId The user identifier 40 | */ 41 | async removeHostPicture(userId) { 42 | await this.updateMultiByQuery( 43 | { "host.userId": userId }, 44 | {}, 45 | { "host.pictureUrl": 1 }, 46 | { writeConcern: { w: 0 } } 47 | ); 48 | } 49 | } 50 | 51 | export default new ProjectInvitationController(); 52 | -------------------------------------------------------------------------------- /platform/controllers/registry.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { RegistryModel } from "../schemas/registry.js"; 3 | 4 | class RegistryController extends BaseController { 5 | constructor() { 6 | super(RegistryModel); 7 | } 8 | } 9 | 10 | export default new RegistryController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/tcpProxyPort.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { TCPProxyPortModel } from "../schemas/tcpProxyPort.js"; 3 | 4 | class TCPProxyPortController extends BaseController { 5 | constructor() { 6 | super(TCPProxyPortModel); 7 | } 8 | } 9 | 10 | export default new TCPProxyPortController(); 11 | -------------------------------------------------------------------------------- /platform/controllers/user.js: -------------------------------------------------------------------------------- 1 | import BaseController from "./base.js"; 2 | import { UserModel } from "../schemas/user.js"; 3 | 4 | class UserController extends BaseController { 5 | constructor() { 6 | super(UserModel); 7 | } 8 | } 9 | 10 | export default new UserController(); 11 | -------------------------------------------------------------------------------- /platform/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | 4 | export default [ 5 | { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, 6 | pluginJs.configs.recommended, 7 | ]; 8 | -------------------------------------------------------------------------------- /platform/handlers/manifests/cronjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: dummy-cron-job 5 | namespace: dummy-namespace 6 | spec: 7 | schedule: "*/5 * * * *" # Runs every 5 minutes 8 | timezone: "Etc/UTC" # Set the timezone for the cron schedule 9 | concurrencyPolicy: Forbid # Prevent concurrent jobs from running 10 | suspend: false # Set to true to suspend the cron job 11 | successfulJobsHistoryLimit: 3 # Number of successful job history to keep 12 | failedJobsHistoryLimit: 1 # Number of failed job history to keep 13 | jobTemplate: 14 | spec: 15 | template: 16 | spec: 17 | restartPolicy: OnFailure 18 | containers: 19 | - name: dummy 20 | image: docker.io/alpine 21 | resources: 22 | requests: 23 | memory: "64Mi" 24 | cpu: "100m" 25 | limits: 26 | memory: "1Gi" 27 | cpu: "1" 28 | env: 29 | - name: DUMMY_KEY_1 30 | value: "dummy_value_1" 31 | - name: DUMMY_KEY_2 32 | value: "dummy_value_2" 33 | - name: DUMMY_KEY_3 34 | value: "dummy_value_3" 35 | volumeMounts: 36 | - name: dummy-volume 37 | mountPath: /dummy 38 | volumes: 39 | - name: data-volume 40 | persistentVolumeClaim: 41 | claimName: data-pvc 42 | -------------------------------------------------------------------------------- /platform/handlers/manifests/hpa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v2 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: example-hpa 5 | namespace: default 6 | spec: 7 | scaleTargetRef: 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | name: example-deployment 11 | minReplicas: 1 12 | maxReplicas: 5 13 | metrics: 14 | - type: Resource 15 | resource: 16 | name: cpu 17 | target: 18 | type: Utilization 19 | averageUtilization: 50 20 | - type: Resource 21 | resource: 22 | name: memory 23 | target: 24 | type: AverageValue 25 | averageUtilization: 150Mi 26 | -------------------------------------------------------------------------------- /platform/handlers/manifests/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: my-namespace -------------------------------------------------------------------------------- /platform/handlers/manifests/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: data-pvc 5 | namespace: dummy-namespace 6 | spec: 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 1Gi -------------------------------------------------------------------------------- /platform/handlers/manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: my-clusterip-service 5 | namespace: my-namespace 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: myapp 10 | ports: 11 | - protocol: TCP 12 | port: 80 13 | targetPort: 9376 -------------------------------------------------------------------------------- /platform/init/db.js: -------------------------------------------------------------------------------- 1 | import config from "config"; 2 | import mongoose from "mongoose"; 3 | 4 | export const connectToDatabase = async () => { 5 | try { 6 | mongoose.set("strictQuery", false); 7 | await mongoose.connect(process.env.CLUSTER_DB_URI, { 8 | user: process.env.CLUSTER_DB_USER, 9 | pass: process.env.CLUSTER_DB_PWD, 10 | maxPoolSize: config.get("database.maxPoolSize"), 11 | }); 12 | 13 | console.info(`Connected to the database @${process.env.CLUSTER_DB_URI}`); 14 | } catch (err) { 15 | console.error(`Cannot connect to the database`, { details: err }); 16 | process.exit(1); 17 | } 18 | }; 19 | 20 | export const disconnectFromDatabase = async () => { 21 | await mongoose.disconnect(); 22 | console.info("Disconnected from the database"); 23 | }; 24 | -------------------------------------------------------------------------------- /platform/init/sync.js: -------------------------------------------------------------------------------- 1 | import config from "config"; 2 | import { io } from "socket.io-client"; 3 | import helper from "../util/helper.js"; 4 | 5 | var socket = null; 6 | 7 | export function initializeSyncClient() { 8 | let syncConfig = config.get("sync"); 9 | socket = io(`${helper.getSyncUrl()}/${syncConfig.namespace}`, { 10 | reconnection: syncConfig.reconnection, 11 | reconnectionDelay: syncConfig.reconnectionDelay, 12 | transports: ["websocket", "polling"], 13 | path: syncConfig.path, 14 | }); 15 | 16 | socket.on("connect", () => { 17 | console.info( 18 | `Connection established to synronization server @${helper.getSyncUrl()}` 19 | ); 20 | }); 21 | 22 | socket.io.on("reconnect", () => { 23 | console.info( 24 | `Connection re-established to synronization server @${helper.getSyncUrl()}` 25 | ); 26 | }); 27 | } 28 | 29 | export function disconnectSyncClient() { 30 | socket.close(); 31 | } 32 | 33 | export function sendMessage(channel, message) { 34 | socket.emit("channel:message", { channel: channel.toString(), message }); 35 | } 36 | -------------------------------------------------------------------------------- /platform/middlewares/authMasterToken.js: -------------------------------------------------------------------------------- 1 | import ERROR_CODES from "../config/errorCodes.js"; 2 | 3 | export const authMasterToken = async (req, res, next) => { 4 | // Get token 5 | let token = req.header("Authorization") ?? req.query.token; 6 | 7 | // Check if there is token 8 | if (!token) { 9 | return res.status(401).json({ 10 | error: "Unauthorized", 11 | details: "No access token was found in 'Authorization' header.", 12 | code: ERROR_CODES.missingAccessToken, 13 | }); 14 | } 15 | 16 | // Check if token is valid or not 17 | if (token !== process.env.MASTER_TOKEN) { 18 | return res.status(401).json({ 19 | error: "Unauthorized", 20 | details: "The access token was not authorized or has expired.", 21 | code: ERROR_CODES.invalidAccessToken, 22 | }); 23 | } 24 | 25 | next(); 26 | }; 27 | -------------------------------------------------------------------------------- /platform/middlewares/checkClusterSetupStatus.js: -------------------------------------------------------------------------------- 1 | import userCtrl from "../controllers/user.js"; 2 | import ERROR_CODES from "../config/errorCodes.js"; 3 | 4 | export const checkClusterSetupStatus = async (req, res, next) => { 5 | // Get cluster owner 6 | let user = await userCtrl.getOneByQuery({ isClusterOwner: true }); 7 | 8 | // Check if there is user 9 | if (user) { 10 | return res.status(401).json({ 11 | error: "Not Allowed", 12 | details: 13 | "The cluster set up has already been initialized. You cannot reinitialize the set up.", 14 | code: ERROR_CODES.notAllowed, 15 | }); 16 | } 17 | 18 | next(); 19 | }; 20 | 21 | export const hasClusterSetUpCompleted = async (req, res, next) => { 22 | // Get cluster owner 23 | let user = await userCtrl.getOneByQuery({ isClusterOwner: true }); 24 | 25 | // Check if there is no cluster owner user 26 | if (!user) { 27 | return res.status(401).json({ 28 | error: "Not Allowed", 29 | details: "The cluster set up has not been completed yet.", 30 | code: ERROR_CODES.notAllowed, 31 | }); 32 | } 33 | 34 | next(); 35 | }; 36 | -------------------------------------------------------------------------------- /platform/middlewares/contentType.js: -------------------------------------------------------------------------------- 1 | import config from "config"; 2 | import express from "express"; 3 | import ERROR_CODES from "../config/errorCodes.js"; 4 | 5 | // Middleware to handle undefined paths or posts 6 | export const checkContentType = (req, res, next) => { 7 | // Check content type 8 | if (req.get("Content-Type") !== "application/json") { 9 | return res.status(415).json({ 10 | error: "Unsupported Media Type", 11 | details: 12 | "The server does not accept the submitted content-type. The content-type should be 'application-json'.", 13 | code: ERROR_CODES.invalidContentType, 14 | }); 15 | } 16 | 17 | // Parse content and build the req.body object 18 | express.json({ limit: config.get("server.maxBodySize") })( 19 | req, 20 | res, 21 | (error) => { 22 | if (error) { 23 | return res.status(400).json({ 24 | error: "Invalid Request Body", 25 | details: 26 | "The server could not understand the request due to either invalid syntax of JSON document in request body or the request body payload is larger than the allowed limit.", 27 | code: ERROR_CODES.invalidRequestBody, 28 | }); 29 | } 30 | 31 | next(); 32 | } 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /platform/middlewares/handleFile.js: -------------------------------------------------------------------------------- 1 | import config from "config"; 2 | import multer, { memoryStorage } from "multer"; 3 | import ERROR_CODES from "../config/errorCodes.js"; 4 | 5 | // Multer is required to process file uploads and make them available via req.files. 6 | const fileUpload = multer({ 7 | storage: memoryStorage(), 8 | limits: { 9 | fileSize: config.get("general.maxImageSizeMB") * 1000 * 1000, 10 | }, 11 | }).single("picture"); 12 | 13 | export const fileUploadMiddleware = (req, res, next) => { 14 | fileUpload(req, res, function (err) { 15 | if (err instanceof multer.MulterError) { 16 | // Handle Multer-specific errors 17 | if (err.code === "LIMIT_FILE_SIZE") { 18 | return res.status(400).json({ 19 | error: "File Too Large", 20 | code: ERROR_CODES.fileSizeTooLarge, 21 | details: `File size exceeds the limit of ${config.get( 22 | "general.maxImageSizeMB" 23 | )}MB.`, 24 | }); 25 | } 26 | return res.status(500).json({ 27 | error: "File Upload Error", 28 | code: ERROR_CODES.fileUploadError, 29 | details: `An error has occured when uploading the file. ${err.message}`, 30 | }); 31 | } else if (err) { 32 | return res.status(500).json({ 33 | error: "File Upload Error", 34 | code: ERROR_CODES.fileUploadError, 35 | details: `An error has occured when uploading the file. ${err.message}`, 36 | }); 37 | } 38 | // If everything went fine, move to the next middleware 39 | next(); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /platform/middlewares/logRequest.js: -------------------------------------------------------------------------------- 1 | // Log requests to console 2 | export const logRequest = (req, res, time) => { 3 | if (req.originalUrl !== "/health") 4 | console.info( 5 | `${req.method} (${res.statusCode}) ${Math.round(time * 10) / 10}ms ${ 6 | req.originalUrl 7 | }` 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /platform/middlewares/rateLimiter.js: -------------------------------------------------------------------------------- 1 | import { RateLimiterRedis } from "rate-limiter-flexible"; 2 | import { getRedisClient } from "../init/cache.js"; 3 | import helper from "../util/helper.js"; 4 | import ERROR_CODES from "../config/errorCodes.js"; 5 | 6 | // Apply rate limits to platform endpoints 7 | export const createRateLimiter = (rateLimitConfig) => { 8 | const rateLimiter = new RateLimiterRedis({ 9 | storeClient: getRedisClient(), 10 | points: rateLimitConfig.rateLimitMaxHits, // Limit each unique identifier (IP or userId) to N requests per `window` 11 | duration: rateLimitConfig.rateLimitWindowSec, // Window duration in seconds 12 | }); 13 | 14 | return (req, res, next) => { 15 | rateLimiter 16 | .consume(helper.getIP(req)) 17 | .then(() => { 18 | next(); 19 | }) 20 | .catch(() => { 21 | return res.status(429).json({ 22 | error: "Rate Limit Exceeded", 23 | details: "Too many requests, please try again later.", 24 | code: ERROR_CODES.rateLimitExceeded, 25 | }); 26 | }); 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /platform/middlewares/undefinedPaths.js: -------------------------------------------------------------------------------- 1 | import ERROR_CODES from "../config/errorCodes.js"; 2 | 3 | // Middleware to handle undefined paths or posts 4 | export const handleUndefinedPaths = (req, res) => { 5 | return res.status(404).json({ 6 | error: "Not Found", 7 | details: "The server can not find the requested resource.", 8 | code: ERROR_CODES.resourceNotFound, 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /platform/middlewares/validate.js: -------------------------------------------------------------------------------- 1 | import { validationResult } from "express-validator"; 2 | import ERROR_CODES from "../config/errorCodes.js"; 3 | 4 | // Middleare the create the error message for failed request input validations 5 | export const validate = (req, res, next) => { 6 | const errors = validationResult(req); 7 | if (!errors.isEmpty()) { 8 | return res.status(400).json({ 9 | error: "Invalid Input", 10 | details: 11 | "The request parameters has failed to pass the validation rules.", 12 | code: ERROR_CODES.validationError, 13 | fields: errors.array(), 14 | }); 15 | } 16 | 17 | next(); 18 | }; 19 | -------------------------------------------------------------------------------- /platform/middlewares/validateCluster.js: -------------------------------------------------------------------------------- 1 | import userCtrl from "../controllers/user.js"; 2 | import clsCtrl from "../controllers/cluster.js"; 3 | import helper from "../util/helper.js"; 4 | 5 | import ERROR_CODES from "../config/errorCodes.js"; 6 | 7 | export const validateCluster = async (req, res, next) => { 8 | try { 9 | let user = await userCtrl.getOneByQuery({ isClusterOwner: true }); 10 | if (!user) { 11 | return res.status(401).json({ 12 | error: "Not Authorized", 13 | details: "Cluster set up has not been completed yet.", 14 | code: ERROR_CODES.unauthorized, 15 | }); 16 | } 17 | 18 | // Get the cluster object 19 | const cluster = await clsCtrl.getOneByQuery( 20 | { 21 | clusterAccesssToken: process.env.CLUSTER_ACCESS_TOKEN, 22 | }, 23 | { 24 | cacheKey: process.env.CLUSTER_ACCESS_TOKEN, 25 | } 26 | ); 27 | 28 | // Assign cluster data 29 | req.cluster = cluster; 30 | 31 | next(); 32 | } catch (err) { 33 | return helper.handleError(req, res, err); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /platform/middlewares/validateContainer.js: -------------------------------------------------------------------------------- 1 | import cntrCtrl from "../controllers/container.js"; 2 | import helper from "../util/helper.js"; 3 | 4 | import ERROR_CODES from "../config/errorCodes.js"; 5 | 6 | export const validateContainer = async (req, res, next) => { 7 | try { 8 | const { containerId } = req.params; 9 | 10 | // Get the container object 11 | let container = await cntrCtrl.getOneById(containerId, { 12 | cacheKey: containerId, 13 | }); 14 | 15 | if (!container) { 16 | return res.status(404).json({ 17 | error: "Not Found", 18 | details: `No such container with the provided id '${containerId}' exists.`, 19 | code: ERROR_CODES.notFound, 20 | }); 21 | } 22 | 23 | if (container.environmentId.toString() !== req.environment._id.toString()) { 24 | return res.status(401).json({ 25 | error: "Not Authorized", 26 | details: `Project environment does not have a container with the provided id '${containerId}'`, 27 | code: ERROR_CODES.unauthorized, 28 | }); 29 | } 30 | 31 | // Assign container data 32 | req.container = container; 33 | 34 | next(); 35 | } catch (err) { 36 | return helper.handleError(req, res, err); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /platform/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platform-core", 3 | "version": "v1.0.5", 4 | "description": "Platform core api server", 5 | "main": "server.js", 6 | "type": "module", 7 | "scripts": { 8 | "prod": "NODE_ENV=production node server", 9 | "dev": "NODE_ENV=development nodemon server" 10 | }, 11 | "author": "Ümit Çakmak", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@kubernetes/client-node": "0.18.1", 15 | "@octokit/core": "5.1.0", 16 | "axios": "1.3.3", 17 | "bcrypt": "5.1.0", 18 | "config": "3.3.9", 19 | "cors": "2.8.5", 20 | "cron-parser": "4.8.1", 21 | "crypto-js": "4.1.1", 22 | "express": "4.18.2", 23 | "express-validator": "6.14.3", 24 | "helmet": "6.0.1", 25 | "js-yaml": "4.1.0", 26 | "minio": "7.1.1", 27 | "mongodb": "5.1.0", 28 | "mongoose": "6.9.1", 29 | "multer": "1.4.5-lts.1", 30 | "nanoid": "4.0.1", 31 | "nocache": "3.0.4", 32 | "on-headers": "1.0.2", 33 | "psl": "1.9.0", 34 | "randomcolor": "0.6.2", 35 | "rate-limiter-flexible": "2.4.1", 36 | "redis": "3.1.2", 37 | "response-time": "2.3.2", 38 | "sharp": "0.31.3", 39 | "socket.io-client": "4.6.1", 40 | "ua-parser-js": "1.0.33", 41 | "winston": "3.8.2", 42 | "winston-transport": "4.5.0" 43 | }, 44 | "devDependencies": { 45 | "@eslint/js": "^9.4.0", 46 | "eslint": "^9.4.0", 47 | "globals": "^15.3.0", 48 | "nodemon": "2.0.20" 49 | } 50 | } -------------------------------------------------------------------------------- /platform/routes/registry.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import regCtrl from "../controllers/registry.js"; 3 | import { authSession } from "../middlewares/authSession.js"; 4 | import helper from "../util/helper.js"; 5 | 6 | const router = express.Router({ mergeParams: true }); 7 | 8 | /* 9 | @route /v1/registry 10 | @method GET 11 | @desc Get all registries for the cluster 12 | @access private 13 | */ 14 | router.get("/", authSession, async (req, res) => { 15 | try { 16 | const registries = await regCtrl.getManyByQuery({}); 17 | res.json(registries); 18 | } catch (err) { 19 | helper.handleError(req, res, err); 20 | } 21 | }); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /platform/routes/system.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | 3 | const router = express.Router({ mergeParams: true }); 4 | 5 | /* 6 | @route / 7 | @method GET 8 | @desc Checks liveliness of platform server 9 | @access public 10 | */ 11 | router.get("/health", (req, res) => { 12 | res 13 | .status(200) 14 | .send( 15 | `${new Date().toISOString()} - Healthy platform server - ${ 16 | process.env.RELEASE_NUMBER 17 | }` 18 | ); 19 | }); 20 | 21 | /* 22 | @route /ping 23 | @method GET 24 | @desc Checks liveliness of platform server 25 | @access public 26 | */ 27 | router.get("/ping", (req, res) => { 28 | res.status(200).send(`${new Date().toISOString()} - Pong!`); 29 | }); 30 | 31 | export default router; 32 | -------------------------------------------------------------------------------- /platform/routes/types.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { authSession } from "../middlewares/authSession.js"; 3 | import { 4 | orgRoles, 5 | orgRoleDesc, 6 | projectRoles, 7 | projectRoleDesc, 8 | } from "../config/constants.js"; 9 | import { timezones } from "../config/timezones.js"; 10 | 11 | const router = express.Router({ mergeParams: true }); 12 | 13 | /* 14 | @route /all 15 | @method GET 16 | @desc Returns all types used in the platform 17 | @access public 18 | */ 19 | router.get("/all", authSession, (req, res) => { 20 | res.json({ 21 | orgRoles, 22 | orgRoleDesc, 23 | projectRoles, 24 | projectRoleDesc, 25 | timezones, 26 | }); 27 | }); 28 | 29 | export default router; 30 | -------------------------------------------------------------------------------- /platform/schemas/domain.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | /** 4 | * Keeps information about the custom domains added to the containers or cluster itself. This is used to prevent duplicate domain names and creation of conflicting ingress rules. 5 | */ 6 | export const DomainModel = mongoose.model( 7 | "domain", 8 | new mongoose.Schema( 9 | { 10 | domain: { 11 | type: String, 12 | required: true, 13 | index: true, 14 | }, 15 | __v: { 16 | type: Number, 17 | select: false, 18 | }, 19 | }, 20 | { timestamps: true } 21 | ) 22 | ); 23 | -------------------------------------------------------------------------------- /platform/schemas/rules/cronJob.js: -------------------------------------------------------------------------------- 1 | import { 2 | checkName, 3 | checkRepoOrRegistry, 4 | checkRegistry, 5 | checkRepo, 6 | checkVariables, 7 | checkStorageConfig, 8 | checkPodConfig, 9 | checkCronJobConfig, 10 | } from "./checks.js"; 11 | 12 | export default (actionType) => { 13 | switch (actionType) { 14 | case "create": 15 | case "update": 16 | return [ 17 | ...checkName("cronjob", actionType), 18 | ...checkRepoOrRegistry("cronjob", actionType), 19 | ...checkRegistry("cronjob", actionType), 20 | ...checkRepo("cronjob", actionType), 21 | ...checkVariables("cronjob", actionType), 22 | ...checkStorageConfig("cronjob", actionType), 23 | ...checkPodConfig("cronjob", actionType), 24 | ...checkCronJobConfig("cronjob", actionType), 25 | ]; 26 | default: 27 | return []; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /platform/schemas/rules/deployment.js: -------------------------------------------------------------------------------- 1 | import { 2 | checkName, 3 | checkRepoOrRegistry, 4 | checkRegistry, 5 | checkRepo, 6 | checkVariables, 7 | checkNetworking, 8 | checkStorageConfig, 9 | checkPodConfig, 10 | checkDeploymentConfig, 11 | checkProbes, 12 | } from "./checks.js"; 13 | 14 | export default (actionType) => { 15 | switch (actionType) { 16 | case "create": 17 | case "update": 18 | return [ 19 | ...checkName("deployment", actionType), 20 | ...checkRepoOrRegistry("deployment", actionType), 21 | ...checkRegistry("deployment", actionType), 22 | ...checkRepo("deployment", actionType), 23 | ...checkVariables("deployment", actionType), 24 | ...checkStorageConfig("deployment", actionType), 25 | ...checkNetworking("deployment", actionType), 26 | ...checkPodConfig("deployment", actionType), 27 | ...checkDeploymentConfig("deployment", actionType), 28 | ...checkProbes("deployment", actionType), 29 | ]; 30 | default: 31 | return []; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /platform/schemas/rules/statefulSet.js: -------------------------------------------------------------------------------- 1 | import { 2 | checkName, 3 | checkRepoOrRegistry, 4 | checkRegistry, 5 | checkRepo, 6 | checkVariables, 7 | checkStorageConfig, 8 | checkNetworking, 9 | checkPodConfig, 10 | checkStatefulSetConfig, 11 | checkProbes, 12 | } from "./checks.js"; 13 | 14 | export default (actionType) => { 15 | switch (actionType) { 16 | case "create": 17 | case "update": 18 | return [ 19 | ...checkName("statefulset", actionType), 20 | ...checkRepoOrRegistry("statefulset", actionType), 21 | ...checkRegistry("statefulset", actionType), 22 | ...checkRepo("statefulset", actionType), 23 | ...checkVariables("statefulset", actionType), 24 | ...checkStorageConfig("statefulset", actionType), 25 | ...checkNetworking("statefulset", actionType), 26 | ...checkPodConfig("statefulset", actionType), 27 | ...checkStatefulSetConfig("statefulset", actionType), 28 | ...checkProbes("statefulset", actionType), 29 | ]; 30 | default: 31 | return []; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /platform/schemas/tcpProxyPort.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | /** 4 | * Keeps track of the used tcp proxy port numbers 5 | */ 6 | export const TCPProxyPortModel = mongoose.model( 7 | "tcp_proxy_port", 8 | new mongoose.Schema( 9 | { 10 | port: { 11 | type: Number, 12 | index: true, 13 | }, 14 | __v: { 15 | type: Number, 16 | select: false, 17 | }, 18 | }, 19 | { timestamps: true } 20 | ) 21 | ); 22 | -------------------------------------------------------------------------------- /releases/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/releases/.gitkeep -------------------------------------------------------------------------------- /releases/latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.10", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.5", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.5" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.10.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.10", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.7", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.7" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.11.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.11", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.8", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.7" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.12.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.12", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.9", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.8" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.13.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.13", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.9", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.9" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.14.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.14", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.9", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.10" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.15.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.15", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.9", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.11" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.16.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.16", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.10", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.11" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.17.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.17", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.11", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.12" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.18.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.18", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.12", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.13" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.19.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.19", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.13", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.14" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.2", 3 | "modules": { 4 | "monitor": "v0.0.2", 5 | "platform": "v0.0.2", 6 | "sync": "v0.0.2", 7 | "webhook": "v0.0.2", 8 | "studio": "v0.0.1" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.20.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.20", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.13", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.15" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.21.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.21", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.14", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.16" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.22.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.22", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.15", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.23.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.23", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.16", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.24.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.24", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.17", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.25.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.25", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.18", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.26.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.26", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.19", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.27.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.27", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.20", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.28.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.28", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.21", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.17" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.29.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.29", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.22", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.18" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.3", 3 | "modules": { 4 | "monitor": "v0.0.3", 5 | "platform": "v0.0.3", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.3", 8 | "studio": "v0.0.2" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.30.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.30", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.23", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.19" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.31.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.31", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.24", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.19" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.32.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.32", 3 | "modules": { 4 | "monitor": "v0.0.5", 5 | "platform": "v0.0.25", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.20" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.33.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.33", 3 | "modules": { 4 | "monitor": "v0.0.6", 5 | "platform": "v0.0.26", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.21" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.34.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.34", 3 | "modules": { 4 | "monitor": "v0.0.7", 5 | "platform": "v0.0.27", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.22" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.35.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.35", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.28", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.22" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.36.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.36", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.29", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.22" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.37.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.37", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.29", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.23" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.38.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.38", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.29", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.24" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.39.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.39", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.29", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.25" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.4", 3 | "modules": { 4 | "monitor": "v0.0.3", 5 | "platform": "v0.0.3", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.3", 8 | "studio": "v0.0.3" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.40.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.40", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.30", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.25" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.41.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.41", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.30", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.26" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.42.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.42", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.30", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.27" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.43.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.43", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.30", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.28" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.44.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.44", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.30", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.29" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.45.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.45", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.31", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.29" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.46.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.46", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.32", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.29" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.47.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.47", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.33", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.29" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.48.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.48", 3 | "modules": { 4 | "monitor": "v0.0.8", 5 | "platform": "v0.0.34", 6 | "sync": "v0.0.4", 7 | "webhook": "v0.0.6", 8 | "studio": "v0.0.29" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.5.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.5", 3 | "modules": { 4 | "monitor": "v0.0.3", 5 | "platform": "v0.0.3", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.3", 8 | "studio": "v0.0.4" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.6.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.6", 3 | "modules": { 4 | "monitor": "v0.0.3", 5 | "platform": "v0.0.3", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.3", 8 | "studio": "v0.0.5" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.7.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.7", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.4", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.3", 8 | "studio": "v0.0.6" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.8.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.8", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.5", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.4", 8 | "studio": "v0.0.6" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v0.0.9.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v0.0.9", 3 | "modules": { 4 | "monitor": "v0.0.4", 5 | "platform": "v0.0.6", 6 | "sync": "v0.0.3", 7 | "webhook": "v0.0.5", 8 | "studio": "v0.0.6" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.0", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.0", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.0.0" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.1", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.1", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.0.0" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.10.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.10", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.5", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.5" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.2", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.2", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.0.0" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.3", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.2", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.0" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.4", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.2", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.1" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.5.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.5", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.2", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.2" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.6.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.6", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.3", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.2" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.7.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.7", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.3", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.3" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.8.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.8", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.4", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.4" 9 | } 10 | } -------------------------------------------------------------------------------- /releases/v1.0.9.json: -------------------------------------------------------------------------------- 1 | { 2 | "release": "v1.0.9", 3 | "modules": { 4 | "monitor": "v1.0.0", 5 | "platform": "v1.0.5", 6 | "sync": "v1.0.0", 7 | "webhook": "v1.0.0", 8 | "studio": "v1.1.4" 9 | } 10 | } -------------------------------------------------------------------------------- /studio/.Dockerignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /studio/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /studio/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "all", 7 | "jsxSingleQuote": true, 8 | "bracketSpacing": true, 9 | "useTabs": true 10 | } 11 | -------------------------------------------------------------------------------- /studio/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build Stage 2 | # Use a Node.js base image 3 | FROM node:20-alpine as build-stage 4 | 5 | # Set the working directory 6 | WORKDIR /app 7 | 8 | # Copy package.json and package-lock.json (or yarn.lock if you use yarn) 9 | COPY package*.json ./ 10 | 11 | # Install dependencies 12 | RUN npm install 13 | 14 | # Copy the rest of your app's source code 15 | COPY . . 16 | 17 | # Build your app 18 | RUN npm run build 19 | 20 | # Run Stage 21 | # Use an Nginx base image 22 | FROM nginx:alpine 23 | 24 | # Copy the built app from the build stage to Nginx's serve directory 25 | COPY --from=build-stage /app/dist /usr/share/nginx/html 26 | COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf 27 | 28 | # Expose port 4000 29 | EXPOSE 4000 30 | 31 | # Start Nginx and keep it running 32 | CMD ["nginx", "-g", "daemon off;"] 33 | -------------------------------------------------------------------------------- /studio/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Get the base node image from docker hub 2 | FROM node:20-alpine 3 | # Set the working directory in our docker image, anything that is copied from now on 4 | # from local machine to the image will be moved under '/app' directory in docker image 5 | WORKDIR '/app' 6 | # Copy package.json file from local machine to the image under '/app' directory 7 | COPY ./package.json ./ 8 | # Run npm install to install dependent modules 9 | RUN npm install 10 | # Copy everything to the image under '/app' directory 11 | COPY ./ ./ 12 | # This will be the start up command of the docker container 13 | CMD [ "npm", "run", "dev" ] 14 | -------------------------------------------------------------------------------- /studio/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 4000; 3 | 4 | # Set a variable for the hostname, excluding port 5 | set $base_host $host; 6 | if ($host ~* "^(.+):(.+)$") { 7 | set $base_host $1; 8 | } 9 | 10 | location /studio { 11 | alias /usr/share/nginx/html/; 12 | index index.html index.htm; 13 | 14 | if ($uri = /studio) { 15 | return 301 $scheme://$base_host/studio/; 16 | } 17 | 18 | try_files $uri $uri/ /studio/index.html; 19 | } 20 | } -------------------------------------------------------------------------------- /studio/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /studio/src/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #70A9FF 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /studio/src/assets/images/agnost-dark-bg-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/agnost-dark-bg-logo.png -------------------------------------------------------------------------------- /studio/src/assets/images/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/android-chrome-192x192.png -------------------------------------------------------------------------------- /studio/src/assets/images/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/android-chrome-512x512.png -------------------------------------------------------------------------------- /studio/src/assets/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/apple-touch-icon.png -------------------------------------------------------------------------------- /studio/src/assets/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/favicon-16x16.png -------------------------------------------------------------------------------- /studio/src/assets/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/favicon-32x32.png -------------------------------------------------------------------------------- /studio/src/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/favicon.ico -------------------------------------------------------------------------------- /studio/src/assets/images/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/ms-icon-310x310.png -------------------------------------------------------------------------------- /studio/src/assets/images/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-agnost/agnost-gitops/6c18a641b9dce8dea274b8797ee7a86ee474d8f2/studio/src/assets/images/mstile-150x150.png -------------------------------------------------------------------------------- /studio/src/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agnost", 3 | "icons": [ 4 | { 5 | "src": "/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /studio/src/assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agnost", 3 | "short_name": "Agnost", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#70A9FF", 17 | "background_color": "#004DC9", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /studio/src/components/Accordion/index.ts: -------------------------------------------------------------------------------- 1 | export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './Accordion'; 2 | -------------------------------------------------------------------------------- /studio/src/components/ActionsCell/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ActionsCell } from './ActionsCell'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Alert/Feedback.tsx: -------------------------------------------------------------------------------- 1 | import { Error, SuccessCheck } from '@/components/icons'; 2 | import { cn } from '@/utils'; 3 | interface Props { 4 | success?: boolean; 5 | title: string; 6 | description: string; 7 | className?: string; 8 | } 9 | export default function Feedback({ success, title, description, className }: Props) { 10 | const Icon = success ? SuccessCheck : Error; 11 | return ( 12 |
13 | 14 |

{title}

15 |

{description}

16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /studio/src/components/Alert/alert.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | @apply relative border border-transparent flex items-center justify-center w-full; 3 | &-title { 4 | @apply text-default text-sm font-semibold [&_p]:leading-tight; 5 | } 6 | &-md { 7 | &.alert { 8 | @apply rounded-lg gap-6 p-4; 9 | } 10 | .alert-icon { 11 | @apply text-6xl; 12 | } 13 | .alert-title { 14 | @apply text-sm; 15 | } 16 | } 17 | &-sm { 18 | &.alert { 19 | @apply rounded-none gap-2 p-1.5; 20 | } 21 | .alert-icon { 22 | @apply text-lg; 23 | } 24 | .alert-title { 25 | @apply text-xs; 26 | } 27 | } 28 | 29 | &-success { 30 | @apply bg-surface-green; 31 | } 32 | 33 | &-error { 34 | @apply bg-surface-red; 35 | } 36 | 37 | &-warning { 38 | @apply bg-surface-yellow; 39 | } 40 | 41 | &-body { 42 | @apply flex-1; 43 | } 44 | &-icon { 45 | @apply rounded-full flex items-center justify-center; 46 | } 47 | 48 | &-description { 49 | @apply text-xs text-subtle [&_p]:leading-relaxed; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /studio/src/components/Alert/index.ts: -------------------------------------------------------------------------------- 1 | export { Alert, AlertDescription, AlertTitle } from './Alert'; 2 | export { default as Feedback } from './Feedback'; 3 | -------------------------------------------------------------------------------- /studio/src/components/AuthUserAvatar/AuthUserAvatar.tsx: -------------------------------------------------------------------------------- 1 | import useAuthStore from '@/store/auth/authStore.ts'; 2 | import { Avatar, AvatarFallback, AvatarImage, AvatarProps } from '@/components/Avatar'; 3 | 4 | export default function AuthUserAvatar(props: AvatarProps) { 5 | const { user } = useAuthStore(); 6 | 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /studio/src/components/AuthUserAvatar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AuthUserAvatar } from './AuthUserAvatar.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Avatar/avatar.scss: -------------------------------------------------------------------------------- 1 | .avatar { 2 | @apply relative flex shrink-0 overflow-hidden rounded-full; 3 | &-image { 4 | @apply aspect-square h-full w-full; 5 | } 6 | &-fallback { 7 | @apply flex h-full w-full items-center justify-center; 8 | &-user { 9 | @apply text-default-reverse dark:text-default; 10 | } 11 | &-org { 12 | @apply text-default dark:text-default-reverse; 13 | } 14 | } 15 | &-xxs { 16 | @apply size-5 text-[10px]; 17 | } 18 | &-xs { 19 | @apply size-6 text-xs; 20 | } 21 | &-sm { 22 | @apply size-7 text-xs; 23 | } 24 | &-md { 25 | @apply size-8 text-base; 26 | } 27 | &-lg { 28 | @apply size-12 text-lg; 29 | } 30 | &-xl { 31 | @apply size-14 text-2xl; 32 | } 33 | &-2xl { 34 | @apply size-16 text-2xl; 35 | } 36 | &-3xl { 37 | @apply size-20 text-3xl; 38 | } 39 | &-4xl { 40 | @apply size-32 text-6xl; 41 | } 42 | &-square { 43 | @apply rounded-sm; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /studio/src/components/Avatar/index.ts: -------------------------------------------------------------------------------- 1 | export { Avatar, AvatarFallback, AvatarImage } from './Avatar'; 2 | export type { AvatarProps, AvatarFallbackProps } from './Avatar'; 3 | -------------------------------------------------------------------------------- /studio/src/components/Badge/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Badge } from './Badge'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Button/ButtonGroup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './Button.scss'; 3 | export default function ButtonGroup({ children }: { children: React.ReactNode }) { 4 | return
{children}
; 5 | } 6 | -------------------------------------------------------------------------------- /studio/src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button, buttonVariants } from './Button'; 2 | export type { ButtonProps } from './Button'; 3 | export { default as ButtonGroup } from './ButtonGroup'; 4 | -------------------------------------------------------------------------------- /studio/src/components/ChangeAvatar/changeAvatar.scss: -------------------------------------------------------------------------------- 1 | .avatar { 2 | &-actions { 3 | @apply inline-flex relative; 4 | &-button { 5 | @apply absolute bottom-0 left-1/2 bg-base/70 w-full hidden; 6 | @apply transform -translate-x-1/2 rounded-none space-x-2; 7 | } 8 | &:hover { 9 | .avatar-actions-button { 10 | @apply flex items-center justify-center; 11 | } 12 | } 13 | &-icon { 14 | @apply w-5 h-5; 15 | } 16 | &-loading { 17 | @apply text-icon-secondary; 18 | @apply absolute top-[20%] left-[20%]; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /studio/src/components/ChangeAvatar/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ChangeAvatar } from './ChangeAvatar'; 2 | -------------------------------------------------------------------------------- /studio/src/components/ChangeNameForm/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ChangeNameForm } from './ChangeNameForm'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Checkbox/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { Label } from '@/components/Label'; 2 | import { cn } from '@/utils'; 3 | import { Check } from '@phosphor-icons/react'; 4 | import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; 5 | import * as React from 'react'; 6 | import './checkbox.scss'; 7 | 8 | interface CheckboxProps extends React.ComponentPropsWithoutRef { 9 | className?: string; 10 | id?: string; 11 | label?: string; 12 | } 13 | 14 | const Checkbox = React.forwardRef, CheckboxProps>( 15 | ({ className, id, label, ...props }, ref) => ( 16 |
17 | 23 | 24 | 25 | 26 | 27 | {label && ( 28 | 31 | )} 32 |
33 | ), 34 | ); 35 | Checkbox.displayName = CheckboxPrimitive.Root.displayName; 36 | 37 | export { Checkbox }; 38 | -------------------------------------------------------------------------------- /studio/src/components/Checkbox/checkbox.scss: -------------------------------------------------------------------------------- 1 | .checkbox { 2 | @apply h-4 w-4 shrink-0 rounded-sm; 3 | @apply border border-input-border ring-offset-wrapper-background-base; 4 | @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-button-primary focus-visible:ring-offset-2; 5 | @apply disabled:cursor-not-allowed disabled:bg-input-disabled-border disabled:text-icon-disabled disabled:data-[state=checked]:opacity-50 disabled:pointer-events-none; 6 | @apply data-[state=checked]:bg-button-primary data-[state=checked]:text-white; 7 | @apply hover:data-[state=checked]:bg-button-primary-hover; 8 | 9 | &-wrapper { 10 | @apply flex items-center space-x-2; 11 | } 12 | 13 | &-indicator { 14 | @apply flex items-center justify-center text-current; 15 | } 16 | &-label { 17 | @apply cursor-pointer; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /studio/src/components/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export { Checkbox } from './Checkbox.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/ConfirmationModal/confirmationModal.scss: -------------------------------------------------------------------------------- 1 | .confirmation-modal { 2 | @apply flex flex-col gap-y-4; 3 | 4 | &-actions { 5 | @apply flex justify-end items-center gap-4 self-end; 6 | } 7 | &-desc { 8 | @apply text-sm leading-6 text-subtle font-normal tracking-wide self-start; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /studio/src/components/ConfirmationModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConfirmationModal } from './ConfirmationModal.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/CopyButton/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@/components/Button'; 2 | import { copyToClipboard } from '@/utils'; 3 | import { Copy } from '@phosphor-icons/react'; 4 | 5 | interface CopyButtonProps { 6 | text: string; 7 | className?: string; 8 | } 9 | export default function CopyButton({ text, className }: CopyButtonProps) { 10 | return ( 11 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /studio/src/components/CopyButton/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CopyButton } from './CopyButton.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/CopyInput/CopyInput.tsx: -------------------------------------------------------------------------------- 1 | import { Input } from '@/components/Input'; 2 | import { cn, copyToClipboard } from '@/utils'; 3 | import { Copy } from '@phosphor-icons/react'; 4 | import * as React from 'react'; 5 | import { useState } from 'react'; 6 | import { Button } from '../Button'; 7 | import './copyInput.scss'; 8 | import { useUpdateEffect } from '@/hooks'; 9 | 10 | interface CopyInputProps extends React.ComponentPropsWithoutRef<'input'> { 11 | hasError?: boolean; 12 | } 13 | 14 | const CopyInput = React.forwardRef( 15 | ({ className, value, readOnly, hasError, placeholder, ...props }, ref) => { 16 | const [inputValue, setInputValue] = useState(value as string); 17 | 18 | useUpdateEffect(() => { 19 | setInputValue(value as string); 20 | }, [value]); 21 | 22 | return ( 23 |
24 | setInputValue(e.target.value)} 29 | placeholder={placeholder} 30 | className={cn('copy-input', hasError && 'input-error')} 31 | /> 32 | 41 |
42 | ); 43 | }, 44 | ); 45 | CopyInput.displayName = 'CopyInput'; 46 | 47 | export { CopyInput }; 48 | -------------------------------------------------------------------------------- /studio/src/components/CopyInput/copyInput.scss: -------------------------------------------------------------------------------- 1 | .copy-input-wrapper { 2 | @apply relative; 3 | .copy-input { 4 | @apply pr-14 cursor-default; 5 | 6 | &-button { 7 | @apply absolute text-subtle right-1 top-0.5 hover:text-default; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /studio/src/components/CopyInput/index.ts: -------------------------------------------------------------------------------- 1 | export { CopyInput } from './CopyInput'; 2 | -------------------------------------------------------------------------------- /studio/src/components/DataTable/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DataTable } from './DataTable'; 2 | export { SortButton } from './SortButton'; 3 | -------------------------------------------------------------------------------- /studio/src/components/DataTable/sortButton.scss: -------------------------------------------------------------------------------- 1 | @media (hover: hover) { 2 | .resizer { 3 | opacity: 0; 4 | } 5 | 6 | *:hover > .resizer { 7 | opacity: 1; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /studio/src/components/DateRangePicker/index.ts: -------------------------------------------------------------------------------- 1 | export { DateRangePicker } from './DateRangePicker'; 2 | -------------------------------------------------------------------------------- /studio/src/components/DateText/DateText.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { DATE_FORMAT_MONTH_DAY_YEAR, TIME_FORMAT, cn, formatDate } from '@/utils'; 3 | import { Avatar, AvatarFallback, AvatarImage } from '../Avatar'; 4 | import { OrganizationMember } from '@/types'; 5 | interface DateTextProps { 6 | date: string | Date; 7 | children?: ReactNode; 8 | className?: string; 9 | user?: OrganizationMember; 10 | style?: React.CSSProperties; 11 | } 12 | export default function DateText({ date, children, className, user, ...props }: DateTextProps) { 13 | return ( 14 |
15 | {children} 16 | {user && ( 17 | 18 | 19 | 20 | 21 | )} 22 | {date ? ( 23 |
24 |

25 | {formatDate(date, DATE_FORMAT_MONTH_DAY_YEAR)} 26 |

27 | 28 |
29 | ) : ( 30 |
-
31 | )} 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /studio/src/components/DateText/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DateText } from './DateText'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Description/Description.scss: -------------------------------------------------------------------------------- 1 | .description { 2 | @apply space-y-2; 3 | &-title { 4 | @apply text-default text-sm leading-8 font-semibold; 5 | } 6 | &-content { 7 | @apply text-xs text-subtle font-normal leading-6; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /studio/src/components/Description/Description.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { cn } from '@/utils'; 3 | 4 | import './Description.scss'; 5 | 6 | type Props = { 7 | className?: string; 8 | children?: ReactNode; 9 | title?: string | null; 10 | }; 11 | 12 | export default function Description({ children, title, className }: Props) { 13 | return ( 14 |
15 | {title &&

{title}

} 16 | {children &&

{children}

} 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /studio/src/components/Description/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Description } from './Description.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/Dialog/dialog.scss: -------------------------------------------------------------------------------- 1 | .dialog { 2 | &-portal { 3 | @apply fixed inset-0 z-[999] flex items-start justify-center sm:items-center; 4 | } 5 | &-overlay { 6 | @apply fixed inset-0 z-50 bg-base/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in; 7 | } 8 | &-content { 9 | @apply fixed z-50 grid w-full gap-4 rounded-b-lg bg-subtle p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-2xl sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0; 10 | } 11 | &-close { 12 | @apply absolute right-4 top-4 aspect-square w-10 h-10 text-icon-base hover:text-icon-secondary; 13 | } 14 | &-header { 15 | @apply flex flex-col space-y-1.5 text-center sm:text-left; 16 | } 17 | &-footer { 18 | @apply flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2; 19 | } 20 | &-title { 21 | @apply text-xl text-default font-semibold leading-8; 22 | } 23 | &-description { 24 | @apply text-sm text-subtle; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /studio/src/components/Dialog/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Dialog, 3 | DialogTrigger, 4 | DialogContent, 5 | DialogHeader, 6 | DialogFooter, 7 | DialogTitle, 8 | DialogDescription, 9 | DialogClose, 10 | } from './Dialog'; 11 | -------------------------------------------------------------------------------- /studio/src/components/Drawer/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Drawer, 3 | DrawerClose, 4 | DrawerContent, 5 | DrawerFooter, 6 | DrawerHeader, 7 | DrawerTitle, 8 | DrawerTrigger, 9 | } from './Drawer'; 10 | -------------------------------------------------------------------------------- /studio/src/components/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DropdownMenu, 3 | DropdownMenuTrigger, 4 | DropdownMenuContent, 5 | DropdownMenuItem, 6 | DropdownMenuCheckboxItem, 7 | DropdownMenuRadioItem, 8 | DropdownMenuLabel, 9 | DropdownMenuSeparator, 10 | DropdownMenuShortcut, 11 | DropdownMenuGroup, 12 | DropdownMenuPortal, 13 | DropdownMenuSub, 14 | DropdownMenuSubContent, 15 | DropdownMenuSubTrigger, 16 | DropdownMenuRadioGroup, 17 | DropdownMenuItemContainer, 18 | } from './Dropdown.tsx'; 19 | -------------------------------------------------------------------------------- /studio/src/components/EmptyState/EmptyState.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/utils'; 2 | import { Bell, Envelope, ShippingContainer, Users } from '@phosphor-icons/react'; 3 | import { ProjectorScreenChart } from '@phosphor-icons/react/dist/ssr'; 4 | import React, { ElementType } from 'react'; 5 | 6 | export type Modules = 'org' | 'invitation' | 'project' | 'container' | 'notification'; 7 | interface EmptyStateProps { 8 | title: string; 9 | children?: React.ReactNode; 10 | type: Modules; 11 | className?: string; 12 | } 13 | 14 | export default function EmptyState({ type, title, className, children }: EmptyStateProps) { 15 | const ICON_MAP: Record = { 16 | invitation: Envelope, 17 | org: Users, 18 | notification: Bell, 19 | project: ProjectorScreenChart, 20 | container: ShippingContainer, 21 | }; 22 | const Icon = ICON_MAP[type]; 23 | 24 | return ( 25 |
26 |
27 | {} 28 |
29 |

{title}

30 | {children} 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /studio/src/components/EmptyState/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EmptyState } from './EmptyState'; 2 | export type { Modules } from './EmptyState'; 3 | -------------------------------------------------------------------------------- /studio/src/components/Error/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowLeft } from '@phosphor-icons/react'; 2 | import { t } from 'i18next'; 3 | import React from 'react'; 4 | import { Button } from '../Button'; 5 | import { Svg404 } from '../icons'; 6 | 7 | export default function NotFound({ children }: { children?: React.ReactNode }) { 8 | return ( 9 |
10 |
11 | 12 |

{t('general.pageNotFound')}

13 |

{t('general.errorPageDescription')}

14 |

{t('general.pageNotFoundDescription')}

15 |
16 | 17 | {children ?? ( 18 | 22 | )} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /studio/src/components/Error/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Error } from './Error'; 2 | export { default as NotFound } from './NotFound'; 3 | -------------------------------------------------------------------------------- /studio/src/components/Form/form.scss: -------------------------------------------------------------------------------- 1 | .form { 2 | &-message { 3 | @apply text-error text-xs; 4 | } 5 | 6 | &-label-error { 7 | @apply text-error; 8 | } 9 | 10 | &-description { 11 | @apply text-subtle text-xs; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /studio/src/components/Form/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | useFormField, 3 | Form, 4 | FormItem, 5 | FormLabel, 6 | FormControl, 7 | FormDescription, 8 | FormMessage, 9 | FormField, 10 | FormFieldGroup, 11 | } from './Form'; 12 | -------------------------------------------------------------------------------- /studio/src/components/Header/header.scss: -------------------------------------------------------------------------------- 1 | .header-menu { 2 | --height: var(--header-menu-height); 3 | @apply bg-base flex justify-between items-center h-[var(--height)] py-1.5 px-3 border-b border-border; 4 | @apply sticky top-0 z-10; 5 | &-divider { 6 | @apply w-[1px] h-[40px] bg-border; 7 | } 8 | 9 | &-left { 10 | @apply flex gap-4 items-center; 11 | 12 | &-organization-select { 13 | @apply text-icon-base; 14 | } 15 | } 16 | 17 | &-right { 18 | @apply flex gap-3 items-center text-icon-base; 19 | &-nav { 20 | @apply hidden 2xl:flex gap-1 items-center; 21 | } 22 | &-actions { 23 | @apply flex items-center; 24 | 25 | .btn { 26 | @apply w-[32px] h-[32px] p-0 rounded-full; 27 | svg { 28 | @apply text-[17px]; 29 | } 30 | } 31 | 32 | &-user { 33 | @apply flex items-center gap-2; 34 | &-avatar { 35 | @apply rounded-full overflow-hidden; 36 | } 37 | &-name { 38 | @apply leading-6 font-normal text-sm; 39 | } 40 | } 41 | } 42 | 43 | .header-menu-divider { 44 | @apply hidden 2xl:block; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /studio/src/components/Header/index.ts: -------------------------------------------------------------------------------- 1 | export { Header } from './Header'; 2 | -------------------------------------------------------------------------------- /studio/src/components/InfoModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default as InfoModal } from './InfoModal.tsx'; 2 | -------------------------------------------------------------------------------- /studio/src/components/InfoModal/infoModal.scss: -------------------------------------------------------------------------------- 1 | .info-modal { 2 | @apply flex flex-col items-center gap-y-6; 3 | 4 | &-actions { 5 | @apply flex justify-center items-center gap-4; 6 | } 7 | &-text { 8 | @apply space-y-1; 9 | 10 | &-title { 11 | @apply text-xl text-default leading-8 font-semibold text-center; 12 | } 13 | 14 | &-desc { 15 | @apply text-sm leading-6 text-subtle text-center font-normal tracking-wide; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /studio/src/components/Input/Input.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/utils'; 2 | import * as React from 'react'; 3 | import './input.scss'; 4 | import { VariantProps, cva } from 'class-variance-authority'; 5 | 6 | const inputVariants = cva('input', { 7 | variants: { 8 | variant: { 9 | sm: 'input-sm', 10 | md: 'input-md', 11 | lg: 'input-lg', 12 | }, 13 | }, 14 | defaultVariants: { 15 | variant: 'md', 16 | }, 17 | }); 18 | export interface InputProps 19 | extends React.InputHTMLAttributes, 20 | VariantProps { 21 | type?: React.HTMLInputTypeAttribute; 22 | className?: string; 23 | error?: boolean; 24 | } 25 | 26 | const Input = React.forwardRef( 27 | ({ className, type, error, onChange, variant, ...props }, ref) => { 28 | const [value, setValue] = React.useState(props.value || ''); 29 | 30 | const handleChange = (event: React.ChangeEvent) => { 31 | setValue(event.target.value); 32 | if (onChange) { 33 | onChange(event); 34 | } 35 | }; 36 | return ( 37 | 46 | ); 47 | }, 48 | ); 49 | Input.displayName = 'Input'; 50 | 51 | export { Input }; 52 | -------------------------------------------------------------------------------- /studio/src/components/Input/Textarea.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { cn } from '@/utils'; 3 | 4 | export interface TextareaProps extends React.TextareaHTMLAttributes { 5 | showCount?: boolean; 6 | error?: boolean; 7 | className?: string; 8 | } 9 | 10 | const Textarea = React.forwardRef( 11 | ({ className, showCount, error, ...props }, ref) => { 12 | const [count, setCount] = useState(0); 13 | return ( 14 |
15 |