mirror of
https://github.com/mentos1386/zdravko.git
synced 2024-11-25 08:58:50 +00:00
feat(settings): improve overall design
This commit is contained in:
parent
cd7abb7e33
commit
3d7fb901d0
19 changed files with 428 additions and 263 deletions
|
@ -2,12 +2,14 @@ package handlers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.tjo.space/mentos1386/zdravko/internal/models"
|
||||
"code.tjo.space/mentos1386/zdravko/internal/services"
|
||||
"code.tjo.space/mentos1386/zdravko/pkg/api"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ApiV1WorkersConnectGETResponse struct {
|
||||
|
@ -17,12 +19,21 @@ type ApiV1WorkersConnectGETResponse struct {
|
|||
}
|
||||
|
||||
func (h *BaseHandler) ApiV1WorkersConnectGET(c echo.Context) error {
|
||||
ctx := context.Background()
|
||||
cc := c.(AuthenticatedContext)
|
||||
|
||||
worker, err := services.GetWorker(ctx, h.query, cc.Principal.Worker.Slug)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized, "Token invalid")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
response := ApiV1WorkersConnectGETResponse{
|
||||
Endpoint: h.config.Temporal.ServerHost,
|
||||
Group: cc.Principal.Worker.Group,
|
||||
Slug: cc.Principal.Worker.Slug,
|
||||
Group: worker.Group,
|
||||
Slug: worker.Slug,
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, response)
|
||||
|
@ -36,8 +47,16 @@ func (h *BaseHandler) ApiV1HealthchecksHistoryPOST(c echo.Context) error {
|
|||
|
||||
slug := c.Param("slug")
|
||||
|
||||
worker, err := services.GetWorker(ctx, h.query, slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if worker == nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Worker not found")
|
||||
}
|
||||
|
||||
var body api.ApiV1HealthchecksHistoryPOSTBody
|
||||
err := (&echo.DefaultBinder{}).BindBody(c, &body)
|
||||
err = (&echo.DefaultBinder{}).BindBody(c, &body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -102,8 +102,7 @@ func (h *BaseHandler) AuthenticateRequestWithToken(r *http.Request) (*Authentica
|
|||
user = &AuthenticatedUser{}
|
||||
} else if splitSubject[0] == "worker" {
|
||||
worker = &AuthenticatedWorker{
|
||||
Slug: splitSubject[1],
|
||||
Group: claims.WorkerGroup,
|
||||
Slug: splitSubject[1],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ var SettingsPages = []*components.Page{
|
|||
{Path: "/settings/cronjobs", Title: "Cronjobs", Breadcrumb: "Cronjobs"},
|
||||
{Path: "/settings/workers", Title: "Workers", Breadcrumb: "Workers"},
|
||||
{Path: "/settings/workers/create", Title: "Workers Create", Breadcrumb: "Create"},
|
||||
{Path: "/settings/notifications", Title: "Notifications", Breadcrumb: "Notifications"},
|
||||
{Path: "/settings/temporal", Title: "Temporal", Breadcrumb: "Temporal"},
|
||||
{Path: "/oauth2/logout", Title: "Logout", Breadcrumb: "Logout"},
|
||||
}
|
||||
|
@ -44,6 +45,7 @@ var SettingsNavbar = []*components.Page{
|
|||
GetPageByTitle(SettingsPages, "Healthchecks"),
|
||||
GetPageByTitle(SettingsPages, "Cronjobs"),
|
||||
GetPageByTitle(SettingsPages, "Workers"),
|
||||
GetPageByTitle(SettingsPages, "Notifications"),
|
||||
GetPageByTitle(SettingsPages, "Temporal"),
|
||||
GetPageByTitle(SettingsPages, "Logout"),
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ import (
|
|||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type WorkerWithToken struct {
|
||||
*models.Worker
|
||||
Token string
|
||||
}
|
||||
|
||||
type SettingsWorkers struct {
|
||||
*Settings
|
||||
Workers []*models.Worker
|
||||
|
@ -22,7 +27,7 @@ type SettingsWorkers struct {
|
|||
|
||||
type SettingsWorker struct {
|
||||
*Settings
|
||||
Worker *models.Worker
|
||||
Worker *WorkerWithToken
|
||||
}
|
||||
|
||||
func (h *BaseHandler) SettingsWorkersGET(c echo.Context) error {
|
||||
|
@ -54,7 +59,13 @@ func (h *BaseHandler) SettingsWorkersDescribeGET(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "setting_workers_describe.tmpl", &SettingsWorker{
|
||||
// Allow write access to default namespace
|
||||
token, err := jwt.NewTokenForWorker(h.config.Jwt.PrivateKey, h.config.Jwt.PublicKey, worker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "settings_workers_describe.tmpl", &SettingsWorker{
|
||||
Settings: NewSettings(
|
||||
cc.Principal.User,
|
||||
GetPageByTitle(SettingsPages, "Workers"),
|
||||
|
@ -66,7 +77,10 @@ func (h *BaseHandler) SettingsWorkersDescribeGET(c echo.Context) error {
|
|||
Breadcrumb: worker.Name,
|
||||
},
|
||||
}),
|
||||
Worker: worker,
|
||||
Worker: &WorkerWithToken{
|
||||
Worker: worker,
|
||||
Token: token,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -108,20 +122,3 @@ func (h *BaseHandler) SettingsWorkersCreatePOST(c echo.Context) error {
|
|||
|
||||
return c.Redirect(http.StatusSeeOther, "/settings/workers")
|
||||
}
|
||||
|
||||
func (h *BaseHandler) SettingsWorkersTokenGET(c echo.Context) error {
|
||||
slug := c.Param("slug")
|
||||
|
||||
worker, err := services.GetWorker(context.Background(), h.query, slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allow write access to default namespace
|
||||
token, err := jwt.NewTokenForWorker(h.config.Jwt.PrivateKey, h.config.Jwt.PublicKey, worker)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, map[string]string{"token": token})
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ func JwtPublicKey(publicKey string) (*rsa.PublicKey, error) {
|
|||
type Claims struct {
|
||||
jwt.RegisteredClaims
|
||||
Permissions []string `json:"permissions"`
|
||||
WorkerGroup string `json:"group"`
|
||||
}
|
||||
|
||||
func NewTokenForUser(privateKey string, publicKey string, email string) (string, error) {
|
||||
|
@ -50,7 +49,6 @@ func NewTokenForUser(privateKey string, publicKey string, email string) (string,
|
|||
},
|
||||
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
||||
[]string{"temporal-system:admin", "default:admin"},
|
||||
"",
|
||||
}
|
||||
|
||||
return NewToken(privateKey, publicKey, claims)
|
||||
|
@ -68,7 +66,6 @@ func NewTokenForServer(privateKey string, publicKey string) (string, error) {
|
|||
},
|
||||
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
||||
[]string{"temporal-system:admin", "default:admin"},
|
||||
"",
|
||||
}
|
||||
|
||||
return NewToken(privateKey, publicKey, claims)
|
||||
|
@ -86,7 +83,6 @@ func NewTokenForWorker(privateKey string, publicKey string, worker *models.Worke
|
|||
},
|
||||
// Ref: https://docs.temporal.io/self-hosted-guide/security#authorization
|
||||
[]string{"default:read", "default:write", "default:worker"},
|
||||
worker.Group,
|
||||
}
|
||||
|
||||
return NewToken(privateKey, publicKey, claims)
|
||||
|
|
|
@ -86,7 +86,6 @@ func (s *Server) Start() error {
|
|||
settings.GET("/workers/create", h.SettingsWorkersCreateGET)
|
||||
settings.POST("/workers/create", h.SettingsWorkersCreatePOST)
|
||||
settings.GET("/workers/:slug", h.SettingsWorkersDescribeGET)
|
||||
settings.GET("/workers/:slug/token", h.SettingsWorkersTokenGET)
|
||||
settings.Match([]string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"}, "/temporal*", h.Temporal)
|
||||
|
||||
// OAuth2
|
||||
|
|
|
@ -35,6 +35,14 @@ func getConnectionConfig(token string, apiUrl string) (*ConnectionConfig, error)
|
|||
return nil, errors.Wrap(err, "failed to connect to API")
|
||||
}
|
||||
|
||||
if res.StatusCode == http.StatusUnauthorized {
|
||||
panic("WORKER_TOKEN is invalid. Either it expired or the worker was removed!")
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, errors.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read response body")
|
||||
|
|
|
@ -47,10 +47,6 @@
|
|||
@apply bg-blue-700 text-white;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
@apply flex mb-4;
|
||||
}
|
||||
|
||||
.healthchecks {
|
||||
@apply grid justify-items-stretch justify-stretch items-center mt-20 bg-white shadow-md p-5 rounded-lg;
|
||||
}
|
||||
|
@ -70,3 +66,28 @@
|
|||
.healthchecks .time-range > a:last-child {
|
||||
@apply rounded-e-lg;
|
||||
}
|
||||
|
||||
.settings {
|
||||
@apply grid grid-cols-1 gap-5 grid-rows-[min-content] h-fit;
|
||||
}
|
||||
.settings section {
|
||||
@apply relative overflow-x-auto shadow-md sm:rounded-lg text-gray-500 bg-white;
|
||||
}
|
||||
.settings section h2 {
|
||||
@apply flex flex-col mb-5 text-lg font-semibold text-gray-900;
|
||||
}
|
||||
.settings section h2 span {
|
||||
@apply text-sm font-medium text-gray-500;
|
||||
}
|
||||
.settings section form {
|
||||
@apply grid gap-4 md:grid-cols-[2fr_1fr];
|
||||
}
|
||||
.settings section form input {
|
||||
@apply h-min bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5;
|
||||
}
|
||||
.settings section form label {
|
||||
@apply col-span-2 block text-sm font-medium text-gray-900;
|
||||
}
|
||||
.settings section form p {
|
||||
@apply text-sm font-normal text-gray-500;
|
||||
}
|
||||
|
|
|
@ -590,8 +590,8 @@ video {
|
|||
}
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
.col-span-1 {
|
||||
grid-column: span 1 / span 1;
|
||||
}
|
||||
|
||||
.col-span-2 {
|
||||
|
@ -616,10 +616,6 @@ video {
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.mb-8 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
@ -632,6 +628,10 @@ video {
|
|||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -732,10 +732,6 @@ video {
|
|||
max-width: 1280px;
|
||||
}
|
||||
|
||||
.max-w-sm {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
@ -752,6 +748,10 @@ video {
|
|||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-\[auto_min-content\] {
|
||||
grid-template-columns: auto min-content;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -1047,6 +1047,11 @@ video {
|
|||
color: rgb(37 99 235 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-blue-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(29 78 216 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity));
|
||||
|
@ -1101,18 +1106,17 @@ video {
|
|||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.shadow-md {
|
||||
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.shadow-sm {
|
||||
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.blur {
|
||||
--tw-blur: blur(8px);
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-top: 2.5rem;
|
||||
display: flex;
|
||||
|
@ -1233,11 +1237,6 @@ video {
|
|||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.healthchecks {
|
||||
margin-top: 5rem;
|
||||
display: grid;
|
||||
|
@ -1308,6 +1307,106 @@ video {
|
|||
border-end-end-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.settings {
|
||||
display: grid;
|
||||
height: -moz-fit-content;
|
||||
height: fit-content;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
grid-template-rows: min-content;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.settings section {
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.settings section {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.settings section h2 {
|
||||
margin-bottom: 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
font-weight: 600;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.settings section h2 span {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 500;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.settings section form {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.settings section form {
|
||||
grid-template-columns: 2fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.settings section form input {
|
||||
display: block;
|
||||
height: -moz-min-content;
|
||||
height: min-content;
|
||||
width: 100%;
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||
padding: 0.625rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.settings section form input:focus {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(59 130 246 / var(--tw-border-opacity));
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.settings section form label {
|
||||
grid-column: span 2 / span 2;
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 500;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(17 24 39 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.settings section form p {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 400;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.odd\:bg-white:nth-child(odd) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
|
@ -1347,11 +1446,6 @@ video {
|
|||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.focus\:border-blue-500:focus {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(59 130 246 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.focus\:outline-none:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
|
@ -1368,11 +1462,6 @@ video {
|
|||
--tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
.focus\:ring-blue-500:focus {
|
||||
--tw-ring-opacity: 1;
|
||||
--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity));
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:w-auto {
|
||||
width: auto;
|
||||
|
@ -1392,10 +1481,6 @@ video {
|
|||
margin-bottom: calc(0px * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.sm\:rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.sm\:px-8 {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
|
@ -1403,10 +1488,6 @@ video {
|
|||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.md\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.md\:text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem;
|
||||
|
@ -1414,8 +1495,8 @@ video {
|
|||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.lg\:grid-cols-\[min-content_auto\] {
|
||||
grid-template-columns: min-content auto;
|
||||
.lg\:grid-cols-\[min-content_minmax\(0\2c 1fr\)\] {
|
||||
grid-template-columns: min-content minmax(0,1fr);
|
||||
}
|
||||
|
||||
.lg\:px-40 {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{{ $path = .SettingsSidebarActive.Path }}
|
||||
{{ end }}
|
||||
|
||||
<div class="container max-w-screen-lg mt-20 grid grid-cols-1 lg:grid-cols-[min-content_auto] gap-8">
|
||||
<div class="container max-w-screen-lg mt-20 grid grid-cols-1 lg:grid-cols-[min-content_minmax(0,1fr)] gap-8">
|
||||
<ul class="sidebar">
|
||||
{{range .SettingsSidebar}}
|
||||
<li>
|
||||
|
@ -20,8 +20,8 @@
|
|||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
<div>
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||||
<div class="settings">
|
||||
<nav aria-label="Breadcrumb">
|
||||
<ol class="inline-flex items-center">
|
||||
{{ range .SettingsBreadcrumbs }}
|
||||
<li class="inline-flex items-center">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{define "main"}}
|
||||
<div class="text-center">
|
||||
<div class="text-center mt-20">
|
||||
<h1 class="text-3xl mb-4 font-bold"><span class="text-red-600">Error 404:</span> Page was not found!</h1>
|
||||
<p>We didn't find the page you were looking for. Please check the URL and try again.</p>
|
||||
<p>Or you can go back to the <a class="underline text-blue-600" href="/">homepage</a>.</p>
|
||||
|
|
|
@ -3,24 +3,22 @@
|
|||
{{ $description := "Healthchecks represent periodicly the k6 script, to see if the monitored service is healthy." }}
|
||||
|
||||
{{ if eq .HealthchecksLength 0 }}
|
||||
<section>
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
There are no healthchecks yet.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
|
||||
<a href="/settings/healthchecks/create" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create First Healthcheck
|
||||
<svg class="feather ml-1 h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
There are no healthchecks yet.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
|
||||
<a href="/settings/healthchecks/create" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create First Healthcheck
|
||||
<svg class="feather ml-1 h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<section>
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
|
||||
<caption class="p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white">
|
||||
List of Healthchecks
|
||||
|
@ -77,6 +75,6 @@
|
|||
</tbody>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
{{define "settings"}}
|
||||
<section class="relative overflow-x-auto shadow-md sm:rounded-lg p-5 text-gray-500 bg-white">
|
||||
<h1 class="text-lg font-semibold text-gray-900">
|
||||
Creating new Healthcheck.
|
||||
</h1>
|
||||
<form class="mt-4" action="/settings/healthchecks/create" method="post">
|
||||
<div class="mb-5">
|
||||
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">Name</label>
|
||||
<input type="name" name="name" id="name" placeholder="Github.com" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="workergroups" class="block mb-2 text-sm font-medium text-gray-900">Worker Groups</label>
|
||||
<input type="text" name="workergroups" id="workergroups" placeholder="europe,asia" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="schedule" class="block mb-2 text-sm font-medium text-gray-900">Schedule</label>
|
||||
<input type="text" name="schedule" id="schedule" placeholder="* * * * *" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="script" class="block mb-2 text-sm font-medium text-gray-900">Script</label>
|
||||
<input required type="hidden" id="script" name="script">
|
||||
<div id="editor" class="block w-full h-96 rounded-lg border border-gray-300 overflow-hidden"></div>
|
||||
</div>
|
||||
<button type="submit" onclick="save()" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center">Create</button>
|
||||
<section class="p-5">
|
||||
<form action="/settings/healthchecks/create" method="post">
|
||||
<label for="name">Name</label>
|
||||
<input type="name" name="name" id="name" placeholder="Github.com">
|
||||
<p> Name of the healthcheck can be anything.</p>
|
||||
<label for="workergroups">Worker Groups</label>
|
||||
<input type="text" name="workergroups" id="workergroups" placeholder="NA EU"/>
|
||||
<p> Worker groups are used to distribute the healthcheck to specific workers.</p>
|
||||
<label for="schedule">Schedule</label>
|
||||
<input type="text" name="schedule" id="schedule" placeholder="* * * * *"/>
|
||||
<p> Schedule is a cron expression that defines when the healthcheck should be executed.</p>
|
||||
<label for="script">Script</label>
|
||||
<input required type="hidden" id="script" name="script">
|
||||
<div id="editor" class="block w-full h-96 rounded-lg border border-gray-300 overflow-hidden"></div>
|
||||
<p>
|
||||
Script is what determines the status of a service.
|
||||
You can read more about it on <a target="_blank" href="https://k6.io/docs/using-k6/http-requests/" class="text-blue-700">k6 documentation</a>.
|
||||
</p>
|
||||
<button type="submit" onclick="save()" class="col-span-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center">Create</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -1,59 +1,63 @@
|
|||
{{define "settings"}}
|
||||
<section class="relative overflow-x-auto shadow-md sm:rounded-lg p-5 text-gray-500 bg-white">
|
||||
<section class="p-5">
|
||||
<form action="/settings/healthchecks/{{ .Healthcheck.Slug }}" method="post">
|
||||
<div class="grid md:grid-cols-2">
|
||||
<h1 class="text-2xl font-semibold text-gray-900">
|
||||
{{ .Healthcheck.Name }}
|
||||
</h1>
|
||||
<button type="submit" onclick="save()" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center inline-flex justify-self-end">Save</button>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="workergroups" class="block mb-2 text-sm font-medium text-gray-900">Worker Groups</label>
|
||||
<input type="text" name="workergroups" id="workergroups" value="{{ StringsJoin .Healthcheck.WorkerGroups " " }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="schedule" class="block mb-2 text-sm font-medium text-gray-900">Schedule</label>
|
||||
<input type="text" name="schedule" id="schedule" value="{{ .Healthcheck.Schedule }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="script" class="block mb-2 text-sm font-medium text-gray-900">Script</label>
|
||||
<input required type="hidden" id="script" name="script">
|
||||
<div id="editor" class="block w-full h-96 rounded-lg border border-gray-300 overflow-hidden"></div>
|
||||
</div>
|
||||
<h2>
|
||||
Configuration
|
||||
</h2>
|
||||
<button type="submit" onclick="save()" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center inline-flex justify-self-end">Save</button>
|
||||
<label for="workergroups">Worker Groups</label>
|
||||
<input type="text" name="workergroups" id="workergroups" value="{{ StringsJoin .Healthcheck.WorkerGroups " " }}"/>
|
||||
<p> Worker groups are used to distribute the healthcheck to specific workers.</p>
|
||||
<label for="schedule">Schedule</label>
|
||||
<input type="text" name="schedule" id="schedule" value="{{ .Healthcheck.Schedule }}"/>
|
||||
<p> Schedule is a cron expression that defines when the healthcheck should be executed.</p>
|
||||
<label for="script">Script</label>
|
||||
<input required type="hidden" id="script" name="script">
|
||||
<div id="editor" class="block w-full h-96 rounded-lg border border-gray-300 overflow-hidden"></div>
|
||||
<p>
|
||||
Script is what determines the status of a service.
|
||||
You can read more about it on <a target="_blank" href="https://k6.io/docs/using-k6/http-requests/" class="text-blue-700">k6 documentation</a>.
|
||||
</p>
|
||||
</form>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">History</h2>
|
||||
<table class="min-w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Created At</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Duration</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Healthcheck.History}}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{if eq .Status "SUCCESS"}}bg-green-100 text-green-800{{else}}bg-red-100 text-red-800{{end}}">
|
||||
{{ .Status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{{ .CreatedAt.Format "2006-01-02 15:04:05" }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{ .Duration }
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{{ .Note }}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<table class="min-w-full">
|
||||
<caption class="p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white">
|
||||
History
|
||||
<p class="mt-1 text-sm font-normal text-gray-500">
|
||||
Last executions of healthcheck script.
|
||||
</p>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Created At</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Duration</th>
|
||||
<th class="px-6 py-3 border-b-2 border-gray-300 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Healthcheck.History}}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{if eq .Status "SUCCESS"}}bg-green-100 text-green-800{{else}}bg-red-100 text-red-800{{end}}">
|
||||
{{ .Status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{{ .CreatedAt.Format "2006-01-02 15:04:05" }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{ .Duration }
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
{{ .Note }}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<script src="/static/monaco/vs/loader.js"></script>
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
{{define "settings"}}
|
||||
<section>
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
Hi there, {{.User.Email}}.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
Hi there, {{.User.Email}}.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -3,72 +3,70 @@
|
|||
{{ $description := "Workers are executing healthchecks. You can deploy multiple of thems to multiple regions for wider coverage." }}
|
||||
|
||||
{{ if eq .WorkersLength 0 }}
|
||||
<section>
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
There are no workers yet.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
|
||||
<a href="/settings/workers/create" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create First Worker
|
||||
<svg class="feather ml-1 h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16">
|
||||
<h1 class="mb-4 text-2xl font-extrabold tracking-tight leading-none text-gray-900 md:text-3xl lg:text-4xl">
|
||||
There are no workers yet.
|
||||
</h1>
|
||||
<p class="mb-8 text-l font-normal text-gray-500 lg:text-l sm:px-8 lg:px-40">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
|
||||
<a href="/settings/workers/create" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create First Worker
|
||||
<svg class="feather ml-1 h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{{ else }}
|
||||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
|
||||
<caption class="p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white">
|
||||
List of Workers
|
||||
<div class="mt-1 gap-4 flex justify-between">
|
||||
<p class="mt-1 text-sm font-normal text-gray-500">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<a href="/settings/workers/create" class="inline-flex justify-center items-center py-1 px-2 text-sm font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create New
|
||||
<svg class="feather h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</caption>
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Group
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{range .Workers}}
|
||||
<tbody>
|
||||
<tr class="odd:bg-white even:bg-gray-50">
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
{{.Name}}
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
{{.Group}}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
OK
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a href="/settings/workers/{{.Slug}}" class="font-medium text-blue-600 hover:underline">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
{{ else }}
|
||||
<section>
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
|
||||
<caption class="p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white">
|
||||
List of Workers
|
||||
<div class="mt-1 gap-4 flex justify-between">
|
||||
<p class="mt-1 text-sm font-normal text-gray-500">
|
||||
{{ $description }}
|
||||
</p>
|
||||
<a href="/settings/workers/create" class="inline-flex justify-center items-center py-1 px-2 text-sm font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300">
|
||||
Create New
|
||||
<svg class="feather h-5 w-5 overflow-visible"><use href="/static/icons/feather-sprite.svg#plus" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</caption>
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Name
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Group
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{range .Workers}}
|
||||
<tbody>
|
||||
<tr class="odd:bg-white even:bg-gray-50">
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
|
||||
{{.Name}}
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
{{.Group}}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
OK
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a href="/settings/workers/{{.Slug}}" class="font-medium text-blue-600 hover:underline">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{{end}}
|
||||
</table>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
{{define "settings"}}
|
||||
<section class="relative overflow-x-auto shadow-md sm:rounded-lg p-5 text-gray-500 bg-white">
|
||||
<h1 class="text-lg font-semibold text-gray-900">
|
||||
Creating new worker.
|
||||
</h1>
|
||||
<form class="max-w-sm mt-4" action="/settings/workers/create" method="post">
|
||||
<div class="mb-5">
|
||||
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="FooBar" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<label for="group" class="block mb-2 text-sm font-medium text-gray-900">Group</label>
|
||||
<input type="text" name="group" id="group" placeholder="Europe" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"/>
|
||||
</div>
|
||||
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center">Create</button>
|
||||
<section class="p-5">
|
||||
<form action="/settings/workers/create" method="post">
|
||||
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">Name</label>
|
||||
<input type="text" name="name" id="name" placeholder="aws-eu-central-1"/>
|
||||
<p>Worker name can be anything.</p>
|
||||
<label for="group" class="block mb-2 text-sm font-medium text-gray-900">Group</label>
|
||||
<input type="text" name="group" id="group" placeholder="EU"/>
|
||||
<p>
|
||||
Group is used to distinguish between different workers.
|
||||
For example, you can have a group for different regions,
|
||||
different datacenters or different environments.
|
||||
</p>
|
||||
<button type="submit" class="col-span-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center">Create</button>
|
||||
</form>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
51
web/templates/pages/settings_workers_describe.tmpl
Normal file
51
web/templates/pages/settings_workers_describe.tmpl
Normal file
|
@ -0,0 +1,51 @@
|
|||
{{define "settings"}}
|
||||
<section class="p-5">
|
||||
<form action="/settings/workers/{{ .Worker.Slug }}" method="post">
|
||||
<h2>
|
||||
Configuration
|
||||
</h2>
|
||||
<button type="submit" onclick="save()" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center inline-flex justify-self-end">Save</button>
|
||||
<label for="group">Group</label>
|
||||
<input type="text" name="group" id="group" value="{{ .Worker.Group }}"/>
|
||||
<p>
|
||||
Group is used to distinguish between different workers.
|
||||
For example, you can have a group for different regions,
|
||||
different datacenters or different environments.
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
<section class="p-5">
|
||||
<h2>
|
||||
Token
|
||||
<span>Use it as <code>WORKER_TOKEN</code> configuration option.</span>
|
||||
</h2>
|
||||
<div class="grid grid-cols-[auto_min-content] gap-2">
|
||||
<pre id="token" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg block w-full p-2.5 overflow-x-auto">{{ .Worker.Token }}</pre>
|
||||
<button id="copy-token" data-copy-to-clipboard-target="npm-install" class="col-span-1 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto p-2.5 text-center items-center inline-flex justify-center">
|
||||
<span id="default-message">Copy</span>
|
||||
<span id="success-message" class="hidden inline-flex items-center">
|
||||
<svg class="feather h-4 w-4 mr-1 overflow-visible"><use href="/static/icons/feather-sprite.svg#check"/></svg>
|
||||
Copied!
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const copyTokenButton = document.getElementById('copy-token');
|
||||
|
||||
copyTokenButton.addEventListener('click', function() {
|
||||
this.blur();
|
||||
const copyText = document.getElementById('token');
|
||||
navigator.clipboard.writeText(copyText.innerText);
|
||||
const defaultMessage = document.getElementById('default-message');
|
||||
const successMessage = document.getElementById('success-message');
|
||||
defaultMessage.classList.add('hidden');
|
||||
successMessage.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
defaultMessage.classList.remove('hidden');
|
||||
successMessage.classList.add('hidden');
|
||||
}, 1500);
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
|
@ -44,6 +44,7 @@ func NewTemplates() *Templates {
|
|||
"settings_overview.tmpl": loadSettings("pages/settings_overview.tmpl"),
|
||||
"settings_workers.tmpl": loadSettings("pages/settings_workers.tmpl"),
|
||||
"settings_workers_create.tmpl": loadSettings("pages/settings_workers_create.tmpl"),
|
||||
"settings_workers_describe.tmpl": loadSettings("pages/settings_workers_describe.tmpl"),
|
||||
"settings_healthchecks.tmpl": loadSettings("pages/settings_healthchecks.tmpl"),
|
||||
"settings_healthchecks_create.tmpl": loadSettings("pages/settings_healthchecks_create.tmpl"),
|
||||
"settings_healthchecks_describe.tmpl": loadSettings("pages/settings_healthchecks_describe.tmpl"),
|
||||
|
|
Loading…
Reference in a new issue