diff --git a/.env.example b/.env.example index 74a6744..3f08637 100644 --- a/.env.example +++ b/.env.example @@ -15,3 +15,4 @@ PAGESHIP_TOKEN_AUTHORITY=http://api.localtest.me:8001 PAGESHIP_CLEANUP_EXPIRED_CRONTAB=* * * * * # PAGESHIP_HOST_ID_SCHEME=suffix +# PAGESHIP_CUSTOM_DOMAIN_MESSAGE= diff --git a/cmd/controller/app/start.go b/cmd/controller/app/start.go index fe30599..eb26303 100644 --- a/cmd/controller/app/start.go +++ b/cmd/controller/app/start.go @@ -7,6 +7,7 @@ import ( "os" "time" + "github.com/carlmjohnson/versioninfo" "github.com/dustin/go-humanize" "github.com/oursky/pageship/internal/command" "github.com/oursky/pageship/internal/config" @@ -59,6 +60,8 @@ func init() { startCmd.PersistentFlags().String("token-authority", "pageship", "auth token authority") startCmd.PersistentFlags().String("token-signing-key", "", "auth token signing key") + startCmd.PersistentFlags().String("custom-domain-message", "", "message for custom domain users") + startCmd.PersistentFlags().String("cleanup-expired-crontab", "", "cleanup expired schedule") startCmd.PersistentFlags().Duration("keep-after-expired", time.Hour*24, "keep-after-expired") @@ -101,6 +104,8 @@ type StartControllerConfig struct { TokenAuthority string `mapstructure:"token-authority"` ReservedApps []string `mapstructure:"reserved-apps"` APIACLFile string `mapstructure:"api-acl" validate:"omitempty,filepath"` + + CustomDomainMessage string `mapstructure:"custom-domain-message"` } type StartCronConfig struct { @@ -171,13 +176,15 @@ func (s *setup) controller(domain string, conf StartControllerConfig, sitesConf } controllerConf := controller.Config{ - MaxDeploymentSize: int64(maxDeploymentSize), - StorageKeyPrefix: conf.StorageKeyPrefix, - HostIDScheme: sitesConf.HostIDScheme, - HostPattern: config.NewHostPattern(sitesConf.HostPattern), - ReservedApps: reservedApps, - TokenSigningKey: []byte(tokenSigningKey), - TokenAuthority: conf.TokenAuthority, + MaxDeploymentSize: int64(maxDeploymentSize), + StorageKeyPrefix: conf.StorageKeyPrefix, + HostIDScheme: sitesConf.HostIDScheme, + HostPattern: config.NewHostPattern(sitesConf.HostPattern), + ReservedApps: reservedApps, + TokenSigningKey: []byte(tokenSigningKey), + TokenAuthority: conf.TokenAuthority, + ServerVersion: versioninfo.Short(), + CustomDomainMessage: conf.CustomDomainMessage, } if conf.APIACLFile != "" { diff --git a/cmd/pageship/app/domains.go b/cmd/pageship/app/domains.go index 26f4269..07630aa 100644 --- a/cmd/pageship/app/domains.go +++ b/cmd/pageship/app/domains.go @@ -35,6 +35,11 @@ var domainsCmd = &cobra.Command{ return fmt.Errorf("app ID is not set") } + manifest, err := API().GetManifest(cmd.Context()) + if err != nil { + return fmt.Errorf("failed to get manifest: %w", err) + } + app, err := API().GetApp(cmd.Context(), appID) if err != nil { return fmt.Errorf("failed to get app: %w", err) @@ -93,6 +98,12 @@ var domainsCmd = &cobra.Command{ fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", domain.name, site, createdAt, status) } w.Flush() + + if manifest.CustomDomainMessage != "" { + os.Stdout.WriteString("\n") + Info(manifest.CustomDomainMessage) + } + return nil }, } diff --git a/internal/api/client.go b/internal/api/client.go index daa0742..91996a9 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -46,6 +46,29 @@ func (c *Client) attachToken(r *http.Request) error { return nil } +func (c *Client) GetManifest(ctx context.Context) (*APIManifest, error) { + endpoint, err := url.JoinPath(c.endpoint, "api", "v1", "manifest") + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + if err != nil { + return nil, err + } + if err := c.attachToken(req); err != nil { + return nil, err + } + + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return decodeJSONResponse[*APIManifest](resp) +} + func (c *Client) CreateApp(ctx context.Context, appID string) (*APIApp, error) { endpoint, err := url.JoinPath(c.endpoint, "api", "v1", "apps") if err != nil { diff --git a/internal/api/models.go b/internal/api/models.go index 94111d7..dd9998f 100644 --- a/internal/api/models.go +++ b/internal/api/models.go @@ -4,6 +4,11 @@ import ( "github.com/oursky/pageship/internal/models" ) +type APIManifest struct { + Version string `json:"version"` + CustomDomainMessage string `json:"customDomainMessage,omitempty"` +} + type APIApp struct { *models.App URL string `json:"url"` diff --git a/internal/handler/controller/config.go b/internal/handler/controller/config.go index 8c4b1af..cb0e0a1 100644 --- a/internal/handler/controller/config.go +++ b/internal/handler/controller/config.go @@ -14,4 +14,7 @@ type Config struct { TokenAuthority string TokenSigningKey []byte ACL *watch.File[config.ACL] + + ServerVersion string + CustomDomainMessage string } diff --git a/internal/handler/controller/controller.go b/internal/handler/controller/controller.go index cb84cba..4eeb852 100644 --- a/internal/handler/controller/controller.go +++ b/internal/handler/controller/controller.go @@ -100,6 +100,8 @@ func (c *Controller) Handler() http.Handler { }) }) + r.With(requireAuth).Get("/manifest", c.handleManifest) + r.With(requireAuth).Get("/auth/me", c.handleMe) r.Get("/auth/github-ssh", c.handleAuthGithubSSH) r.Post("/auth/github-oidc", c.handleAuthGithubOIDC) diff --git a/internal/handler/controller/manifest.go b/internal/handler/controller/manifest.go new file mode 100644 index 0000000..1362e8c --- /dev/null +++ b/internal/handler/controller/manifest.go @@ -0,0 +1,17 @@ +package controller + +import ( + "net/http" +) + +type apiManifest struct { + Version string `json:"version"` + CustomDomainMessage string `json:"customDomainMessage,omitempty"` +} + +func (c *Controller) handleManifest(w http.ResponseWriter, r *http.Request) { + writeResponse(w, &apiManifest{ + Version: c.Config.ServerVersion, + CustomDomainMessage: c.Config.CustomDomainMessage, + }, nil) +}