blob: e327f9e4f60e31de4db4f517b5d0cb8f5767bd73 [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.16
// +build go1.16
package relui
import (
// fileServerHandler returns a http.Handler rooted at root. It will
// call the next handler provided for requests to "/".
// The returned handler sets the appropriate Content-Type and
// Cache-Control headers for the returned file.
func fileServerHandler(fs fs.FS, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
next.ServeHTTP(w, r)
w.Header().Set("Content-Type", mime.TypeByExtension(path.Ext(r.URL.Path)))
w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
s := http.FileServer(http.FS(fs))
s.ServeHTTP(w, r)
var (
homeTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFS(templates, "templates/home.html"))
layoutTmpl = template.Must(template.ParseFS(templates, "templates/layout.html"))
newWorkflowTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFS(templates, "templates/new_workflow.html"))
// Server implements the http handlers for relui.
type Server struct {
// store is for persisting application state.
store store
// NewServer initializes a server with a database connection specified
// by pgConn. Any libpq compatible connection strings or URIs are
// supported.
func NewServer(ctx context.Context, pgConn string) (*Server, error) {
d := new(PgStore)
if err := d.Connect(ctx, pgConn); err != nil {
return nil, err
defer d.Close()
s := &Server{
store: d,
http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
http.Handle("/", fileServerHandler(static, http.HandlerFunc(s.homeHandler)))
return s, nil
func (s *Server) Serve(port string) error {
return http.ListenAndServe(":"+port, http.DefaultServeMux)
type homeResponse struct {
Workflows []interface{}
// homeHandler renders the homepage.
func (s *Server) homeHandler(w http.ResponseWriter, _ *http.Request) {
out := bytes.Buffer{}
if err := homeTmpl.Execute(&out, homeResponse{}); err != nil {
log.Printf("homeHandler: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
io.Copy(w, &out)
// newWorkflowHandler presents a form for creating a new workflow.
func (s *Server) newWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
out := bytes.Buffer{}
if err := newWorkflowTmpl.Execute(&out, nil); err != nil {
log.Printf("newWorkflowHandler: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
io.Copy(w, &out)
// createWorkflowHandler persists a new workflow in the datastore.
func (s *Server) createWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
http.Error(w, "Unable to create workflow: no workflows configured", http.StatusInternalServerError)