├── .dockerignore ├── .editorconfig ├── .gitignore ├── .prettierrc ├── Dockerfile ├── Dockerfile.worker ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── abstruse-server │ └── main.go └── abstruse-worker │ └── main.go ├── configs ├── demo │ └── default │ │ └── docker-compose.yml └── testing │ ├── e2e │ ├── data │ │ ├── gitea │ │ │ ├── git │ │ │ │ ├── .gitconfig │ │ │ │ ├── .ssh │ │ │ │ │ └── environment │ │ │ │ └── repositories │ │ │ │ │ └── gitea │ │ │ │ │ ├── chisel.git │ │ │ │ │ ├── HEAD │ │ │ │ │ ├── config │ │ │ │ │ ├── description │ │ │ │ │ ├── hooks │ │ │ │ │ │ ├── applypatch-msg.sample │ │ │ │ │ │ ├── commit-msg.sample │ │ │ │ │ │ ├── post-receive │ │ │ │ │ │ ├── post-receive.d │ │ │ │ │ │ │ └── gitea │ │ │ │ │ │ ├── post-update.sample │ │ │ │ │ │ ├── pre-applypatch.sample │ │ │ │ │ │ ├── pre-commit.sample │ │ │ │ │ │ ├── pre-merge-commit.sample │ │ │ │ │ │ ├── pre-push.sample │ │ │ │ │ │ ├── pre-rebase.sample │ │ │ │ │ │ ├── pre-receive │ │ │ │ │ │ ├── pre-receive.d │ │ │ │ │ │ │ └── gitea │ │ │ │ │ │ ├── pre-receive.sample │ │ │ │ │ │ ├── prepare-commit-msg.sample │ │ │ │ │ │ ├── update │ │ │ │ │ │ ├── update.d │ │ │ │ │ │ │ └── gitea │ │ │ │ │ │ └── update.sample │ │ │ │ │ ├── info │ │ │ │ │ │ ├── exclude │ │ │ │ │ │ └── refs │ │ │ │ │ ├── objects │ │ │ │ │ │ ├── info │ │ │ │ │ │ │ └── packs │ │ │ │ │ │ └── pack │ │ │ │ │ │ │ ├── pack-240a97588b15c19566b570d8f679902d93b64069.idx │ │ │ │ │ │ │ └── pack-240a97588b15c19566b570d8f679902d93b64069.pack │ │ │ │ │ └── refs │ │ │ │ │ │ └── heads │ │ │ │ │ │ └── master │ │ │ │ │ └── d3-bundle.git │ │ │ │ │ ├── HEAD │ │ │ │ │ ├── config │ │ │ │ │ ├── description │ │ │ │ │ ├── hooks │ │ │ │ │ ├── applypatch-msg.sample │ │ │ │ │ ├── commit-msg.sample │ │ │ │ │ ├── post-receive │ │ │ │ │ ├── post-receive.d │ │ │ │ │ │ └── gitea │ │ │ │ │ ├── post-update.sample │ │ │ │ │ ├── pre-applypatch.sample │ │ │ │ │ ├── pre-commit.sample │ │ │ │ │ ├── pre-merge-commit.sample │ │ │ │ │ ├── pre-push.sample │ │ │ │ │ ├── pre-rebase.sample │ │ │ │ │ ├── pre-receive │ │ │ │ │ ├── pre-receive.d │ │ │ │ │ │ └── gitea │ │ │ │ │ ├── pre-receive.sample │ │ │ │ │ ├── prepare-commit-msg.sample │ │ │ │ │ ├── update │ │ │ │ │ ├── update.d │ │ │ │ │ │ └── gitea │ │ │ │ │ └── update.sample │ │ │ │ │ ├── info │ │ │ │ │ ├── exclude │ │ │ │ │ └── refs │ │ │ │ │ ├── objects │ │ │ │ │ ├── 23 │ │ │ │ │ │ └── dac78c3317010b4851964fc0dff362fc40dd87 │ │ │ │ │ ├── 45 │ │ │ │ │ │ └── 9f7a9284ba653a382d73bd08dbff0ce48c04bd │ │ │ │ │ ├── 49 │ │ │ │ │ │ └── 4f47baeda0f20be99ceca28973a5b4e2639fac │ │ │ │ │ ├── 61 │ │ │ │ │ │ └── 2c0b44b5a2569f8392b391b571f5b0cf38d822 │ │ │ │ │ ├── 64 │ │ │ │ │ │ └── 4b14725c53c94d515e58e60cf55b45d2b0387d │ │ │ │ │ ├── 66 │ │ │ │ │ │ └── fe6594c1a643e6d9d3ac655948804543f361db │ │ │ │ │ ├── 75 │ │ │ │ │ │ └── 704c60b07c63ca3c23f7558669a86ee046b1b6 │ │ │ │ │ ├── 94 │ │ │ │ │ │ ├── 65c94d506140766fca573414c9819a8cd326bc │ │ │ │ │ │ └── dba4cd133625a3a9a8d8fe1fdc3e9fc116d79e │ │ │ │ │ ├── 06 │ │ │ │ │ │ └── 7e8657dcd509c2e63376eb64c17fbc218dcdf2 │ │ │ │ │ ├── 0e │ │ │ │ │ │ └── 117226e9611523c1bd75611a497294c34a89ee │ │ │ │ │ ├── 0f │ │ │ │ │ │ └── 449daa627f17a95f1feca1b6a2bdffe035a354 │ │ │ │ │ ├── 1d │ │ │ │ │ │ └── 9d875edb469786a9c6dbae5aaeef9a3bcb2c33 │ │ │ │ │ ├── 5e │ │ │ │ │ │ └── 331c4a57c0fbd6b3f3b0c44e23b969b815ab5c │ │ │ │ │ ├── 6e │ │ │ │ │ │ └── 0c2d4ca40cff19e81e7ccc540f83599ac46ba4 │ │ │ │ │ ├── 6f │ │ │ │ │ │ └── dd39e7e47bb01e92fdacd36aa510aa61dbe94a │ │ │ │ │ ├── 7e │ │ │ │ │ │ └── 02de72ffb166c8102057246b26b07f15fe0d3f │ │ │ │ │ ├── 8e │ │ │ │ │ │ └── 1c6452d41d7a45d0b16d5f711befdbbe2c0320 │ │ │ │ │ ├── 9b │ │ │ │ │ │ └── 160ccb0fe44106a5f535e4bf2ee8fade9c7457 │ │ │ │ │ ├── 9f │ │ │ │ │ │ └── ec0d70b748bd8292f43d32612948c0b134296e │ │ │ │ │ ├── a2 │ │ │ │ │ │ ├── 8f4dc019162842bd5b211ccab3938a4c869058 │ │ │ │ │ │ └── 9d30d933cc3b0f8ac921bb30912ae8d2eb0ac8 │ │ │ │ │ ├── a8 │ │ │ │ │ │ └── a375133818257773488e4d99a2423b5d87da0e │ │ │ │ │ ├── b2 │ │ │ │ │ │ └── 6adb425bcdc41d853a4a99d666f7e5922fc412 │ │ │ │ │ ├── b6 │ │ │ │ │ │ └── 640f6e51b9e54d757685915363dec6102c3c31 │ │ │ │ │ ├── b8 │ │ │ │ │ │ └── 3008725f2e4de6ed0eb82a3906c15a6bab36dc │ │ │ │ │ ├── ca │ │ │ │ │ │ └── 77fb843d6016f246237cf416116c7c692661ac │ │ │ │ │ ├── ce │ │ │ │ │ │ └── 382e7f0e87e08eed171b74218242a580548727 │ │ │ │ │ ├── d5 │ │ │ │ │ │ └── 186b0fd377a7c1598297bcdc7fea5025dd318b │ │ │ │ │ ├── da │ │ │ │ │ │ └── db4b56294c5feae8eb8cde4ac1bacd4218f6dc │ │ │ │ │ ├── df │ │ │ │ │ │ └── b770e6519e1e0606eb65dc09de0871078ab8ca │ │ │ │ │ ├── e0 │ │ │ │ │ │ └── 523379a7c09728f497fd99fb534fca7f740e62 │ │ │ │ │ ├── ea │ │ │ │ │ │ └── 78036835f061e9b31853d1649ac5f9bd522ff5 │ │ │ │ │ ├── eb │ │ │ │ │ │ └── 966e2602a8d73a9b2dd488c0ade54d84baa405 │ │ │ │ │ ├── f6 │ │ │ │ │ │ └── e575b38b60c0bee86677480861c4e474400d09 │ │ │ │ │ ├── fb │ │ │ │ │ │ └── 6a8a9e1aa5815c8eb7831eef10f0267d803b62 │ │ │ │ │ └── info │ │ │ │ │ │ └── packs │ │ │ │ │ └── refs │ │ │ │ │ └── heads │ │ │ │ │ └── master │ │ │ ├── gitea │ │ │ │ ├── avatars │ │ │ │ │ ├── afa79842299060b5ee62d1566448cd42 │ │ │ │ │ └── c3a8e0afa692c2437d775e44b59082aa │ │ │ │ ├── conf │ │ │ │ │ └── app.ini │ │ │ │ └── gitea.db │ │ │ └── ssh │ │ │ │ ├── ssh_host_dsa_key │ │ │ │ ├── ssh_host_dsa_key.pub │ │ │ │ ├── ssh_host_ecdsa_key │ │ │ │ ├── ssh_host_ecdsa_key.pub │ │ │ │ ├── ssh_host_ed25519_key │ │ │ │ ├── ssh_host_ed25519_key.pub │ │ │ │ ├── ssh_host_rsa_key │ │ │ │ └── ssh_host_rsa_key.pub │ │ └── gitea_credentials.txt │ └── docker-compose.yml │ └── e2e_postgres │ └── docker-compose.yml ├── docs ├── ABSTRUSE_YML.md ├── INTEGRATING_GIT_PROVIDERS.md └── QUICKSTART.md ├── go.mod ├── go.sum ├── internal ├── auth │ ├── auth.go │ ├── bcrypt.go │ ├── claims.go │ ├── grpc.go │ ├── htpasswd.go │ ├── jwt.go │ └── worker.go └── version │ └── version.go ├── pb └── api.proto ├── pkg ├── fs │ └── fs.go ├── gitscm │ ├── gitea.go │ ├── scm.go │ └── types.go ├── lib │ ├── collection.go │ ├── id.go │ ├── json.go │ ├── net.go │ ├── rand.go │ ├── tcp_wait.go │ └── time.go ├── stats │ ├── cpu.go │ ├── mem.go │ └── stats.go └── tlsutil │ └── certgen.go ├── server ├── api │ ├── api.go │ ├── badge │ │ └── badge.go │ ├── build │ │ ├── find.go │ │ ├── find_job.go │ │ ├── list.go │ │ ├── log.go │ │ ├── restart.go │ │ ├── restart_job.go │ │ ├── stop.go │ │ ├── stop_job.go │ │ └── trigger.go │ ├── middlewares │ │ └── auth.go │ ├── provider │ │ ├── create.go │ │ ├── delete.go │ │ ├── find.go │ │ ├── listuser.go │ │ ├── sync.go │ │ └── update.go │ ├── render │ │ ├── errors.go │ │ ├── render.go │ │ └── types.go │ ├── repo │ │ ├── active.go │ │ ├── config.go │ │ ├── create_env.go │ │ ├── create_hooks.go │ │ ├── create_mount.go │ │ ├── delete_env.go │ │ ├── delete_mount.go │ │ ├── find.go │ │ ├── list.go │ │ ├── list_env.go │ │ ├── list_hooks.go │ │ ├── list_mount.go │ │ ├── update_env.go │ │ ├── update_misc.go │ │ ├── update_mount.go │ │ └── update_sshkey.go │ ├── setup │ │ ├── ready.go │ │ └── user.go │ ├── stats │ │ ├── jobs.go │ │ ├── pause.go │ │ ├── resume.go │ │ └── stats.go │ ├── system │ │ └── version.go │ ├── team │ │ ├── create.go │ │ ├── find.go │ │ ├── list.go │ │ └── update.go │ ├── user │ │ ├── avatar.go │ │ ├── create.go │ │ ├── list.go │ │ ├── login.go │ │ ├── password.go │ │ ├── profile.go │ │ ├── update.go │ │ └── update_profile.go │ ├── webhook │ │ └── hook.go │ └── worker │ │ ├── auth.go │ │ ├── download_cache.go │ │ ├── list.go │ │ └── upload_cache.go ├── cmd │ ├── cmd.go │ └── wire.go ├── config │ └── config.go ├── core │ ├── build.go │ ├── env.go │ ├── githook.go │ ├── job.go │ ├── mount.go │ ├── permission.go │ ├── provider.go │ ├── registry.go │ ├── repo.go │ ├── scheduler.go │ ├── stats.go │ ├── team.go │ ├── timestamp.go │ ├── user.go │ └── worker.go ├── http │ └── http.go ├── logger │ └── logger.go ├── parser │ ├── env.go │ └── parser.go ├── scheduler │ └── scheduler.go ├── service │ ├── githook │ │ └── parser.go │ └── stats │ │ └── stats.go ├── store │ ├── build │ │ └── build.go │ ├── envvariable │ │ └── envvariable.go │ ├── job │ │ └── job.go │ ├── mounts │ │ └── mounts.go │ ├── permission │ │ └── permission.go │ ├── provider │ │ └── provider.go │ ├── repo │ │ └── repo.go │ ├── store.go │ ├── team │ │ └── team.go │ └── user │ │ └── user.go ├── worker │ └── registry.go └── ws │ ├── app.go │ ├── client.go │ ├── proxy.go │ ├── server.go │ └── types.go ├── tests ├── e2e │ ├── e2e.go │ └── tcp_wait.go └── webhooks │ ├── gitea │ ├── branch │ │ ├── headers.txt │ │ └── webhook.json │ ├── pull_request │ │ ├── headers.txt │ │ └── webhook.json │ ├── push │ │ ├── headers.txt │ │ └── webhook.json │ └── tag │ │ ├── headers.txt │ │ └── webhook.json │ ├── github │ ├── branch │ │ ├── headers.txt │ │ └── webhook.json │ ├── pull_request │ │ ├── headers.txt │ │ └── webhook.json │ ├── push │ │ ├── headers.txt │ │ ├── sha.js │ │ └── webhook.json │ └── tag │ │ ├── headers.txt │ │ └── webhook.json │ └── main.go ├── web └── abstruse │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── karma.conf.js │ ├── ngsw-config.json │ ├── package-lock.json │ ├── package.json │ ├── proxy.conf.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── auth │ │ │ ├── auth.module.ts │ │ │ ├── login │ │ │ │ ├── login.component.html │ │ │ │ ├── login.component.sass │ │ │ │ └── login.component.ts │ │ │ └── shared │ │ │ │ ├── already-auth-guard.service.ts │ │ │ │ ├── auth-guard.service.ts │ │ │ │ ├── auth.model.ts │ │ │ │ └── auth.service.ts │ │ ├── builds │ │ │ ├── build │ │ │ │ ├── build.component.html │ │ │ │ ├── build.component.sass │ │ │ │ └── build.component.ts │ │ │ ├── builds-routing.module.ts │ │ │ ├── builds.module.ts │ │ │ ├── common │ │ │ │ ├── build-list-item │ │ │ │ │ ├── build-list-item.component.html │ │ │ │ │ ├── build-list-item.component.sass │ │ │ │ │ └── build-list-item.component.ts │ │ │ │ ├── builds-common.module.ts │ │ │ │ └── builds-items │ │ │ │ │ ├── builds-items-options.model.ts │ │ │ │ │ ├── builds-items.component.html │ │ │ │ │ ├── builds-items.component.sass │ │ │ │ │ └── builds-items.component.ts │ │ │ ├── index │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.sass │ │ │ │ └── index.component.ts │ │ │ ├── job-list-item │ │ │ │ ├── job-list-item.component.html │ │ │ │ ├── job-list-item.component.sass │ │ │ │ └── job-list-item.component.ts │ │ │ ├── job │ │ │ │ ├── job.component.html │ │ │ │ ├── job.component.sass │ │ │ │ └── job.component.ts │ │ │ └── shared │ │ │ │ ├── build.model.ts │ │ │ │ └── builds.service.ts │ │ ├── core │ │ │ ├── core.module.ts │ │ │ ├── gateway-timeout │ │ │ │ ├── gateway-timeout.component.html │ │ │ │ ├── gateway-timeout.component.sass │ │ │ │ └── gateway-timeout.component.ts │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.sass │ │ │ │ └── header.component.ts │ │ │ ├── index.ts │ │ │ ├── interceptors │ │ │ │ ├── api.interceptor.ts │ │ │ │ └── error.interceptor.ts │ │ │ └── not-found │ │ │ │ ├── not-found.component.html │ │ │ │ ├── not-found.component.sass │ │ │ │ └── not-found.component.ts │ │ ├── dashboard │ │ │ ├── dashboard-routing.module.ts │ │ │ ├── dashboard.module.ts │ │ │ ├── index │ │ │ │ ├── index.component.html │ │ │ │ ├── index.component.sass │ │ │ │ └── index.component.ts │ │ │ └── shared │ │ │ │ └── dashboard.service.ts │ │ ├── profile │ │ │ ├── profile-routing.module.ts │ │ │ ├── profile.module.ts │ │ │ ├── profile │ │ │ │ ├── profile.component.html │ │ │ │ ├── profile.component.sass │ │ │ │ └── profile.component.ts │ │ │ ├── security │ │ │ │ ├── security.component.html │ │ │ │ ├── security.component.sass │ │ │ │ └── security.component.ts │ │ │ ├── settings │ │ │ │ ├── settings.component.html │ │ │ │ ├── settings.component.sass │ │ │ │ └── settings.component.ts │ │ │ └── shared │ │ │ │ ├── password.model.ts │ │ │ │ └── profile.service.ts │ │ ├── providers │ │ │ ├── provider-item │ │ │ │ ├── provider-item.component.html │ │ │ │ ├── provider-item.component.sass │ │ │ │ └── provider-item.component.ts │ │ │ ├── providers-modal │ │ │ │ ├── providers-modal.component.html │ │ │ │ ├── providers-modal.component.sass │ │ │ │ └── providers-modal.component.ts │ │ │ ├── providers-routing.module.ts │ │ │ ├── providers.module.ts │ │ │ ├── providers │ │ │ │ ├── providers.component.html │ │ │ │ ├── providers.component.sass │ │ │ │ └── providers.component.ts │ │ │ └── shared │ │ │ │ ├── provider.class.ts │ │ │ │ └── providers.service.ts │ │ ├── repos │ │ │ ├── branches │ │ │ │ ├── branches.component.html │ │ │ │ ├── branches.component.sass │ │ │ │ └── branches.component.ts │ │ │ ├── builds │ │ │ │ ├── builds.component.html │ │ │ │ ├── builds.component.sass │ │ │ │ └── builds.component.ts │ │ │ ├── pull-requests │ │ │ │ ├── pull-requests.component.html │ │ │ │ ├── pull-requests.component.sass │ │ │ │ └── pull-requests.component.ts │ │ │ ├── repo-item │ │ │ │ ├── repo-item.component.html │ │ │ │ ├── repo-item.component.sass │ │ │ │ └── repo-item.component.ts │ │ │ ├── repo │ │ │ │ ├── repo.component.html │ │ │ │ ├── repo.component.sass │ │ │ │ └── repo.component.ts │ │ │ ├── repos-routing.module.ts │ │ │ ├── repos.module.ts │ │ │ ├── repos │ │ │ │ ├── repos.component.html │ │ │ │ ├── repos.component.sass │ │ │ │ └── repos.component.ts │ │ │ ├── settings-env-modal │ │ │ │ ├── settings-env-modal.component.html │ │ │ │ ├── settings-env-modal.component.sass │ │ │ │ └── settings-env-modal.component.ts │ │ │ ├── settings-envs │ │ │ │ ├── env-variable.model.ts │ │ │ │ ├── settings-envs.component.html │ │ │ │ ├── settings-envs.component.sass │ │ │ │ └── settings-envs.component.ts │ │ │ ├── settings-mount-modal │ │ │ │ ├── settings-mount-modal.component.html │ │ │ │ ├── settings-mount-modal.component.sass │ │ │ │ └── settings-mount-modal.component.ts │ │ │ ├── settings-mount │ │ │ │ ├── mount-variable.model.ts │ │ │ │ ├── settings-mount.component.html │ │ │ │ ├── settings-mount.component.sass │ │ │ │ └── settings-mount.component.ts │ │ │ ├── settings-ssh-modal │ │ │ │ ├── settings-ssh-modal.component.html │ │ │ │ ├── settings-ssh-modal.component.sass │ │ │ │ ├── settings-ssh-modal.component.spec.ts │ │ │ │ └── settings-ssh-modal.component.ts │ │ │ ├── settings-ssh │ │ │ │ ├── settings-ssh.component.html │ │ │ │ ├── settings-ssh.component.sass │ │ │ │ ├── settings-ssh.component.spec.ts │ │ │ │ └── settings-ssh.component.ts │ │ │ ├── settings │ │ │ │ ├── settings.component.html │ │ │ │ ├── settings.component.sass │ │ │ │ └── settings.component.ts │ │ │ └── shared │ │ │ │ ├── hook.model.ts │ │ │ │ ├── repo.model.ts │ │ │ │ └── repos.service.ts │ │ ├── setup │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.sass │ │ │ │ └── header.component.ts │ │ │ ├── setup-routing.module.ts │ │ │ ├── setup.component.html │ │ │ ├── setup.component.ts │ │ │ ├── setup.module.ts │ │ │ ├── shared │ │ │ │ ├── admin.model.ts │ │ │ │ ├── setup-done-guard.service.ts │ │ │ │ ├── setup.model.ts │ │ │ │ └── setup.service.ts │ │ │ └── user │ │ │ │ ├── user.component.html │ │ │ │ ├── user.component.sass │ │ │ │ └── user.component.ts │ │ ├── shared │ │ │ ├── common │ │ │ │ ├── bytes.ts │ │ │ │ ├── colors.ts │ │ │ │ ├── random-hash.ts │ │ │ │ └── random-int.ts │ │ │ ├── components │ │ │ │ ├── loader │ │ │ │ │ ├── loader.component.html │ │ │ │ │ ├── loader.component.sass │ │ │ │ │ └── loader.component.ts │ │ │ │ ├── modal │ │ │ │ │ ├── content-ref.class.ts │ │ │ │ │ ├── modal-config.service.ts │ │ │ │ │ ├── modal-ref.class.ts │ │ │ │ │ ├── modal-stack.class.ts │ │ │ │ │ ├── modal.component.html │ │ │ │ │ ├── modal.component.sass │ │ │ │ │ ├── modal.component.ts │ │ │ │ │ ├── modal.module.ts │ │ │ │ │ └── modal.service.ts │ │ │ │ ├── progress-wizard │ │ │ │ │ ├── progress-wizard.component.html │ │ │ │ │ ├── progress-wizard.component.sass │ │ │ │ │ └── progress-wizard.component.ts │ │ │ │ └── terminal │ │ │ │ │ ├── terminal.component.html │ │ │ │ │ ├── terminal.component.sass │ │ │ │ │ └── terminal.component.ts │ │ │ ├── directives │ │ │ │ ├── stop-propagation.directive.ts │ │ │ │ ├── tooltip.directive.spec.ts │ │ │ │ └── tooltip.directive.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── monaco.model.ts │ │ │ │ ├── socket.class.ts │ │ │ │ └── socket.model.ts │ │ │ ├── providers │ │ │ │ ├── data.service.ts │ │ │ │ ├── socket.service.ts │ │ │ │ └── time.service.ts │ │ │ ├── shared.module.ts │ │ │ ├── validators │ │ │ │ └── validators.ts │ │ │ └── widgets │ │ │ │ ├── avatar-picker │ │ │ │ ├── avatar-picker.component.html │ │ │ │ ├── avatar-picker.component.sass │ │ │ │ ├── avatar-picker.component.spec.ts │ │ │ │ └── avatar-picker.component.ts │ │ │ │ ├── checkbox │ │ │ │ ├── checkbox.component.html │ │ │ │ ├── checkbox.component.sass │ │ │ │ ├── checkbox.component.spec.ts │ │ │ │ └── checkbox.component.ts │ │ │ │ ├── color-picker │ │ │ │ ├── color-picker.component.html │ │ │ │ ├── color-picker.component.sass │ │ │ │ └── color-picker.component.ts │ │ │ │ ├── progress-bar │ │ │ │ ├── progress-bar.component.html │ │ │ │ ├── progress-bar.component.sass │ │ │ │ ├── progress-bar.component.spec.ts │ │ │ │ ├── progress-bar.component.ts │ │ │ │ └── progress-bar.interface.ts │ │ │ │ ├── selectbox │ │ │ │ ├── selectbox.component.html │ │ │ │ ├── selectbox.component.sass │ │ │ │ ├── selectbox.component.spec.ts │ │ │ │ └── selectbox.component.ts │ │ │ │ └── toggle │ │ │ │ ├── toggle.component.html │ │ │ │ ├── toggle.component.sass │ │ │ │ └── toggle.component.ts │ │ ├── system │ │ │ ├── shared │ │ │ │ ├── system.service.ts │ │ │ │ └── version.class.ts │ │ │ ├── system-routing.module.ts │ │ │ ├── system.module.ts │ │ │ └── version │ │ │ │ ├── version.component.html │ │ │ │ ├── version.component.sass │ │ │ │ └── version.component.ts │ │ ├── teams │ │ │ ├── shared │ │ │ │ ├── teams.service.ts │ │ │ │ ├── user.model.ts │ │ │ │ └── users.service.ts │ │ │ ├── team-item │ │ │ │ ├── team-item.component.html │ │ │ │ ├── team-item.component.sass │ │ │ │ └── team-item.component.ts │ │ │ ├── team-modal │ │ │ │ ├── team-modal.component.html │ │ │ │ ├── team-modal.component.sass │ │ │ │ └── team-modal.component.ts │ │ │ ├── teams-list │ │ │ │ ├── teams-list.component.html │ │ │ │ ├── teams-list.component.sass │ │ │ │ └── teams-list.component.ts │ │ │ ├── teams-routing.module.ts │ │ │ ├── teams.module.ts │ │ │ ├── user-list-item │ │ │ │ ├── user-list-item.component.html │ │ │ │ ├── user-list-item.component.sass │ │ │ │ └── user-list-item.component.ts │ │ │ ├── user-modal │ │ │ │ ├── user-modal.component.html │ │ │ │ ├── user-modal.component.sass │ │ │ │ └── user-modal.component.ts │ │ │ └── users │ │ │ │ ├── users.component.html │ │ │ │ ├── users.component.sass │ │ │ │ └── users.component.ts │ │ └── workers │ │ │ ├── shared │ │ │ ├── worker.model.ts │ │ │ └── workers.service.ts │ │ │ ├── worker-modal │ │ │ ├── worker-modal.component.html │ │ │ ├── worker-modal.component.sass │ │ │ └── worker-modal.component.ts │ │ │ ├── worker-table-item │ │ │ ├── worker-table-item.component.html │ │ │ ├── worker-table-item.component.sass │ │ │ └── worker-table-item.component.ts │ │ │ ├── workers-routing.module.ts │ │ │ ├── workers.module.ts │ │ │ └── workers │ │ │ ├── workers.component.html │ │ │ ├── workers.component.sass │ │ │ └── workers.component.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── fonts │ │ │ ├── Roboto │ │ │ │ ├── roboto-v20-latin-100.woff │ │ │ │ ├── roboto-v20-latin-100.woff2 │ │ │ │ ├── roboto-v20-latin-300.woff │ │ │ │ ├── roboto-v20-latin-300.woff2 │ │ │ │ ├── roboto-v20-latin-500.woff │ │ │ │ ├── roboto-v20-latin-500.woff2 │ │ │ │ ├── roboto-v20-latin-700.woff │ │ │ │ ├── roboto-v20-latin-700.woff2 │ │ │ │ ├── roboto-v20-latin-900.woff │ │ │ │ ├── roboto-v20-latin-900.woff2 │ │ │ │ ├── roboto-v20-latin-regular.woff │ │ │ │ └── roboto-v20-latin-regular.woff2 │ │ │ ├── Rubik │ │ │ │ ├── rubik-v9-latin-300.woff │ │ │ │ ├── rubik-v9-latin-300.woff2 │ │ │ │ ├── rubik-v9-latin-500.woff │ │ │ │ ├── rubik-v9-latin-500.woff2 │ │ │ │ ├── rubik-v9-latin-700.woff │ │ │ │ ├── rubik-v9-latin-700.woff2 │ │ │ │ ├── rubik-v9-latin-900.woff │ │ │ │ ├── rubik-v9-latin-900.woff2 │ │ │ │ ├── rubik-v9-latin-regular.woff │ │ │ │ └── rubik-v9-latin-regular.woff2 │ │ │ ├── SourceCodePro │ │ │ │ ├── SourceCodePro-Bold.woff │ │ │ │ ├── SourceCodePro-Bold.woff2 │ │ │ │ ├── SourceCodePro-Regular.woff │ │ │ │ └── SourceCodePro-Regular.woff2 │ │ │ └── octicons │ │ │ │ ├── octicons.svg │ │ │ │ ├── octicons.woff │ │ │ │ └── octicons.woff2 │ │ ├── icons │ │ │ ├── icon-192x192.png │ │ │ └── icon-512x512.png │ │ └── images │ │ │ ├── abstruse-square-dark.svg │ │ │ ├── abstruse-square.svg │ │ │ ├── abstruse-text-black.svg │ │ │ ├── abstruse.svg │ │ │ ├── avatars │ │ │ ├── avatar_1.svg │ │ │ ├── avatar_10.svg │ │ │ ├── avatar_11.svg │ │ │ ├── avatar_12.svg │ │ │ ├── avatar_13.svg │ │ │ ├── avatar_14.svg │ │ │ ├── avatar_15.svg │ │ │ ├── avatar_16.svg │ │ │ ├── avatar_17.svg │ │ │ ├── avatar_18.svg │ │ │ ├── avatar_19.svg │ │ │ ├── avatar_2.svg │ │ │ ├── avatar_20.svg │ │ │ ├── avatar_21.svg │ │ │ ├── avatar_22.svg │ │ │ ├── avatar_23.svg │ │ │ ├── avatar_24.svg │ │ │ ├── avatar_25.svg │ │ │ ├── avatar_26.svg │ │ │ ├── avatar_27.svg │ │ │ ├── avatar_28.svg │ │ │ ├── avatar_29.svg │ │ │ ├── avatar_3.svg │ │ │ ├── avatar_30.svg │ │ │ ├── avatar_4.svg │ │ │ ├── avatar_5.svg │ │ │ ├── avatar_6.svg │ │ │ ├── avatar_7.svg │ │ │ ├── avatar_8.svg │ │ │ └── avatar_9.svg │ │ │ └── icons │ │ │ ├── gitea-bg.svg │ │ │ ├── gitea-gray.svg │ │ │ └── gitea.svg │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── manifest.webmanifest │ ├── polyfills.ts │ ├── styles │ │ ├── _animations.sass │ │ ├── _colours.sass │ │ ├── _mixins.sass │ │ ├── _typography.sass │ │ ├── _variables.sass │ │ ├── app.sass │ │ ├── button.sass │ │ ├── common.sass │ │ ├── form.sass │ │ ├── header.sass │ │ ├── hero.sass │ │ ├── list.sass │ │ ├── main.sass │ │ ├── notification.sass │ │ ├── octicons.sass │ │ ├── pagination.sass │ │ ├── placeholder.sass │ │ ├── section.sass │ │ ├── status.sass │ │ ├── subheader.sass │ │ ├── table.sass │ │ ├── tabs.sass │ │ ├── tag.sass │ │ ├── terminal.sass │ │ └── tooltip.sass │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── webpack.config.js └── worker ├── app ├── app.go ├── interceptor.go └── server.go ├── cache ├── cache.go ├── download.go └── upload.go ├── cmd ├── cmd.go └── wire.go ├── config └── config.go ├── docker ├── docker.go ├── image.go ├── init.go └── util.go ├── git ├── clone.go └── git.go ├── http ├── bearer.go └── client.go └── logger └── logger.go /.dockerignore: -------------------------------------------------------------------------------- 1 | build/ 2 | pb/api.pb.go 3 | pb/api_grpc.pb.go 4 | server/ui 5 | worker/data 6 | server/cmd/wire_gen.go 7 | worker/cmd/wire_gen.go 8 | .DS_Store 9 | tmp/ 10 | configs/ 11 | web/abstruse/node_modules 12 | web/abstruse/dist 13 | ./**/*/testdata/ 14 | .git/ 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.go] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /server/ui/ 3 | /worker/data 4 | /server/cmd/wire_gen.go 5 | /worker/cmd/wire_gen.go 6 | /pb/api.pb.go 7 | /pb/api_grpc.pb.go 8 | .DS_Store 9 | tmp/ 10 | configs/testing/**/*.pem 11 | configs/testing/**/*.log 12 | configs/testing/**/*/indexers/ 13 | configs/testing/**/*/log/ 14 | configs/testing/**/*/queues/ 15 | configs/testing/**/*/sessions/ 16 | testdata/ 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true, 8 | "trailingComma": "none", 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bleenco GmbH https://bleenco.com 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 | -------------------------------------------------------------------------------- /cmd/abstruse-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bleenco/abstruse/server/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /cmd/abstruse-worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bleenco/abstruse/worker/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Gitea 3 | email = gitea@fake.local 4 | [core] 5 | quotepath = false 6 | commitGraph = true 7 | [gc] 8 | writeCommitGraph = true 9 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/.ssh/environment: -------------------------------------------------------------------------------- 1 | GITEA_CUSTOM=/data/gitea 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | ignorecase = true 6 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/post-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | data=$(cat) 3 | exitcodes="" 4 | hookname=$(basename $0) 5 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 6 | 7 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 8 | test -x "${hook}" && test -f "${hook}" || continue 9 | echo "${data}" | "${hook}" 10 | exitcodes="${exitcodes} $?" 11 | done 12 | 13 | for i in ${exitcodes}; do 14 | [ ${i} -eq 0 ] || exit ${i} 15 | done 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/post-receive.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' post-receive 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/pre-merge-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git merge" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message to 6 | # stderr if it wants to stop the merge commit. 7 | # 8 | # To enable this hook, rename this file to "pre-merge-commit". 9 | 10 | . git-sh-setup 11 | test -x "$GIT_DIR/hooks/pre-commit" && 12 | exec "$GIT_DIR/hooks/pre-commit" 13 | : 14 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | data=$(cat) 3 | exitcodes="" 4 | hookname=$(basename $0) 5 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 6 | 7 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 8 | test -x "${hook}" && test -f "${hook}" || continue 9 | echo "${data}" | "${hook}" 10 | exitcodes="${exitcodes} $?" 11 | done 12 | 13 | for i in ${exitcodes}; do 14 | [ ${i} -eq 0 ] || exit ${i} 15 | done 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/pre-receive.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' pre-receive 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exitcodes="" 3 | hookname=$(basename $0) 4 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 5 | 6 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 7 | test -x "${hook}" && test -f "${hook}" || continue 8 | "${hook}" $1 $2 $3 9 | exitcodes="${exitcodes} $?" 10 | done 11 | 12 | for i in ${exitcodes}; do 13 | [ ${i} -eq 0 ] || exit ${i} 14 | done 15 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/hooks/update.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' update $1 $2 $3 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/info/refs: -------------------------------------------------------------------------------- 1 | 872e99a0de70656cbac0638953458bbbbf016629 refs/heads/master 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/objects/info/packs: -------------------------------------------------------------------------------- 1 | P pack-240a97588b15c19566b570d8f679902d93b64069.pack 2 | 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/objects/pack/pack-240a97588b15c19566b570d8f679902d93b64069.idx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/objects/pack/pack-240a97588b15c19566b570d8f679902d93b64069.idx -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/objects/pack/pack-240a97588b15c19566b570d8f679902d93b64069.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/objects/pack/pack-240a97588b15c19566b570d8f679902d93b64069.pack -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/chisel.git/refs/heads/master: -------------------------------------------------------------------------------- 1 | 872e99a0de70656cbac0638953458bbbbf016629 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | ignorecase = true 6 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/applypatch-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message taken by 4 | # applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. The hook is 8 | # allowed to edit the commit message file. 9 | # 10 | # To enable this hook, rename this file to "applypatch-msg". 11 | 12 | . git-sh-setup 13 | commitmsg="$(git rev-parse --git-path hooks/commit-msg)" 14 | test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} 15 | : 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/commit-msg.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to check the commit log message. 4 | # Called by "git commit" with one argument, the name of the file 5 | # that has the commit message. The hook should exit with non-zero 6 | # status after issuing an appropriate message if it wants to stop the 7 | # commit. The hook is allowed to edit the commit message file. 8 | # 9 | # To enable this hook, rename this file to "commit-msg". 10 | 11 | # Uncomment the below to add a Signed-off-by line to the message. 12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg 13 | # hook is more suited to it. 14 | # 15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 17 | 18 | # This example catches duplicate Signed-off-by lines. 19 | 20 | test "" = "$(grep '^Signed-off-by: ' "$1" | 21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { 22 | echo >&2 Duplicate Signed-off-by lines. 23 | exit 1 24 | } 25 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/post-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | data=$(cat) 3 | exitcodes="" 4 | hookname=$(basename $0) 5 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 6 | 7 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 8 | test -x "${hook}" && test -f "${hook}" || continue 9 | echo "${data}" | "${hook}" 10 | exitcodes="${exitcodes} $?" 11 | done 12 | 13 | for i in ${exitcodes}; do 14 | [ ${i} -eq 0 ] || exit ${i} 15 | done 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/post-receive.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' post-receive 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/post-update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to prepare a packed repository for use over 4 | # dumb transports. 5 | # 6 | # To enable this hook, rename this file to "post-update". 7 | 8 | exec git update-server-info 9 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/pre-applypatch.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed 4 | # by applypatch from an e-mail message. 5 | # 6 | # The hook should exit with non-zero status after issuing an 7 | # appropriate message if it wants to stop the commit. 8 | # 9 | # To enable this hook, rename this file to "pre-applypatch". 10 | 11 | . git-sh-setup 12 | precommit="$(git rev-parse --git-path hooks/pre-commit)" 13 | test -x "$precommit" && exec "$precommit" ${1+"$@"} 14 | : 15 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/pre-merge-commit.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git merge" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message to 6 | # stderr if it wants to stop the merge commit. 7 | # 8 | # To enable this hook, rename this file to "pre-merge-commit". 9 | 10 | . git-sh-setup 11 | test -x "$GIT_DIR/hooks/pre-commit" && 12 | exec "$GIT_DIR/hooks/pre-commit" 13 | : 14 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | data=$(cat) 3 | exitcodes="" 4 | hookname=$(basename $0) 5 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 6 | 7 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 8 | test -x "${hook}" && test -f "${hook}" || continue 9 | echo "${data}" | "${hook}" 10 | exitcodes="${exitcodes} $?" 11 | done 12 | 13 | for i in ${exitcodes}; do 14 | [ ${i} -eq 0 ] || exit ${i} 15 | done 16 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/pre-receive.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' pre-receive 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/pre-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to make use of push options. 4 | # The example simply echoes all push options that start with 'echoback=' 5 | # and rejects all pushes when the "reject" push option is used. 6 | # 7 | # To enable this hook, rename this file to "pre-receive". 8 | 9 | if test -n "$GIT_PUSH_OPTION_COUNT" 10 | then 11 | i=0 12 | while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" 13 | do 14 | eval "value=\$GIT_PUSH_OPTION_$i" 15 | case "$value" in 16 | echoback=*) 17 | echo "echo from the pre-receive-hook: ${value#*=}" >&2 18 | ;; 19 | reject) 20 | exit 1 21 | esac 22 | i=$((i + 1)) 23 | done 24 | fi 25 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exitcodes="" 3 | hookname=$(basename $0) 4 | GIT_DIR=${GIT_DIR:-$(dirname $0)} 5 | 6 | for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 7 | test -x "${hook}" && test -f "${hook}" || continue 8 | "${hook}" $1 $2 $3 9 | exitcodes="${exitcodes} $?" 10 | done 11 | 12 | for i in ${exitcodes}; do 13 | [ ${i} -eq 0 ] || exit ${i} 14 | done 15 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/hooks/update.d/gitea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | "/app/gitea/gitea" hook --config='/data/gitea/conf/app.ini' update $1 $2 $3 3 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/info/exclude: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/info/refs: -------------------------------------------------------------------------------- 1 | ce382e7f0e87e08eed171b74218242a580548727 refs/heads/master 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/06/7e8657dcd509c2e63376eb64c17fbc218dcdf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/06/7e8657dcd509c2e63376eb64c17fbc218dcdf2 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/0e/117226e9611523c1bd75611a497294c34a89ee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/0e/117226e9611523c1bd75611a497294c34a89ee -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/0f/449daa627f17a95f1feca1b6a2bdffe035a354: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/0f/449daa627f17a95f1feca1b6a2bdffe035a354 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/1d/9d875edb469786a9c6dbae5aaeef9a3bcb2c33: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/1d/9d875edb469786a9c6dbae5aaeef9a3bcb2c33 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/23/dac78c3317010b4851964fc0dff362fc40dd87: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/23/dac78c3317010b4851964fc0dff362fc40dd87 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/45/9f7a9284ba653a382d73bd08dbff0ce48c04bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/45/9f7a9284ba653a382d73bd08dbff0ce48c04bd -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/49/4f47baeda0f20be99ceca28973a5b4e2639fac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/49/4f47baeda0f20be99ceca28973a5b4e2639fac -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/5e/331c4a57c0fbd6b3f3b0c44e23b969b815ab5c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/5e/331c4a57c0fbd6b3f3b0c44e23b969b815ab5c -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/61/2c0b44b5a2569f8392b391b571f5b0cf38d822: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/61/2c0b44b5a2569f8392b391b571f5b0cf38d822 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/64/4b14725c53c94d515e58e60cf55b45d2b0387d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/64/4b14725c53c94d515e58e60cf55b45d2b0387d -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/66/fe6594c1a643e6d9d3ac655948804543f361db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/66/fe6594c1a643e6d9d3ac655948804543f361db -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/6e/0c2d4ca40cff19e81e7ccc540f83599ac46ba4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/6e/0c2d4ca40cff19e81e7ccc540f83599ac46ba4 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/6f/dd39e7e47bb01e92fdacd36aa510aa61dbe94a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/6f/dd39e7e47bb01e92fdacd36aa510aa61dbe94a -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/75/704c60b07c63ca3c23f7558669a86ee046b1b6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/75/704c60b07c63ca3c23f7558669a86ee046b1b6 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/7e/02de72ffb166c8102057246b26b07f15fe0d3f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/7e/02de72ffb166c8102057246b26b07f15fe0d3f -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/8e/1c6452d41d7a45d0b16d5f711befdbbe2c0320: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/8e/1c6452d41d7a45d0b16d5f711befdbbe2c0320 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/94/65c94d506140766fca573414c9819a8cd326bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/94/65c94d506140766fca573414c9819a8cd326bc -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/94/dba4cd133625a3a9a8d8fe1fdc3e9fc116d79e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/94/dba4cd133625a3a9a8d8fe1fdc3e9fc116d79e -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/9b/160ccb0fe44106a5f535e4bf2ee8fade9c7457: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/9b/160ccb0fe44106a5f535e4bf2ee8fade9c7457 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/9f/ec0d70b748bd8292f43d32612948c0b134296e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/9f/ec0d70b748bd8292f43d32612948c0b134296e -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a2/8f4dc019162842bd5b211ccab3938a4c869058: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a2/8f4dc019162842bd5b211ccab3938a4c869058 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a2/9d30d933cc3b0f8ac921bb30912ae8d2eb0ac8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a2/9d30d933cc3b0f8ac921bb30912ae8d2eb0ac8 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a8/a375133818257773488e4d99a2423b5d87da0e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/a8/a375133818257773488e4d99a2423b5d87da0e -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b2/6adb425bcdc41d853a4a99d666f7e5922fc412: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b2/6adb425bcdc41d853a4a99d666f7e5922fc412 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b6/640f6e51b9e54d757685915363dec6102c3c31: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b6/640f6e51b9e54d757685915363dec6102c3c31 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b8/3008725f2e4de6ed0eb82a3906c15a6bab36dc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/b8/3008725f2e4de6ed0eb82a3906c15a6bab36dc -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ca/77fb843d6016f246237cf416116c7c692661ac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ca/77fb843d6016f246237cf416116c7c692661ac -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ce/382e7f0e87e08eed171b74218242a580548727: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ce/382e7f0e87e08eed171b74218242a580548727 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/d5/186b0fd377a7c1598297bcdc7fea5025dd318b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/d5/186b0fd377a7c1598297bcdc7fea5025dd318b -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/da/db4b56294c5feae8eb8cde4ac1bacd4218f6dc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/da/db4b56294c5feae8eb8cde4ac1bacd4218f6dc -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/df/b770e6519e1e0606eb65dc09de0871078ab8ca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/df/b770e6519e1e0606eb65dc09de0871078ab8ca -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/e0/523379a7c09728f497fd99fb534fca7f740e62: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/e0/523379a7c09728f497fd99fb534fca7f740e62 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ea/78036835f061e9b31853d1649ac5f9bd522ff5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/ea/78036835f061e9b31853d1649ac5f9bd522ff5 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/eb/966e2602a8d73a9b2dd488c0ade54d84baa405: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/eb/966e2602a8d73a9b2dd488c0ade54d84baa405 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/f6/e575b38b60c0bee86677480861c4e474400d09: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/f6/e575b38b60c0bee86677480861c4e474400d09 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/fb/6a8a9e1aa5815c8eb7831eef10f0267d803b62: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/fb/6a8a9e1aa5815c8eb7831eef10f0267d803b62 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/objects/info/packs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/git/repositories/gitea/d3-bundle.git/refs/heads/master: -------------------------------------------------------------------------------- 1 | ce382e7f0e87e08eed171b74218242a580548727 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/gitea/avatars/afa79842299060b5ee62d1566448cd42: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/gitea/avatars/afa79842299060b5ee62d1566448cd42 -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/gitea/avatars/c3a8e0afa692c2437d775e44b59082aa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/gitea/avatars/c3a8e0afa692c2437d775e44b59082aa -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/gitea/gitea.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/configs/testing/e2e/data/gitea/gitea/gitea.db -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_dsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-dss AAAAB3NzaC1kc3MAAACBAOGfq8V8IU07Nc4zQC6MtUyLx5wdhGWedm3qlWHT7UUovP+e+xt0Rf8Z/V8uoTB4nBDKA7juby7gifEvWfrzd/y6BqbIP9poD5l+jNAzQIPB2r0W4Grfw6ZLFnC1LB+PQEGaMm2Y5y8u6HVQzxV9BdUCeaoN5UhXTDr4pt0IYyeJAAAAFQDep7rcUupZTfuwikZDR1TmYai2DQAAAIBpw6aPEUdiQ2qvarl604KGEQ9f+mCPQT+6IA97T6/znayerwzCaUhZJ8Kkup7ztjzqw7evUdQntwNp9MY59J98oLSxpK4msX15vX4wec9Z/NI43HoYsctAQgukBWs9IP4XUqPTWdt3Ka5KfkEV6MYka8Zty31q1HvABrIXexU6fAAAAIAcBOfCaUkH9XP4uxLAdUIS51k8/7b7TGr/RyTJiBvBPyyNnn8Vxd8gm1X1KAZ53mwYapSl+mYED31Srgb3EOd3S0JedOJY/e0RTJwiKuv41G9pQ7XW2gVHiLUkKVyuxfSuJf/x/EGeJk0fgMRhaC9Ud0dCmEc6fLcI3BPwpu+TUA== root@4a37bd2d0f91 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_ecdsa_key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS 3 | 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQoSEMVoE2wrze0aUdqN5qhPY9UDKp6 4 | 2tQEbVswbtzV3D5BygdfmI7Cu6KKGqvRdvLM9RLasV2yt2n4xAvVMz8EAAAAsFad3fhWnd 5 | 34AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBChIQxWgTbCvN7Rp 6 | R2o3mqE9j1QMqnra1ARtWzBu3NXcPkHKB1+YjsK7oooaq9F28sz1EtqxXbK3afjEC9UzPw 7 | QAAAAhAJfBv477skQzc+dtwN0T3ioA2aZGQtmJ9B8SiPxiBEKNAAAAEXJvb3RANGEzN2Jk 8 | MmQwZjkxAQIDBAUG 9 | -----END OPENSSH PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_ecdsa_key.pub: -------------------------------------------------------------------------------- 1 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBChIQxWgTbCvN7RpR2o3mqE9j1QMqnra1ARtWzBu3NXcPkHKB1+YjsK7oooaq9F28sz1EtqxXbK3afjEC9UzPwQ= root@4a37bd2d0f91 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_ed25519_key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 3 | QyNTUxOQAAACCF/Qjua5JUTB73iFM0AWKablT3xxJvkMxklt6BqQVNrgAAAJigZegtoGXo 4 | LQAAAAtzc2gtZWQyNTUxOQAAACCF/Qjua5JUTB73iFM0AWKablT3xxJvkMxklt6BqQVNrg 5 | AAAECdEkCeskyuZ8tSYtklnQfxPd9sAOEeWUDQX4LB4spxXIX9CO5rklRMHveIUzQBYppu 6 | VPfHEm+QzGSW3oGpBU2uAAAAEXJvb3RANGEzN2JkMmQwZjkxAQIDBA== 7 | -----END OPENSSH PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_ed25519_key.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIX9CO5rklRMHveIUzQBYppuVPfHEm+QzGSW3oGpBU2u root@4a37bd2d0f91 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea/ssh/ssh_host_rsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMseyFuyMAVteCMHdn8wydeXnAhzKiFKWbn4zpaCJo7E4REYm5ERuTNFLJ1VId8T7+w9JyMwDtgy/jsEs5HxBfa7NXoWYZJvlUnSGHLozyOKeTJGIXmGk49+B8DiZ588U05QP6YmZlTlb2++TOdt6TuIfx/72pUbFajMLAgZdTcBqBvabhC7hWe0PTf5ptbmA4IugN2Uir0dNevmIj133sOVSHvOdigzsfzk0EPxxNgjenepYW8LfJh59m2rDF6EhkDYZ7xjh4gRX4tTaMzbFHgOKz+TCTr1BT4AOVu97x6YVgcgJUL2k75jpTukLVCFpUeKJBwn8aBuYwZuYhs3CX root@4a37bd2d0f91 2 | -------------------------------------------------------------------------------- /configs/testing/e2e/data/gitea_credentials.txt: -------------------------------------------------------------------------------- 1 | url: http://10.20.20.50:3200/ 2 | username: gitea 3 | password: Gitea321! 4 | access_token: 868615bbceac2bbcf02037709a82eae9e32fc519 5 | secret: 6256a5206ef 6 | 7 | // misc commands 8 | git update-index --assume-unchanged configs/testing/e2e/data/gitea/gitea/gitea.db 9 | -------------------------------------------------------------------------------- /internal/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | // JWT is exposed JWT authenticator with middlewares 9 | // to verify access tokens. 10 | var JWT *JWTAuth 11 | 12 | var ( 13 | // JWTSecret secred from config for signing tokens. 14 | JWTSecret []byte 15 | ) 16 | 17 | // Init authentication constants from config. 18 | func Init(secret string) { 19 | JWTSecret = []byte(secret) 20 | JWT = NewJWTAuth("HS256") 21 | } 22 | 23 | func fatal(msg interface{}) { 24 | fmt.Println(msg) 25 | os.Exit(1) 26 | } 27 | -------------------------------------------------------------------------------- /internal/auth/bcrypt.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "golang.org/x/crypto/bcrypt" 5 | ) 6 | 7 | // Password defines password to be hashed. 8 | type Password struct { 9 | Password string 10 | Cost int 11 | } 12 | 13 | // HashPassword generates encrypted password from password string 14 | func HashPassword(passwd Password) (string, error) { 15 | if passwd.Cost == 0 { 16 | passwd.Cost = bcrypt.DefaultCost 17 | } 18 | bytes, err := bcrypt.GenerateFromPassword([]byte(passwd.Password), passwd.Cost) 19 | return string(bytes), err 20 | } 21 | 22 | // CheckPasswordHash compares password string with encrypted hash. 23 | func CheckPasswordHash(password, hash string) bool { 24 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 25 | return err == nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/auth/grpc.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "context" 4 | 5 | // Authentication holds the identifier/jwt token credentials. 6 | type Authentication struct { 7 | Identifier string 8 | JWT string 9 | } 10 | 11 | // GetRequestMetadata gets the current request metadata. 12 | func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { 13 | return map[string]string{ 14 | "identifier": a.Identifier, 15 | "jwt": a.JWT, 16 | }, nil 17 | } 18 | 19 | // RequireTransportSecurity indicates whether the credentials requires transport security. 20 | func (a *Authentication) RequireTransportSecurity() bool { 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /internal/auth/htpasswd.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bleenco/abstruse/pkg/fs" 7 | ) 8 | 9 | func generateHtpasswdFile(filePath, user, password string) error { 10 | passwd, err := HashPassword(Password{Password: password, Cost: 1}) 11 | if err != nil { 12 | return err 13 | } 14 | creds := fmt.Sprintf("%s:%s\n", user, passwd) 15 | return fs.WriteFile(filePath, creds) 16 | } 17 | -------------------------------------------------------------------------------- /internal/auth/worker.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "fmt" 5 | 6 | jwt "github.com/dgrijalva/jwt-go" 7 | ) 8 | 9 | // GenerateWorkerJWT generates workers json web token. 10 | func GenerateWorkerJWT(id string) (string, error) { 11 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ 12 | "identifier": id, 13 | }) 14 | 15 | return token.SignedString(JWTSecret) 16 | } 17 | 18 | // GetWorkerIdentifierByJWT return workers id by token string. 19 | func GetWorkerIdentifierByJWT(token string) (string, error) { 20 | var id string 21 | 22 | if token == "" { 23 | return id, fmt.Errorf("invalid token") 24 | } 25 | 26 | t, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { 27 | if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { 28 | return nil, fmt.Errorf("Unexpected signing method: %v", t.Header["alg"]) 29 | } 30 | 31 | return JWTSecret, nil 32 | }) 33 | if err != nil { 34 | return id, err 35 | } 36 | 37 | if claims, ok := t.Claims.(jwt.MapClaims); ok && t.Valid { 38 | id = claims["identifier"].(string) 39 | } 40 | 41 | return id, nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/gitscm/types.go: -------------------------------------------------------------------------------- 1 | package gitscm 2 | 3 | // HookForm struct for saving webhooks. 4 | type HookForm struct { 5 | Branch bool `json:"branch" valid:"required"` 6 | Push bool `json:"push" valid:"required"` 7 | PullRequest bool `json:"pullRequest" valid:"required"` 8 | Tag bool `json:"tag" valid:"required"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/lib/id.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | ) 6 | 7 | // ID returns new uuid in string format. 8 | func ID() string { 9 | return uuid.New().String() 10 | } 11 | -------------------------------------------------------------------------------- /pkg/lib/json.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | ) 8 | 9 | // DecodeJSON is a convenience function to create a JSON decoder 10 | // set it up to disallow unknown fields and then decode into the 11 | // given value. 12 | func DecodeJSON(data io.Reader, out interface{}) error { 13 | if data == nil { 14 | return io.EOF 15 | } 16 | 17 | decoder := json.NewDecoder(data) 18 | decoder.DisallowUnknownFields() 19 | return decoder.Decode(&out) 20 | } 21 | 22 | // UnmarshalJSON is a convenience function around calling DecodeJSON. 23 | func UnmarshalJSON(data []byte, out interface{}) error { 24 | return DecodeJSON(bytes.NewReader(data), out) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/lib/rand.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | ) 7 | 8 | // RandomString returns random string. 9 | func RandomString() string { 10 | b := make([]byte, 4) 11 | rand.Read(b) 12 | return fmt.Sprintf("%x", b) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/lib/tcp_wait.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // WaitTCP is used to wait for external service is available on specific host and port. 10 | func WaitTCP(duration time.Duration, host string, port int) error { 11 | timeout := time.After(duration) 12 | tick := time.Tick(1 * time.Second) 13 | host = fmt.Sprintf("%s:%d", host, port) 14 | for { 15 | select { 16 | case <-timeout: 17 | return fmt.Errorf("timed out") 18 | case <-tick: 19 | conn, err := net.DialTimeout("tcp", host, 500*time.Millisecond) 20 | if err == nil { 21 | conn.Close() 22 | return nil 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/lib/time.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // FormatTime returns DB compatible datetime string. 8 | func FormatTime(t time.Time) string { 9 | if t.IsZero() { 10 | return "0000-00-00 00:00:00" 11 | } 12 | 13 | return t.Format("2006-01-02 15:04:05") 14 | } 15 | 16 | // ParseTime returns time.Time from string. 17 | func ParseTime(str string) time.Time { 18 | t, err := time.Parse("2006-01-02 15:04:05", str) 19 | if err != nil { 20 | return time.Now() 21 | } 22 | return t 23 | } 24 | 25 | // TimeNow returns current datetime. 26 | func TimeNow() *time.Time { 27 | return func(t time.Time) *time.Time { return &t }(time.Now()) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/stats/cpu.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import "github.com/shirou/gopsutil/cpu" 4 | 5 | func getCPUPercent() int32 { 6 | percent, err := cpu.Percent(0, true) 7 | if err != nil { 8 | return 0 9 | } 10 | total := func() int { 11 | var t float64 12 | for _, p := range percent { 13 | t += p 14 | } 15 | return int(int(t) / len(percent)) 16 | }() 17 | 18 | return int32(total) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/stats/mem.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import "github.com/shirou/gopsutil/mem" 4 | 5 | func getMemoryPercent() int32 { 6 | stat, err := mem.VirtualMemory() 7 | if err != nil { 8 | return 0 9 | } 10 | return int32(stat.UsedPercent) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/stats/stats.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import "github.com/shirou/gopsutil/host" 4 | 5 | // GetUsageStats returns system utilization stats. 6 | func GetUsageStats() (int32, int32) { 7 | return getCPUPercent(), getMemoryPercent() 8 | } 9 | 10 | // GetHostStats returns system information. 11 | func GetHostStats() (*host.InfoStat, error) { 12 | return host.Info() 13 | } 14 | -------------------------------------------------------------------------------- /server/api/badge/badge.go: -------------------------------------------------------------------------------- 1 | package badge 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | "github.com/go-chi/chi" 9 | "github.com/narqo/go-badge" 10 | ) 11 | 12 | // HandleBadge returns an http.HandlerFunc that writes SVG status build 13 | // icon to the http response body. 14 | func HandleBadge(builds core.BuildStore) http.HandlerFunc { 15 | return func(w http.ResponseWriter, r *http.Request) { 16 | token := chi.URLParam(r, "token") 17 | branch := r.URL.Query().Get("branch") 18 | 19 | status, err := builds.FindStatus(token, branch) 20 | if err != nil { 21 | status = core.BuildStatusUnknown 22 | } 23 | color := "#555555" 24 | if status == core.BuildStatusPassing { 25 | color = "#48bb78" 26 | } else if status == core.BuildStatusFailing { 27 | color = "#e74c3c" 28 | } else if status == core.BuildStatusRunning { 29 | color = "#ecc94b" 30 | } 31 | 32 | svg, err := badge.RenderBytes("build", status, badge.Color(color)) 33 | if err != nil { 34 | render.InternalServerError(w, err.Error()) 35 | return 36 | } 37 | 38 | w.Header().Set("Content-Type", "image/svg+xml") 39 | w.Write(svg) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/api/build/find.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleFind returns an http.HandlerFunc that writes JSON encoded 14 | // result of build to the http response. 15 | func HandleFind(builds core.BuildStore) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | claims := middlewares.ClaimsFromCtx(r.Context()) 18 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 19 | if err != nil { 20 | render.BadRequestError(w, err.Error()) 21 | return 22 | } 23 | 24 | build, err := builds.FindUser(uint(id), claims.ID) 25 | if err != nil { 26 | render.NotFoundError(w, err.Error()) 27 | return 28 | } 29 | 30 | render.JSON(w, http.StatusOK, build) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/api/build/find_job.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleFindJob returns http.handlerFunc that writes JSON encoded 14 | // job result to the http response body. 15 | func HandleFindJob(jobs core.JobStore, scheduler core.Scheduler) http.HandlerFunc { 16 | type resp struct { 17 | *core.Job 18 | Log string `json:"log"` 19 | } 20 | 21 | return func(w http.ResponseWriter, r *http.Request) { 22 | claims := middlewares.ClaimsFromCtx(r.Context()) 23 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 24 | if err != nil { 25 | render.BadRequestError(w, err.Error()) 26 | return 27 | } 28 | 29 | job, err := jobs.FindUser(uint(id), claims.ID) 30 | if err != nil { 31 | render.NotFoundError(w, err.Error()) 32 | return 33 | } 34 | 35 | if currentLog, err := scheduler.JobLog(uint(id)); err == nil { 36 | job.Log = currentLog 37 | } 38 | 39 | render.JSON(w, http.StatusOK, resp{job, job.Log}) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/api/provider/find.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleFind writes JSON encoded provider data to the http response body. 14 | func HandleFind(providers core.ProviderStore, users core.UserStore) http.HandlerFunc { 15 | return func(w http.ResponseWriter, r *http.Request) { 16 | claims := middlewares.ClaimsFromCtx(r.Context()) 17 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 18 | if err != nil { 19 | render.InternalServerError(w, err.Error()) 20 | return 21 | } 22 | 23 | user, err := users.Find(claims.ID) 24 | if err != nil { 25 | render.UnathorizedError(w, err.Error()) 26 | return 27 | } 28 | 29 | provider, err := providers.Find(uint(id)) 30 | if err != nil { 31 | render.InternalServerError(w, err.Error()) 32 | return 33 | } 34 | 35 | if provider.UserID == claims.ID || user.Role == "admin" { 36 | render.JSON(w, http.StatusOK, provider) 37 | } 38 | 39 | render.UnathorizedError(w, err.Error()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/api/provider/listuser.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/middlewares" 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | ) 10 | 11 | // HandleListUser returns http.HandlerFunc that writes JSON encoded 12 | // list of providers based on user id to http response body. 13 | func HandleListUser(providers core.ProviderStore) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | claims := middlewares.ClaimsFromCtx(r.Context()) 16 | 17 | providers, err := providers.ListUser(claims.ID) 18 | if err != nil { 19 | render.InternalServerError(w, err.Error()) 20 | return 21 | } 22 | 23 | render.JSON(w, http.StatusOK, providers) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/api/render/errors.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // InternalServerError helper. 8 | func InternalServerError(w http.ResponseWriter, msg string) { 9 | JSON(w, http.StatusInternalServerError, Error{Message: msg}) 10 | } 11 | 12 | // UnathorizedError helper. 13 | func UnathorizedError(w http.ResponseWriter, msg string) { 14 | JSON(w, http.StatusUnauthorized, Error{Message: msg}) 15 | } 16 | 17 | // NotFoundError helper. 18 | func NotFoundError(w http.ResponseWriter, msg string) { 19 | JSON(w, http.StatusNotFound, Error{Message: msg}) 20 | } 21 | 22 | // ForbiddenError helper. 23 | func ForbiddenError(w http.ResponseWriter, msg string) { 24 | JSON(w, http.StatusForbidden, Error{Message: msg}) 25 | } 26 | 27 | // BadRequestError helper. 28 | func BadRequestError(w http.ResponseWriter, msg string) { 29 | JSON(w, http.StatusBadRequest, Error{Message: msg}) 30 | } 31 | -------------------------------------------------------------------------------- /server/api/render/render.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/http" 7 | ) 8 | 9 | // JSON render encodes given data to JSON and writes it to response. 10 | func JSON(w http.ResponseWriter, status int, v interface{}) { 11 | buf := &bytes.Buffer{} 12 | enc := json.NewEncoder(buf) 13 | enc.SetEscapeHTML(true) 14 | if err := enc.Encode(v); err != nil { 15 | http.Error(w, err.Error(), http.StatusInternalServerError) 16 | return 17 | } 18 | 19 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 20 | w.WriteHeader(status) 21 | w.Write(buf.Bytes()) 22 | } 23 | -------------------------------------------------------------------------------- /server/api/render/types.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | // Empty represents an empty response. 4 | type Empty struct{} 5 | 6 | // Error represents a JSON encoded API error. 7 | type Error struct { 8 | Message string `json:"message"` 9 | } 10 | 11 | type BoolResponse struct { 12 | Status bool `json:"status"` 13 | } 14 | -------------------------------------------------------------------------------- /server/api/repo/find.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleFind returns an http.HandlerFunc that writes JSON encoded 14 | // repository result to the http response body. 15 | func HandleFind(repos core.RepositoryStore) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | claims := middlewares.ClaimsFromCtx(r.Context()) 18 | 19 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 20 | if err != nil { 21 | render.InternalServerError(w, err.Error()) 22 | return 23 | } 24 | 25 | repo, err := repos.Find(uint(id), claims.ID) 26 | if err != nil { 27 | render.NotFoundError(w, err.Error()) 28 | return 29 | } 30 | 31 | render.JSON(w, http.StatusOK, repo) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/api/repo/list_hooks.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleListHooks returns an http.HandlerFunc that writes JSON encoded 14 | // list of hooks to the http response body. 15 | func HandleListHooks(repos core.RepositoryStore) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | claims := middlewares.ClaimsFromCtx(r.Context()) 18 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 19 | if err != nil { 20 | render.InternalServerError(w, err.Error()) 21 | return 22 | } 23 | 24 | hooks, err := repos.ListHooks(uint(id), claims.ID) 25 | if err != nil { 26 | render.NotFoundError(w, err.Error()) 27 | return 28 | } 29 | 30 | render.JSON(w, http.StatusOK, hooks) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/api/repo/list_mount.go: -------------------------------------------------------------------------------- 1 | package repo 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/middlewares" 8 | "github.com/bleenco/abstruse/server/api/render" 9 | "github.com/bleenco/abstruse/server/core" 10 | "github.com/go-chi/chi" 11 | ) 12 | 13 | // HandleListMount returns http.HandlerFunc that writes JSON encoded 14 | // list of env variables for repository to the http response body. 15 | func HandleListMount(mounts core.MountsStore, repos core.RepositoryStore) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | claims := middlewares.ClaimsFromCtx(r.Context()) 18 | 19 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 20 | if err != nil { 21 | render.InternalServerError(w, err.Error()) 22 | return 23 | } 24 | 25 | if perm := repos.GetPermissions(uint(id), claims.ID); !perm.Read { 26 | render.UnathorizedError(w, "permission denied") 27 | return 28 | } 29 | 30 | mts, err := mounts.List(uint(id)) 31 | if err != nil { 32 | render.InternalServerError(w, err.Error()) 33 | return 34 | } 35 | 36 | render.JSON(w, http.StatusOK, mts) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/api/setup/ready.go: -------------------------------------------------------------------------------- 1 | package setup 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // HandleReady returns an http.HandlerFunc that writes JSON encoded 11 | // status to the http response body. 12 | func HandleReady(users core.UserStore) http.HandlerFunc { 13 | type resp struct { 14 | User bool `json:"user"` 15 | } 16 | 17 | return func(w http.ResponseWriter, r *http.Request) { 18 | res, err := users.List() 19 | if err != nil { 20 | render.InternalServerError(w, err.Error()) 21 | return 22 | } 23 | user := false 24 | if len(res) > 0 { 25 | user = true 26 | } 27 | 28 | render.JSON(w, http.StatusOK, resp{User: user}) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/api/stats/jobs.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | ) 10 | 11 | // HandleJobs returns an http.HandlerFunc that writes JSON encoded 12 | // result about jobs statistics to the http response body. 13 | func HandleJobs(jobs core.JobStore) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | layout := "2006-01-02" 16 | from := r.URL.Query().Get("from") 17 | to := r.URL.Query().Get("to") 18 | 19 | tfrom, err := time.Parse(layout, from) 20 | if err != nil { 21 | tfrom = time.Now().AddDate(0, 0, -7) 22 | } 23 | tto, err := time.Parse(layout, to) 24 | if err != nil { 25 | tto = time.Now() 26 | } 27 | 28 | tfrom = time.Date(tfrom.Year(), tfrom.Month(), tfrom.Day(), 0, 0, 0, 0, time.UTC) 29 | tto = time.Date(tto.Year(), tto.Month(), tto.Day(), 23, 59, 59, 0, time.UTC) 30 | 31 | jobs, err := jobs.List(tfrom, tto) 32 | if err != nil { 33 | render.InternalServerError(w, err.Error()) 34 | return 35 | } 36 | 37 | render.JSON(w, http.StatusOK, jobs) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/api/stats/pause.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/middlewares" 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | ) 10 | 11 | // HandlePause returns an http.HandlerFunc which writes JSON encoded 12 | // result about pausing scheduler to the http response body 13 | func HandlePause(users core.UserStore, scheduler core.Scheduler) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | claims := middlewares.ClaimsFromCtx(r.Context()) 16 | 17 | if user, err := users.Find(claims.ID); err != nil || user.Role != "admin" { 18 | render.UnathorizedError(w, err.Error()) 19 | return 20 | } 21 | 22 | if err := scheduler.Pause(); err != nil { 23 | render.InternalServerError(w, err.Error()) 24 | return 25 | } 26 | 27 | render.JSON(w, http.StatusOK, render.Empty{}) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/api/stats/resume.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/middlewares" 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | ) 10 | 11 | // HandleResume returns an http.HandlerFunc which writes JSON encoded 12 | // result about resuming scheduler to the http response body 13 | func HandleResume(users core.UserStore, scheduler core.Scheduler) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | claims := middlewares.ClaimsFromCtx(r.Context()) 16 | 17 | if user, err := users.Find(claims.ID); err != nil || user.Role != "admin" { 18 | render.UnathorizedError(w, err.Error()) 19 | return 20 | } 21 | 22 | if err := scheduler.Resume(); err != nil { 23 | render.InternalServerError(w, err.Error()) 24 | return 25 | } 26 | 27 | render.JSON(w, http.StatusOK, render.Empty{}) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/api/stats/stats.go: -------------------------------------------------------------------------------- 1 | package stats 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // HandleStats returns an http.HandlerFunc that writes JSON encoded 11 | // server stats to the http response body. 12 | func HandleStats(stats core.StatsService) http.HandlerFunc { 13 | type resp struct { 14 | Usage []core.Usage `json:"usage"` 15 | Stats []core.SchedulerStats `json:"stats"` 16 | Status bool `json:"status"` 17 | } 18 | 19 | return func(w http.ResponseWriter, r *http.Request) { 20 | usage, statistics := stats.GetHistory() 21 | status := stats.SchedulerStatus() 22 | render.JSON(w, http.StatusOK, resp{usage, statistics, status}) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/api/system/version.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/internal/version" 7 | "github.com/bleenco/abstruse/server/api/render" 8 | ) 9 | 10 | // HandleVersion returns an http.HandlerFunc that writes JSON 11 | // encoded version data to the http response body. 12 | func HandleVersion() http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | render.JSON(w, http.StatusOK, version.GetBuildInfo()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/api/team/find.go: -------------------------------------------------------------------------------- 1 | package team 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | "github.com/go-chi/chi" 10 | ) 11 | 12 | // HandleFind returns an http.HandlerFunc that writes JSON encoded 13 | // team result to the http response body. 14 | func HandleFind(teams core.TeamStore) http.HandlerFunc { 15 | return func(w http.ResponseWriter, r *http.Request) { 16 | id, err := strconv.Atoi(chi.URLParam(r, "id")) 17 | if err != nil { 18 | render.InternalServerError(w, err.Error()) 19 | return 20 | } 21 | 22 | team, err := teams.Find(uint(id)) 23 | if err != nil { 24 | render.NotFoundError(w, err.Error()) 25 | return 26 | } 27 | 28 | render.JSON(w, http.StatusOK, team) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/api/team/list.go: -------------------------------------------------------------------------------- 1 | package team 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // HandleList returns an http.HandlerFunc that writes JSON encoded 11 | // teams result to the http response body. 12 | func HandleList(teams core.TeamStore) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | teams, err := teams.List() 15 | if err != nil { 16 | render.InternalServerError(w, err.Error()) 17 | return 18 | } 19 | 20 | render.JSON(w, http.StatusOK, teams) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/api/user/list.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // HandleList returns an http.HandlerFunc that writes JSON encoded 11 | // list of users to the http response body. 12 | func HandleList(users core.UserStore) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | data, err := users.List() 15 | if err != nil { 16 | render.InternalServerError(w, err.Error()) 17 | return 18 | } 19 | render.JSON(w, http.StatusOK, data) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/api/user/profile.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/middlewares" 7 | "github.com/bleenco/abstruse/server/api/render" 8 | "github.com/bleenco/abstruse/server/core" 9 | ) 10 | 11 | // HandleProfile returns an http.HandlerFunc that writes JSON encoded 12 | // user data to the http response body. 13 | func HandleProfile(users core.UserStore) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | claims := middlewares.ClaimsFromCtx(r.Context()) 16 | 17 | user, err := users.Find(claims.ID) 18 | if err != nil { 19 | render.NotFoundError(w, err.Error()) 20 | return 21 | } 22 | 23 | render.JSON(w, http.StatusOK, user) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/api/worker/list.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bleenco/abstruse/server/api/render" 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // HandleList returns an http.HandlerFunc that writes JSON encoded 11 | // list of workers in registry to http response body. 12 | func HandleList(workers core.WorkerRegistry) http.HandlerFunc { 13 | type resp struct { 14 | ID string `json:"id"` 15 | Addr string `json:"addr"` 16 | Host core.HostInfo `json:"host"` 17 | Usage []core.WorkerUsage `json:"usage"` 18 | } 19 | 20 | return func(w http.ResponseWriter, r *http.Request) { 21 | workers, err := workers.List() 22 | if err != nil { 23 | render.InternalServerError(w, err.Error()) 24 | return 25 | } 26 | 27 | var response []resp 28 | for _, worker := range workers { 29 | response = append(response, resp{worker.ID, worker.Addr, worker.Host, worker.Usage}) 30 | } 31 | 32 | render.JSON(w, http.StatusOK, response) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/api/worker/upload_cache.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/bleenco/abstruse/server/api/render" 10 | "github.com/bleenco/abstruse/server/config" 11 | ) 12 | 13 | // HandleUploadCache returns http.handlerFunc that writes JSON encoded 14 | // result about uploading cache to the http response body. 15 | func HandleUploadCache(config *config.Config) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | r.ParseMultipartForm(1000 << 20) 18 | 19 | src, handler, err := r.FormFile("file") 20 | if err != nil { 21 | render.InternalServerError(w, err.Error()) 22 | return 23 | } 24 | defer src.Close() 25 | 26 | filePath := filepath.Join(config.DataDir, "cache", handler.Filename) 27 | dst, err := os.Create(filePath) 28 | if err != nil { 29 | render.InternalServerError(w, err.Error()) 30 | return 31 | } 32 | defer dst.Close() 33 | 34 | if _, err := io.Copy(dst, src); err != nil { 35 | render.InternalServerError(w, err.Error()) 36 | return 37 | } 38 | 39 | render.JSON(w, http.StatusOK, render.BoolResponse{Status: true}) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/core/env.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type ( 4 | // EnvVariable defines `env_variables` db table. 5 | EnvVariable struct { 6 | ID uint `gorm:"primary_key;auto_increment;not null" json:"id"` 7 | Key string `gorm:"not null" json:"key"` 8 | Value string `gorm:"not null" sql:"type:text" json:"value"` 9 | Secret bool `gorm:"not null,default:false" json:"secret"` 10 | RepositoryID uint `gorm:"not null" json:"repositoryID"` 11 | Repository Repository `json:"repository"` 12 | Timestamp 13 | } 14 | 15 | // EnvVariableStore defines operations on environment variables 16 | // in datastore. 17 | EnvVariableStore interface { 18 | // Find returns env variable from datastore. 19 | Find(uint) (*EnvVariable, error) 20 | 21 | // List returns list of environment variables from the datastore. 22 | List(uint) ([]*EnvVariable, error) 23 | 24 | // Create persists a new env variable to the datastore. 25 | Create(*EnvVariable) error 26 | 27 | // Update persists updated env variable to the datastore. 28 | Update(*EnvVariable) error 29 | 30 | // Delete deletes env variable from the datastore. 31 | Delete(*EnvVariable) error 32 | } 33 | ) 34 | -------------------------------------------------------------------------------- /server/core/mount.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type ( 4 | // Mount defines `mounts` db table. 5 | Mount struct { 6 | ID uint `gorm:"primary_key;auto_increment;not null" json:"id"` 7 | Host string `gorm:"not null" json:"host"` 8 | Container string `gorm:"not null" json:"container"` 9 | RepositoryID uint `gorm:"not null" json:"repositoryID"` 10 | Repository Repository `json:"repository"` 11 | Timestamp 12 | } 13 | 14 | // MountsStore defines operations on mounts in datastore 15 | MountsStore interface { 16 | // Find returns mounts from datastore. 17 | Find(uint) (*Mount, error) 18 | 19 | // List returns list of mounts from the datastore. 20 | List(uint) ([]*Mount, error) 21 | 22 | // Create persists a new mount to the datastore. 23 | Create(*Mount) error 24 | 25 | // Update persists updated mounts to the datastore. 26 | Update(*Mount) error 27 | 28 | // Delete deletes mounts from the datastore. 29 | Delete(*Mount) error 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /server/core/registry.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type ( 4 | // Registry represents worker nodes registry. 5 | Registry interface{} 6 | ) 7 | -------------------------------------------------------------------------------- /server/core/stats.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "time" 4 | 5 | // StatsHistoryCount history units 6 | const StatsHistoryCount = 120 7 | 8 | type ( 9 | // Usage defines server usage stats. 10 | Usage struct { 11 | CPU int32 `json:"cpu"` 12 | Mem int32 `json:"mem"` 13 | Timestamp time.Time `json:"timestamp"` 14 | } 15 | 16 | // StatsService defines operations on server statistics. 17 | StatsService interface { 18 | GetHistory() ([]Usage, []SchedulerStats) 19 | 20 | SchedulerStatus() bool 21 | } 22 | ) 23 | -------------------------------------------------------------------------------- /server/core/timestamp.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "time" 4 | 5 | // Timestamp defines timestamp fields. 6 | type Timestamp struct { 7 | CreatedAt time.Time `json:"createdAt"` 8 | UpdatedAt time.Time `json:"updatedAt"` 9 | DeletedAt *time.Time `json:"deletedAt"` 10 | } 11 | -------------------------------------------------------------------------------- /server/parser/env.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bleenco/abstruse/server/core" 8 | ) 9 | 10 | // GenerateGlobalEnv generates global env variables. 11 | func GenerateGlobalEnv(build *core.Build) []string { 12 | envs := make(map[string]string) 13 | 14 | envs["ABSTRUSE_REF"] = build.Ref 15 | envs["ABSTRUSE_BRANCH"] = build.Branch 16 | envs["ABSTRUSE_COMMIT"] = build.Commit 17 | 18 | if build.PR == 0 { 19 | envs["ABSTRUSE_PULL_REQUEST"] = "false" 20 | } else { 21 | envs["ABSTRUSE_PULL_REQUEST"] = fmt.Sprintf("%d", build.PR) 22 | } 23 | 24 | if strings.HasPrefix(build.Ref, "refs/tags/") { 25 | envs["ABSTRUSE_TAG"] = strings.TrimPrefix(build.Ref, "refs/tags/") 26 | } else { 27 | envs["ABSTRUSE_TAG"] = "false" 28 | } 29 | 30 | return toSlice(envs) 31 | } 32 | 33 | func toSlice(envs map[string]string) []string { 34 | var result []string 35 | for key, val := range envs { 36 | result = append(result, fmt.Sprintf("%s=%s", key, val)) 37 | } 38 | return result 39 | } 40 | -------------------------------------------------------------------------------- /server/store/envvariable/envvariable.go: -------------------------------------------------------------------------------- 1 | package envvariable 2 | 3 | import ( 4 | "github.com/bleenco/abstruse/server/core" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | // New returns a new EnvVariableStore. 9 | func New(db *gorm.DB) core.EnvVariableStore { 10 | return envVariableStore{db} 11 | } 12 | 13 | type envVariableStore struct { 14 | db *gorm.DB 15 | } 16 | 17 | func (s envVariableStore) Find(id uint) (*core.EnvVariable, error) { 18 | env := &core.EnvVariable{} 19 | err := s.db.Where("id = ?", id).First(&env).Error 20 | return env, err 21 | } 22 | 23 | func (s envVariableStore) List(id uint) ([]*core.EnvVariable, error) { 24 | var envs []*core.EnvVariable 25 | err := s.db.Where("repository_id = ?", id).Find(&envs).Error 26 | return envs, err 27 | } 28 | 29 | func (s envVariableStore) Create(env *core.EnvVariable) error { 30 | return s.db.Create(&env).Error 31 | } 32 | 33 | func (s envVariableStore) Update(env *core.EnvVariable) error { 34 | return s.db.Model(env).Updates(&env).Error 35 | } 36 | 37 | func (s envVariableStore) Delete(env *core.EnvVariable) error { 38 | return s.db.Delete(&env).Error 39 | } 40 | -------------------------------------------------------------------------------- /server/store/mounts/mounts.go: -------------------------------------------------------------------------------- 1 | package mount 2 | 3 | import ( 4 | "github.com/bleenco/abstruse/server/core" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | // New returns a new MountsStore. 9 | func New(db *gorm.DB) core.MountsStore { 10 | return mountsStore{db} 11 | } 12 | 13 | type mountsStore struct { 14 | db *gorm.DB 15 | } 16 | 17 | func (s mountsStore) Find(id uint) (*core.Mount, error) { 18 | mnt := &core.Mount{} 19 | err := s.db.Where("id = ?", id).First(&mnt).Error 20 | return mnt, err 21 | } 22 | 23 | func (s mountsStore) List(id uint) ([]*core.Mount, error) { 24 | var mnts []*core.Mount 25 | err := s.db.Where("repository_id = ?", id).Find(&mnts).Error 26 | return mnts, err 27 | } 28 | 29 | func (s mountsStore) Create(mnt *core.Mount) error { 30 | return s.db.Create(&mnt).Error 31 | } 32 | 33 | func (s mountsStore) Update(mnt *core.Mount) error { 34 | return s.db.Model(mnt).Updates(&mnt).Error 35 | } 36 | 37 | func (s mountsStore) Delete(mnt *core.Mount) error { 38 | return s.db.Delete(&mnt).Error 39 | } 40 | -------------------------------------------------------------------------------- /server/ws/proxy.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "net/http" 7 | ) 8 | 9 | // UpstreamHandler proxies HTTP requests to running 10 | // WebSocket server or application. 11 | func UpstreamHandler(addr string) http.HandlerFunc { 12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | peer, err := net.Dial("tcp", addr) 14 | if err != nil { 15 | w.WriteHeader(http.StatusBadGateway) 16 | return 17 | } 18 | if err := r.Write(peer); err != nil { 19 | w.WriteHeader(http.StatusBadGateway) 20 | return 21 | } 22 | hj, ok := w.(http.Hijacker) 23 | if !ok { 24 | w.WriteHeader(http.StatusInternalServerError) 25 | return 26 | } 27 | conn, _, err := hj.Hijack() 28 | if err != nil { 29 | w.WriteHeader(http.StatusInternalServerError) 30 | return 31 | } 32 | 33 | go pipe(peer, conn) 34 | go pipe(conn, peer) 35 | }) 36 | } 37 | 38 | func pipe(c1 net.Conn, c2 net.Conn) { 39 | defer c1.Close() 40 | defer c2.Close() 41 | io.Copy(c1, c2) 42 | } 43 | -------------------------------------------------------------------------------- /server/ws/types.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | // Object represents generic message parameters. 4 | type Object map[string]interface{} 5 | 6 | // Message represents websocket message. 7 | type Message struct { 8 | Type string `json:"type"` 9 | Data Object `json:"data"` 10 | } 11 | -------------------------------------------------------------------------------- /tests/e2e/tcp_wait.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func waitTCP(duration time.Duration, port int) error { 10 | timeout := time.After(duration) 11 | tick := time.Tick(1 * time.Second) 12 | host := fmt.Sprintf("%s:%d", "localhost", port) 13 | for { 14 | select { 15 | case <-timeout: 16 | return fmt.Errorf("timed out") 17 | case <-tick: 18 | conn, err := net.DialTimeout("tcp", host, 500*time.Millisecond) 19 | if err == nil { 20 | conn.Close() 21 | return nil 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/webhooks/gitea/branch/headers.txt: -------------------------------------------------------------------------------- 1 | X-Github-Delivery 730e6b14-9fe5-47b6-a354-b96dd096e835 2 | X-Gogs-Delivery 730e6b14-9fe5-47b6-a354-b96dd096e835 3 | X-Gogs-Signature 3c8086439c3ccfcba3dc0d1c685a307aaf7bc91a5c5f2a70f5c18752f2e8e6c5 4 | Content-Length 2611 5 | X-Gitea-Signature 3c8086439c3ccfcba3dc0d1c685a307aaf7bc91a5c5f2a70f5c18752f2e8e6c5 6 | Content-Type application/json 7 | X-Gitea-Event push 8 | User-Agent Go-http-client/1.1 9 | X-Github-Event push 10 | X-Gitea-Delivery 730e6b14-9fe5-47b6-a354-b96dd096e835 11 | X-Gogs-Event push 12 | Accept-Encoding gzip 13 | -------------------------------------------------------------------------------- /tests/webhooks/gitea/pull_request/headers.txt: -------------------------------------------------------------------------------- 1 | User-Agent Go-http-client/1.1 2 | X-Gogs-Signature 094c646dde5b76ec96d5a4ae633c6159d40ff1b36043cc6c484a452d11840fe1 3 | X-Github-Event pull_request 4 | X-Gogs-Delivery 11484fa2-29f7-4a54-8b9c-d66559f7f402 5 | X-Gitea-Signature 094c646dde5b76ec96d5a4ae633c6159d40ff1b36043cc6c484a452d11840fe1 6 | X-Gogs-Event pull_request 7 | Content-Length 7499 8 | Content-Type application/json 9 | X-Github-Delivery 11484fa2-29f7-4a54-8b9c-d66559f7f402 10 | X-Gitea-Delivery 11484fa2-29f7-4a54-8b9c-d66559f7f402 11 | X-Gitea-Event pull_request 12 | Accept-Encoding gzip 13 | -------------------------------------------------------------------------------- /tests/webhooks/gitea/push/headers.txt: -------------------------------------------------------------------------------- 1 | X-Gogs-Signature 7fd629115274f61e2d5ee74dd6e3e73a93a9bc39451a9566436d0a541ab3cd07 2 | Accept-Encoding gzip 3 | Content-Length 3207 4 | X-Github-Event push 5 | X-Gitea-Delivery 2dbe459a-8a38-4c95-b8a9-6830dd2d24f4 6 | X-Gitea-Event push 7 | X-Gitea-Signature 7fd629115274f61e2d5ee74dd6e3e73a93a9bc39451a9566436d0a541ab3cd07 8 | X-Gogs-Delivery 2dbe459a-8a38-4c95-b8a9-6830dd2d24f4 9 | X-Github-Delivery 2dbe459a-8a38-4c95-b8a9-6830dd2d24f4 10 | X-Gogs-Event push 11 | User-Agent Go-http-client/1.1 12 | Content-Type application/json 13 | -------------------------------------------------------------------------------- /tests/webhooks/gitea/tag/headers.txt: -------------------------------------------------------------------------------- 1 | X-Gogs-Delivery 6e691c45-ffba-4893-a79f-dbd6957d4f4d 2 | Accept-Encoding gzip 3 | X-Gitea-Event create 4 | X-Github-Delivery 6e691c45-ffba-4893-a79f-dbd6957d4f4d 5 | X-Github-Event create 6 | X-Gitea-Delivery 6e691c45-ffba-4893-a79f-dbd6957d4f4d 7 | X-Gogs-Event create 8 | X-Gogs-Signature d9eacbfb9a3d90bf91cf643dfc0075cac83f22fa73d8b240440c5657ac068994 9 | Content-Length 2142 10 | Content-Type application/json 11 | X-Gitea-Signature d9eacbfb9a3d90bf91cf643dfc0075cac83f22fa73d8b240440c5657ac068994 12 | User-Agent Go-http-client/1.1 13 | -------------------------------------------------------------------------------- /tests/webhooks/github/branch/headers.txt: -------------------------------------------------------------------------------- 1 | User-Agent GitHub-Hookshot/bf33810 2 | Accept */* 3 | X-Github-Delivery 5caf9f78-d227-11ea-809c-e9a7311b8abc 4 | X-Github-Event push 5 | X-Hub-Signature sha1=1b4679ed18978d26b2262725e7bb6a352959f547 6 | X-Forwarded-For 140.82.115.146, 10.100.10.1 7 | Content-Length 7435 8 | X-Forwarded-Proto https 9 | Accept-Encoding gzip 10 | X-Real-Ip 140.82.115.146 11 | Content-Type application/json 12 | -------------------------------------------------------------------------------- /tests/webhooks/github/pull_request/headers.txt: -------------------------------------------------------------------------------- 1 | Accept */* 2 | Content-Type application/json 3 | X-Github-Delivery 18833c00-d228-11ea-9d45-18711679016d 4 | X-Github-Event pull_request 5 | X-Forwarded-For 140.82.115.248, 10.100.10.1 6 | X-Forwarded-Proto https 7 | X-Real-Ip 140.82.115.248 8 | Accept-Encoding gzip 9 | X-Hub-Signature sha1=e325b9168763ce61576f05f8fac01b977b92358b 10 | User-Agent GitHub-Hookshot/bf33810 11 | Content-Length 21578 12 | -------------------------------------------------------------------------------- /tests/webhooks/github/push/headers.txt: -------------------------------------------------------------------------------- 1 | X-Hub-Signature sha1=2fe41c5e4351d0622da9a09a1d37f49728d48664 2 | User-Agent GitHub-Hookshot/bf33810 3 | Content-Length 7444 4 | X-Real-Ip 140.82.115.244 5 | Accept-Encoding gzip 6 | X-Forwarded-For 140.82.115.244, 10.100.10.1 7 | Content-Type application/json 8 | X-Forwarded-Proto https 9 | X-Github-Delivery 68c158c8-d228-11ea-9b9f-827201ccb51d 10 | X-Github-Event push 11 | Accept */* 12 | -------------------------------------------------------------------------------- /tests/webhooks/github/push/sha.js: -------------------------------------------------------------------------------- 1 | const crypto = require("crypto"); 2 | const payload = require("./webhook.json"); 3 | const secret = "73f5195de6"; 4 | 5 | const sig = crypto 6 | .createHmac("sha1", secret) 7 | .update(JSON.stringify(payload)) 8 | .digest("hex"); 9 | console.log(sig); 10 | -------------------------------------------------------------------------------- /tests/webhooks/github/tag/headers.txt: -------------------------------------------------------------------------------- 1 | X-Forwarded-For 140.82.115.248, 10.100.10.1 2 | X-Real-Ip 140.82.115.248 3 | User-Agent GitHub-Hookshot/bf33810 4 | Accept */* 5 | X-Forwarded-Proto https 6 | X-Github-Event push 7 | X-Hub-Signature sha1=07be3d99714674dbe09e00fe624e81a8904a90f4 8 | Content-Type application/json 9 | Content-Length 7164 10 | X-Github-Delivery 78024492-d227-11ea-8439-2a65180843e5 11 | Accept-Encoding gzip 12 | -------------------------------------------------------------------------------- /web/abstruse/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /web/abstruse/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /web/abstruse/README.md: -------------------------------------------------------------------------------- 1 | # Abstruse 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.2. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /web/abstruse/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | jasmineHtmlReporter: { 19 | suppressAll: true // removes the duplicated traces 20 | }, 21 | coverageReporter: { 22 | dir: require('path').join(__dirname, './coverage/abstruse'), 23 | subdir: '.', 24 | reporters: [{ type: 'html' }, { type: 'text-summary' }] 25 | }, 26 | reporters: ['progress', 'kjhtml'], 27 | port: 9876, 28 | colors: true, 29 | logLevel: config.LOG_INFO, 30 | autoWatch: true, 31 | browsers: ['Chrome'], 32 | singleRun: false, 33 | restartOnFileChange: true 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /web/abstruse/ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/service-worker/config/schema.json", 3 | "index": "/index.html", 4 | "dataGroups": [ 5 | { 6 | "name": "api", 7 | "urls": ["/api", "/badge", "/uploads"], 8 | "cacheConfig": { 9 | "maxSize": 0, 10 | "maxAge": "0u", 11 | "strategy": "freshness" 12 | } 13 | } 14 | ], 15 | "assetGroups": [ 16 | { 17 | "name": "app", 18 | "installMode": "prefetch", 19 | "resources": { 20 | "files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"] 21 | } 22 | }, 23 | { 24 | "name": "assets", 25 | "installMode": "lazy", 26 | "updateMode": "prefetch", 27 | "resources": { 28 | "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /web/abstruse/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost", 4 | "secure": false 5 | }, 6 | "/ws": { 7 | "target": "ws://localhost", 8 | "secure": false, 9 | "ws": true 10 | }, 11 | "/badge": { 12 | "target": "http://localhost", 13 | "secure": false 14 | }, 15 | "/uploads": { 16 | "target": "http://localhost", 17 | "secure": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web/abstruse/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/abstruse/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from '@angular/core'; 2 | import { AuthService } from './auth/shared/auth.service'; 3 | import { Observable, Subscription } from 'rxjs'; 4 | import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; 5 | import { DataService } from './shared/providers/data.service'; 6 | 7 | @UntilDestroy() 8 | @Component({ 9 | selector: 'app-root', 10 | templateUrl: './app.component.html' 11 | }) 12 | export class AppComponent implements OnInit, OnDestroy { 13 | loggedIn: Observable; 14 | sub = new Subscription(); 15 | 16 | constructor(private auth: AuthService, private dataService: DataService) { 17 | this.loggedIn = this.auth.authenticated.asObservable().pipe(untilDestroyed(this)); 18 | } 19 | 20 | ngOnInit(): void { 21 | this.loggedIn.pipe(untilDestroyed(this)).subscribe(loggedIn => { 22 | if (loggedIn) { 23 | this.sub = new Subscription(); 24 | this.sub.add(this.dataService.socketOutput.subscribe()); 25 | } else { 26 | this.sub.unsubscribe(); 27 | } 28 | }); 29 | } 30 | 31 | ngOnDestroy(): void { 32 | this.sub.unsubscribe(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web/abstruse/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { CoreModule } from './core'; 4 | import { SharedModule } from './shared'; 5 | import { AuthModule } from './auth/auth.module'; 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { AppComponent } from './app.component'; 8 | import { BuildsModule } from './builds/builds.module'; 9 | import { ServiceWorkerModule } from '@angular/service-worker'; 10 | import { environment } from '../environments/environment'; 11 | 12 | @NgModule({ 13 | imports: [ 14 | BrowserModule, 15 | AppRoutingModule, 16 | CoreModule, 17 | SharedModule.forRoot(), 18 | AuthModule, 19 | BuildsModule, 20 | ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) 21 | ], 22 | declarations: [AppComponent], 23 | bootstrap: [AppComponent] 24 | }) 25 | export class AppModule {} 26 | -------------------------------------------------------------------------------- /web/abstruse/src/app/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ReactiveFormsModule } from '@angular/forms'; 4 | import { SharedModule } from '../shared'; 5 | import { AuthGuardService } from './shared/auth-guard.service'; 6 | import { AlreadyAuthGuardService } from './shared/already-auth-guard.service'; 7 | import { LoginComponent } from './login/login.component'; 8 | 9 | @NgModule({ 10 | imports: [CommonModule, ReactiveFormsModule, SharedModule], 11 | declarations: [LoginComponent], 12 | providers: [AuthGuardService, AlreadyAuthGuardService] 13 | }) 14 | export class AuthModule {} 15 | -------------------------------------------------------------------------------- /web/abstruse/src/app/auth/login/login.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | 4 | .section 5 | border: none 6 | 7 | .logo-section 8 | padding: 20px 0 9 | border-bottom: 1px solid $border-default 10 | margin: 0 20px 20px 20px 11 | 12 | img 13 | display: block 14 | height: 40px 15 | margin: 0 auto 16 | 17 | .form 18 | padding: 0 20px 19 | 20 | .form-buttons 21 | margin-top: 20px 22 | -------------------------------------------------------------------------------- /web/abstruse/src/app/auth/shared/already-auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; 3 | import { AuthService } from './auth.service'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class AlreadyAuthGuardService { 7 | constructor(private auth: AuthService, private router: Router) {} 8 | 9 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { 10 | return this.authGuard(); 11 | } 12 | 13 | private authGuard(): boolean { 14 | if (!this.auth.data) { 15 | return true; 16 | } 17 | 18 | this.router.navigate(['/']); 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /web/abstruse/src/app/auth/shared/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, Route, Router, RouterStateSnapshot, UrlSegment } from '@angular/router'; 3 | import { AuthService } from './auth.service'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class AuthGuardService { 7 | constructor(private auth: AuthService, private router: Router) {} 8 | 9 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { 10 | return this.authGuard(); 11 | } 12 | 13 | canLoad(route: Route, segments: UrlSegment[]): Promise { 14 | return this.authGuard(); 15 | } 16 | 17 | private async authGuard(): Promise { 18 | const auth = this.auth.isAuthenticated; 19 | if (!auth) { 20 | this.router.navigate(['/login']); 21 | return Promise.resolve(false); 22 | } 23 | 24 | return Promise.resolve(true); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/auth/shared/auth.model.ts: -------------------------------------------------------------------------------- 1 | export const AUTH_TOKEN_KEY = 'abstruse-auth-data'; 2 | export const TIMEOUT_FACTOR = 0.75; 3 | 4 | export interface Login { 5 | email: string; 6 | password: string; 7 | } 8 | 9 | export interface UserData { 10 | id: number; 11 | email: string; 12 | name: string; 13 | lastname: string; 14 | role: string; 15 | avatar: string; 16 | } 17 | 18 | export interface TokenResponse { 19 | token: string; 20 | } 21 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/builds-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { AuthGuardService } from '../auth/shared/auth-guard.service'; 4 | import { BuildComponent } from './build/build.component'; 5 | import { JobComponent } from './job/job.component'; 6 | import { IndexComponent } from './index/index.component'; 7 | 8 | const routes: Routes = [ 9 | { path: 'builds', component: IndexComponent, canActivate: [AuthGuardService] }, 10 | { path: 'builds/:id', component: BuildComponent, canActivate: [AuthGuardService] }, 11 | { path: 'builds/:buildid/:jobid', component: JobComponent, canActivate: [AuthGuardService] } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [RouterModule.forChild(routes)], 16 | exports: [RouterModule] 17 | }) 18 | export class BuildsRoutingModule {} 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/builds.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { BuildsRoutingModule } from './builds-routing.module'; 4 | import { SharedModule } from '../shared'; 5 | import { BuildsCommonModule } from './common/builds-common.module'; 6 | import { BuildComponent } from './build/build.component'; 7 | import { JobListItemComponent } from './job-list-item/job-list-item.component'; 8 | import { JobComponent } from './job/job.component'; 9 | import { IndexComponent } from './index/index.component'; 10 | 11 | @NgModule({ 12 | declarations: [BuildComponent, JobListItemComponent, JobComponent, IndexComponent], 13 | imports: [CommonModule, BuildsRoutingModule, SharedModule, BuildsCommonModule] 14 | }) 15 | export class BuildsModule {} 16 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/common/build-list-item/build-list-item.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | 3 | .avatars-container 4 | width: 30px 5 | height: 30px 6 | display: block 7 | margin-right: 20px 8 | position: relative 9 | 10 | .committer-avatar 11 | margin-right: 0 12 | 13 | .author-avatar 14 | width: 20px 15 | height: 20px 16 | display: block 17 | border-radius: 50% 18 | position: absolute 19 | top: -5px 20 | left: -5px 21 | z-index: 9 22 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/common/builds-common.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { SharedModule } from '../../shared'; 3 | import { BuildListItemComponent } from './build-list-item/build-list-item.component'; 4 | import { BuildsItemsComponent } from './builds-items/builds-items.component'; 5 | import { RouterModule } from '@angular/router'; 6 | 7 | @NgModule({ 8 | imports: [SharedModule, RouterModule], 9 | declarations: [BuildListItemComponent, BuildsItemsComponent], 10 | exports: [BuildListItemComponent, BuildsItemsComponent] 11 | }) 12 | export class BuildsCommonModule {} 13 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/common/builds-items/builds-items-options.model.ts: -------------------------------------------------------------------------------- 1 | export interface BuildsItemsOptions { 2 | type: 'latest' | 'commits' | 'branches' | 'pull-requests'; 3 | repoID?: number; 4 | } 5 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/common/builds-items/builds-items.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/builds/common/builds-items/builds-items.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/index/index.component.html: -------------------------------------------------------------------------------- 1 |
2 | 17 |
18 |
19 |
20 |

Builds

21 |

{{ title }}

22 |
23 |
24 |
25 |
26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/index/index.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/builds/index/index.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/builds/job-list-item/job-list-item.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/builds/job-list-item/job-list-item.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/core/gateway-timeout/gateway-timeout.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/core/gateway-timeout/gateway-timeout.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/core/gateway-timeout/gateway-timeout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { AuthService } from 'src/app/auth/shared/auth.service'; 4 | 5 | @Component({ 6 | selector: 'app-gateway-timeout', 7 | templateUrl: './gateway-timeout.component.html', 8 | styleUrls: ['./gateway-timeout.component.sass'] 9 | }) 10 | export class GatewayTimeoutComponent implements OnInit { 11 | loggedIn: Observable; 12 | 13 | constructor(private auth: AuthService) { 14 | this.loggedIn = this.auth.authenticated.asObservable(); 15 | } 16 | 17 | ngOnInit(): void {} 18 | } 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/core/header/header.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/core/header/header.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core.module'; 2 | export * from './interceptors/api.interceptor'; 3 | export * from './interceptors/error.interceptor'; 4 | export * from './not-found/not-found.component'; 5 | export * from './gateway-timeout/gateway-timeout.component'; 6 | -------------------------------------------------------------------------------- /web/abstruse/src/app/core/interceptors/api.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Provider } from '@angular/core'; 2 | import { 3 | HttpEvent, 4 | HttpInterceptor, 5 | HttpHandler, 6 | HttpRequest, 7 | HTTP_INTERCEPTORS 8 | } from '@angular/common/http'; 9 | import { Observable } from 'rxjs'; 10 | import { environment } from 'src/environments/environment'; 11 | import { AuthService } from 'src/app/auth/shared/auth.service'; 12 | 13 | @Injectable({ providedIn: 'root' }) 14 | export class ApiInterceptor implements HttpInterceptor { 15 | constructor(public auth: AuthService) {} 16 | 17 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 18 | const url = `${environment.apiURL}/${request.url}`.replace(/\/+/g, '/').replace(/\/+$/, ''); 19 | request = request.clone({ url, setHeaders: { Authorization: `Bearer ${this.auth.token}` } }); 20 | return next.handle(request); 21 | } 22 | } 23 | 24 | export const ApiInterceptorProvider: Provider = { 25 | provide: HTTP_INTERCEPTORS, 26 | useClass: ApiInterceptor, 27 | multi: true 28 | }; 29 | -------------------------------------------------------------------------------- /web/abstruse/src/app/core/interceptors/error.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Provider } from '@angular/core'; 2 | import { 3 | HttpEvent, 4 | HttpInterceptor, 5 | HttpHandler, 6 | HttpRequest, 7 | HTTP_INTERCEPTORS, 8 | HttpErrorResponse 9 | } from '@angular/common/http'; 10 | import { Observable } from 'rxjs'; 11 | import { catchError } from 'rxjs/operators'; 12 | import { Router } from '@angular/router'; 13 | 14 | @Injectable({ providedIn: 'root' }) 15 | export class ErrorInterceptor implements HttpInterceptor { 16 | constructor(private router: Router) {} 17 | 18 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 19 | return next.handle(request).pipe(catchError(error => this.handleError(error))); 20 | } 21 | 22 | private handleError(response: HttpErrorResponse): Observable> { 23 | if (response.status === 504) { 24 | this.router.navigate(['/gateway-timeout']); 25 | } 26 | throw response.error; 27 | } 28 | } 29 | 30 | export const ErrorInterceptorProvider: Provider = { 31 | provide: HTTP_INTERCEPTORS, 32 | useClass: ErrorInterceptor, 33 | multi: true 34 | }; 35 | -------------------------------------------------------------------------------- /web/abstruse/src/app/core/not-found/not-found.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/core/not-found/not-found.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/core/not-found/not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from 'src/app/auth/shared/auth.service'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Component({ 6 | selector: 'app-not-found', 7 | templateUrl: './not-found.component.html', 8 | styleUrls: ['./not-found.component.sass'] 9 | }) 10 | export class NotFoundComponent implements OnInit { 11 | loggedIn: Observable; 12 | 13 | constructor(private auth: AuthService) { 14 | this.loggedIn = this.auth.authenticated.asObservable(); 15 | } 16 | 17 | ngOnInit(): void {} 18 | } 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/dashboard/dashboard-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { IndexComponent } from './index/index.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | pathMatch: 'full', 9 | component: IndexComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forChild(routes)], 15 | exports: [RouterModule] 16 | }) 17 | export class DashboardRoutingModule {} 18 | -------------------------------------------------------------------------------- /web/abstruse/src/app/dashboard/dashboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { BarChartModule, RealtimeCanvasChartModule } from 'ngx-graph'; 4 | import { SharedModule } from '../shared'; 5 | import { DashboardRoutingModule } from './dashboard-routing.module'; 6 | import { IndexComponent } from './index/index.component'; 7 | 8 | @NgModule({ 9 | declarations: [IndexComponent], 10 | imports: [ 11 | CommonModule, 12 | DashboardRoutingModule, 13 | SharedModule, 14 | BarChartModule, 15 | RealtimeCanvasChartModule 16 | ] 17 | }) 18 | export class DashboardModule {} 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/dashboard/shared/dashboard.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpParams } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { format } from 'date-fns'; 4 | import { Observable } from 'rxjs'; 5 | import { map } from 'rxjs/operators'; 6 | import { generateJobModel, Job } from 'src/app/builds/shared/build.model'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class DashboardService { 12 | constructor(private http: HttpClient) {} 13 | 14 | stats(): Observable { 15 | return this.http.get('/stats'); 16 | } 17 | 18 | jobs(from: Date, to: Date): Observable { 19 | let params = new HttpParams(); 20 | params = params.append('from', format(from, `yyyy-MM-dd`)); 21 | params = params.append('to', format(to, `yyyy-MM-dd`)); 22 | return this.http 23 | .get('/stats/jobs', { params }) 24 | .pipe(map(data => (data && data.length ? data.map(generateJobModel) : []))); 25 | } 26 | 27 | resumeScheduler(): Observable { 28 | return this.http.put('/stats/scheduler/resume', {}); 29 | } 30 | 31 | pauseScheduler(): Observable { 32 | return this.http.put('/stats/scheduler/pause', {}); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/profile-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ProfileComponent } from './profile/profile.component'; 4 | import { SecurityComponent } from './security/security.component'; 5 | import { SettingsComponent } from './settings/settings.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: ProfileComponent, 11 | children: [ 12 | { path: '', pathMatch: 'full', redirectTo: 'settings' }, 13 | { path: 'settings', component: SettingsComponent }, 14 | { path: 'security', component: SecurityComponent } 15 | ] 16 | } 17 | ]; 18 | 19 | @NgModule({ 20 | imports: [RouterModule.forChild(routes)], 21 | exports: [RouterModule] 22 | }) 23 | export class ProfileRoutingModule {} 24 | -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/profile.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SharedModule } from '../shared'; 4 | import { ProfileRoutingModule } from './profile-routing.module'; 5 | import { ProfileComponent } from './profile/profile.component'; 6 | import { SecurityComponent } from './security/security.component'; 7 | import { SettingsComponent } from './settings/settings.component'; 8 | 9 | @NgModule({ 10 | declarations: [ProfileComponent, SecurityComponent, SettingsComponent], 11 | imports: [CommonModule, ProfileRoutingModule, SharedModule] 12 | }) 13 | export class ProfileModule {} 14 | -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/profile/profile.component.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 |
8 |
9 |

Profile

10 |

{{ title }}

11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/profile/profile.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/profile/profile/profile.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/security/security.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/profile/security/security.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/settings/settings.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/profile/settings/settings.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/shared/password.model.ts: -------------------------------------------------------------------------------- 1 | export interface Password { 2 | currentPassword: string; 3 | newPassword: string; 4 | } 5 | -------------------------------------------------------------------------------- /web/abstruse/src/app/profile/shared/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Password } from './password.model'; 4 | import { map } from 'rxjs/operators'; 5 | import { Observable } from 'rxjs'; 6 | import { Profile, User, generateUser } from '../../teams/shared/user.model'; 7 | import { TokenResponse } from 'src/app/auth/shared/auth.model'; 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class ProfileService { 13 | constructor(private http: HttpClient) {} 14 | 15 | findProfile(): Observable { 16 | return this.http.get('/users/profile').pipe(map(generateUser)); 17 | } 18 | 19 | updateProfile(data: Profile): Observable { 20 | return this.http.put('/users/profile', data); 21 | } 22 | 23 | updatePassword(data: Password): Observable { 24 | return this.http.put('/users/password', data); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/providers/providers-modal/providers-modal.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/providers/providers-modal/providers-modal.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/providers/providers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ProvidersComponent } from './providers/providers.component'; 4 | 5 | const routes: Routes = [{ path: '', pathMatch: 'full', component: ProvidersComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class ProvidersRoutingModule {} 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/providers/providers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ProvidersRoutingModule } from './providers-routing.module'; 4 | import { SharedModule } from '../shared'; 5 | import { ProvidersComponent } from './providers/providers.component'; 6 | import { ProviderItemComponent } from './provider-item/provider-item.component'; 7 | import { ProvidersModalComponent } from './providers-modal/providers-modal.component'; 8 | 9 | @NgModule({ 10 | declarations: [ProvidersModalComponent, ProvidersComponent, ProviderItemComponent], 11 | imports: [CommonModule, ProvidersRoutingModule, SharedModule] 12 | }) 13 | export class ProvidersModule {} 14 | -------------------------------------------------------------------------------- /web/abstruse/src/app/providers/providers/providers.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/providers/providers/providers.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/providers/shared/providers.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Provider, generateProvider } from './provider.class'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { map } from 'rxjs/operators'; 5 | import { Observable } from 'rxjs'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class ProvidersService { 9 | constructor(private http: HttpClient) {} 10 | 11 | find(): Observable { 12 | return this.http.get('/providers').pipe(map(data => data.map(generateProvider))); 13 | } 14 | 15 | sync(id: number): Observable { 16 | return this.http.put('/providers/sync', { id }); 17 | } 18 | 19 | create(data: any): Observable { 20 | return this.http.post('/providers', data).pipe(map(d => new Provider(d))); 21 | } 22 | 23 | update(data: any): Observable { 24 | return this.http.put('/providers', data).pipe(map(d => new Provider(d))); 25 | } 26 | 27 | delete(id: number): Observable { 28 | return this.http.delete(`/providers/${id}`); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/branches/branches.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/branches/branches.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/branches/branches.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/branches/branches.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { BuildsItemsOptions } from '../../builds/common/builds-items/builds-items-options.model'; 3 | import { ActivatedRoute } from '@angular/router'; 4 | 5 | @Component({ 6 | selector: 'app-branches', 7 | templateUrl: './branches.component.html', 8 | styleUrls: ['./branches.component.sass'] 9 | }) 10 | export class BranchesComponent implements OnInit { 11 | options: BuildsItemsOptions = { type: 'branches' }; 12 | 13 | constructor(private route: ActivatedRoute) { 14 | this.options = { ...this.options, ...{ repoID: route.snapshot.parent?.params.id } }; 15 | } 16 | 17 | ngOnInit(): void {} 18 | } 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/builds/builds.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/builds/builds.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/builds/builds.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/builds/builds.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { BuildsItemsOptions } from '../../builds/common/builds-items/builds-items-options.model'; 4 | 5 | @Component({ 6 | selector: 'app-builds', 7 | templateUrl: './builds.component.html', 8 | styleUrls: ['./builds.component.sass'] 9 | }) 10 | export class BuildsComponent implements OnInit { 11 | options: BuildsItemsOptions = { type: 'latest' }; 12 | 13 | constructor(private route: ActivatedRoute) { 14 | this.options = { ...this.options, ...{ repoID: route.snapshot.parent?.params.id } }; 15 | } 16 | 17 | ngOnInit(): void {} 18 | } 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/pull-requests/pull-requests.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/pull-requests/pull-requests.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/pull-requests/pull-requests.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/pull-requests/pull-requests.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { BuildsItemsOptions } from '../../builds/common/builds-items/builds-items-options.model'; 4 | 5 | @Component({ 6 | selector: 'app-pull-requests', 7 | templateUrl: './pull-requests.component.html', 8 | styleUrls: ['./pull-requests.component.sass'] 9 | }) 10 | export class PullRequestsComponent implements OnInit { 11 | options: BuildsItemsOptions = { type: 'pull-requests' }; 12 | 13 | constructor(private route: ActivatedRoute) { 14 | this.options = { ...this.options, ...{ repoID: route.snapshot.parent?.params.id } }; 15 | } 16 | 17 | ngOnInit(): void {} 18 | } 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repo-item/repo-item.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/repo-item/repo-item.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repo-item/repo-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { Repo } from '../shared/repo.model'; 3 | import { ReposService } from '../shared/repos.service'; 4 | import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; 5 | 6 | @UntilDestroy() 7 | @Component({ 8 | selector: 'app-repo-item', 9 | templateUrl: './repo-item.component.html', 10 | styleUrls: ['./repo-item.component.sass'] 11 | }) 12 | export class RepoItemComponent implements OnInit { 13 | @Input() repo!: Repo; 14 | 15 | constructor(private reposService: ReposService) {} 16 | 17 | ngOnInit(): void {} 18 | 19 | onActiveChange(): void { 20 | this.reposService 21 | .setActive(this.repo.id as number, this.repo.active as boolean) 22 | .pipe(untilDestroyed(this)) 23 | .subscribe( 24 | () => {}, 25 | () => { 26 | this.repo.active = !this.repo.active; 27 | } 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repo/repo.component.html: -------------------------------------------------------------------------------- 1 |
2 | 13 |
14 |
15 |
16 |

{{ (reposService.repo | async)?.fullName }}

17 |

{{ title }}

18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repo/repo.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/repo/repo.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repos-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ReposComponent } from './repos/repos.component'; 4 | import { RepoComponent } from './repo/repo.component'; 5 | import { SettingsComponent } from './settings/settings.component'; 6 | import { BuildsComponent } from './builds/builds.component'; 7 | import { BranchesComponent } from './branches/branches.component'; 8 | import { PullRequestsComponent } from './pull-requests/pull-requests.component'; 9 | 10 | const routes: Routes = [ 11 | { path: '', pathMatch: 'full', component: ReposComponent }, 12 | { 13 | path: ':id', 14 | component: RepoComponent, 15 | children: [ 16 | { path: '', pathMatch: 'full', redirectTo: 'builds' }, 17 | { path: 'builds', component: BuildsComponent }, 18 | { path: 'branches', component: BranchesComponent }, 19 | { path: 'pull-requests', component: PullRequestsComponent }, 20 | { path: 'settings', component: SettingsComponent } 21 | ] 22 | } 23 | ]; 24 | 25 | @NgModule({ 26 | imports: [RouterModule.forChild(routes)], 27 | exports: [RouterModule] 28 | }) 29 | export class ReposRoutingModule {} 30 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/repos/repos.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/repos/repos.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-env-modal/settings-env-modal.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/settings-env-modal/settings-env-modal.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-envs/env-variable.model.ts: -------------------------------------------------------------------------------- 1 | export class EnvVariable { 2 | constructor( 3 | public id?: number, 4 | public key?: string, 5 | public value?: string, 6 | public secret?: boolean 7 | ) {} 8 | 9 | get val(): string { 10 | return !!this.secret ? '**********' : String(this.value); 11 | } 12 | } 13 | 14 | export const generateEnvVariable = (data: any): EnvVariable => { 15 | return new EnvVariable(data.id, data.key, data.value, Boolean(data.secret)); 16 | }; 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-envs/settings-envs.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | @import 'mixins' 4 | 5 | h4 6 | color: $green 7 | 8 | .env-variable-list 9 | display: block 10 | border: 1px solid $border-default 11 | border-radius: $border-radius 12 | 13 | .env-variable-list-item 14 | display: block 15 | border-bottom: 1px solid $border-default 16 | padding: 10px 17 | border-radius: $border-radius 18 | 19 | &.is-heading 20 | color: $text-secondary 21 | font-weight: $weight-medium 22 | 23 | &:last-child 24 | border-bottom: none 25 | 26 | .columns 27 | 28 | .column 29 | display: inline-flex 30 | align-items: center 31 | 32 | span 33 | +ellipsis 34 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-mount-modal/settings-mount-modal.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/repos/settings-mount-modal/settings-mount-modal.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-mount/mount-variable.model.ts: -------------------------------------------------------------------------------- 1 | export class MountVariable { 2 | constructor(public id?: number, public host?: string, public container?: string) {} 3 | 4 | get val(): string { 5 | return `${this.host}:${this.container}`; 6 | } 7 | } 8 | 9 | export const generateMountVariable = (data: any): MountVariable => { 10 | return new MountVariable(data.id, data.host, data.container); 11 | }; 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-mount/settings-mount.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | @import 'mixins' 4 | 5 | h4 6 | color: $green 7 | 8 | .mount-variable-list 9 | display: block 10 | border: 1px solid $border-default 11 | border-radius: $border-radius 12 | 13 | .mount-variable-list-item 14 | display: block 15 | border-bottom: 1px solid $border-default 16 | padding: 10px 17 | border-radius: $border-radius 18 | 19 | &.is-heading 20 | color: $text-secondary 21 | font-weight: $weight-medium 22 | 23 | &:last-child 24 | border-bottom: none 25 | 26 | .columns 27 | 28 | .column 29 | display: inline-flex 30 | align-items: center 31 | 32 | span 33 | +ellipsis 34 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-ssh-modal/settings-ssh-modal.component.sass: -------------------------------------------------------------------------------- 1 | .input 2 | height: 400px 3 | resize: none 4 | line-height: 22px 5 | font-size: 12px 6 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-ssh-modal/settings-ssh-modal.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SettingsSshModalComponent } from './settings-ssh-modal.component'; 4 | 5 | describe('SettingsSshModalComponent', () => { 6 | let component: SettingsSshModalComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ SettingsSshModalComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SettingsSshModalComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-ssh/settings-ssh.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

SSH Settings

4 |
5 |
6 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |

{{ error }}.

16 |
17 |
18 | 19 |
20 | 26 |
27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-ssh/settings-ssh.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | @import 'mixins' 4 | 5 | h4 6 | color: $green 7 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings-ssh/settings-ssh.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SettingsSshComponent } from './settings-ssh.component'; 4 | 5 | describe('SettingsSshComponent', () => { 6 | let component: SettingsSshComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ SettingsSshComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SettingsSshComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/settings/settings.component.sass: -------------------------------------------------------------------------------- 1 | @import 'colours' 2 | @import 'variables' 3 | @import 'mixins' 4 | 5 | .section 6 | padding: 10px 0 20px 7 | border-bottom: 1px solid $border-default 8 | 9 | h4 10 | margin-bottom: 20px 11 | color: $green 12 | 13 | .inner-section 14 | padding: 20px 20px 10px 15 | height: 100% 16 | border: 1px solid $border-default 17 | border-radius: 3px 18 | 19 | .config-container 20 | border: 1px solid $border-default 21 | margin: 10px 0 22 | padding-right: 1px 23 | 24 | .sub-title 25 | font-size: 14px 26 | display: block 27 | margin: 15px 0 5px 28 | color: $text-primary 29 | font-weight: $weight-medium 30 | 31 | .badge-result 32 | position: relative 33 | width: 100% 34 | display: block 35 | margin: 10px 0 36 | font-size: 14px 37 | border: 1px solid $border-default 38 | padding: 10px 39 | border-radius: $border-radius 40 | 41 | i 42 | position: absolute 43 | font-size: 20px 44 | top: 9px 45 | right: 10px 46 | cursor: pointer 47 | -------------------------------------------------------------------------------- /web/abstruse/src/app/repos/shared/hook.model.ts: -------------------------------------------------------------------------------- 1 | export class Hook { 2 | constructor( 3 | public id: number, 4 | public active: boolean, 5 | public name: string, 6 | public target: string, 7 | public skipVerify: boolean, 8 | public events: string[] 9 | ) {} 10 | } 11 | 12 | export interface HookData { 13 | branch: boolean; 14 | pullRequest: boolean; 15 | push: boolean; 16 | tag: boolean; 17 | } 18 | 19 | export const generateHook = (data: any): Hook => { 20 | return new Hook( 21 | Number(data.ID), 22 | Boolean(data.Active), 23 | data.Name, 24 | data.Target, 25 | Boolean(data.SkipVerify), 26 | data.Events 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/header/header.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/setup/header/header.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-header', 5 | templateUrl: './header.component.html', 6 | styleUrls: ['./header.component.sass'] 7 | }) 8 | export class HeaderComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/setup-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { UserComponent } from './user/user.component'; 4 | import { SetupComponent } from './setup.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: SetupComponent, 10 | children: [ 11 | { path: '', pathMatch: 'full', redirectTo: 'user' }, 12 | { path: 'user', component: UserComponent } 13 | ] 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [RouterModule.forChild(routes)], 19 | exports: [RouterModule] 20 | }) 21 | export class SetupRoutingModule {} 22 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/setup.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/setup.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-setup', 5 | templateUrl: './setup.component.html' 6 | }) 7 | export class SetupComponent implements OnInit { 8 | constructor() {} 9 | 10 | ngOnInit(): void {} 11 | } 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/setup.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SetupDoneGuardService } from './shared/setup-done-guard.service'; 4 | import { SetupRoutingModule } from './setup-routing.module'; 5 | import { UserComponent } from './user/user.component'; 6 | import { SetupComponent } from './setup.component'; 7 | import { SharedModule } from '../shared'; 8 | import { HeaderComponent } from './header/header.component'; 9 | import { ReactiveFormsModule } from '@angular/forms'; 10 | 11 | @NgModule({ 12 | imports: [CommonModule, SetupRoutingModule, SharedModule, ReactiveFormsModule], 13 | declarations: [UserComponent, SetupComponent, HeaderComponent], 14 | providers: [SetupDoneGuardService] 15 | }) 16 | export class SetupModule {} 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/shared/admin.model.ts: -------------------------------------------------------------------------------- 1 | export class Admin { 2 | readonly role: string = 'admin'; 3 | readonly active: boolean = true; 4 | 5 | constructor( 6 | public email: string, 7 | public login: string, 8 | public name: string, 9 | public avatar: string, 10 | public password: string 11 | ) {} 12 | } 13 | 14 | export const generateAdminModel = (data: any): Admin => { 15 | return new Admin(data.email, data.login, data.name, data.avatar, data.password); 16 | }; 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/shared/setup-done-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; 3 | import { SetupService } from './setup.service'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class SetupDoneGuardService { 7 | constructor(private setup: SetupService, private router: Router) {} 8 | 9 | async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { 10 | try { 11 | const ready = await this.setup.ready(); 12 | if (ready) { 13 | this.router.navigate(['/login']); 14 | return false; 15 | } 16 | return true; 17 | } catch (e) { 18 | console.error(e); 19 | this.router.navigate(['/gateway-timeout']); 20 | return false; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/shared/setup.model.ts: -------------------------------------------------------------------------------- 1 | export interface Setup { 2 | user: boolean; 3 | } 4 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/shared/setup.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Setup } from './setup.model'; 4 | import { Observable, lastValueFrom } from 'rxjs'; 5 | import { Admin } from './admin.model'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class SetupService { 9 | constructor(private http: HttpClient) { } 10 | 11 | saveUser(form: Admin): Observable { 12 | return this.http.post('/setup/user', form); 13 | } 14 | 15 | async ready(): Promise { 16 | return lastValueFrom(this.http.get('/setup/ready')) 17 | .then(resp => resp.user) 18 | .catch(() => false); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web/abstruse/src/app/setup/user/user.component.sass: -------------------------------------------------------------------------------- 1 | @import 'colours' 2 | @import 'variables' 3 | 4 | .setup-heading 5 | display: block 6 | margin: 20px 0 7 | border-bottom: 1px solid $border-default 8 | 9 | h1 10 | display: block 11 | font-size: 18px 12 | color: $text-primary 13 | padding: 10px 0 14 | font-weight: $weight-medium 15 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/common/bytes.ts: -------------------------------------------------------------------------------- 1 | export function humanizeBytes(bytes: number): string { 2 | if (bytes === 0) { 3 | return '0 Byte'; 4 | } 5 | 6 | const k = 1024; 7 | const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; 8 | const i: number = Math.floor(Math.log(bytes) / Math.log(k)); 9 | 10 | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; 11 | } 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/common/colors.ts: -------------------------------------------------------------------------------- 1 | export function hexToRgba(hex: string, alpha: number = 1): string { 2 | const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 3 | hex = hex.replace(shorthandRegex, (_, r, g, b) => { 4 | return r + r + g + g + b + b; 5 | }); 6 | 7 | const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 8 | if (!r) { 9 | return hex; 10 | } 11 | 12 | return `rgba(${parseInt(r[1], 16)}, ${parseInt(r[2], 16)}, ${parseInt(r[3], 16)}, ${alpha})`; 13 | } 14 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/common/random-hash.ts: -------------------------------------------------------------------------------- 1 | export const randomHash = (len?: number): string => { 2 | const arr = new Uint8Array((len || 40) / 2); 3 | window.crypto.getRandomValues(arr); 4 | return Array.from(arr, dec2hex).join(''); 5 | }; 6 | 7 | const dec2hex = (dec: number): string => { 8 | return dec < 10 ? '0' + String(dec) : dec.toString(16); 9 | }; 10 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/common/random-int.ts: -------------------------------------------------------------------------------- 1 | export const randomInt = (min: number, max: number): number => { 2 | return Math.floor(Math.random() * (max - min + 1)) + min; 3 | }; 4 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/loader/loader.component.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/loader/loader.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-loader', 5 | templateUrl: './loader.component.html', 6 | styleUrls: ['./loader.component.sass'] 7 | }) 8 | export class LoaderComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/modal/modal-config.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector } from '@angular/core'; 2 | 3 | export type ModalOption = 4 | | 'backdrop' 5 | | 'backdropOpacity' 6 | | 'beforeDismiss' 7 | | 'container' 8 | | 'injector' 9 | | 'keyboard' 10 | | 'scrollable' 11 | | 'size'; 12 | 13 | export interface ModalOptions { 14 | backdrop?: boolean | 'white'; 15 | backdropOpacity?: number; 16 | beforeDismiss?: () => boolean | Promise; 17 | container?: string; 18 | injector?: Injector; 19 | keyboard?: boolean; 20 | scrollable?: boolean; 21 | size?: 'small' | 'large' | 'medium'; 22 | } 23 | 24 | @Injectable({ providedIn: 'root' }) 25 | export class ModalConfig implements Required { 26 | backdrop: boolean | 'white' = true; 27 | backdropOpacity = 0.8; 28 | beforeDismiss!: () => boolean | Promise; 29 | container!: string; 30 | injector!: Injector; 31 | keyboard = true; 32 | scrollable!: boolean; 33 | size!: 'small' | 'large' | 'medium'; 34 | } 35 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/modal/modal.component.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/modal/modal.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ModalComponent } from './modal.component'; 4 | import { ModalService } from './modal.service'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule], 8 | declarations: [ModalComponent], 9 | providers: [ModalService], 10 | exports: [ModalComponent] 11 | }) 12 | export class ModalModule {} 13 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/modal/modal.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector, ComponentFactoryResolver } from '@angular/core'; 2 | import { ModalOptions, ModalConfig } from './modal-config.service'; 3 | import { ModalRef } from './modal-ref.class'; 4 | import { ModalStack } from './modal-stack.class'; 5 | 6 | @Injectable({ providedIn: 'root' }) 7 | export class ModalService { 8 | constructor( 9 | private moduleComponentFactoryResolver: ComponentFactoryResolver, 10 | private injector: Injector, 11 | private modalStack: ModalStack, 12 | private config: ModalConfig 13 | ) {} 14 | 15 | open(content: T, options: ModalOptions = {}): ModalRef { 16 | const combinedOptions = Object.assign({}, this.config, options); 17 | return this.modalStack.open( 18 | this.moduleComponentFactoryResolver, 19 | this.injector, 20 | content, 21 | combinedOptions 22 | ); 23 | } 24 | 25 | dismissAll(reason?: any): void { 26 | this.modalStack.dismissAll(reason); 27 | } 28 | hasOpenModals(): boolean { 29 | return this.modalStack.hasOpenModals(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/progress-wizard/progress-wizard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, OnChanges } from '@angular/core'; 2 | 3 | export interface ProgressWizardOptions { 4 | steps: string[]; 5 | } 6 | 7 | @Component({ 8 | selector: 'app-progress-wizard', 9 | templateUrl: './progress-wizard.component.html', 10 | styleUrls: ['./progress-wizard.component.sass'] 11 | }) 12 | export class ProgressWizardComponent implements OnInit, OnChanges { 13 | @Input() options: ProgressWizardOptions = { steps: [] }; 14 | @Input() step!: number; 15 | 16 | progressPercent!: number; 17 | 18 | constructor() {} 19 | 20 | ngOnInit(): void { 21 | this.progressPercent = this.calcPercent(); 22 | } 23 | 24 | ngOnChanges(): void { 25 | this.progressPercent = this.calcPercent(); 26 | } 27 | 28 | private calcPercent(): number { 29 | if (!this.step || !this.options.steps || !this.options.steps.length) { 30 | return 0; 31 | } 32 | 33 | return (100 * (this.step - 1 || 0)) / (this.options.steps.length - 1); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/terminal/terminal.component.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/components/terminal/terminal.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/shared/components/terminal/terminal.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/directives/stop-propagation.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appStopPropagation]' 5 | }) 6 | export class StopPropagationDirective { 7 | @HostListener('click', ['$event']) onClick(event: MouseEvent): void { 8 | event.stopPropagation(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './shared.module'; 2 | export * from './components/progress-wizard/progress-wizard.component'; 3 | export * from './common/random-hash'; 4 | export * from './common/random-int'; 5 | export * from './validators/validators'; 6 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/models/socket.model.ts: -------------------------------------------------------------------------------- 1 | export class SocketEvent { 2 | constructor(public type: string, public data: { event: string; id: string } | any) {} 3 | } 4 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/providers/time.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, timer } from 'rxjs'; 3 | import { share } from 'rxjs/operators'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class TimeService { 7 | timer: Observable; 8 | 9 | constructor() { 10 | this.timer = timer(0, 1000); 11 | } 12 | 13 | getCurrentTime(): Observable { 14 | return new Observable(observer => { 15 | const sub = this.timer.subscribe(() => { 16 | observer.next(new Date()); 17 | }); 18 | 19 | return () => sub.unsubscribe(); 20 | }).pipe(share()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/validators/validators.ts: -------------------------------------------------------------------------------- 1 | import { ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; 2 | import parseDuration from 'parse-duration'; 3 | 4 | export function durationValidator(compare: AbstractControl): ValidatorFn { 5 | return (control: AbstractControl): ValidationErrors | null => { 6 | const cmpdur = parseDuration(compare.value) as number; 7 | const dur = parseDuration(control.value) as number; 8 | return cmpdur < dur ? null : { duration: true }; 9 | }; 10 | } 11 | 12 | export function equalValidator(compare: AbstractControl): ValidatorFn { 13 | return (control: AbstractControl): ValidationErrors | null => { 14 | return compare.value === control.value ? null : { mismatch: true }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/avatar-picker/avatar-picker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/avatar-picker/avatar-picker.component.sass: -------------------------------------------------------------------------------- 1 | @import 'colours' 2 | @import 'variables' 3 | 4 | .avatar-picker-container 5 | padding: 10px 6 | display: flex 7 | justify-content: center 8 | position: relative 9 | width: 100% 10 | height: auto 11 | margin: 0 auto 12 | 13 | .avatar-image 14 | cursor: pointer 15 | width: auto 16 | max-height: 120px 17 | display: block 18 | border-radius: 50% 19 | 20 | .avatars-picker 21 | position: absolute 22 | top: 10px 23 | left: 0 24 | width: 100% 25 | border: 1px solid $border-default 26 | background: $white 27 | display: flex 28 | align-items: center 29 | justify-content: center 30 | z-index: 9 31 | 32 | .avatars-container 33 | display: flex 34 | flex-wrap: wrap 35 | width: 100% 36 | height: 100% 37 | padding: 10px 38 | 39 | .avatar-small 40 | border: 2px solid transparent 41 | width: 40px 42 | height: 40px 43 | margin: 3px 44 | cursor: pointer 45 | display: block 46 | border-radius: 50% 47 | 48 | &:hover, &.is-active 49 | border-color: $green 50 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/checkbox/checkbox.component.html: -------------------------------------------------------------------------------- 1 |
2 | 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/checkbox/checkbox.component.sass: -------------------------------------------------------------------------------- 1 | @import 'colours' 2 | @import 'variables' 3 | @import 'mixins' 4 | 5 | .checkbox 6 | position: relative 7 | display: inline-flex 8 | align-items: center 9 | justify-content: flex-start 10 | margin: 5px 0 11 | 12 | .label 13 | +unselectable 14 | margin: 4px 0 0 20px 15 | cursor: pointer 16 | 17 | .checkbox-label 18 | width: 22px 19 | height: 22px 20 | cursor: pointer 21 | position: absolute 22 | top: 0 23 | left: 0 24 | border-radius: $border-radius 25 | background: $background-input 26 | border: 1px solid $border-input 27 | 28 | &:after 29 | content: '' 30 | width: 12px 31 | height: 7px 32 | position: absolute 33 | top: 6px 34 | left: 4px 35 | border: 3px solid $text-primary 36 | border-top: none 37 | border-right: none 38 | background: transparent 39 | opacity: 0 40 | transform: rotate(-45deg) 41 | 42 | &:hover::after 43 | opacity: 0.3 44 | 45 | input[type="checkbox"] 46 | visibility: hidden 47 | 48 | &:checked + label:after 49 | opacity: 1 50 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/color-picker/color-picker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/color-picker/color-picker.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | 4 | .colors-container 5 | display: block 6 | 7 | .color-item 8 | display: block 9 | float: left 10 | border-radius: $border-radius 11 | width: 20px 12 | height: 20px 13 | border: 2px solid transparent 14 | margin: 0 2px 2px 0 15 | cursor: pointer 16 | 17 | &.is-active 18 | border-color: $border-dark 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/progress-bar/progress-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{ percentage }} 4 | {{ placeholder }} 5 |
6 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/progress-bar/progress-bar.component.sass: -------------------------------------------------------------------------------- 1 | @import 'colours' 2 | @import 'variables' 3 | 4 | $progress-bar-height: 24px 5 | $progress-bar-border-radius: 4px 6 | 7 | \:host 8 | display: block 9 | width: 100% 10 | 11 | .progress-bar-container 12 | display: block 13 | width: 100% 14 | border-radius: $progress-bar-border-radius 15 | height: $progress-bar-height 16 | position: relative 17 | 18 | .progress-bar 19 | display: block 20 | position: absolute 21 | top: 0 22 | left: 0 23 | height: 100% 24 | border-radius: $progress-bar-border-radius 25 | z-index: 5 26 | transition: width 300ms ease-in-out 27 | 28 | .progress-text 29 | display: flex 30 | position: absolute 31 | top: 0 32 | left: 0 33 | width: 100% 34 | height: 100% 35 | border-radius: $progress-bar-border-radius 36 | background: transparent 37 | color: $white 38 | font-size: 13px 39 | font-weight: $weight-medium 40 | align-items: center 41 | justify-content: center 42 | z-index: 6 43 | 44 | 45 | @each $name, $color in $colors 46 | 47 | &.is-#{$name} 48 | background: rgba($color, .6) 49 | 50 | .progress-bar 51 | background: $color 52 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/progress-bar/progress-bar.interface.ts: -------------------------------------------------------------------------------- 1 | export type style = 2 | | 'red' 3 | | 'orange' 4 | | 'yellow' 5 | | 'green' 6 | | 'teal' 7 | | 'blue' 8 | | 'indigo' 9 | | 'purple' 10 | | 'pink' 11 | | 'gray'; 12 | 13 | export interface ProgressBarSettings { 14 | color?: style; 15 | transition?: boolean; 16 | transitionDuration?: number; 17 | width?: string; 18 | height?: string; 19 | } 20 | 21 | export const defaultProgressBarSettings: ProgressBarSettings = { 22 | color: 'green', 23 | transition: true, 24 | transitionDuration: 400, 25 | width: '100%', 26 | height: '24px' 27 | }; 28 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/selectbox/selectbox.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | {{ placeholderText }} 5 | 6 | 7 |
8 |
9 |
15 | {{ val.placeholder }} 16 | 17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/toggle/toggle.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |
7 | -------------------------------------------------------------------------------- /web/abstruse/src/app/shared/widgets/toggle/toggle.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; 3 | 4 | @Component({ 5 | selector: 'app-toggle', 6 | templateUrl: './toggle.component.html', 7 | styleUrls: ['./toggle.component.sass'], 8 | providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true }] 9 | }) 10 | export class ToggleComponent implements ControlValueAccessor { 11 | @Input() label = ''; 12 | 13 | isEnabled!: boolean; 14 | 15 | private onTouchedCallback: () => void = () => {}; 16 | private onChangeCallback: (_: any) => void = () => {}; 17 | 18 | get value(): boolean { 19 | return this.isEnabled; 20 | } 21 | 22 | set value(val: boolean) { 23 | this.isEnabled = val; 24 | this.onChangeCallback(this.isEnabled); 25 | } 26 | 27 | toggle(): void { 28 | this.value = !this.value; 29 | } 30 | 31 | writeValue(val: boolean): void { 32 | this.isEnabled = val; 33 | } 34 | 35 | registerOnChange(fn: any): void { 36 | this.onChangeCallback = fn; 37 | } 38 | 39 | registerOnTouched(fn: any): void { 40 | this.onTouchedCallback = fn; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web/abstruse/src/app/system/shared/system.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { finalize } from 'rxjs/operators'; 4 | import { Version, generateVersion } from './version.class'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class SystemService { 10 | version!: Version; 11 | fetchingVersion = false; 12 | 13 | constructor(public http: HttpClient) {} 14 | 15 | fetchVersion(): void { 16 | this.fetchingVersion = true; 17 | this.http 18 | .get('/system/version') 19 | .pipe(finalize(() => (this.fetchingVersion = false))) 20 | .subscribe(version => (this.version = generateVersion(version))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/abstruse/src/app/system/shared/version.class.ts: -------------------------------------------------------------------------------- 1 | export class Version { 2 | constructor( 3 | public api: string, 4 | public ui: string, 5 | public commitHash: string, 6 | public buildDate: Date, 7 | public os: string, 8 | public arch: string 9 | ) {} 10 | 11 | get getCommit(): string { 12 | return this.commitHash.substring(0, 6); 13 | } 14 | } 15 | 16 | export const generateVersion = (data: any): Version => { 17 | return new Version(data.api, data.ui, data.commitHash, data.buildDate, data.os, data.arch); 18 | }; 19 | -------------------------------------------------------------------------------- /web/abstruse/src/app/system/system-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { VersionComponent } from './version/version.component'; 4 | 5 | const routes: Routes = [ 6 | { path: '', pathMatch: 'full', redirectTo: 'version' }, 7 | { path: 'version', component: VersionComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class SystemRoutingModule {} 15 | -------------------------------------------------------------------------------- /web/abstruse/src/app/system/system.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SharedModule } from '../shared'; 4 | import { SystemRoutingModule } from './system-routing.module'; 5 | import { VersionComponent } from './version/version.component'; 6 | 7 | @NgModule({ 8 | imports: [CommonModule, SystemRoutingModule, SharedModule], 9 | declarations: [VersionComponent] 10 | }) 11 | export class SystemModule {} 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/system/version/version.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/system/version/version.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/system/version/version.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { SystemService } from '../shared/system.service'; 3 | 4 | @Component({ 5 | selector: 'app-version', 6 | templateUrl: './version.component.html', 7 | styleUrls: ['./version.component.sass'] 8 | }) 9 | export class VersionComponent implements OnInit { 10 | constructor(public system: SystemService) {} 11 | 12 | ngOnInit(): void { 13 | this.system.fetchVersion(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/shared/teams.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { generateTeam, Team } from './user.model'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class TeamsService { 11 | constructor(private http: HttpClient) {} 12 | 13 | find(id: number): Observable { 14 | return this.http.get(`/teams/${id}`).pipe(map(generateTeam)); 15 | } 16 | 17 | list(): Observable { 18 | return this.http 19 | .get('/teams') 20 | .pipe(map((resp: any) => (resp && resp.length ? resp.map(generateTeam) : []))); 21 | } 22 | 23 | create(data: any): Observable { 24 | return this.http.post('/teams', data).pipe(map(generateTeam)); 25 | } 26 | 27 | update(data: any): Observable { 28 | return this.http.put('/teams', data).pipe(map(generateTeam)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/shared/users.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { TokenResponse } from 'src/app/auth/shared/auth.model'; 6 | import { generateUser, User } from './user.model'; 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class UsersService { 12 | constructor(private http: HttpClient) {} 13 | 14 | list(): Observable { 15 | return this.http 16 | .get('/users') 17 | .pipe(map((resp: any) => (resp && resp.length ? resp.map(generateUser) : []))); 18 | } 19 | 20 | create(data: any): Observable { 21 | return this.http.post('/users', data); 22 | } 23 | 24 | update(data: any): Observable { 25 | return this.http.put('/users', data); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/team-item/team-item.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |

{{ team.name }}

7 | {{ team.about }} 8 | 9 | {{ team.membersCount }} 10 | 11 | 12 | {{ team.reposCount }} 13 | 14 |
15 | 24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/team-item/team-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 | import { AuthService } from 'src/app/auth/shared/auth.service'; 3 | import { ModalService } from 'src/app/shared/components/modal/modal.service'; 4 | import { Team } from '../shared/user.model'; 5 | import { TeamModalComponent } from '../team-modal/team-modal.component'; 6 | 7 | @Component({ 8 | selector: 'app-team-item', 9 | templateUrl: './team-item.component.html', 10 | styleUrls: ['./team-item.component.sass'] 11 | }) 12 | export class TeamItemComponent implements OnInit { 13 | @Input() team!: Team; 14 | @Output() saved = new EventEmitter(); 15 | 16 | constructor(public auth: AuthService, public modal: ModalService) {} 17 | 18 | ngOnInit(): void {} 19 | 20 | openTeamModal(): void { 21 | const modalRef = this.modal.open(TeamModalComponent, { size: 'medium' }); 22 | modalRef.componentInstance.team = { ...this.team }; 23 | modalRef.result.then( 24 | ok => { 25 | if (ok) { 26 | this.saved.next(); 27 | } 28 | }, 29 | () => {} 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/team-modal/team-modal.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | @import 'mixins' 4 | 5 | $member-item-height-small: 40px 6 | 7 | .h250 8 | height: 250px 9 | 10 | .members-list 11 | height: 250px 12 | overflow-y: scroll 13 | border: 1px solid $border-default 14 | +scrollbar 15 | 16 | .member-item 17 | padding: 10px 18 | border-bottom: 1px solid $border-default 19 | 20 | &.is-small 21 | height: $member-item-height-small 22 | padding: 0 0 0 5px 23 | 24 | .columns 25 | height: $member-item-height-small 26 | 27 | .column 28 | height: $member-item-height-small 29 | 30 | .avatar-img 31 | width: 20px 32 | height: 20px 33 | 34 | .columns 35 | 36 | .column 37 | display: flex 38 | align-items: center 39 | 40 | span 41 | +ellipsis 42 | 43 | .avatar-img 44 | display: block 45 | width: 30px 46 | height: 30px 47 | border-radius: 50% 48 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/teams-list/teams-list.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/teams/teams-list/teams-list.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/teams-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { TeamsListComponent } from './teams-list/teams-list.component'; 4 | import { UsersComponent } from './users/users.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', pathMatch: 'full', redirectTo: 'users' }, 8 | { path: 'users', component: UsersComponent }, 9 | { path: 'list', component: TeamsListComponent } 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forChild(routes)], 14 | exports: [RouterModule] 15 | }) 16 | export class TeamsRoutingModule {} 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/teams.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TeamsRoutingModule } from './teams-routing.module'; 4 | import { SharedModule } from '../shared'; 5 | import { TeamModalComponent } from './team-modal/team-modal.component'; 6 | import { UsersComponent } from './users/users.component'; 7 | import { UserListItemComponent } from './user-list-item/user-list-item.component'; 8 | import { UserModalComponent } from './user-modal/user-modal.component'; 9 | import { TeamItemComponent } from './team-item/team-item.component'; 10 | import { TeamsListComponent } from './teams-list/teams-list.component'; 11 | 12 | @NgModule({ 13 | declarations: [ 14 | TeamItemComponent, 15 | TeamModalComponent, 16 | UsersComponent, 17 | UserListItemComponent, 18 | UserModalComponent, 19 | TeamsListComponent 20 | ], 21 | imports: [CommonModule, TeamsRoutingModule, SharedModule] 22 | }) 23 | export class TeamsModule {} 24 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/user-list-item/user-list-item.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/teams/user-list-item/user-list-item.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/user-list-item/user-list-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 | import { AuthService } from 'src/app/auth/shared/auth.service'; 3 | import { ModalService } from 'src/app/shared/components/modal/modal.service'; 4 | import { User } from '../shared/user.model'; 5 | import { UserModalComponent } from '../user-modal/user-modal.component'; 6 | 7 | @Component({ 8 | selector: 'app-user-list-item', 9 | templateUrl: './user-list-item.component.html', 10 | styleUrls: ['./user-list-item.component.sass'] 11 | }) 12 | export class UserListItemComponent implements OnInit { 13 | @Input() user!: User; 14 | @Output() updated = new EventEmitter(); 15 | 16 | constructor(public modal: ModalService, public auth: AuthService) {} 17 | 18 | ngOnInit(): void {} 19 | 20 | openUserModal(): void { 21 | const modalRef = this.modal.open(UserModalComponent, { size: 'medium' }); 22 | modalRef.componentInstance.user = this.user; 23 | modalRef.result.then( 24 | ok => { 25 | if (ok) { 26 | this.updated.next(); 27 | } 28 | }, 29 | () => {} 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/user-modal/user-modal.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/teams/user-modal/user-modal.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/teams/users/users.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/teams/users/users.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/shared/workers.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { Worker, generateWorker } from './worker.model'; 5 | import { map } from 'rxjs/operators'; 6 | 7 | export const workerSubAddEvent = '/subs/workers_add'; 8 | export const workerSubDeleteEvent = '/subs/workers_delete'; 9 | export const workerSubUsageEvent = '/subs/workers_usage'; 10 | 11 | @Injectable({ providedIn: 'root' }) 12 | export class WorkersService { 13 | constructor(private http: HttpClient) {} 14 | 15 | find(): Observable { 16 | return this.http.get(`/workers`).pipe( 17 | map(resp => (resp && resp.length ? resp : [])), 18 | map(data => data.map(generateWorker)) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/worker-modal/worker-modal.component.sass: -------------------------------------------------------------------------------- 1 | @import 'variables' 2 | @import 'colours' 3 | @import 'mixins' 4 | 5 | .text-label, .text-data 6 | display: block 7 | +ellipsis 8 | 9 | .text-label 10 | font-size: 12px 11 | font-weight: $weight-medium 12 | margin-bottom: 8px 13 | 14 | .text-data 15 | font-size: 13px 16 | color: $text-secondary 17 | -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/worker-table-item/worker-table-item.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/workers/worker-table-item/worker-table-item.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/worker-table-item/worker-table-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { ModalService } from 'src/app/shared/components/modal/modal.service'; 3 | import { Worker } from '../shared/worker.model'; 4 | import { WorkerModalComponent } from '../worker-modal/worker-modal.component'; 5 | 6 | @Component({ 7 | selector: 'app-worker-table-item', 8 | templateUrl: './worker-table-item.component.html', 9 | styleUrls: ['./worker-table-item.component.sass'] 10 | }) 11 | export class WorkerTableItemComponent implements OnInit { 12 | @Input() worker!: Worker; 13 | 14 | constructor(public modalService: ModalService) {} 15 | 16 | ngOnInit(): void {} 17 | 18 | openWorkerModal(): void { 19 | const modalRef = this.modalService.open(WorkerModalComponent, { size: 'large' }); 20 | modalRef.componentInstance.worker = this.worker; 21 | modalRef.result.then( 22 | () => {}, 23 | () => {} 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/workers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { WorkersComponent } from './workers/workers.component'; 4 | 5 | const routes: Routes = [{ path: '', pathMatch: 'full', component: WorkersComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forChild(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class WorkersRoutingModule {} 12 | -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/workers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { WorkersRoutingModule } from './workers-routing.module'; 4 | import { WorkersComponent } from './workers/workers.component'; 5 | import { SharedModule } from '../shared'; 6 | import { WorkerTableItemComponent } from './worker-table-item/worker-table-item.component'; 7 | import { WorkerModalComponent } from './worker-modal/worker-modal.component'; 8 | import { RealtimeCanvasChartModule } from 'ngx-graph'; 9 | 10 | @NgModule({ 11 | imports: [CommonModule, WorkersRoutingModule, SharedModule, RealtimeCanvasChartModule], 12 | declarations: [WorkersComponent, WorkerTableItemComponent, WorkerModalComponent] 13 | }) 14 | export class WorkersModule {} 15 | -------------------------------------------------------------------------------- /web/abstruse/src/app/workers/workers/workers.component.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/app/workers/workers/workers.component.sass -------------------------------------------------------------------------------- /web/abstruse/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/.gitkeep -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-100.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-100.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-100.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-100.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-300.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-300.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-500.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-500.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-700.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-700.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-900.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-900.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-regular.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Roboto/roboto-v20-latin-regular.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-300.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-300.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-500.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-500.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-700.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-700.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-900.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-900.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-regular.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/Rubik/rubik-v9-latin-regular.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Bold.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Bold.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Regular.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/SourceCodePro/SourceCodePro-Regular.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/octicons/octicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/octicons/octicons.woff -------------------------------------------------------------------------------- /web/abstruse/src/assets/fonts/octicons/octicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/fonts/octicons/octicons.woff2 -------------------------------------------------------------------------------- /web/abstruse/src/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /web/abstruse/src/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /web/abstruse/src/assets/images/abstruse-square-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /web/abstruse/src/assets/images/abstruse-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /web/abstruse/src/assets/images/icons/gitea-bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /web/abstruse/src/assets/images/icons/gitea.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/abstruse/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | apiURL: '/api/v1/' 4 | }; 5 | -------------------------------------------------------------------------------- /web/abstruse/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | apiURL: '/api/v1/' 8 | }; 9 | 10 | /* 11 | * For easier debugging in development mode, you can import the following file 12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 13 | * 14 | * This import should be commented out in production mode because it will have a negative impact 15 | * on performance if an error is thrown. 16 | */ 17 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /web/abstruse/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bleenco/abstruse/b28dd99d7f1b8e163a1dce0b55cf02a36f0cf30a/web/abstruse/src/favicon.ico -------------------------------------------------------------------------------- /web/abstruse/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Abstruse CI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/abstruse/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /web/abstruse/src/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "abstruse", 3 | "short_name": "abstruse", 4 | "theme_color": "#ffffff", 5 | "background_color": "#fafafa", 6 | "display": "standalone", 7 | "scope": "./", 8 | "start_url": "./", 9 | "icons": [ 10 | { 11 | "src": "assets/icons/icon-128x128.png", 12 | "sizes": "128x128", 13 | "type": "image/png", 14 | "purpose": "maskable any" 15 | }, 16 | { 17 | "src": "assets/icons/icon-512x512.png", 18 | "sizes": "512x512", 19 | "type": "image/png", 20 | "purpose": "maskable any" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/_animations.sass: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes load 2 | 0% 3 | -webkit-transform: rotate(0deg) 4 | transform: rotate(0deg) 5 | 100% 6 | -webkit-transform: rotate(360deg) 7 | transform: rotate(360deg) 8 | 9 | @keyframes load 10 | 0% 11 | -webkit-transform: rotate(0deg) 12 | transform: rotate(0deg) 13 | 100% 14 | -webkit-transform: rotate(360deg) 15 | transform: rotate(360deg) 16 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/_mixins.sass: -------------------------------------------------------------------------------- 1 | =unselectable 2 | -webkit-touch-callout: none 3 | -webkit-user-select: none 4 | -moz-user-select: none 5 | -ms-user-select: none 6 | user-select: none 7 | 8 | =ellipsis 9 | width: calc(100%) 10 | white-space: nowrap 11 | overflow: hidden 12 | text-overflow: ellipsis 13 | 14 | =placeholder 15 | &::placeholder 16 | @content 17 | &::-webkit-input-placeholder 18 | @content 19 | &:-moz-placeholder 20 | @content 21 | &::-moz-placeholder 22 | @content 23 | &:-ms-input-placeholder 24 | @content 25 | 26 | =scrollbar 27 | &::-webkit-scrollbar 28 | width: 8px 29 | &::-webkit-scrollbar-thumb 30 | background: $gray-500 31 | border-radius: $border-radius 32 | &::-webkit-scrollbar-track 33 | background: transparent 34 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/_variables.sass: -------------------------------------------------------------------------------- 1 | $fa-font-path: "/assets/fonts/fa" 2 | $octicons-font-path: "/assets/fonts/octicons/" 3 | 4 | $box-shadow: 0 2px 5px 0 rgba(0,0,0,0.05) 5 | $box-shadow-light: 0 2px 12px 0 rgba(0,0,0,0.04) 6 | $box-shadow-modal: 0 6px 20px 4px rgba(0, 0, 0, 0.1) 7 | $box-shadow-stroke: 0 0 0 1px rgba(6,6,7,0.08) 8 | 9 | $border-radius: 4px 10 | $border-radius-floating: 6px 11 | 12 | $weight-thin: 100 !default 13 | $weight-light: 300 !default 14 | $weight-normal: 400 !default 15 | $weight-medium: 500 !default 16 | $weight-semibold: 700 !default 17 | $weight-bold: 900 !default 18 | 19 | $font-size: 14px 20 | 21 | $input-height: 40px 22 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/app.sass: -------------------------------------------------------------------------------- 1 | @charset 'utf8' 2 | 3 | @import 'colours' 4 | @import 'variables' 5 | @import 'mixins' 6 | @import 'animations' 7 | 8 | @import '@fortawesome/fontawesome-free/scss/fontawesome' 9 | @import '@fortawesome/fontawesome-free/scss/solid' 10 | @import '@fortawesome/fontawesome-free/scss/regular' 11 | @import '@fortawesome/fontawesome-free/scss/brands' 12 | @import 'octicons' 13 | 14 | @import 'bulma/sass/base/minireset' 15 | @import 'bulma/sass/utilities/all' 16 | @import 'bulma/sass/elements/container' 17 | @import 'bulma/sass/grid/columns' 18 | 19 | @import 'xterm/css/xterm' 20 | 21 | @import 'typography' 22 | @import 'animations' 23 | 24 | @import 'main' 25 | @import 'common' 26 | @import 'header' 27 | 28 | @import 'hero' 29 | @import 'subheader' 30 | @import 'section' 31 | @import 'form' 32 | @import 'button' 33 | @import 'tooltip' 34 | @import 'notification' 35 | @import 'list' 36 | @import 'tag' 37 | @import 'status' 38 | @import 'tabs' 39 | @import 'pagination' 40 | @import 'placeholder' 41 | @import 'terminal' 42 | @import 'table' 43 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/hero.sass: -------------------------------------------------------------------------------- 1 | .hero 2 | align-items: stretch 3 | display: flex 4 | flex-direction: column 5 | justify-content: space-between 6 | .navbar 7 | background: none 8 | .tabs 9 | ul 10 | border-bottom: none 11 | 12 | &.is-small 13 | .hero-body 14 | padding-bottom: 1.5rem 15 | padding-top: 1.5rem 16 | &.is-medium 17 | +tablet 18 | .hero-body 19 | padding-bottom: 9rem 20 | padding-top: 9rem 21 | &.is-large 22 | +tablet 23 | .hero-body 24 | padding-bottom: 18rem 25 | padding-top: 18rem 26 | &.is-halfheight, 27 | &.is-fullheight 28 | .hero-body 29 | align-items: center 30 | display: flex 31 | & > .container 32 | flex-grow: 1 33 | flex-shrink: 1 34 | &.is-halfheight 35 | min-height: 50vh 36 | &.is-fullheight 37 | min-height: 100vh 38 | 39 | .hero-head, 40 | .hero-foot 41 | flex-grow: 0 42 | flex-shrink: 0 43 | 44 | .hero-body 45 | flex-grow: 1 46 | flex-shrink: 0 47 | padding: 3rem 1.5rem 48 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/pagination.sass: -------------------------------------------------------------------------------- 1 | .pagination 2 | +unselectable 3 | display: flex 4 | margin: 30px 0 0 0 5 | align-items: center 6 | justify-content: center 7 | 8 | .page 9 | display: inline-block 10 | cursor: pointer 11 | padding: 5px 10px 12 | border: 1px solid transparent 13 | border-radius: $border-radius 14 | min-width: 38px 15 | text-align: center 16 | 17 | &:hover, &.is-active 18 | border: 1px solid $green 19 | color: $green 20 | 21 | &.is-hidden 22 | display: none 23 | 24 | .btn 25 | padding: 5px 10px 26 | margin: 0 5px 27 | cursor: pointer 28 | border: 1px solid transparent 29 | border-radius: $border-radius 30 | 31 | &:hover 32 | border: 1px solid $green 33 | color: $green 34 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/placeholder.sass: -------------------------------------------------------------------------------- 1 | .placeholder 2 | +unselectable 3 | width: 45px 4 | height: 45px 5 | border-radius: 50% 6 | display: inline-flex 7 | align-items: center 8 | justify-content: center 9 | color: $text-secondary 10 | background: $white 11 | border: 1px solid $border-default 12 | font-size: 20px 13 | font-weight: $weight-medium 14 | 15 | @each $name, $color in $colors 16 | &.is-#{$name} 17 | color: $color 18 | background: rgba($color, .2) 19 | border-color: $color 20 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/section.sass: -------------------------------------------------------------------------------- 1 | .section 2 | width: 100% 3 | height: 100% 4 | display: block 5 | // border: 1px solid $border-default 6 | border-radius: $border-radius 7 | // padding: 15px 20px 8 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/status.sass: -------------------------------------------------------------------------------- 1 | .status 2 | display: inline-block 3 | margin: 0 5px 0 0 4 | padding: 0 0 0 20px 5 | position: relative 6 | color: $gray-500 7 | font-size: 13px 8 | font-weight: $weight-medium 9 | 10 | &::before 11 | content: '' 12 | display: block 13 | width: 12px 14 | height: 12px 15 | border-radius: 50% 16 | background: $gray-500 17 | position: absolute 18 | left: 0 19 | top: 2px 20 | 21 | @each $name, $color in $colors 22 | &.is-#{$name} 23 | color: $color 24 | 25 | &::before 26 | background: $color 27 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/tabs.sass: -------------------------------------------------------------------------------- 1 | .tabs 2 | display: flex 3 | width: 100% 4 | justify-content: flex-end 5 | padding: 0 10px 6 | border-bottom: 1px solid $border-default 7 | margin-top: 10px 8 | 9 | &.is-centered 10 | justify-content: center 11 | 12 | .tab 13 | display: inline-flex 14 | justify-content: center 15 | align-content: center 16 | padding: 10px 25px 17 | cursor: pointer 18 | border: 1px solid transparent 19 | border-top-left-radius: $border-radius 20 | border-top-right-radius: $border-radius 21 | margin-bottom: -1px 22 | margin-left: 3px 23 | font-size: 13px 24 | text-transform: uppercase 25 | color: $text-secondary 26 | 27 | &:hover, &.is-active 28 | border-top: 1px solid $border-default 29 | border-left: 1px solid $border-default 30 | border-right: 1px solid $border-default 31 | border-bottom: 1px solid $white 32 | color: $text-primary 33 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/terminal.sass: -------------------------------------------------------------------------------- 1 | .terminal-container 2 | display: block 3 | margin: 0 4 | padding: 0 5 | width: 100% 6 | height: 520px 7 | position: relative 8 | border-radius: $border-radius 9 | overflow: hidden 10 | 11 | .xterm 12 | position: absolute 13 | top: 0 14 | left: 0 15 | right: 0 16 | bottom: 0 17 | width: 100% 18 | height: 100% 19 | padding: 18px 0 5px 15px 20 | border-radius: $border-radius 21 | border: 1px solid $border-default 22 | 23 | .xterm-viewport 24 | border-radius: $border-radius 25 | height: 100% 26 | +scrollbar 27 | -------------------------------------------------------------------------------- /web/abstruse/src/styles/tooltip.sass: -------------------------------------------------------------------------------- 1 | .tooltip-container 2 | min-width: 80px 3 | max-width: 300px 4 | width: auto 5 | padding: 10px 6 | background: $background-tooltip 7 | color: $white 8 | display: inline-flex 9 | align-items: center 10 | justify-content: center 11 | border-radius: 3px 12 | font-weight: $weight-medium 13 | position: relative 14 | z-index: 20 15 | font-size: 14px 16 | font-family: $font-family-roboto 17 | border-radius: $border-radius 18 | 19 | &::before 20 | content: '' 21 | position: absolute 22 | width: 10px 23 | height: 10px 24 | transform: rotate(-45deg) 25 | background: $background-tooltip 26 | top: calc(100% - 5px) 27 | left: calc(50% - 5px) 28 | -------------------------------------------------------------------------------- /web/abstruse/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { 12 | teardown: { destroyAfterEach: false } 13 | }); 14 | -------------------------------------------------------------------------------- /web/abstruse/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /web/abstruse/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "sourceMap": true, 12 | "declaration": false, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "moduleResolution": "node", 16 | "importHelpers": true, 17 | "target": "ES2022", 18 | "module": "es2020", 19 | "lib": [ 20 | "es2018", 21 | "dom" 22 | ], 23 | "useDefineForClassFields": false 24 | }, 25 | "angularCompilerOptions": { 26 | "strictInjectionParameters": true, 27 | "strictInputAccessModifiers": true, 28 | "strictTemplates": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /web/abstruse/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "files": ["src/test.ts", "src/polyfills.ts"], 9 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /web/abstruse/webpack.config.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob'); 2 | const { PurgeCSSPlugin } = require('purgecss-webpack-plugin'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | new PurgeCSSPlugin({ 7 | paths: glob.sync('./src/**/*.html', { nodir: true }), 8 | safelist: [/^xterm/, /^tooltip-container/] 9 | }) 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /worker/cmd/wire.go: -------------------------------------------------------------------------------- 1 | // +build wireinject 2 | 3 | package cmd 4 | 5 | import ( 6 | "github.com/bleenco/abstruse/worker/app" 7 | "github.com/bleenco/abstruse/worker/logger" 8 | "github.com/google/wire" 9 | ) 10 | 11 | func CreateApp() (*application, error) { 12 | panic(wire.Build(wire.NewSet( 13 | wire.NewSet(logger.New), 14 | wire.NewSet(app.NewApp), 15 | wire.NewSet(newApplication, newConfig), 16 | ))) 17 | } 18 | -------------------------------------------------------------------------------- /worker/docker/init.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import "github.com/bleenco/abstruse/worker/config" 4 | 5 | var ( 6 | cfg *config.Registry 7 | ) 8 | 9 | // Init initializes global variables 10 | func Init(config *config.Registry) { 11 | cfg = config 12 | } 13 | -------------------------------------------------------------------------------- /worker/docker/util.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/logrusorgru/aurora" 7 | ) 8 | 9 | func genExitMessage(code int) string { 10 | if code == 0 { 11 | return green(fmt.Sprintf("\nExit code: %d\r\n", code)) 12 | } 13 | return red(fmt.Sprintf("\nExit code: %d\r\n", code)) 14 | } 15 | 16 | func green(str string) string { 17 | return aurora.Bold(aurora.Green(str)).String() 18 | } 19 | 20 | func yellow(str string) string { 21 | return aurora.Bold(aurora.Yellow(str)).String() 22 | } 23 | 24 | func red(str string) string { 25 | return aurora.Bold(aurora.Red(str)).String() 26 | } 27 | -------------------------------------------------------------------------------- /worker/git/git.go: -------------------------------------------------------------------------------- 1 | package git 2 | -------------------------------------------------------------------------------- /worker/http/bearer.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // TokenAuth is an http.RoundTripper that makes HTTP 9 | // requests, wrapping a base RoundTripper and adding 10 | // a bearer token authorization header. 11 | type TokenAuth struct { 12 | Base http.RoundTripper 13 | Token string 14 | } 15 | 16 | // RoundTrip adds the Authorization header to the request. 17 | func (t *TokenAuth) RoundTrip(r *http.Request) (*http.Response, error) { 18 | if r.Header.Get("Authorization") != "" { 19 | return t.base().RoundTrip(r) 20 | } 21 | req := cloneRequest(r) 22 | req.Header.Add("Authorization", t.token()) 23 | return t.base().RoundTrip(req) 24 | } 25 | 26 | func (t *TokenAuth) base() http.RoundTripper { 27 | if t.Base != nil { 28 | return t.Base 29 | } 30 | return http.DefaultTransport 31 | } 32 | 33 | func (t *TokenAuth) token() string { 34 | return fmt.Sprintf("Bearer %s", t.Token) 35 | } 36 | 37 | func cloneRequest(r *http.Request) *http.Request { 38 | req := new(http.Request) 39 | *req = *r 40 | req.Header = make(http.Header, len(r.Header)) 41 | for k, s := range r.Header { 42 | req.Header[k] = append([]string(nil), s...) 43 | } 44 | return req 45 | } 46 | --------------------------------------------------------------------------------