start scaffolding
This commit is contained in:
parent
6e5af42d93
commit
d6dc30bea2
3
Makefile
Normal file
3
Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
furthur: $(shell find . -type f -name "*.go")
|
||||
go build ./cmd/furthur
|
||||
|
18
api/auth.go
Normal file
18
api/auth.go
Normal file
@ -0,0 +1,18 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func getLoginHandleFunc(sessions SessionProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// login html page
|
||||
}
|
||||
}
|
||||
|
||||
func postLoginHandleFunc(sessions SessionProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// login html page
|
||||
}
|
||||
}
|
38
api/common.go
Normal file
38
api/common.go
Normal file
@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func versionHandleFunc(log *slog.Logger, version string) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Debug("version handler")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(version))
|
||||
}
|
||||
}
|
||||
|
||||
func staticHandleFunc() func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func sessionMiddleware(sessions SessionProvider, log *slog.Logger) func(http.HandlerFunc) http.HandlerFunc {
|
||||
return func(h http.HandlerFunc) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := r.Cookie("FURTHUR_SESS")
|
||||
if err != nil {
|
||||
log.Warn("error loading session")
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if !sessions.Valid(c.Value) {
|
||||
log.Warn("user provided invalid session")
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
h(w, r)
|
||||
})
|
||||
}
|
||||
}
|
10
api/link.go
Normal file
10
api/link.go
Normal file
@ -0,0 +1,10 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func linkHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
50
api/manage.go
Normal file
50
api/manage.go
Normal file
@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func getManageHandleFunc(log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func getLinkListHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func getLinkHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func postLinkHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func putLinkHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func deleteLinkHandleFunc(links LinkProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func getUserListHandleFunc(users UserProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func getUserHandleFunc(sers UserProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func postUserHandleFunc(sers UserProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func putUserHandleFunc(sers UserProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
|
||||
func deleteUserHandleFunc(sers UserProvider, log *slog.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
117
api/server.go
Normal file
117
api/server.go
Normal file
@ -0,0 +1,117 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.yetaga.in/alazyreader/going-further/storage"
|
||||
)
|
||||
|
||||
type LinkProvider interface {
|
||||
Link(key string) *url.URL
|
||||
List() []string
|
||||
Details(key string) storage.Link
|
||||
}
|
||||
|
||||
type SessionProvider interface {
|
||||
Login(username string, password string) (string, bool) // session token, valid
|
||||
Valid(token string) bool
|
||||
User(token string) string // token -> username
|
||||
}
|
||||
|
||||
type UserProvider interface {
|
||||
List() []string // usernames
|
||||
Add(username string, password string) error
|
||||
Update(username string, password string) error
|
||||
Remove(username string) error
|
||||
}
|
||||
|
||||
type shutdown struct {
|
||||
wait int
|
||||
timeout int
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
host string
|
||||
port string
|
||||
version string
|
||||
|
||||
users UserProvider
|
||||
sessions SessionProvider
|
||||
links LinkProvider
|
||||
|
||||
http *http.Server
|
||||
waits shutdown
|
||||
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewServer(logger *slog.Logger, port string, version string) *Server {
|
||||
return &Server{
|
||||
log: logger,
|
||||
port: port,
|
||||
version: version,
|
||||
waits: shutdown{
|
||||
wait: 10,
|
||||
timeout: 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Setup returns start and stop functions for the http service
|
||||
func (s *Server) Setup() (func(), func()) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /version", versionHandleFunc(s.log, s.version))
|
||||
|
||||
mux.HandleFunc("GET /login", getLoginHandleFunc(s.sessions, s.log))
|
||||
mux.HandleFunc("POST /login", postLoginHandleFunc(s.sessions, s.log))
|
||||
|
||||
mux.HandleFunc("GET /manage", getManageHandleFunc(s.log))
|
||||
|
||||
mux.HandleFunc("GET /manage/links", getLinkListHandleFunc(s.links, s.log))
|
||||
mux.HandleFunc("GET /manage/links/{name}", getLinkHandleFunc(s.links, s.log))
|
||||
mux.HandleFunc("POST /manage/links", postLinkHandleFunc(s.links, s.log))
|
||||
mux.HandleFunc("PUT /manage/links/{name}", putLinkHandleFunc(s.links, s.log))
|
||||
mux.HandleFunc("DELETE /manage/links/{name}", deleteLinkHandleFunc(s.links, s.log))
|
||||
|
||||
mux.HandleFunc("GET /manage/users", getUserListHandleFunc(s.users, s.log))
|
||||
mux.HandleFunc("GET /manage/users/{name}", getUserHandleFunc(s.users, s.log))
|
||||
mux.HandleFunc("POST /manage/users", postUserHandleFunc(s.users, s.log))
|
||||
mux.HandleFunc("PUT /manage/users/{name}", putUserHandleFunc(s.users, s.log))
|
||||
mux.HandleFunc("DELETE /manage/users/{name}", deleteUserHandleFunc(s.users, s.log))
|
||||
|
||||
mux.HandleFunc("GET /static/", staticHandleFunc())
|
||||
|
||||
mux.HandleFunc("GET /", linkHandleFunc(s.links, s.log))
|
||||
|
||||
s.http = &http.Server{
|
||||
Addr: net.JoinHostPort(s.host, s.port),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
return s.start(), s.stop()
|
||||
}
|
||||
|
||||
func (s *Server) start() func() {
|
||||
return func() {
|
||||
s.log.Info("server startup", slog.String("addr", s.http.Addr))
|
||||
if err := s.http.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
s.log.Error("error listening and serving", slog.String("error", err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *Server) stop() func() {
|
||||
return func() {
|
||||
s.log.Info("server shutdown")
|
||||
time.Sleep(time.Duration(s.waits.wait) * time.Second)
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Duration(s.waits.timeout)*time.Second)
|
||||
defer cancel()
|
||||
if err := s.http.Shutdown(shutdownCtx); err != nil {
|
||||
s.log.Error("error shutting down http server", slog.String("error", err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,26 @@
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"git.yetaga.in/alazyreader/going-further/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)).
|
||||
With(slog.String("application", "furthur"))
|
||||
slog.SetDefault(logger)
|
||||
|
||||
server := api.NewServer(logger, "8080", "v0.0.1")
|
||||
start, stop := server.Setup()
|
||||
|
||||
start()
|
||||
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
<-c
|
||||
|
||||
stop()
|
||||
}
|
||||
|
@ -65,11 +65,14 @@ It turns out channels are hard to reason about, but that's because _concurrency_
|
||||
hard to reason about.
|
||||
|
||||
Always remember that read/write access to a shared map _must_ be gated with a mutex.
|
||||
Concurrent read-only access is safe, however.
|
||||
|
||||
## Go Generate
|
||||
|
||||
## Build tags
|
||||
|
||||
## Templates
|
||||
|
||||
## Logging
|
||||
|
||||
`slog` package
|
||||
@ -77,7 +80,7 @@ Always remember that read/write access to a shared map _must_ be gated with a mu
|
||||
## init functions and globals
|
||||
|
||||
Don't use them! They're hard to reason about and until recent versions of go
|
||||
the order they ran in was undefined, leading to subtle bugs.
|
||||
the order they ran in was under-defined, leading to subtle bugs.
|
||||
|
||||
## Common tools
|
||||
|
||||
|
1
storage/file.go
Normal file
1
storage/file.go
Normal file
@ -0,0 +1 @@
|
||||
package storage
|
1
storage/memory.go
Normal file
1
storage/memory.go
Normal file
@ -0,0 +1 @@
|
||||
package storage
|
4
storage/struct.go
Normal file
4
storage/struct.go
Normal file
@ -0,0 +1,4 @@
|
||||
package storage
|
||||
|
||||
type Link struct {
|
||||
}
|
Loading…
Reference in New Issue
Block a user