├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yaml │ ├── config.yml │ └── feature-request.yaml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ci-protobuf.yaml │ ├── ci.yaml │ ├── dashboard-ci.yaml │ ├── main.yaml │ ├── preview-comment.yaml │ ├── preview.yaml │ └── release.yaml ├── .gitignore ├── .golangci.yml ├── .local-dev ├── .gitignore ├── buildkitd │ └── buildkitd.toml ├── config │ ├── grafana.ini │ ├── loki.yaml │ ├── ns-auth.yaml │ ├── ns.yaml │ ├── prometheus.yml │ ├── promtail.yaml │ └── sablier.yaml ├── grafana │ └── provisioning │ │ └── datasources │ │ └── loki.yaml ├── keys │ ├── id_ed25519 │ └── id_ed25519.pub ├── local-storage │ ├── artifacts │ │ └── .keep │ └── buildlogs │ │ └── .keep ├── registry │ ├── entrypoint.sh │ └── gc.sh └── traefik │ └── middlewares.yaml ├── .local-ext-builder ├── Makefile ├── README.md └── manifest │ ├── builder-deployment.yaml │ ├── config │ ├── .gitignore │ ├── buildkitd.toml │ └── ns.yaml │ ├── coredns-configmap.yaml │ └── kustomization.yaml ├── .local-manifest ├── .gitignore ├── Makefile ├── README.md ├── alloy │ ├── kustomization.yaml │ └── values-ds.yaml ├── auth │ ├── deployment.yaml │ ├── dev-deployment.yaml │ ├── dev-middleware.yaml │ ├── dev-service.yaml │ ├── kustomization.yaml │ ├── middleware.yaml │ └── service.yaml ├── coredns-patch │ ├── coredns-configmap.yaml │ └── kustomization.yaml ├── crd │ └── kustomization.yaml ├── db │ ├── adminer-deployment.yaml │ ├── adminer-ingress-route.yaml │ ├── adminer-service.yaml │ ├── kustomization.yaml │ ├── mariadb-service.yaml │ ├── mariadb-stateful-set.yaml │ ├── mongo-service.yaml │ └── mongo-stateful-set.yaml ├── k3d.yaml ├── kustomization.yaml ├── monitor │ ├── grafana │ │ ├── ingress-route.yaml │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ └── stateful-set.yaml │ ├── kustomization.yaml │ ├── loki │ │ ├── ingress-route.yaml │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ └── stateful-set.yaml │ ├── victoria-logs │ │ ├── kustomization.yaml │ │ └── values.yaml │ └── victoria-metrics │ │ ├── cluster-role-binding.yaml │ │ ├── cluster-role.yaml │ │ ├── config │ │ └── prometheus.yml │ │ ├── kustomization.yaml │ │ ├── service-account.yaml │ │ ├── service.yaml │ │ └── stateful-set.yaml ├── ns-apps │ ├── auth-hard-middleware.yaml │ ├── auth-middleware.yaml │ ├── auth-soft-middleware.yaml │ ├── kustomization.yaml │ └── network-policy.yaml ├── ns-system │ ├── builder-deployment.yaml │ ├── controller-role-binding.yaml │ ├── controller-role.yaml │ ├── controller-service-account.yaml │ ├── controller-service.yaml │ ├── controller-ssh-service.yaml │ ├── controller-stateful-set.yaml │ ├── dashboard-deployment.yaml │ ├── dashboard-service.yaml │ ├── gateway-deployment.yaml │ ├── gateway-service.yaml │ ├── ingress-route.yaml │ ├── kustomization.yaml │ ├── migrate-deployment.yaml │ ├── ssgen-service.yaml │ └── ssgen-stateful-set.yaml ├── registry │ ├── deployment.yaml │ ├── frontend-deployment.yaml │ ├── frontend-ingress-route.yaml │ ├── frontend-service.yaml │ ├── ingress-route.yaml │ ├── kustomization.yaml │ └── service.yaml ├── sablier │ ├── kustomization.yaml │ ├── sablier-deployment.yaml │ ├── sablier-role-binding.yaml │ ├── sablier-role.yaml │ ├── sablier-service-account.yaml │ └── sablier-service.yaml └── traefik │ ├── dashboard-ingress-route.yaml │ ├── kustomization.yaml │ ├── traefik-cluster-role.yaml │ ├── traefik-role-binding.yaml │ ├── traefik-service-account.yaml │ ├── traefik-service.yaml │ └── traefik-stateful-set.yaml ├── .spectral.yml ├── .tbls.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── api └── proto │ └── neoshowcase │ └── protobuf │ ├── controller.proto │ ├── gateway.proto │ └── null.proto ├── buf.gen.go.yaml ├── buf.gen.ts.yaml ├── buf.yaml ├── cmd ├── auth-dev │ └── server.go ├── builder │ └── server.go ├── buildpack-helper │ └── server.go ├── config.go ├── controller │ └── server.go ├── gateway │ └── server.go ├── gitea-integration │ └── server.go ├── main.go ├── providers.go ├── ssgen │ └── server.go ├── wire.go └── wire_gen.go ├── codecov.yml ├── compose.yaml ├── dashboard ├── .dockerignore ├── .gitignore ├── .yarn │ └── .gitkeep ├── .yarnrc.yml ├── Caddyfile ├── Dockerfile ├── biome.json ├── index.html ├── package.json ├── public │ └── favicon.ico ├── src │ ├── App.tsx │ ├── animate.css │ ├── api │ │ └── neoshowcase │ │ │ └── protobuf │ │ │ ├── gateway_pb.ts │ │ │ └── null_pb.ts │ ├── assets │ │ ├── icons │ │ │ ├── appState │ │ │ │ ├── deploying.svg │ │ │ │ ├── error.svg │ │ │ │ ├── failed.svg │ │ │ │ ├── idle.svg │ │ │ │ └── running.svg │ │ │ └── check.svg │ │ ├── logo.svg │ │ └── logo_small.svg │ ├── components │ │ ├── UI │ │ │ ├── Badge.tsx │ │ │ ├── Button.tsx │ │ │ ├── CheckBoxIcon.tsx │ │ │ ├── Code.tsx │ │ │ ├── JumpButton.tsx │ │ │ ├── LogContainer.tsx │ │ │ ├── ModalDeleteConfirm.tsx │ │ │ ├── RadioIcon.tsx │ │ │ ├── Skeleton.module.css │ │ │ ├── Skeleton.tsx │ │ │ ├── StepProgress.tsx │ │ │ ├── TabRound.tsx │ │ │ ├── TextField.tsx │ │ │ ├── ToolTip.tsx │ │ │ ├── TooltipInfoIcon.tsx │ │ │ ├── URLText.tsx │ │ │ ├── UserAvater.tsx │ │ │ └── UserMenuButton.tsx │ │ ├── layouts │ │ │ ├── DataTable.tsx │ │ │ ├── ErrorView.tsx │ │ │ ├── FormBox.tsx │ │ │ ├── MainView.tsx │ │ │ ├── SideView.tsx │ │ │ ├── SuspenseContainer.tsx │ │ │ ├── WithHeader.tsx │ │ │ └── WithNav.tsx │ │ ├── styled-components.tsx │ │ └── templates │ │ │ ├── AppsNav.tsx │ │ │ ├── CheckBox.tsx │ │ │ ├── FormItem.tsx │ │ │ ├── Header.tsx │ │ │ ├── List.tsx │ │ │ ├── MobileNavigation.tsx │ │ │ ├── Nav.tsx │ │ │ ├── OwnerList.tsx │ │ │ ├── RadioGroups.tsx │ │ │ ├── Select.tsx │ │ │ ├── SettingSkeleton.tsx │ │ │ ├── app │ │ │ ├── AppBranchResolution.tsx │ │ │ ├── AppConfigInfo.tsx │ │ │ ├── AppDeployInfo.tsx │ │ │ ├── AppLatestBuilds.tsx │ │ │ ├── AppMetrics.tsx │ │ │ ├── AppNav.tsx │ │ │ ├── AppRow.tsx │ │ │ ├── AppStatusIcon.tsx │ │ │ ├── AppsFilter.tsx │ │ │ ├── ContainerLog.tsx │ │ │ └── ContainerLogExport.tsx │ │ │ ├── build │ │ │ ├── ArtifactRow.tsx │ │ │ ├── BuildLog.tsx │ │ │ ├── BuildRow.tsx │ │ │ ├── BuildStatusIcon.tsx │ │ │ ├── BuildStatusTable.tsx │ │ │ └── RuntimeImageRow.tsx │ │ │ └── repo │ │ │ ├── ReposFilter.tsx │ │ │ ├── RepositoryNav.tsx │ │ │ └── RepositoryRow.tsx │ ├── features │ │ ├── application │ │ │ ├── components │ │ │ │ └── form │ │ │ │ │ ├── BuildConfigForm.tsx │ │ │ │ │ ├── CreateAppForm.tsx │ │ │ │ │ ├── EnvVarConfigForm.tsx │ │ │ │ │ ├── GeneralConfigForm.tsx │ │ │ │ │ ├── GeneralStep.tsx │ │ │ │ │ ├── PortForwardingForm.tsx │ │ │ │ │ ├── RepositoryStep.tsx │ │ │ │ │ ├── WebsiteStep.tsx │ │ │ │ │ ├── WebsitesConfigForm.tsx │ │ │ │ │ ├── config │ │ │ │ │ ├── BuildTypeField.tsx │ │ │ │ │ ├── ConfigField.tsx │ │ │ │ │ ├── build │ │ │ │ │ │ ├── BuildConfigField.tsx │ │ │ │ │ │ ├── BuildpackConfigField.tsx │ │ │ │ │ │ ├── CmdConfigField.tsx │ │ │ │ │ │ └── DockerfileConfigField.tsx │ │ │ │ │ └── deploy │ │ │ │ │ │ ├── DeployConfigField.tsx │ │ │ │ │ │ ├── RuntimeConfigField.tsx │ │ │ │ │ │ └── StaticConfigField.tsx │ │ │ │ │ ├── general │ │ │ │ │ ├── BranchField.tsx │ │ │ │ │ ├── NameField.tsx │ │ │ │ │ └── RepositoryIdField.tsx │ │ │ │ │ ├── portForwarding │ │ │ │ │ └── PortField.tsx │ │ │ │ │ └── website │ │ │ │ │ ├── UrlField.tsx │ │ │ │ │ └── WebsiteFieldGroup.tsx │ │ │ ├── provider │ │ │ │ └── applicationFormProvider.tsx │ │ │ └── schema │ │ │ │ ├── applicationConfigSchema.ts │ │ │ │ ├── applicationSchema.test.ts │ │ │ │ ├── applicationSchema.ts │ │ │ │ ├── envVarSchema.test.ts │ │ │ │ ├── envVarSchema.ts │ │ │ │ ├── portPublicationSchema.ts │ │ │ │ └── websiteSchema.ts │ │ └── repository │ │ │ ├── components │ │ │ ├── AuthConfigForm.tsx │ │ │ ├── AuthMethodField.tsx │ │ │ ├── CreateForm.tsx │ │ │ ├── DeleteForm.tsx │ │ │ └── GeneralConfigForm.tsx │ │ │ ├── provider │ │ │ └── repositoryFormProvider.tsx │ │ │ └── schema │ │ │ ├── repositorySchema.test.ts │ │ │ └── repositorySchema.ts │ ├── global.css │ ├── images │ │ └── logo.svg │ ├── index.tsx │ ├── libs │ │ ├── api.ts │ │ ├── application.tsx │ │ ├── branchesSuggestion.ts │ │ ├── buffers.ts │ │ ├── casing.ts │ │ ├── clipboard.ts │ │ ├── clsx.ts │ │ ├── colorOverlay.ts │ │ ├── download.ts │ │ ├── format.tsx │ │ ├── localStore.ts │ │ ├── random.ts │ │ ├── schemaUtil.ts │ │ ├── scroll.ts │ │ ├── sleep.ts │ │ ├── timestamp.ts │ │ ├── unique.ts │ │ ├── unreachable.ts │ │ ├── useAllUsers.tsx │ │ ├── useFormContext.tsx │ │ └── useModal.tsx │ ├── pages │ │ ├── apps.tsx │ │ ├── apps │ │ │ ├── [id].tsx │ │ │ ├── [id] │ │ │ │ ├── builds.tsx │ │ │ │ ├── builds │ │ │ │ │ └── [id].tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── settings.tsx │ │ │ │ └── settings │ │ │ │ │ ├── build.tsx │ │ │ │ │ ├── envVars.tsx │ │ │ │ │ ├── general.tsx │ │ │ │ │ ├── owners.tsx │ │ │ │ │ ├── portForwarding.tsx │ │ │ │ │ └── urls.tsx │ │ │ └── new.tsx │ │ ├── builds.tsx │ │ ├── repos │ │ │ ├── [id].tsx │ │ │ ├── [id] │ │ │ │ ├── index.tsx │ │ │ │ ├── settings.tsx │ │ │ │ └── settings │ │ │ │ │ ├── authorization.tsx │ │ │ │ │ ├── general.tsx │ │ │ │ │ └── owners.tsx │ │ │ └── new.tsx │ │ └── settings.tsx │ └── routes.tsx ├── tsconfig.json ├── uno.config.ts ├── vite.config.mts └── yarn.lock ├── dbconfig.yml ├── docs ├── architecture.drawio ├── architecture.md ├── architecture.png ├── components.md ├── dbschema │ ├── README.md │ ├── application_config.md │ ├── application_owners.md │ ├── applications.md │ ├── artifacts.md │ ├── builds.md │ ├── environments.md │ ├── port_publications.md │ ├── repositories.md │ ├── repository_auth.md │ ├── repository_commits.md │ ├── repository_owners.md │ ├── runtime_images.md │ ├── user_keys.md │ ├── users.md │ └── websites.md ├── deployment.md └── development.md ├── generate.go ├── go.mod ├── go.sum ├── migrations ├── entrypoint.sh └── schema.sql ├── pkg ├── domain │ ├── app.go │ ├── app_artifact.go │ ├── app_build.go │ ├── app_build_config.go │ ├── app_build_config_test.go │ ├── app_environment.go │ ├── app_ports.go │ ├── app_repository.go │ ├── app_repository_commit.go │ ├── app_repository_test.go │ ├── app_runtime_image.go │ ├── app_test.go │ ├── app_user.go │ ├── app_website.go │ ├── app_website_test.go │ ├── auth.go │ ├── backend.go │ ├── backend_test.go │ ├── builder.go │ ├── builder │ │ ├── backend.go │ │ └── registry.go │ ├── component.go │ ├── dbmanager.go │ ├── environment.go │ ├── git.go │ ├── idgen.go │ ├── log.go │ ├── metrics.go │ ├── pubsub.go │ ├── repository.go │ ├── ssgen.go │ ├── storage.go │ ├── system.go │ └── web │ │ ├── auth.go │ │ ├── client_h2c.go │ │ ├── consts.go │ │ └── server_h2c.go ├── infrastructure │ ├── backend │ │ ├── dockerimpl │ │ │ ├── backend.go │ │ │ ├── backend_test.go │ │ │ ├── config.go │ │ │ ├── exec.go │ │ │ ├── ingress.go │ │ │ ├── list_containers.go │ │ │ ├── list_containers_test.go │ │ │ ├── synchronize.go │ │ │ ├── synchronize_runtime.go │ │ │ ├── synchronize_ss.go │ │ │ └── synchronize_test.go │ │ └── k8simpl │ │ │ ├── backend.go │ │ │ ├── backend_test.go │ │ │ ├── config.go │ │ │ ├── exec.go │ │ │ ├── helper.go │ │ │ ├── ingress.go │ │ │ ├── list_containers.go │ │ │ ├── synchronize.go │ │ │ ├── synchronize_runtime.go │ │ │ ├── synchronize_ss.go │ │ │ └── synchronize_test.go │ ├── buildpack │ │ ├── backend.go │ │ └── config.go │ ├── dbmanager │ │ ├── manager_test.go │ │ ├── mariadb.go │ │ ├── mariadb_test.go │ │ ├── mongodb.go │ │ └── mongodb_test.go │ ├── git │ │ └── service.go │ ├── grpc │ │ ├── api_app_build_service.go │ │ ├── api_app_config_service.go │ │ ├── api_app_info_service.go │ │ ├── api_app_service.go │ │ ├── api_repository_service.go │ │ ├── api_service.go │ │ ├── api_system_service.go │ │ ├── api_user_service.go │ │ ├── auth_interceptor.go │ │ ├── buildpack_helper_service.go │ │ ├── buildpack_helper_service_client.go │ │ ├── cache_interceptor.go │ │ ├── controller_builder_service.go │ │ ├── controller_builder_service_client.go │ │ ├── controller_gitea_integration_service.go │ │ ├── controller_gitea_integration_service_client.go │ │ ├── controller_service.go │ │ ├── controller_service_client.go │ │ ├── controller_ssgen_service.go │ │ ├── controller_ssgen_service_client.go │ │ ├── pb │ │ │ ├── controller.pb.go │ │ │ ├── gateway.pb.go │ │ │ ├── null.pb.go │ │ │ └── pbconnect │ │ │ │ ├── controller.connect.go │ │ │ │ └── gateway.connect.go │ │ ├── pbconvert │ │ │ ├── application.go │ │ │ ├── application_config.go │ │ │ ├── artifact.go │ │ │ ├── available_domain.go │ │ │ ├── available_port.go │ │ │ ├── build.go │ │ │ ├── builder.go │ │ │ ├── environment.go │ │ │ ├── metrics.go │ │ │ ├── output.go │ │ │ ├── port_publication.go │ │ │ ├── repository.go │ │ │ ├── repository_commit.go │ │ │ ├── runtime_image.go │ │ │ ├── system.go │ │ │ ├── timestamp.go │ │ │ ├── user.go │ │ │ └── website.go │ │ └── token_auth_interceptor.go │ ├── log │ │ ├── loki │ │ │ ├── streamer.go │ │ │ └── types.go │ │ └── victorialogs │ │ │ ├── streamer.go │ │ │ └── types.go │ ├── metrics │ │ └── prometheus │ │ │ └── client.go │ ├── registry │ │ └── registry.go │ ├── repository │ │ ├── application.go │ │ ├── artifact.go │ │ ├── build.go │ │ ├── config.go │ │ ├── conn.go │ │ ├── environment.go │ │ ├── error.go │ │ ├── git_repository.go │ │ ├── models │ │ │ ├── application_config.go │ │ │ ├── applications.go │ │ │ ├── artifacts.go │ │ │ ├── boil_queries.go │ │ │ ├── boil_table_names.go │ │ │ ├── boil_types.go │ │ │ ├── boil_view_names.go │ │ │ ├── builds.go │ │ │ ├── environments.go │ │ │ ├── mysql_upsert.go │ │ │ ├── port_publications.go │ │ │ ├── repositories.go │ │ │ ├── repository_auth.go │ │ │ ├── repository_commits.go │ │ │ ├── runtime_images.go │ │ │ ├── user_keys.go │ │ │ ├── users.go │ │ │ └── websites.go │ │ ├── repoconvert │ │ │ ├── application.go │ │ │ ├── application_config.go │ │ │ ├── artifact.go │ │ │ ├── build.go │ │ │ ├── environment.go │ │ │ ├── port_publication.go │ │ │ ├── repository.go │ │ │ ├── repository_commit.go │ │ │ ├── runtime_image.go │ │ │ ├── user.go │ │ │ └── website.go │ │ ├── repository_commit.go │ │ ├── runtime_image.go │ │ └── user.go │ ├── staticserver │ │ ├── builtin │ │ │ └── server.go │ │ └── caddy │ │ │ └── server.go │ ├── storage │ │ ├── local.go │ │ ├── s3.go │ │ └── swift.go │ └── webhook │ │ ├── gitea.go │ │ ├── github.go │ │ └── receiver.go ├── usecase │ ├── apiserver │ │ ├── app_build_service.go │ │ ├── app_config_service.go │ │ ├── app_info_service.go │ │ ├── app_service.go │ │ ├── errors.go │ │ ├── repository_service.go │ │ ├── service.go │ │ ├── system_service.go │ │ └── user_service.go │ ├── builder │ │ ├── build.go │ │ ├── build_buildkit.go │ │ ├── build_buildpack.go │ │ ├── build_clone.go │ │ ├── build_save_artifact.go │ │ ├── build_static.go │ │ ├── mock │ │ │ └── builder.go │ │ ├── service.go │ │ └── state.go │ ├── cdservice │ │ ├── app_deploy_helper.go │ │ ├── container_state_mutator.go │ │ └── service.go │ ├── cleaner │ │ └── service.go │ ├── commit-fetcher │ │ └── service.go │ ├── gitea-integration │ │ ├── integration.go │ │ └── sync.go │ ├── healthcheck │ │ └── healthcheck.go │ ├── logstream │ │ ├── build_log.go │ │ └── service.go │ ├── repofetcher │ │ └── service.go │ ├── ssgen │ │ └── generator.go │ ├── sshserver │ │ └── ssh_server.go │ └── systeminfo │ │ └── service.go └── util │ ├── cli │ ├── config.go │ ├── env.go │ ├── flag.go │ ├── print_conf.go │ ├── signal.go │ └── version.go │ ├── ds │ ├── channel.go │ ├── doc.go │ ├── map.go │ ├── mutex.go │ ├── mutex_test.go │ ├── slice.go │ └── slice_test.go │ ├── fig │ ├── color.go │ ├── fonts.go │ ├── fonts │ │ └── larry3d.flf │ └── render.go │ ├── fmtutil │ └── time.go │ ├── hash │ └── xxh3.go │ ├── loop │ └── loop.go │ ├── mapper │ └── mapper.go │ ├── optional │ ├── of.go │ ├── of_test.go │ └── sql.go │ ├── random │ ├── random.go │ └── random_test.go │ ├── retry │ └── retry.go │ ├── scutil │ ├── coalescer.go │ └── wrap.go │ └── tarfs │ ├── compress.go │ ├── extract.go │ └── extract_test.go ├── sablier ├── Dockerfile └── themes │ └── neoshowcase.html ├── sqlboiler.toml └── tools.go /.dockerignore: -------------------------------------------------------------------------------- 1 | /.github/ 2 | /.idea/ 3 | /.git/ 4 | /.vscode/ 5 | /api/ 6 | /dashboard/ 7 | /docs/ 8 | /img/ 9 | /.local-dev/ 10 | 11 | .dockerignore 12 | .gitignore 13 | .golangci.yml 14 | .spectral.yml 15 | .tbls.yml 16 | dbconfig.yml 17 | docker-compose.yaml 18 | Dockerfile 19 | LICENSE 20 | README.md 21 | sqlboiler.toml 22 | 23 | .DS_Store 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: 機能追加 2 | description: 機能追加リクエストのテンプレート 3 | title: "[Feature]: " 4 | labels: ["kind/feature"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: 問題の説明 9 | description: 今の機能に問題がある場合、その説明 10 | placeholder: ダッシュボードのアプリ一覧にxxが表示されていないため見にくい。 11 | validations: 12 | required: false 13 | - type: textarea 14 | attributes: 15 | label: 新機能の説明 16 | description: 新機能のを簡潔な提案 17 | placeholder: ダッシュボードのアプリ一覧にxxを表示してほしい。 18 | validations: 19 | required: false 20 | - type: textarea 21 | attributes: 22 | label: その他の案 23 | description: 上の提案以外の代替案がある場合 24 | placeholder: アプリの詳細ページにxxを表示してほしい。 25 | validations: 26 | required: false 27 | - type: textarea 28 | attributes: 29 | label: 参考資料 30 | description: その他の文脈や参考資料のリンクなど 31 | validations: 32 | required: false 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## なぜやるか 4 | 5 | 6 | 7 | ## やったこと 8 | 9 | 10 | ## やらなかったこと 11 | 12 | 13 | ## 資料 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/ci-protobuf.yaml: -------------------------------------------------------------------------------- 1 | name: Protobuf CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | paths: 8 | - "api/**" 9 | pull_request: 10 | paths: 11 | - "api/**" 12 | 13 | jobs: 14 | gen-diff: 15 | name: Check diff 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Install buf 20 | run: make init-buf 21 | - name: Generate code 22 | run: make gen-proto 23 | - name: Check diff 24 | run: git diff --exit-code 25 | 26 | format: 27 | name: Format 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Install buf 32 | run: make init-buf 33 | - name: Format proto files 34 | run: | 35 | if ! buf format --diff --exit-code; then 36 | echo "::error::Run 'buf format -w' to fix" 37 | exit 1 38 | fi 39 | 40 | lint: 41 | name: Lint 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v4 45 | - name: Install buf 46 | run: make init-buf 47 | - name: Lint proto files 48 | run: buf lint 49 | -------------------------------------------------------------------------------- /.github/workflows/preview-comment.yaml: -------------------------------------------------------------------------------- 1 | name: Comment preview environment URL 2 | 3 | on: 4 | # pull_request_target を使うにあたって https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ の一読を推奨 5 | pull_request_target: 6 | types: 7 | - opened 8 | 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | 13 | jobs: 14 | comment: 15 | name: Comment preview environment URL 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/github-script@v7 19 | with: 20 | script: | 21 | const url = `https://${context.payload.pull_request.number}.ns-preview.trapti.tech/` 22 | const msg = ` 23 | Preview (prod backend + PR dashboard) → ${url} 24 | ` 25 | 26 | await github.rest.issues.createComment({ 27 | owner: context.repo.owner, 28 | repo: context.repo.repo, 29 | issue_number: context.payload.pull_request.number, 30 | body: msg 31 | }); 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .idea/ 18 | .vscode/ 19 | .tool-versions 20 | 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | linters: 3 | default: none 4 | enable: 5 | - govet 6 | - ineffassign 7 | - staticcheck 8 | - unused 9 | exclusions: 10 | generated: lax 11 | presets: 12 | - comments 13 | - common-false-positives 14 | - legacy 15 | - std-error-handling 16 | formatters: 17 | enable: 18 | - gofmt 19 | - goimports 20 | exclusions: 21 | generated: lax 22 | -------------------------------------------------------------------------------- /.local-dev/.gitignore: -------------------------------------------------------------------------------- 1 | /buildkitd-sock 2 | /dind 3 | # needs uid 472 gid 472 4 | /grafana 5 | /k3d-cache-registry 6 | /k3d-local-storage 7 | /local-storage 8 | # needs uid 10001 gid 10001 9 | /loki 10 | /mongo 11 | /mysql 12 | /promtail 13 | /repositories 14 | /registry-data 15 | /staticsite 16 | /traefik/* 17 | !/traefik/middlewares.yaml 18 | /victoria-metrics 19 | -------------------------------------------------------------------------------- /.local-dev/buildkitd/buildkitd.toml: -------------------------------------------------------------------------------- 1 | [registry."registry.local"] 2 | http = true 3 | 4 | [worker.oci] 5 | enabled = true 6 | gc = true 7 | gckeepstorage = 9000 8 | 9 | [[worker.oci.gcpolicy]] 10 | all = true 11 | keepBytes = 1024000000 12 | -------------------------------------------------------------------------------- /.local-dev/config/grafana.ini: -------------------------------------------------------------------------------- 1 | [server] 2 | http_port = 3000 3 | domain = libra.local.trapti.tech 4 | root_url = http://libra.local.trapti.tech 5 | enable_gzip = true 6 | 7 | [auth.anonymous] 8 | enabled = true 9 | org_name = Main Org. 10 | org_role = Admin 11 | -------------------------------------------------------------------------------- /.local-dev/config/loki.yaml: -------------------------------------------------------------------------------- 1 | auth_enabled: false 2 | 3 | server: 4 | http_listen_port: 3100 5 | 6 | common: 7 | path_prefix: /var/lib/loki 8 | 9 | ingester: 10 | lifecycler: 11 | address: 127.0.0.1 12 | ring: 13 | kvstore: 14 | store: inmemory 15 | replication_factor: 1 16 | final_sleep: 0s 17 | chunk_idle_period: 5m 18 | chunk_retain_period: 30s 19 | wal: 20 | enabled: true 21 | dir: /var/lib/loki/wal 22 | 23 | schema_config: 24 | configs: 25 | - from: "2024-04-01" 26 | object_store: filesystem 27 | store: tsdb 28 | schema: v13 29 | index: 30 | prefix: index_ 31 | period: 24h 32 | 33 | storage_config: 34 | filesystem: 35 | directory: /var/lib/loki/chunks 36 | 37 | limits_config: 38 | reject_old_samples: true 39 | reject_old_samples_max_age: 168h 40 | 41 | table_manager: 42 | retention_deletes_enabled: true 43 | # 28 days retention 44 | retention_period: 672h 45 | -------------------------------------------------------------------------------- /.local-dev/config/ns-auth.yaml: -------------------------------------------------------------------------------- 1 | secret: A0iQP-1A_RLBqaTOfLs6fhUzNWO29bIB 2 | insecure-cookie: true 3 | log-level: info 4 | 5 | auth-host: auth.local.trapti.tech 6 | cookie-domains: 7 | - local.trapti.tech 8 | cookieName: "_forward_auth" 9 | info-fields: 10 | - name 11 | 12 | provider: generic-oauth 13 | 14 | providers: 15 | generic-oauth: 16 | auth-url: https://q.toki317.dev/api/v3/oauth2/authorize 17 | token-url: https://q.toki317.dev/api/v3/oauth2/token 18 | user-url: https://q.toki317.dev/api/v3/users/me 19 | client-id: RFOhjMbt3VsR75uaMwhWH21pUkhfE8DqVeXT 20 | client-secret: N4rzl3JlQJprKNNi2EYl2biifjKprB9HS9PO 21 | scopes: 22 | - read 23 | 24 | rule: 25 | soft: 26 | action: soft-auth 27 | route-rule: Header(`X-Forward-Auth-Type`, `soft`) 28 | hard: 29 | action: auth 30 | route-rule: rule.hard.rule = Header(`X-Forward-Auth-Type`, `hard`) 31 | 32 | headers: 33 | default: 34 | name: X-Showcase-User 35 | source: name 36 | -------------------------------------------------------------------------------- /.local-dev/config/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 1m 3 | scrape_timeout: 10s 4 | 5 | scrape_configs: 6 | - job_name: cadvisor 7 | static_configs: 8 | - targets: 9 | - cadvisor:8080 10 | relabel_configs: 11 | - source_labels: [__address__] 12 | target_label: instance 13 | regex: ^(private\.|)([^:]+):.+ 14 | replacement: ${2} 15 | -------------------------------------------------------------------------------- /.local-dev/config/promtail.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | http_listen_address: 0.0.0.0 3 | http_listen_port: 9080 4 | 5 | positions: 6 | filename: /var/run/promtail/positions.yaml 7 | 8 | clients: 9 | - url: http://loki:3100/loki/api/v1/push 10 | 11 | scrape_configs: 12 | - job_name: containers 13 | docker_sd_configs: 14 | - host: unix:///var/run/docker.sock 15 | relabel_configs: 16 | - action: labelmap 17 | regex: __meta_docker_container_label_(.+) 18 | replacement: $1 19 | - action: replace 20 | source_labels: 21 | - __meta_docker_container_name 22 | target_label: container_name 23 | - action: replace 24 | source_labels: 25 | - __meta_docker_container_log_stream 26 | target_label: log_stream 27 | -------------------------------------------------------------------------------- /.local-dev/config/sablier.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | name: kubernetes 3 | sessions: 4 | default-duration: 5m 5 | expiration-interval: 20s 6 | logging: 7 | level: trace 8 | strategy: 9 | dynamic: 10 | custom-themes-path: /etc/sablier/themes 11 | default-theme: neoshowcase 12 | default-refresh-frequency: 5s 13 | -------------------------------------------------------------------------------- /.local-dev/grafana/provisioning/datasources/loki.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Loki 5 | type: loki 6 | access: proxy 7 | orgId: 1 8 | url: http://loki:3100 9 | editable: true 10 | -------------------------------------------------------------------------------- /.local-dev/keys/id_ed25519: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 3 | QyNTUxOQAAACAC1iAC54T1ooCQN545XcXDPdTxJEEDdt9TsO3MwoPMwwAAAJCX+efxl/nn 4 | 8QAAAAtzc2gtZWQyNTUxOQAAACAC1iAC54T1ooCQN545XcXDPdTxJEEDdt9TsO3MwoPMww 5 | AAAEA+FzwWKIYduEDOqkEOZ2wmxZWPc2wpZeWj+J8e3Q6x0QLWIALnhPWigJA3njldxcM9 6 | 1PEkQQN231Ow7czCg8zDAAAADG1vdG9AbW90by13cwE= 7 | -----END OPENSSH PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /.local-dev/keys/id_ed25519.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIALWIALnhPWigJA3njldxcM91PEkQQN231Ow7czCg8zD moto@moto-ws 2 | -------------------------------------------------------------------------------- /.local-dev/local-storage/artifacts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traPtitech/NeoShowcase/830b8e9e955ff5f8efee55ca53f371c5f6dcafec/.local-dev/local-storage/artifacts/.keep -------------------------------------------------------------------------------- /.local-dev/local-storage/buildlogs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traPtitech/NeoShowcase/830b8e9e955ff5f8efee55ca53f371c5f6dcafec/.local-dev/local-storage/buildlogs/.keep -------------------------------------------------------------------------------- /.local-dev/registry/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | CONFIG="$1" 6 | ${GC_SCRIPT:-/gc.sh} "$CONFIG" & 7 | 8 | set -- registry serve "$@" 9 | exec "$@" 10 | -------------------------------------------------------------------------------- /.local-dev/registry/gc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while true; do 4 | sleep 3600 5 | echo "[gc.sh] Running garbage collection..." 6 | registry garbage-collect "$1" --delete-untagged 7 | echo "[gc.sh] Garbage collection complete." 8 | done 9 | -------------------------------------------------------------------------------- /.local-dev/traefik/middlewares.yaml: -------------------------------------------------------------------------------- 1 | http: 2 | middlewares: 3 | nsapp_auth_soft: 4 | headers: 5 | customRequestHeaders: 6 | X-Forward-Auth-Type: soft 7 | nsapp_auth_hard: 8 | headers: 9 | customRequestHeaders: 10 | X-Forward-Auth-Type: hard 11 | nsapp_auth: 12 | forwardAuth: 13 | address: "http://ns-auth:4181/" 14 | authResponseHeaders: 15 | - "X-Showcase-User" 16 | ns_auth: 17 | forwardAuth: 18 | address: "http://ns-auth:4181/" 19 | authResponseHeaders: 20 | - "X-Showcase-User" 21 | ns_auth_dev: 22 | forwardAuth: 23 | address: "http://ns-auth-dev:4181/" 24 | authResponseHeaders: 25 | - "X-Showcase-User" 26 | -------------------------------------------------------------------------------- /.local-ext-builder/manifest/config/.gitignore: -------------------------------------------------------------------------------- 1 | controller-token.txt 2 | controller-url.txt 3 | known_hosts 4 | -------------------------------------------------------------------------------- /.local-ext-builder/manifest/config/buildkitd.toml: -------------------------------------------------------------------------------- 1 | [registry."registry.local"] 2 | http = true 3 | 4 | [worker.oci] 5 | enabled = true 6 | gc = true 7 | gckeepstorage = 9000 8 | 9 | [[worker.oci.gcpolicy]] 10 | all = true 11 | keepBytes = 1024000000 12 | -------------------------------------------------------------------------------- /.local-ext-builder/manifest/config/ns.yaml: -------------------------------------------------------------------------------- 1 | components: 2 | builder: 3 | buildkit: 4 | address: unix:///run/buildkit/buildkitd.sock 5 | buildpack: 6 | helper: 7 | address: http://localhost:1235 8 | listenPort: 1235 9 | remoteDir: /workspace 10 | platformAPI: "0.11" 11 | # controller: 12 | # url: # e.g. http://host.k3d.internal:10000 13 | priority: 10 14 | 15 | # controller: 16 | # token: 17 | -------------------------------------------------------------------------------- /.local-ext-builder/manifest/coredns-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | 7 | data: 8 | # For local development: resolve "registry.local" to host ip 9 | # k3d default + rewrite plugin 10 | # see https://zenn.dev/toshikish/articles/7f555dbf1b4b7d for rewrite plugin 11 | Corefile: | 12 | .:53 { 13 | rewrite name registry.local host.k3d.internal 14 | errors 15 | health 16 | ready 17 | kubernetes cluster.local in-addr.arpa ip6.arpa { 18 | pods insecure 19 | fallthrough in-addr.arpa ip6.arpa 20 | } 21 | hosts /etc/coredns/NodeHosts { 22 | ttl 60 23 | reload 15s 24 | fallthrough 25 | } 26 | prometheus :9153 27 | forward . /etc/resolv.conf 28 | cache 30 29 | loop 30 | reload 31 | loadbalance 32 | import /etc/coredns/custom/*.override 33 | } 34 | import /etc/coredns/custom/*.server 35 | -------------------------------------------------------------------------------- /.local-ext-builder/manifest/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - builder-deployment.yaml 3 | - coredns-configmap.yaml 4 | 5 | configMapGenerator: 6 | - name: known-hosts 7 | files: 8 | - ./config/known_hosts 9 | - name: ns-config 10 | files: 11 | - ./config/ns.yaml 12 | - ./config/buildkitd.toml 13 | - name: custom-config 14 | files: 15 | - NS_COMPONENTS_BUILDER_CONTROLLER_URL=./config/controller-url.txt 16 | 17 | secretGenerator: 18 | - name: token 19 | files: 20 | - NS_COMPONENTS_CONTROLLER_TOKEN=./config/controller-token.txt 21 | -------------------------------------------------------------------------------- /.local-manifest/.gitignore: -------------------------------------------------------------------------------- 1 | charts 2 | -------------------------------------------------------------------------------- /.local-manifest/README.md: -------------------------------------------------------------------------------- 1 | # manifest 2 | 3 | Manifest files required to deploy NeoShowcase locally using k8s backend 4 | 5 | ## Usage 6 | 7 | tl;dr: 8 | Run `make up` / `make down` 9 | 10 | ### Spin up 11 | 12 | 1. `make k3d-up` to spin up the k3d cluster 13 | 2. `make import` to build and import NeoShowcase images 14 | 3. `make apply` to apply the manifest files 15 | 16 | ### Managing 17 | 18 | - To tail cluster events (pulling image, creating container etc.), `make events` 19 | - To tail specific pod / container logs, `make logs NAMESPACE=ns-system APP=ns-controller` 20 | - For more, visit http://grafana.local.trapti.tech/ and see centralized Loki logs 21 | - Go to http://localhost:8080/ to view traefik dashboard 22 | 23 | #### Optional recommended tools 24 | 25 | - k9s: https://k9scli.io/ 26 | - Lens: https://k8slens.dev/ 27 | 28 | ### Spin down 29 | 30 | 1. `make k3d-down` to take down the k3d cluster completely 31 | -------------------------------------------------------------------------------- /.local-manifest/alloy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: alloy 2 | 3 | helmCharts: 4 | - repo: https://grafana.github.io/helm-charts 5 | name: alloy 6 | version: "1.0.3" 7 | includeCRDs: true 8 | namespace: alloy 9 | releaseName: alloy-ds 10 | valuesFile: values-ds.yaml 11 | -------------------------------------------------------------------------------- /.local-manifest/auth/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ns-auth 5 | 6 | spec: 7 | replicas: 1 8 | revisionHistoryLimit: 0 9 | selector: 10 | matchLabels: 11 | app: ns-auth 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: ns-auth 17 | spec: 18 | enableServiceLinks: false 19 | automountServiceAccountToken: false 20 | 21 | volumes: 22 | - name: project 23 | hostPath: 24 | path: /work 25 | 26 | containers: 27 | - name: ns-auth 28 | image: ghcr.io/traptitech/traefik-forward-auth:3.2.1 29 | args: 30 | - --config=/config.yaml 31 | ports: 32 | - containerPort: 4181 33 | name: http 34 | volumeMounts: 35 | - name: project 36 | subPath: .local-dev/config/ns-auth.yaml 37 | mountPath: /config.yaml 38 | -------------------------------------------------------------------------------- /.local-manifest/auth/dev-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ns-auth-dev 5 | 6 | spec: 7 | replicas: 1 8 | revisionHistoryLimit: 0 9 | selector: 10 | matchLabels: 11 | app: ns-auth-dev 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: ns-auth-dev 17 | spec: 18 | containers: 19 | - name: ns-auth-dev 20 | image: ghcr.io/traptitech/ns-auth-dev:main 21 | imagePullPolicy: Never 22 | ports: 23 | - containerPort: 4181 24 | name: http 25 | env: 26 | - name: HEADER 27 | value: X-Showcase-User 28 | - name: USER 29 | value: toki 30 | -------------------------------------------------------------------------------- /.local-manifest/auth/dev-middleware.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: Middleware 3 | metadata: 4 | name: ns-auth-dev 5 | 6 | spec: 7 | forwardAuth: 8 | address: http://ns-auth-dev.auth.svc.cluster.local/ 9 | authResponseHeaders: 10 | - X-Showcase-User 11 | -------------------------------------------------------------------------------- /.local-manifest/auth/dev-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-auth-dev 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-auth-dev 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/auth/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: auth 2 | 3 | resources: 4 | - deployment.yaml 5 | - dev-deployment.yaml 6 | - dev-middleware.yaml 7 | - dev-service.yaml 8 | - middleware.yaml 9 | - service.yaml 10 | -------------------------------------------------------------------------------- /.local-manifest/auth/middleware.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: Middleware 3 | metadata: 4 | name: ns-auth 5 | 6 | spec: 7 | forwardAuth: 8 | address: http://ns-auth.auth.svc.cluster.local/ 9 | authResponseHeaders: 10 | - X-Showcase-User 11 | -------------------------------------------------------------------------------- /.local-manifest/auth/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-auth 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-auth 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/coredns-patch/coredns-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | 7 | data: 8 | # For local development: resolve "registry.local" to host ip 9 | # k3d default + rewrite plugin 10 | # see https://zenn.dev/toshikish/articles/7f555dbf1b4b7d for rewrite plugin 11 | Corefile: | 12 | .:53 { 13 | rewrite name registry.local host.k3d.internal 14 | errors 15 | health 16 | ready 17 | kubernetes cluster.local in-addr.arpa ip6.arpa { 18 | pods insecure 19 | fallthrough in-addr.arpa ip6.arpa 20 | } 21 | hosts /etc/coredns/NodeHosts { 22 | ttl 60 23 | reload 15s 24 | fallthrough 25 | } 26 | prometheus :9153 27 | forward . /etc/resolv.conf 28 | cache 30 29 | loop 30 | reload 31 | loadbalance 32 | import /etc/coredns/custom/*.override 33 | } 34 | import /etc/coredns/custom/*.server 35 | -------------------------------------------------------------------------------- /.local-manifest/coredns-patch/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - coredns-configmap.yaml 3 | -------------------------------------------------------------------------------- /.local-manifest/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - https://raw.githubusercontent.com/traefik/traefik/v3.0.0/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml 3 | -------------------------------------------------------------------------------- /.local-manifest/db/adminer-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: adminer 5 | 6 | spec: 7 | replicas: 1 8 | revisionHistoryLimit: 0 9 | selector: 10 | matchLabels: 11 | app: adminer 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: adminer 17 | spec: 18 | containers: 19 | - name: adminer 20 | image: adminer:4.8.1 21 | imagePullPolicy: Always 22 | env: 23 | - name: ADMINER_DEFAULT_SERVER 24 | value: mariadb 25 | - name: ADMINER_DESIGN 26 | value: nette 27 | ports: 28 | - name: http 29 | containerPort: 8080 30 | -------------------------------------------------------------------------------- /.local-manifest/db/adminer-ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: adminer 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`adminer.local.trapti.tech`) 13 | services: 14 | - namespace: db 15 | kind: Service 16 | name: adminer 17 | port: 80 18 | scheme: http 19 | strategy: RoundRobin 20 | weight: 1 21 | -------------------------------------------------------------------------------- /.local-manifest/db/adminer-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: adminer 5 | 6 | spec: 7 | selector: 8 | app: adminer 9 | ports: 10 | - name: http 11 | port: 80 12 | targetPort: http 13 | -------------------------------------------------------------------------------- /.local-manifest/db/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: db 2 | 3 | resources: 4 | - adminer-deployment.yaml 5 | - adminer-ingress-route.yaml 6 | - adminer-service.yaml 7 | - mariadb-service.yaml 8 | - mariadb-stateful-set.yaml 9 | - mongo-service.yaml 10 | - mongo-stateful-set.yaml 11 | -------------------------------------------------------------------------------- /.local-manifest/db/mariadb-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mariadb 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: mariadb 10 | ports: 11 | - name: mariadb 12 | port: 3306 13 | targetPort: mariadb 14 | -------------------------------------------------------------------------------- /.local-manifest/db/mariadb-stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: mariadb 5 | 6 | spec: 7 | replicas: 1 8 | serviceName: mariadb 9 | podManagementPolicy: Parallel 10 | selector: 11 | matchLabels: 12 | app: mariadb 13 | 14 | template: 15 | metadata: 16 | labels: 17 | app: mariadb 18 | spec: 19 | enableServiceLinks: false 20 | automountServiceAccountToken: false 21 | 22 | volumes: 23 | - name: project 24 | hostPath: 25 | path: /work 26 | 27 | containers: 28 | - name: mariadb 29 | image: mariadb:10 30 | imagePullPolicy: Always 31 | args: 32 | - mysqld 33 | - --character-set-server=utf8mb4 34 | - --collation-server=utf8mb4_unicode_ci 35 | env: 36 | - name: MYSQL_ROOT_PASSWORD 37 | value: password 38 | - name: MYSQL_DATABASE 39 | value: neoshowcase 40 | ports: 41 | - name: mariadb 42 | containerPort: 3306 43 | volumeMounts: 44 | - name: project 45 | subPath: .local-dev/mysql 46 | mountPath: /var/lib/mysql 47 | -------------------------------------------------------------------------------- /.local-manifest/db/mongo-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mongo 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: mongo 10 | ports: 11 | - name: mongo 12 | port: 27017 13 | targetPort: mongo 14 | -------------------------------------------------------------------------------- /.local-manifest/db/mongo-stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: mongo 5 | 6 | spec: 7 | replicas: 1 8 | serviceName: mongo 9 | podManagementPolicy: Parallel 10 | selector: 11 | matchLabels: 12 | app: mongo 13 | 14 | template: 15 | metadata: 16 | labels: 17 | app: mongo 18 | spec: 19 | enableServiceLinks: false 20 | automountServiceAccountToken: false 21 | 22 | volumes: 23 | - name: project 24 | hostPath: 25 | path: /work 26 | 27 | containers: 28 | - name: mongo 29 | image: mongo:7 30 | imagePullPolicy: Always 31 | env: 32 | - name: MONGO_INITDB_ROOT_USERNAME 33 | value: root 34 | - name: MONGO_INITDB_ROOT_PASSWORD 35 | value: password 36 | args: 37 | - "mongod" 38 | - "--auth" 39 | - "--bind_ip_all" 40 | ports: 41 | - name: mongo 42 | containerPort: 27017 43 | volumeMounts: 44 | - name: project 45 | subPath: .local-dev/mongo 46 | mountPath: /data/db 47 | -------------------------------------------------------------------------------- /.local-manifest/k3d.yaml: -------------------------------------------------------------------------------- 1 | # https://k3d.io/v5.6.0/usage/configfile/ 2 | apiVersion: k3d.io/v1alpha5 3 | kind: Simple 4 | metadata: 5 | name: ns-dev 6 | 7 | image: rancher/k3s:latest 8 | 9 | volumes: 10 | - volume: $PROJECT_ROOT:/work 11 | nodeFilters: 12 | - all 13 | - volume: $PROJECT_ROOT/.local-dev/k3d-local-storage:/var/lib/rancher/k3s/storage 14 | nodeFilters: 15 | - all 16 | - volume: $HOME/.ssh/known_hosts:/known_hosts 17 | nodeFilters: 18 | - all 19 | 20 | ports: 21 | - port: 80:80 22 | nodeFilters: 23 | - loadbalancer 24 | - port: 443:443 25 | nodeFilters: 26 | - loadbalancer 27 | - port: 2201:2201 28 | nodeFilters: 29 | - loadbalancer 30 | - port: 8080:8080 31 | nodeFilters: 32 | - loadbalancer 33 | 34 | registries: 35 | create: 36 | name: docker-io 37 | proxy: 38 | remoteURL: https://registry-1.docker.io 39 | volumes: 40 | - $PROJECT_ROOT/.local-dev/k3d-cache-registry:/var/lib/registry 41 | config: | 42 | mirrors: 43 | "docker.io": 44 | endpoint: 45 | - http://docker-io:5000 46 | # Force insecure (http) access 47 | "registry.local": 48 | endpoint: 49 | - "http://registry.local" 50 | 51 | hostAliases: 52 | - ip: 10.43.11.193 53 | hostnames: 54 | - registry.local 55 | 56 | options: 57 | k3s: 58 | extraArgs: 59 | - arg: --disable=traefik 60 | nodeFilters: 61 | - server:* 62 | -------------------------------------------------------------------------------- /.local-manifest/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - alloy 3 | - auth 4 | - coredns-patch 5 | - db 6 | - monitor 7 | - ns-apps 8 | - ns-system 9 | - registry 10 | - sablier 11 | - traefik 12 | -------------------------------------------------------------------------------- /.local-manifest/monitor/grafana/ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: grafana 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`grafana.local.trapti.tech`) 13 | services: 14 | - namespace: monitor 15 | kind: Service 16 | name: grafana 17 | port: http 18 | scheme: http 19 | strategy: RoundRobin 20 | weight: 1 21 | -------------------------------------------------------------------------------- /.local-manifest/monitor/grafana/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ingress-route.yaml 3 | - service.yaml 4 | - stateful-set.yaml 5 | -------------------------------------------------------------------------------- /.local-manifest/monitor/grafana/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: grafana 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: grafana 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/monitor/grafana/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: grafana 5 | 6 | spec: 7 | replicas: 1 8 | serviceName: grafana 9 | selector: 10 | matchLabels: 11 | app: grafana 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: grafana 17 | spec: 18 | automountServiceAccountToken: false 19 | enableServiceLinks: false 20 | 21 | volumes: 22 | - name: project 23 | hostPath: 24 | path: /work 25 | 26 | containers: 27 | - name: grafana 28 | image: grafana/grafana:11.1.3 29 | imagePullPolicy: IfNotPresent 30 | ports: 31 | - name: http 32 | containerPort: 3000 33 | volumeMounts: 34 | - name: project 35 | subPath: .local-dev/grafana 36 | mountPath: /var/lib/grafana 37 | - name: project 38 | subPath: .local-dev/config/grafana.ini 39 | mountPath: /etc/grafana/grafana.ini 40 | readOnly: true 41 | env: 42 | - name: GF_INSTALL_PLUGINS 43 | value: victoriametrics-logs-datasource 44 | -------------------------------------------------------------------------------- /.local-manifest/monitor/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: monitor 2 | 3 | resources: 4 | - grafana 5 | # - loki 6 | - victoria-logs 7 | - victoria-metrics 8 | -------------------------------------------------------------------------------- /.local-manifest/monitor/loki/ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: loki 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`loki.local.trapti.tech`) 13 | services: 14 | - namespace: monitor 15 | kind: Service 16 | name: loki 17 | port: http 18 | scheme: http 19 | strategy: RoundRobin 20 | weight: 1 21 | -------------------------------------------------------------------------------- /.local-manifest/monitor/loki/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ingress-route.yaml 3 | - service.yaml 4 | - stateful-set.yaml 5 | -------------------------------------------------------------------------------- /.local-manifest/monitor/loki/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: loki 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: loki 10 | ports: 11 | - name: http 12 | port: 3100 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/monitor/loki/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: loki 5 | 6 | spec: 7 | replicas: 1 8 | serviceName: loki 9 | selector: 10 | matchLabels: 11 | app: loki 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: loki 17 | spec: 18 | automountServiceAccountToken: true 19 | enableServiceLinks: true 20 | 21 | volumes: 22 | - name: project 23 | hostPath: 24 | path: /work 25 | 26 | containers: 27 | - name: loki 28 | image: grafana/loki:3.1.0 29 | imagePullPolicy: IfNotPresent 30 | args: 31 | - -config.file=/etc/loki/loki.yaml 32 | ports: 33 | - name: http 34 | containerPort: 3100 35 | volumeMounts: 36 | - name: project 37 | subPath: .local-dev/loki 38 | mountPath: /var/lib/loki 39 | - name: project 40 | subPath: .local-dev/config/loki.yaml 41 | mountPath: /etc/loki/loki.yaml 42 | readOnly: true 43 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-logs/kustomization.yaml: -------------------------------------------------------------------------------- 1 | helmCharts: 2 | - repo: https://victoriametrics.github.io/helm-charts/ 3 | name: victoria-logs-single 4 | version: "0.9.8" 5 | includeCRDs: true 6 | namespace: monitor 7 | releaseName: vl 8 | valuesFile: values.yaml 9 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-logs/values.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | extraArgs: 3 | defaultMsgValue: "-" 4 | service: 5 | clusterIP: "" 6 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: victoria-metrics 5 | 6 | subjects: 7 | - kind: ServiceAccount 8 | name: victoria-metrics 9 | namespace: victoria-metrics 10 | roleRef: 11 | apiGroup: rbac.authorization.k8s.io 12 | kind: ClusterRole 13 | name: victoria-metrics 14 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: victoria-metrics 5 | 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - nodes 11 | - nodes/proxy 12 | - nodes/metrics 13 | - services 14 | - endpoints 15 | - pods 16 | - ingresses 17 | - configmaps 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - "extensions" 24 | - "networking.k8s.io" 25 | resources: 26 | - ingresses/status 27 | - ingresses 28 | verbs: 29 | - get 30 | - list 31 | - watch 32 | - nonResourceURLs: 33 | - "/metrics" 34 | verbs: 35 | - get 36 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - cluster-role.yaml 3 | - cluster-role-binding.yaml 4 | - service.yaml 5 | - service-account.yaml 6 | - stateful-set.yaml 7 | 8 | configMapGenerator: 9 | - name: prometheus-config 10 | files: 11 | - config/prometheus.yml 12 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: victoria-metrics 5 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: victoria-metrics 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: victoria-metrics 10 | ports: 11 | - name: http 12 | port: 8428 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/monitor/victoria-metrics/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: victoria-metrics 5 | 6 | spec: 7 | serviceName: victoria-metrics 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: victoria-metrics 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: victoria-metrics 17 | spec: 18 | enableServiceLinks: false 19 | serviceAccountName: victoria-metrics 20 | 21 | volumes: 22 | - name: config 23 | configMap: 24 | name: prometheus-config 25 | - name: project 26 | hostPath: 27 | path: /work 28 | 29 | containers: 30 | - name: victoria-metrics 31 | image: victoriametrics/victoria-metrics:v1.102.1 32 | imagePullPolicy: IfNotPresent 33 | args: 34 | - --promscrape.config=/etc/config/prometheus.yml 35 | - --promscrape.maxScrapeSize=256MB 36 | - --maxLabelsPerTimeseries=128 37 | - --storageDataPath=/data 38 | - --retentionPeriod=7d 39 | ports: 40 | - name: http 41 | containerPort: 8428 42 | volumeMounts: 43 | - name: config 44 | mountPath: /etc/config 45 | - name: project 46 | subPath: .local-dev/victoria-metrics 47 | mountPath: /data 48 | -------------------------------------------------------------------------------- /.local-manifest/ns-apps/auth-hard-middleware.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: Middleware 3 | metadata: 4 | name: nsapp-auth-hard 5 | 6 | spec: 7 | headers: 8 | customRequestHeaders: 9 | X-Forward-Auth-Type: hard 10 | -------------------------------------------------------------------------------- /.local-manifest/ns-apps/auth-middleware.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: Middleware 3 | metadata: 4 | name: nsapp-auth 5 | 6 | spec: 7 | forwardAuth: 8 | address: http://ns-mc.ns-system.svc.cluster.local/ 9 | authResponseHeaders: 10 | - X-Showcase-User 11 | -------------------------------------------------------------------------------- /.local-manifest/ns-apps/auth-soft-middleware.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: Middleware 3 | metadata: 4 | name: nsapp-auth-soft 5 | 6 | spec: 7 | headers: 8 | customRequestHeaders: 9 | X-Forward-Auth-Type: soft 10 | -------------------------------------------------------------------------------- /.local-manifest/ns-apps/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: ns-apps 2 | 3 | resources: 4 | - auth-hard-middleware.yaml 5 | - auth-middleware.yaml 6 | - auth-soft-middleware.yaml 7 | - network-policy.yaml 8 | -------------------------------------------------------------------------------- /.local-manifest/ns-apps/network-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: apps-network-policy 5 | 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | ns.trap.jp/managed: "true" 10 | policyTypes: 11 | - Egress 12 | egress: 13 | - to: 14 | - ipBlock: 15 | cidr: 0.0.0.0/0 16 | except: 17 | - 10.0.0.0/8 18 | - 172.16.0.0/12 19 | - 192.168.0.0/16 20 | - to: 21 | - namespaceSelector: 22 | matchLabels: 23 | kubernetes.io/metadata.name: kube-system 24 | podSelector: 25 | matchLabels: 26 | k8s-app: kube-dns 27 | - to: 28 | - namespaceSelector: 29 | matchLabels: 30 | kubernetes.io/metadata.name: db 31 | podSelector: 32 | matchLabels: 33 | app: mariadb 34 | ports: 35 | - protocol: TCP 36 | port: mariadb 37 | - to: 38 | - namespaceSelector: 39 | matchLabels: 40 | kubernetes.io/metadata.name: db 41 | podSelector: 42 | matchLabels: 43 | app: mongo 44 | ports: 45 | - protocol: TCP 46 | port: mongo 47 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/controller-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: ns-controller-binding 5 | 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: ns-controller 10 | subjects: 11 | - kind: ServiceAccount 12 | namespace: ns-system 13 | name: ns-controller 14 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/controller-role.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: ns-controller 5 | 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - secrets 11 | - services 12 | - pods 13 | - pods/attach 14 | - pods/exec 15 | verbs: 16 | - "*" 17 | - apiGroups: 18 | - apps 19 | resources: 20 | - statefulsets 21 | verbs: 22 | - "*" 23 | - apiGroups: 24 | - traefik.io 25 | - traefik.containo.us 26 | resources: 27 | - ingressroutes 28 | - middlewares 29 | verbs: 30 | - "*" 31 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/controller-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: ns-controller 5 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/controller-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-controller 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-controller 10 | ports: 11 | - name: webhook 12 | port: 8080 13 | targetPort: webhook 14 | - name: grpc 15 | port: 10000 16 | targetPort: grpc 17 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/controller-ssh-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-controller-ssh 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-controller 10 | ports: 11 | - name: ssh 12 | port: 2201 13 | targetPort: ssh 14 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/dashboard-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ns-dashboard 5 | 6 | spec: 7 | replicas: 1 8 | revisionHistoryLimit: 0 9 | selector: 10 | matchLabels: 11 | app: ns-dashboard 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: ns-dashboard 17 | spec: 18 | enableServiceLinks: false 19 | automountServiceAccountToken: false 20 | 21 | containers: 22 | - name: ns-dashboard 23 | image: ghcr.io/traptitech/ns-dashboard:main 24 | imagePullPolicy: Never 25 | envFrom: 26 | - configMapRef: 27 | name: config-override 28 | ports: 29 | - name: http 30 | containerPort: 80 31 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/dashboard-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-dashboard 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-dashboard 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/gateway-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-gateway 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-gateway 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: ns 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`ns.local.trapti.tech`) 13 | middlewares: 14 | - name: ns-auth-dev 15 | namespace: auth 16 | services: 17 | - namespace: ns-system 18 | kind: Service 19 | name: ns-dashboard 20 | port: 80 21 | scheme: http 22 | strategy: RoundRobin 23 | weight: 1 24 | - kind: Rule 25 | match: Host(`ns.local.trapti.tech`) && PathPrefix(`/api/webhook`) 26 | services: 27 | - namespace: ns-system 28 | kind: Service 29 | name: ns-controller 30 | port: webhook 31 | scheme: http 32 | strategy: RoundRobin 33 | weight: 1 34 | - kind: Rule 35 | match: Host(`ns.local.trapti.tech`) && PathPrefix(`/neoshowcase.protobuf.APIService`) 36 | middlewares: 37 | - name: ns-auth-dev 38 | namespace: auth 39 | services: 40 | - namespace: ns-system 41 | kind: Service 42 | name: ns-gateway 43 | port: http 44 | scheme: h2c 45 | strategy: RoundRobin 46 | weight: 1 47 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/migrate-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: ns-migrate 5 | 6 | spec: 7 | template: 8 | metadata: 9 | labels: 10 | app: ns-migrate 11 | spec: 12 | enableServiceLinks: false 13 | automountServiceAccountToken: false 14 | restartPolicy: Never 15 | 16 | volumes: 17 | - name: project 18 | hostPath: 19 | path: /work 20 | 21 | containers: 22 | - name: ns-migrate 23 | image: ghcr.io/traptitech/ns-migrate:main 24 | imagePullPolicy: Never 25 | env: 26 | - name: DB_HOST 27 | value: mariadb.db.svc.cluster.local 28 | - name: DB_PORT 29 | value: "3306" 30 | - name: DB_USER 31 | value: root 32 | - name: DB_PASS 33 | value: password 34 | - name: DB_NAME 35 | value: neoshowcase 36 | -------------------------------------------------------------------------------- /.local-manifest/ns-system/ssgen-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: ns-ssgen 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ns-ssgen 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | - name: grpc 15 | port: 10000 16 | targetPort: grpc 17 | -------------------------------------------------------------------------------- /.local-manifest/registry/frontend-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: registry-frontend 5 | 6 | spec: 7 | replicas: 1 8 | revisionHistoryLimit: 0 9 | selector: 10 | matchLabels: 11 | app: registry-frontend 12 | 13 | template: 14 | metadata: 15 | labels: 16 | app: registry-frontend 17 | spec: 18 | enableServiceLinks: false 19 | automountServiceAccountToken: false 20 | 21 | containers: 22 | - name: registry-frontend 23 | image: joxit/docker-registry-ui:2 24 | imagePullPolicy: Always 25 | env: 26 | - name: SINGLE_REGISTRY 27 | value: "true" 28 | - name: REGISTRY_TITLE 29 | value: registry.local 30 | - name: NGINX_PROXY_PASS_URL 31 | value: http://registry 32 | - name: SHOW_CONTENT_DIGEST 33 | value: "true" 34 | - name: DELETE_IMAGES 35 | value: "true" 36 | ports: 37 | - containerPort: 80 38 | name: http 39 | -------------------------------------------------------------------------------- /.local-manifest/registry/frontend-ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: registry-frontend 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`registry-frontend.local.trapti.tech`) 13 | services: 14 | - namespace: registry 15 | kind: Service 16 | name: registry-frontend 17 | port: http 18 | scheme: http 19 | strategy: RoundRobin 20 | weight: 1 21 | -------------------------------------------------------------------------------- /.local-manifest/registry/frontend-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: registry-frontend 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: registry-frontend 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/registry/ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: registry 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`registry.local`) 13 | services: 14 | - namespace: registry 15 | kind: Service 16 | name: registry 17 | port: http 18 | scheme: http 19 | strategy: RoundRobin 20 | weight: 1 21 | -------------------------------------------------------------------------------- /.local-manifest/registry/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: registry 2 | 3 | resources: 4 | - deployment.yaml 5 | - frontend-deployment.yaml 6 | - frontend-ingress-route.yaml 7 | - frontend-service.yaml 8 | - ingress-route.yaml 9 | - service.yaml 10 | -------------------------------------------------------------------------------- /.local-manifest/registry/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: registry 5 | 6 | spec: 7 | type: ClusterIP 8 | clusterIP: 10.43.11.193 9 | selector: 10 | app: registry 11 | ports: 12 | - name: http 13 | port: 80 14 | targetPort: http 15 | -------------------------------------------------------------------------------- /.local-manifest/sablier/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: sablier 2 | resources: 3 | - sablier-service-account.yaml 4 | - sablier-role.yaml 5 | - sablier-role-binding.yaml 6 | - sablier-deployment.yaml 7 | - sablier-service.yaml 8 | -------------------------------------------------------------------------------- /.local-manifest/sablier/sablier-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: sablier 5 | labels: 6 | app: sablier 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: sablier 12 | template: 13 | metadata: 14 | labels: 15 | app: sablier 16 | spec: 17 | serviceAccountName: sablier 18 | volumes: 19 | - name: project 20 | hostPath: 21 | path: /work 22 | containers: 23 | - name: sablier 24 | image: sablierapp/sablier:1.8 25 | ports: 26 | - name: http 27 | containerPort: 10000 28 | volumeMounts: 29 | - name: project 30 | subPath: .local-dev/config/sablier.yaml 31 | mountPath: /etc/sablier/sablier.yaml 32 | - name: project 33 | subPath: sablier/themes 34 | mountPath: /etc/sablier/themes 35 | -------------------------------------------------------------------------------- /.local-manifest/sablier/sablier-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: sablier 5 | 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: sablier 10 | subjects: 11 | - kind: ServiceAccount 12 | namespace: sablier 13 | name: sablier 14 | -------------------------------------------------------------------------------- /.local-manifest/sablier/sablier-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: sablier 5 | rules: 6 | - apiGroups: 7 | - apps 8 | - "" 9 | resources: 10 | - deployments 11 | - statefulsets 12 | verbs: 13 | - get # Retrieve info about specific dep 14 | - list # Events 15 | - watch # Events 16 | - apiGroups: 17 | - apps 18 | - "" 19 | resources: 20 | - deployments/scale 21 | - statefulsets/scale 22 | verbs: 23 | - patch # Scale up and down 24 | - update # Scale up and down 25 | - get # Retrieve info about specific dep 26 | - list # Events 27 | - watch # Events 28 | -------------------------------------------------------------------------------- /.local-manifest/sablier/sablier-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: sablier 5 | -------------------------------------------------------------------------------- /.local-manifest/sablier/sablier-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: sablier 5 | 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: sablier 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | -------------------------------------------------------------------------------- /.local-manifest/traefik/dashboard-ingress-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traefik.io/v1alpha1 2 | kind: IngressRoute 3 | metadata: 4 | name: dashboard 5 | 6 | spec: 7 | entryPoints: 8 | - web 9 | - websecure 10 | routes: 11 | - kind: Rule 12 | match: Host(`traefik.local.trapti.tech`) 13 | services: 14 | - kind: TraefikService 15 | name: dashboard@internal 16 | - kind: Rule 17 | match: Host(`traefik.local.trapti.tech`) && PathPrefix(`/api`) 18 | services: 19 | - kind: TraefikService 20 | name: api@internal 21 | -------------------------------------------------------------------------------- /.local-manifest/traefik/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: traefik 2 | 3 | resources: 4 | - traefik-service-account.yaml 5 | - traefik-cluster-role.yaml 6 | - traefik-role-binding.yaml 7 | - traefik-stateful-set.yaml 8 | - traefik-service.yaml 9 | - dashboard-ingress-route.yaml 10 | -------------------------------------------------------------------------------- /.local-manifest/traefik/traefik-cluster-role.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: traefik 5 | 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - services 11 | - secrets 12 | - nodes 13 | verbs: 14 | - get 15 | - list 16 | - watch 17 | - apiGroups: 18 | - extensions 19 | - networking.k8s.io 20 | resources: 21 | - ingresses 22 | - ingressclasses 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - apiGroups: 28 | - extensions 29 | - networking.k8s.io 30 | resources: 31 | - ingresses/status 32 | verbs: 33 | - update 34 | - apiGroups: 35 | - traefik.io 36 | resources: 37 | - middlewares 38 | - middlewaretcps 39 | - ingressroutes 40 | - traefikservices 41 | - ingressroutetcps 42 | - ingressrouteudps 43 | - tlsoptions 44 | - tlsstores 45 | - serverstransports 46 | - serverstransporttcps 47 | verbs: 48 | - get 49 | - list 50 | - watch 51 | - apiGroups: 52 | - discovery.k8s.io 53 | resources: 54 | - endpointslices 55 | verbs: 56 | - list 57 | - watch 58 | -------------------------------------------------------------------------------- /.local-manifest/traefik/traefik-role-binding.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRoleBinding 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: traefik 5 | 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: traefik 10 | subjects: 11 | - kind: ServiceAccount 12 | namespace: traefik 13 | name: traefik 14 | -------------------------------------------------------------------------------- /.local-manifest/traefik/traefik-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: traefik 5 | -------------------------------------------------------------------------------- /.local-manifest/traefik/traefik-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: traefik 5 | 6 | spec: 7 | type: LoadBalancer 8 | selector: 9 | app: traefik 10 | ports: 11 | - name: web 12 | port: 80 13 | targetPort: web 14 | - name: web-secure 15 | port: 443 16 | targetPort: web-secure 17 | - name: dashboard 18 | port: 8080 19 | targetPort: dashboard 20 | -------------------------------------------------------------------------------- /.spectral.yml: -------------------------------------------------------------------------------- 1 | extends: spectral:oas 2 | rules: 3 | operation-success-response: false 4 | -------------------------------------------------------------------------------- /.tbls.yml: -------------------------------------------------------------------------------- 1 | dsn: mariadb://root:password@localhost:5004/neoshowcase 2 | docPath: docs/dbschema 3 | disableOutputSchema: true 4 | er: 5 | format: mermaid 6 | 7 | lint: 8 | requireTableComment: 9 | enabled: true 10 | requireColumnComment: 11 | enabled: true 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 東京工業大学デジタル創作同好会traP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NeoShowcase 2 | 3 | [![](https://github.com/traPtitech/NeoShowcase/actions/workflows/ci.yaml/badge.svg)](https://github.com/traPtitech/NeoShowcase/actions/workflows/ci.yaml) 4 | [![](https://github.com/traPtitech/NeoShowcase/actions/workflows/main.yaml/badge.svg)](https://github.com/traPtitech/NeoShowcase/actions/workflows/main.yaml) 5 | [![](https://codecov.io/gh/traPtitech/NeoShowcase/branch/master/graph/badge.svg)](https://codecov.io/gh/traPtitech/NeoShowcase) 6 | [![](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/traPtitech/NeoShowcase/blob/main/LICENSE) 7 | 8 | NeoShowcase is a PaaS (Platform as a Service) application for [Digital Creators Club traP](https://github.com/traPtitech). 9 | This application is a successor to [kaz/showcase](https://github.com/kaz/showcase). 10 | 11 | ## Development 12 | 13 | Quick start: run `make init && make up`. 14 | 15 | See [development.md](./docs/development.md) for more. 16 | 17 | ## Architecture 18 | 19 | See: 20 | - [architecture.md](./docs/architecture.md) for big picture of the architecture 21 | - [components.md](./docs/components.md) for detailed explanation of components and libraries 22 | 23 | ## Production Deployment 24 | 25 | See [deployment.md](./docs/deployment.md). 26 | -------------------------------------------------------------------------------- /api/proto/neoshowcase/protobuf/null.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package neoshowcase.protobuf; 3 | 4 | import "google/protobuf/timestamp.proto"; 5 | 6 | message NullTimestamp { 7 | google.protobuf.Timestamp timestamp = 1; 8 | bool valid = 2; 9 | } 10 | -------------------------------------------------------------------------------- /buf.gen.go.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/buf.gen.json 2 | 3 | version: v2 4 | managed: 5 | enabled: true 6 | override: 7 | - file_option: go_package 8 | value: github.com/traPtitech/neoshowcase/pkg/infrastructure/grpc/pb 9 | plugins: 10 | - local: ["go", "tool", "protoc-gen-go"] 11 | out: . 12 | opt: 13 | - module=github.com/traPtitech/neoshowcase 14 | - local: ["go", "tool", "protoc-gen-connect-go"] 15 | out: . 16 | opt: 17 | - module=github.com/traPtitech/neoshowcase 18 | -------------------------------------------------------------------------------- /buf.gen.ts.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/buf.gen.json 2 | 3 | version: v2 4 | plugins: 5 | - local: ["sh", "-c", "cd dashboard && yarn protoc-gen-es"] 6 | out: dashboard/src/api 7 | opt: 8 | - target=ts 9 | inputs: 10 | - directory: . 11 | exclude_paths: 12 | - api/proto/neoshowcase/protobuf/controller.proto 13 | -------------------------------------------------------------------------------- /buf.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | modules: 3 | - path: api/proto 4 | lint: 5 | use: 6 | - BASIC 7 | breaking: 8 | use: 9 | - FILE 10 | -------------------------------------------------------------------------------- /cmd/auth-dev/server.go: -------------------------------------------------------------------------------- 1 | package authdev 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/labstack/echo/v4" 9 | ) 10 | 11 | type Server struct { 12 | e *echo.Echo 13 | port int 14 | } 15 | 16 | func NewServer(header string, port int, user string) *Server { 17 | e := echo.New() 18 | e.Any("/*", func(c echo.Context) error { 19 | c.Response().Header().Set(header, user) 20 | return c.NoContent(http.StatusOK) 21 | }) 22 | return &Server{e: e, port: port} 23 | } 24 | 25 | func (s *Server) Start(_ context.Context) error { 26 | return s.e.Start(fmt.Sprintf(":%d", s.port)) 27 | } 28 | 29 | func (s *Server) Shutdown(ctx context.Context) error { 30 | return s.e.Shutdown(ctx) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/builder/server.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "context" 5 | 6 | buildkit "github.com/moby/buildkit/client" 7 | "golang.org/x/sync/errgroup" 8 | 9 | "github.com/traPtitech/neoshowcase/pkg/usecase/builder" 10 | ) 11 | 12 | type Server struct { 13 | Buildkit *buildkit.Client 14 | Builder builder.Service 15 | } 16 | 17 | func (s *Server) Start(ctx context.Context) error { 18 | return s.Builder.Start(ctx) 19 | } 20 | 21 | func (s *Server) Shutdown(ctx context.Context) error { 22 | eg, ctx := errgroup.WithContext(ctx) 23 | 24 | eg.Go(func() error { 25 | return s.Buildkit.Close() 26 | }) 27 | eg.Go(func() error { 28 | return s.Builder.Shutdown(ctx) 29 | }) 30 | 31 | return eg.Wait() 32 | } 33 | -------------------------------------------------------------------------------- /cmd/buildpack-helper/server.go: -------------------------------------------------------------------------------- 1 | package buildpackhelper 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/traPtitech/neoshowcase/pkg/domain/web" 7 | ) 8 | 9 | type APIServer struct { 10 | *web.H2CServer 11 | } 12 | 13 | type Server struct { 14 | Helper *APIServer 15 | } 16 | 17 | func (s *Server) Start(ctx context.Context) error { 18 | return s.Helper.Start(ctx) 19 | } 20 | 21 | func (s *Server) Shutdown(ctx context.Context) error { 22 | return s.Helper.Shutdown(ctx) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/gateway/server.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | _ "github.com/go-sql-driver/mysql" 8 | "golang.org/x/sync/errgroup" 9 | 10 | "github.com/traPtitech/neoshowcase/pkg/domain/web" 11 | ) 12 | 13 | type APIServer struct { 14 | *web.H2CServer 15 | } 16 | 17 | type Server struct { 18 | APIServer *APIServer 19 | DB *sql.DB 20 | } 21 | 22 | func (s *Server) Start(ctx context.Context) error { 23 | return s.APIServer.Start(ctx) 24 | } 25 | 26 | func (s *Server) Shutdown(ctx context.Context) error { 27 | eg, ctx := errgroup.WithContext(ctx) 28 | 29 | eg.Go(func() error { 30 | return s.APIServer.Shutdown(ctx) 31 | }) 32 | eg.Go(func() error { 33 | return s.DB.Close() 34 | }) 35 | 36 | return eg.Wait() 37 | } 38 | -------------------------------------------------------------------------------- /cmd/gitea-integration/server.go: -------------------------------------------------------------------------------- 1 | package giteaintegration 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | _ "github.com/go-sql-driver/mysql" 8 | "golang.org/x/sync/errgroup" 9 | 10 | giteaintegration "github.com/traPtitech/neoshowcase/pkg/usecase/gitea-integration" 11 | ) 12 | 13 | type Server struct { 14 | Integration *giteaintegration.Integration 15 | DB *sql.DB 16 | } 17 | 18 | func (s *Server) Start(_ context.Context) error { 19 | return s.Integration.Start() 20 | } 21 | 22 | func (s *Server) Shutdown(_ context.Context) error { 23 | var eg errgroup.Group 24 | 25 | eg.Go(func() error { 26 | return s.Integration.Shutdown() 27 | }) 28 | eg.Go(func() error { 29 | return s.DB.Close() 30 | }) 31 | 32 | return eg.Wait() 33 | } 34 | -------------------------------------------------------------------------------- /cmd/ssgen/server.go: -------------------------------------------------------------------------------- 1 | package ssgen 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | "golang.org/x/sync/errgroup" 8 | 9 | "github.com/traPtitech/neoshowcase/pkg/domain" 10 | "github.com/traPtitech/neoshowcase/pkg/usecase/healthcheck" 11 | "github.com/traPtitech/neoshowcase/pkg/usecase/ssgen" 12 | ) 13 | 14 | type Server struct { 15 | DB *sql.DB 16 | Service ssgen.GeneratorService 17 | Health healthcheck.Server 18 | Engine domain.StaticServer 19 | } 20 | 21 | func (s *Server) Start(ctx context.Context) error { 22 | eg, ctx := errgroup.WithContext(ctx) 23 | 24 | eg.Go(func() error { 25 | return s.Service.Start(ctx) 26 | }) 27 | eg.Go(func() error { 28 | return s.Health.Start(ctx) 29 | }) 30 | eg.Go(func() error { 31 | return s.Engine.Start(ctx) 32 | }) 33 | 34 | return eg.Wait() 35 | } 36 | 37 | func (s *Server) Shutdown(ctx context.Context) error { 38 | eg, ctx := errgroup.WithContext(ctx) 39 | 40 | eg.Go(func() error { 41 | return s.DB.Close() 42 | }) 43 | eg.Go(func() error { 44 | return s.Service.Shutdown(ctx) 45 | }) 46 | eg.Go(func() error { 47 | return s.Health.Shutdown(ctx) 48 | }) 49 | eg.Go(func() error { 50 | return s.Engine.Shutdown(ctx) 51 | }) 52 | 53 | return eg.Wait() 54 | } 55 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | coverage: 3 | status: 4 | patch: false 5 | project: 6 | default: 7 | base: auto 8 | target: auto 9 | threshold: 3.0 10 | -------------------------------------------------------------------------------- /dashboard/.dockerignore: -------------------------------------------------------------------------------- 1 | .pnp.* 2 | .yarn/* 3 | !.yarn/.gitkeep 4 | !.yarn/patches 5 | !.yarn/plugins 6 | !.yarn/releases 7 | !.yarn/sdks 8 | !.yarn/versions 9 | 10 | dist 11 | node_modules 12 | 13 | .dockerignore 14 | .gitignore 15 | Dockerfile 16 | -------------------------------------------------------------------------------- /dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | .pnp.* 2 | .yarn/* 3 | !.yarn/.gitkeep 4 | !.yarn/patches 5 | !.yarn/plugins 6 | !.yarn/releases 7 | !.yarn/sdks 8 | !.yarn/versions 9 | 10 | node_modules 11 | dist 12 | 13 | yarn-error.log 14 | 15 | !.vscode 16 | -------------------------------------------------------------------------------- /dashboard/.yarn/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traPtitech/NeoShowcase/830b8e9e955ff5f8efee55ca53f371c5f6dcafec/dashboard/.yarn/.gitkeep -------------------------------------------------------------------------------- /dashboard/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /dashboard/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | admin off 3 | } 4 | 5 | :80 6 | 7 | root * /usr/share/caddy 8 | 9 | handle { 10 | @non-static not { 11 | file 12 | path *.ico *.css *.js *.gif *.webp *.avif *.jpg *.jpeg *.png *.svg *.woff *.woff2 13 | } 14 | # Always revalidate, except for static files 15 | header @non-static Cache-Control "no-cache" 16 | 17 | file_server { 18 | precompressed br gzip 19 | } 20 | try_files {path} /index.html 21 | } 22 | -------------------------------------------------------------------------------- /dashboard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM node:22 AS base 2 | 3 | WORKDIR /work 4 | 5 | RUN corepack enable 6 | 7 | COPY package.json yarn.lock .yarnrc.yml ./ 8 | COPY .yarn .yarn 9 | RUN yarn --immutable 10 | 11 | FROM --platform=$BUILDPLATFORM base AS dev 12 | 13 | ENTRYPOINT ["yarn", "dev"] 14 | CMD ["--host", "--port=80"] 15 | 16 | FROM --platform=$BUILDPLATFORM base AS builder 17 | 18 | COPY . . 19 | RUN yarn build 20 | 21 | FROM caddy:2 AS prod 22 | 23 | COPY Caddyfile /etc/caddy/Caddyfile 24 | COPY --from=builder /work/dist/ /usr/share/caddy/ 25 | -------------------------------------------------------------------------------- /dashboard/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "formatter": { 4 | "enabled": true, 5 | "indentStyle": "space", 6 | "indentWidth": 2, 7 | "lineWidth": 120 8 | }, 9 | "javascript": { 10 | "formatter": { 11 | "quoteStyle": "single", 12 | "semicolons": "asNeeded", 13 | "trailingCommas": "all" 14 | } 15 | }, 16 | "linter": { 17 | "enabled": true, 18 | "rules": { 19 | "recommended": true, 20 | "nursery": { 21 | "useSortedClasses": { 22 | "fix": "safe", 23 | "level": "warn", 24 | "options": { 25 | "functions": ["clsx", "styled"] 26 | } 27 | } 28 | }, 29 | "correctness": { 30 | "noUnusedImports": "error" 31 | }, 32 | "style": { 33 | "noNonNullAssertion": "off" 34 | } 35 | } 36 | }, 37 | "files": { 38 | "ignore": ["src/api/*", "package.json"] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dashboard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /dashboard/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traPtitech/NeoShowcase/830b8e9e955ff5f8efee55ca53f371c5f6dcafec/dashboard/public/favicon.ico -------------------------------------------------------------------------------- /dashboard/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { MetaProvider, Title } from '@solidjs/meta' 2 | import type { RouteSectionProps } from '@solidjs/router' 3 | import { type Component, ErrorBoundary } from 'solid-js' 4 | import { Toaster } from 'solid-toast' 5 | import { Routes } from '/@/routes' 6 | import ErrorView from './components/layouts/ErrorView' 7 | import { WithHeader } from './components/layouts/WithHeader' 8 | 9 | const Root: Component = (props) => { 10 | return ( 11 | 12 | NeoShowcase 13 | 19 | 20 | }>{props.children} 21 | 22 | 23 | ) 24 | } 25 | 26 | const App = () => 27 | 28 | export default App 29 | -------------------------------------------------------------------------------- /dashboard/src/assets/icons/appState/deploying.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dashboard/src/assets/icons/appState/idle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dashboard/src/assets/icons/appState/running.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dashboard/src/assets/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/Badge.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps, type ParentComponent, splitProps } from 'solid-js' 2 | import { clsx } from '/@/libs/clsx' 3 | 4 | type VariantProps = { 5 | variant: 'text' | 'success' | 'warn' 6 | } 7 | 8 | const Badge: ParentComponent & VariantProps> = (props) => { 9 | const [_, rest] = splitProps(props, ['variant', 'class']) 10 | return ( 11 |
21 | ) 22 | } 23 | 24 | export default Badge 25 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/CheckBoxIcon.tsx: -------------------------------------------------------------------------------- 1 | import { type Component, Show } from 'solid-js' 2 | import CheckMark from '/@/assets/icons/check.svg' 3 | import { clsx } from '/@/libs/clsx' 4 | 5 | export interface Props { 6 | checked: boolean 7 | disabled?: boolean 8 | } 9 | 10 | export const CheckBoxIcon: Component = (props) => { 11 | return ( 12 |
19 | 20 | 21 | 22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/Code.tsx: -------------------------------------------------------------------------------- 1 | import { type Component, Show } from 'solid-js' 2 | import { writeToClipboard } from '/@/libs/clipboard' 3 | import { clsx } from '/@/libs/clsx' 4 | import { ToolTip } from './ToolTip' 5 | 6 | const Code: Component<{ 7 | value: string 8 | copyable?: boolean 9 | }> = (props) => { 10 | const handleCopy = () => { 11 | writeToClipboard(props.value) 12 | } 13 | 14 | return ( 15 |
21 | {props.value} 22 | 23 | 28 | 35 | 36 | 37 |
38 | ) 39 | } 40 | 41 | export default Code 42 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/JumpButton.tsx: -------------------------------------------------------------------------------- 1 | import { A } from '@solidjs/router' 2 | import type { VoidComponent } from 'solid-js' 3 | import { ToolTip } from '/@/components/UI/ToolTip' 4 | import { clsx } from '/@/libs/clsx' 5 | 6 | const JumpButton: VoidComponent<{ href: string; tooltip?: string }> = (props) => ( 7 | 8 | 9 |
17 |
18 |
19 |
20 | 21 | ) 22 | 23 | export default JumpButton 24 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/LogContainer.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps, type ParentComponent, splitProps } from 'solid-js' 2 | import { clsx } from '/@/libs/clsx' 3 | 4 | type VariantProps = { 5 | overflowX?: 'wrap' | 'scroll' 6 | } 7 | 8 | export const LogContainer: ParentComponent & VariantProps> = (props) => { 9 | const [_, rest] = splitProps(props, ['overflowX', 'class']) 10 | return ( 11 | 20 | {props.children} 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/ModalDeleteConfirm.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | 3 | const ModalDeleteConfirm: ParentComponent = (props) => { 4 | return ( 5 |
6 | {props.children} 7 |
8 | ) 9 | } 10 | 11 | export default ModalDeleteConfirm 12 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/RadioIcon.tsx: -------------------------------------------------------------------------------- 1 | import { type Component, Show } from 'solid-js' 2 | import { clsx } from '/@/libs/clsx' 3 | 4 | export interface Props { 5 | selected: boolean 6 | disabled?: boolean 7 | } 8 | 9 | export const RadioIcon: Component = (props) => { 10 | return ( 11 |
19 | 20 | 21 | Radio Icon 22 | 23 | 24 | 25 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/Skeleton.module.css: -------------------------------------------------------------------------------- 1 | .skeleton-animation::after { 2 | animation: skeleton-loading 1.5s linear infinite; 3 | } 4 | 5 | @keyframes skeleton-loading { 6 | from { 7 | transform: translateX(-100%); 8 | } 9 | to { 10 | transform: translateX(100%); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/Skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton as KSkeleton } from '@kobalte/core' 2 | import { type Component, mergeProps } from 'solid-js' 3 | import { clsx } from '/@/libs/clsx' 4 | import styles from './Skeleton.module.css' 5 | 6 | type SkeletonProps = Parameters[0] 7 | 8 | const defaultProps: SkeletonProps = { 9 | radius: 999, 10 | width: -1, // for `width: auto` 11 | } 12 | 13 | const Skeleton: Component = (props) => { 14 | const mergedProps = mergeProps(defaultProps, props) 15 | 16 | return ( 17 | 27 | {props.children} 28 | 29 | ) 30 | } 31 | 32 | export default Skeleton 33 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/StepProgress.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { styled } from '/@/components/styled-components' 3 | import { clsx } from '/@/libs/clsx' 4 | 5 | const Container = styled('div', 'flex w-full gap-4') 6 | 7 | const StepProgress: Component<{ 8 | title: string 9 | description: string 10 | state: 'complete' | 'current' | 'incomplete' 11 | }> = (props) => { 12 | return ( 13 |
14 |
24 |
34 |
{props.title}
35 |
{props.description}
36 |
37 |
38 | ) 39 | } 40 | 41 | export const Progress = { 42 | Container, 43 | Step: StepProgress, 44 | } 45 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/TabRound.tsx: -------------------------------------------------------------------------------- 1 | import { type JSX, splitProps } from 'solid-js' 2 | import type { ParentComponent } from 'solid-js' 3 | import { clsx } from '/@/libs/clsx' 4 | 5 | export interface Props extends JSX.ButtonHTMLAttributes { 6 | state?: 'active' | 'default' 7 | variant?: 'primary' | 'ghost' 8 | } 9 | 10 | export const TabRound: ParentComponent = (props) => { 11 | const [addedProps, originalButtonProps] = splitProps(props, ['state', 'variant', 'children']) 12 | 13 | return ( 14 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/TooltipInfoIcon.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { ToolTip, type TooltipProps } from './ToolTip' 3 | 4 | export const TooltipInfoIcon: Component = (props) => { 5 | return ( 6 | 7 |
8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/URLText.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { ToolTip } from './ToolTip' 3 | 4 | export interface URLTextProps { 5 | href: string 6 | text: string 7 | } 8 | 9 | export const URLText: Component = (props) => { 10 | return ( 11 | 17 | 18 |
19 | {props.text} 20 |
21 |
22 |
23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /dashboard/src/components/UI/UserAvater.tsx: -------------------------------------------------------------------------------- 1 | import { type Component, type JSX, splitProps } from 'solid-js' 2 | import type { User } from '/@/api/neoshowcase/protobuf/gateway_pb' 3 | 4 | export interface UserAvatarProps extends JSX.HTMLAttributes { 5 | user: User 6 | size?: number 7 | } 8 | 9 | const UserAvatar: Component = (props) => { 10 | const [addedProps, originalImgProps] = splitProps(props, ['user', 'size']) 11 | return ( 12 | {addedProps.user.name} 24 | ) 25 | } 26 | 27 | export default UserAvatar 28 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/DataTable.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '/@/components/styled-components' 2 | 3 | const Container = styled('div', 'flex w-full flex-col gap-4') 4 | 5 | const Title = styled('h2', 'h2-medium flex w-full items-center justify-between text-text-black') 6 | 7 | const SubTitle = styled('div', 'caption-medium text-text-grey') 8 | 9 | const Titles = styled('div', 'flex flex-col items-start') 10 | 11 | export const DataTable = { 12 | Container, 13 | Titles, 14 | Title, 15 | SubTitle, 16 | } 17 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/ErrorView.tsx: -------------------------------------------------------------------------------- 1 | import { A } from '@solidjs/router' 2 | import { type Component, Show } from 'solid-js' 3 | import { Button } from '../UI/Button' 4 | 5 | const ErrorView: Component<{ 6 | error: unknown 7 | }> = (props) => { 8 | const handleReload = () => { 9 | window.location.reload() 10 | } 11 | 12 | return ( 13 |
14 |
15 |

An error has occurred

16 | 17 |

{(props.error as Error).message}

18 |
19 |
20 | 21 | 28 | 29 | 37 |
38 |
39 | ) 40 | } 41 | 42 | export default ErrorView 43 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/FormBox.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '/@/components/styled-components' 2 | 3 | const Container = styled('div', 'w-full rounded-lg border border-ui-border border-solid bg-ui-primary') 4 | 5 | const Forms = styled('div', 'flex w-full flex-col gap-6 px-6 py-5') 6 | 7 | const Actions = styled( 8 | 'div', 9 | 'flex w-full items-center justify-end gap-2 rounded-b-lg border-ui-border border-t bg-ui-secondary px-6 py-4', 10 | ) 11 | 12 | const FormBox = { 13 | Container, 14 | Forms, 15 | Actions, 16 | } 17 | 18 | export default FormBox 19 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/MainView.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps, ParentComponent } from 'solid-js' 2 | import { clsx } from '/@/libs/clsx' 3 | 4 | type VariantProps = { 5 | background?: 'grey' | 'white' 6 | scrollable?: boolean 7 | } 8 | 9 | export const MainViewContainer: ParentComponent & VariantProps> = (props) => ( 10 |
21 | ) 22 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/SideView.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '/@/components/styled-components' 2 | 3 | const Container = styled( 4 | 'div', 5 | 'grid w-full grid-cols-[235px_minmax(0,1fr)] gap-12 max-lg:grid-cols-1 max-lg:grid-rows-[auto_auto] max-lg:gap-6', 6 | ) 7 | 8 | const Side = styled('div', 'h-full w-full') 9 | 10 | const Main = styled('div', 'h-full w-full') 11 | 12 | export const SideView = { 13 | Container, 14 | Side, 15 | Main, 16 | } 17 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/SuspenseContainer.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps, ParentComponent } from 'solid-js' 2 | import { clsx } from '/@/libs/clsx' 3 | 4 | type VariantProps = { 5 | isPending?: boolean 6 | } 7 | 8 | const SuspenseContainer: ParentComponent & VariantProps> = (props) => ( 9 |
17 | {props.children} 18 |
19 | ) 20 | 21 | export default SuspenseContainer 22 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/WithHeader.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | import { Header } from '../templates/Header' 3 | 4 | export const WithHeader: ParentComponent = (props) => { 5 | return ( 6 |
7 |
8 |
{props.children}
9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /dashboard/src/components/layouts/WithNav.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '/@/components/styled-components' 2 | 3 | const Container = styled('div', 'grid h-full w-full grid-cols-[minmax(0,1fr)] grid-rows-[max-content_1fr]') 4 | 5 | const Navs = styled('div', 'h-auto overflow-x-hidden border-ui-border border-b') 6 | 7 | const Body = styled('div', 'relative w-full') 8 | 9 | const TabContainer = styled( 10 | 'div', 11 | 'mx-auto flex w-full max-w-[min(1000px,calc(100%-64px))] gap-2 overflow-x-auto pb-4 max-md:max-w-[min(1000px,calc(100%-32px))]', 12 | ) 13 | 14 | export const WithNav = { 15 | Container, 16 | Navs, 17 | Tabs: TabContainer, 18 | Body, 19 | } 20 | -------------------------------------------------------------------------------- /dashboard/src/components/styled-components.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps, ParentComponent } from 'solid-js' 2 | import { Dynamic } from 'solid-js/web' 3 | import { clsx } from '/@/libs/clsx' 4 | 5 | type Tag = 'div' | 'p' | 'h2' | 'h3' | 'button' 6 | 7 | export const styled = (tag: T, className: string): ParentComponent> => { 8 | return (props) => 9 | } 10 | -------------------------------------------------------------------------------- /dashboard/src/components/templates/AppsNav.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { Nav } from './Nav' 3 | 4 | export const AppsNav: Component = () => { 5 | return