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