Start back end and skeleton front end
This commit is contained in:
commit
5f96a929d4
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module gitlab.codemonkeysoftware.net/b/henwen
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
crawshaw.io/sqlite v0.2.5
|
||||
gitlab.codemonkeysoftware.net/b/hatmill v0.0.5
|
||||
)
|
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
||||
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
|
||||
crawshaw.io/sqlite v0.2.5 h1:lUjWtEQJZApoL4V83cHsNeaGZkY5Ofmh8cGGBdZNxPU=
|
||||
crawshaw.io/sqlite v0.2.5/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
||||
github.com/leanovate/gopter v0.2.4/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
|
||||
gitlab.codemonkeysoftware.net/b/hatmill v0.0.5 h1:7L70G1Qmdm7bSwH58qWY5psXOwoqcvokLhc4BPSIjWo=
|
||||
gitlab.codemonkeysoftware.net/b/hatmill v0.0.5/go.mod h1:3jggrD9qkK8kXt0d3+Kwg3zti2PfPcociz7r3oxtjWU=
|
121
http/server.go
Normal file
121
http/server.go
Normal file
@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
h "gitlab.codemonkeysoftware.net/b/hatmill"
|
||||
a "gitlab.codemonkeysoftware.net/b/hatmill/attribute"
|
||||
e "gitlab.codemonkeysoftware.net/b/hatmill/element"
|
||||
_ "gitlab.codemonkeysoftware.net/b/henwen"
|
||||
)
|
||||
|
||||
const Addr = ":8080"
|
||||
const Title = "Henwen"
|
||||
|
||||
const (
|
||||
PathRoot = "/"
|
||||
PathCreate = "/create"
|
||||
PathDoCreate = "/create/do"
|
||||
)
|
||||
|
||||
func writePage(w io.Writer, contents h.Term) error {
|
||||
page := e.Html()(
|
||||
e.Head()(
|
||||
e.Title()(h.Text(Title)),
|
||||
),
|
||||
e.Body()(
|
||||
e.H1()(h.Text(Title)),
|
||||
e.Div()(contents),
|
||||
),
|
||||
)
|
||||
_, err := h.WriteDocument(w, page)
|
||||
return err
|
||||
}
|
||||
|
||||
func pageRoot() h.Term {
|
||||
return h.Terms{
|
||||
e.H2()(h.Text("Welcome!")),
|
||||
e.A(a.Href(PathCreate))(
|
||||
h.Text("Create event"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
fieldNameEarliest = "earliestDate"
|
||||
fieldNameLatest = "latestDate"
|
||||
fieldNameEventName = "eventName"
|
||||
)
|
||||
|
||||
func pageCreate() h.Term {
|
||||
return h.Terms{
|
||||
e.H2()(h.Text("Create an event")),
|
||||
e.Form(a.Action(PathDoCreate), a.Method("POST"))(
|
||||
e.Label(a.For(fieldNameEventName))(h.Text("Event name")),
|
||||
e.Input(a.Name(fieldNameEventName)),
|
||||
|
||||
e.Label(a.For(fieldNameEarliest))(h.Text("Earliest date")),
|
||||
e.Input(a.Name(fieldNameEarliest), a.Type("date")),
|
||||
|
||||
e.Label(a.For(fieldNameLatest))(h.Text("Latest date")),
|
||||
e.Input(a.Name(fieldNameLatest), a.Type("date")),
|
||||
|
||||
e.Input(a.Type("submit")),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func pageDoCreate(name string, earliest, latest time.Time) h.Term {
|
||||
return h.Terms{
|
||||
e.H2()(h.Text("Created event!")),
|
||||
|
||||
e.H3()(h.Text("Name")),
|
||||
h.Text(name),
|
||||
|
||||
e.H3()(h.Text("Earliest date")),
|
||||
h.Text(earliest.Format(time.ANSIC)),
|
||||
|
||||
e.H3()(h.Text("Latest date")),
|
||||
h.Text(latest.Format(time.ANSIC)),
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(PathRoot, func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = writePage(w, pageRoot())
|
||||
})
|
||||
mux.HandleFunc(PathCreate, func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = writePage(w, pageCreate())
|
||||
})
|
||||
mux.HandleFunc(PathDoCreate, func(w http.ResponseWriter, r *http.Request) {
|
||||
const dateFmt = "2006-01-02"
|
||||
earliest, err := time.Parse(dateFmt, r.FormValue(fieldNameEarliest))
|
||||
if err != nil {
|
||||
fmt.Fprint(w, "bad earliest date")
|
||||
return
|
||||
}
|
||||
latest, err := time.Parse(dateFmt, r.FormValue(fieldNameLatest))
|
||||
if err != nil {
|
||||
fmt.Fprint(w, "bad latest date")
|
||||
return
|
||||
}
|
||||
eventName := r.FormValue(fieldNameEventName)
|
||||
if eventName == "" {
|
||||
fmt.Fprint(w, "event name is required")
|
||||
return
|
||||
}
|
||||
|
||||
_ = writePage(w, pageDoCreate(eventName, earliest, latest))
|
||||
})
|
||||
|
||||
srv := http.Server{
|
||||
Addr: Addr,
|
||||
Handler: mux,
|
||||
}
|
||||
log.Println(srv.ListenAndServe())
|
||||
}
|
131
store.go
Normal file
131
store.go
Normal file
@ -0,0 +1,131 @@
|
||||
package henwen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"crawshaw.io/sqlite/sqlitex"
|
||||
)
|
||||
|
||||
type GenString func() (string, error)
|
||||
|
||||
type Store struct {
|
||||
pool *sqlitex.Pool
|
||||
genString GenString
|
||||
}
|
||||
|
||||
const InMemory = ":memory:"
|
||||
|
||||
func NewStore(filename string, genString GenString) (*Store, error) {
|
||||
var needCreate bool
|
||||
if filename == InMemory {
|
||||
filename = "file::memory:?mode=memory"
|
||||
needCreate = true
|
||||
} else if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
needCreate = true
|
||||
}
|
||||
// If the file exists, then assume it was created properly.
|
||||
|
||||
pool, err := sqlitex.Open(filename, 0, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store := &Store{
|
||||
pool: pool,
|
||||
genString: genString,
|
||||
}
|
||||
if needCreate {
|
||||
err = store.createSchema()
|
||||
if err != nil {
|
||||
defer pool.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func (s *Store) Close() error {
|
||||
return s.pool.Close()
|
||||
}
|
||||
|
||||
const schema = `
|
||||
CREATE TABLE event (
|
||||
id INTEGER PRIMARY KEY,
|
||||
alpha_id TEXT NOT NULL,
|
||||
admin_alpha_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
earliest_date DATE NOT NULL,
|
||||
latest_date DATE NOT NULL,
|
||||
duration INTEGER NOT NULL,
|
||||
|
||||
UNIQUE (alpha_id)
|
||||
);
|
||||
|
||||
CREATE TABLE response (
|
||||
id INTEGER PRIMARY KEY,
|
||||
alpha_id TEXT NOT NULL,
|
||||
event_id INTEGER NOT NULL,
|
||||
|
||||
UNIQUE (alpha_id),
|
||||
FOREIGN KEY (event_id) REFERENCES event(id)
|
||||
);
|
||||
|
||||
CREATE TABLE response_time (
|
||||
response_id INTEGER PRIMARY KEY,
|
||||
time INTEGER NOT NULL,
|
||||
|
||||
CHECK (0 <= time < 24)
|
||||
);`
|
||||
|
||||
func (s *Store) createSchema() error {
|
||||
conn := s.pool.Get(context.Background())
|
||||
defer s.pool.Put(conn)
|
||||
return sqlitex.ExecScript(conn, schema)
|
||||
}
|
||||
|
||||
type CreateEventCommand struct {
|
||||
Name string
|
||||
EarliestDate, LatestDate time.Time
|
||||
Duration int
|
||||
}
|
||||
|
||||
type CreateEventResult struct {
|
||||
AlphaID, AdminAlphaID string
|
||||
}
|
||||
|
||||
func (s *Store) CreateEvent(ctx context.Context, cmd CreateEventCommand) (result CreateEventResult, err error) {
|
||||
conn := s.pool.Get(ctx)
|
||||
defer s.pool.Put(conn)
|
||||
|
||||
alphaID, err := s.genString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
adminAlphaID, err := s.genString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
const dateFmt = "2006-01-02"
|
||||
const query = `
|
||||
INSERT INTO event(alpha_id, admin_alpha_id, name, earliest_date, latest_date, duration)
|
||||
VALUES (?, ?, ?, ?, ?, ?);`
|
||||
err = sqlitex.Exec(conn, query, nil,
|
||||
alphaID,
|
||||
adminAlphaID,
|
||||
cmd.Name,
|
||||
cmd.EarliestDate.Format(dateFmt),
|
||||
cmd.LatestDate.Format(dateFmt),
|
||||
cmd.Duration,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.AdminAlphaID = adminAlphaID
|
||||
result.AlphaID = alphaID
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue
Block a user