Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code refactor for Issue #40 #43

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ARG TZ=America/Chicago

FROM umputun/baseimage:buildgo-latest as build-backend
FROM umputun/baseimage:buildgo-latest AS build-backend

ARG CI
ARG GIT_BRANCH
Expand Down
23 changes: 10 additions & 13 deletions app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,23 @@ func main() {

setupLog(opts.Dbg)

templateCache, err := server.NewTemplateCache()
if err != nil {
log.Printf("[ERROR] can't create template cache, %+v", err)
os.Exit(1)
}

dataStore := getEngine(opts.Engine, opts.BoltDB)
crypter := messager.Crypt{Key: messager.MakeSignKey(opts.SignKey, opts.PinSize)}
params := messager.Params{MaxDuration: opts.MaxExpire, MaxPinAttempts: opts.MaxPinAttempts}
srv := server.Server{
Messager: messager.New(dataStore, crypter, params),

srv, err := server.New(messager.New(dataStore, crypter, params), revision, server.Config{
Domain: opts.Domain,
PinSize: opts.PinSize,
MaxExpire: opts.MaxExpire,
MaxPinAttempts: opts.MaxPinAttempts,
MaxExpire: opts.MaxExpire,
WebRoot: opts.WebRoot,
Version: revision,
Domain: opts.Domain,
TemplateCache: templateCache,
})

if err != nil {
log.Fatalf("[ERROR] can't create server, %v", err)
}
if err := srv.Run(context.Background()); err != nil {

if err = srv.Run(context.Background()); err != nil {
log.Printf("[ERROR] failed, %+v", err)
}
}
Expand Down
52 changes: 36 additions & 16 deletions app/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,36 @@ import (
"github.com/umputun/secrets/app/store"
)

// Server is a rest with store
type Server struct {
Messager Messager
// Config is a configuration for the server
type Config struct {
Domain string
WebRoot string
// Validation parameters
PinSize int
MaxPinAttempts int
MaxExpire time.Duration
WebRoot string
Version string
Domain string
TemplateCache map[string]*template.Template
}

// Server is a rest with store
type Server struct {
messager Messager
cfg Config
version string
templateCache map[string]*template.Template
}

// New creates a new server with template cache
func New(m Messager, version string, cfg Config) (Server, error) {
cache, err := newTemplateCache()
if err != nil {
return Server{}, errors.Wrap(err, "can't create template cache")
}
return Server{
messager: m,
cfg: cfg,
version: version,
templateCache: cache,
}, nil
}

// Messager interface making and loading messages
Expand Down Expand Up @@ -75,7 +95,7 @@ func (s Server) routes() chi.Router {

router.Use(middleware.RequestID, middleware.RealIP, um.Recoverer(log.Default()))
router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
router.Use(um.AppInfo("secrets", "Umputun", s.Version), um.Ping, um.SizeLimit(64*1024))
router.Use(um.AppInfo("secrets", "Umputun", s.version), um.Ping, um.SizeLimit(64*1024))
router.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil)))

router.Route("/api/v1", func(r chi.Router) {
Expand Down Expand Up @@ -109,7 +129,7 @@ func (s Server) routes() chi.Router {
r.Get("/", s.indexCtrl)
})

fs, err := um.NewFileServer("/static", s.WebRoot)
fs, err := um.NewFileServer("/static", s.cfg.WebRoot)
if err != nil {
log.Fatalf("[ERROR] can't create file server %v", err)
}
Expand All @@ -133,14 +153,14 @@ func (s Server) saveMessageCtrl(w http.ResponseWriter, r *http.Request) {
return
}

if len(request.Pin) != s.PinSize {
if len(request.Pin) != s.cfg.PinSize {
log.Printf("[WARN] incorrect pin size %d", len(request.Pin))
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, JSON{"error": "Incorrect pin size"})
return
}

msg, err := s.Messager.MakeMessage(time.Second*time.Duration(request.Exp), request.Message, request.Pin)
msg, err := s.messager.MakeMessage(time.Second*time.Duration(request.Exp), request.Message, request.Pin)
if err != nil {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, JSON{"error": err.Error()})
Expand All @@ -154,15 +174,15 @@ func (s Server) saveMessageCtrl(w http.ResponseWriter, r *http.Request) {
func (s Server) getMessageCtrl(w http.ResponseWriter, r *http.Request) {

key, pin := chi.URLParam(r, "key"), chi.URLParam(r, "pin")
if key == "" || pin == "" || len(pin) != s.PinSize {
if key == "" || pin == "" || len(pin) != s.cfg.PinSize {
log.Print("[WARN] no valid key or pin in get request")
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, JSON{"error": "no key or pin passed"})
return
}

serveRequest := func() (status int, res JSON) {
msg, err := s.Messager.LoadMessage(key, pin)
msg, err := s.messager.LoadMessage(key, pin)
if err != nil {
log.Printf("[WARN] failed to load key %v", key)
if err == messager.ErrBadPinAttempt {
Expand All @@ -188,9 +208,9 @@ func (s Server) getParamsCtrl(w http.ResponseWriter, r *http.Request) {
MaxPinAttempts int `json:"max_pin_attempts"`
MaxExpSecs int `json:"max_exp_sec"`
}{
PinSize: s.PinSize,
MaxPinAttempts: s.MaxPinAttempts,
MaxExpSecs: int(s.MaxExpire.Seconds()),
PinSize: s.cfg.PinSize,
MaxPinAttempts: s.cfg.MaxPinAttempts,
MaxExpSecs: int(s.cfg.MaxExpire.Seconds()),
}
render.JSON(w, r, params)
}
47 changes: 28 additions & 19 deletions app/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

func TestServer_saveAndLoadMemory(t *testing.T) {
ts, teardown := prepTestServer()
ts, teardown := prepTestServer(t)
defer teardown()

// save message
Expand Down Expand Up @@ -74,16 +74,20 @@ func TestServer_saveAndLoadBolt(t *testing.T) {
require.NoError(t, os.Remove("/tmp/secrets-test.bdb"))
}()
signKey := messager.MakeSignKey("stew-pub-barcan-scatty-daimio-wicker-yakona", 5)
srv := Server{
Messager: messager.New(eng, messager.Crypt{Key: signKey}, messager.Params{
srv, err := New(
messager.New(eng, messager.Crypt{Key: signKey}, messager.Params{
MaxDuration: 10 * time.Hour,
MaxPinAttempts: 3,
}),
PinSize: 5,
MaxPinAttempts: 3,
MaxExpire: 10 * time.Hour,
Version: "1",
}
"1",
Config{
PinSize: 5,
MaxPinAttempts: 3,
MaxExpire: 10 * time.Hour,
})

assert.NoError(t, err)

ts := httptest.NewServer(srv.routes())
defer ts.Close()

Expand Down Expand Up @@ -133,7 +137,7 @@ func TestServer_saveAndLoadBolt(t *testing.T) {
}

func TestServer_saveAndManyPinAttempt(t *testing.T) {
ts, teardown := prepTestServer()
ts, teardown := prepTestServer(t)
defer teardown()

// save message
Expand Down Expand Up @@ -183,7 +187,7 @@ func TestServer_saveAndManyPinAttempt(t *testing.T) {
}

func TestServer_saveAndGoodPinAttempt(t *testing.T) {
ts, teardown := prepTestServer()
ts, teardown := prepTestServer(t)
defer teardown()

// save message
Expand Down Expand Up @@ -228,7 +232,7 @@ func TestServer_saveAndGoodPinAttempt(t *testing.T) {
}

func TestServer_getParams(t *testing.T) {
ts, teardown := prepTestServer()
ts, teardown := prepTestServer(t)
defer teardown()

client := http.Client{Timeout: time.Second}
Expand All @@ -244,18 +248,23 @@ func TestServer_getParams(t *testing.T) {
assert.Equal(t, `{"pin_size":5,"max_pin_attempts":3,"max_exp_sec":36000}`+"\n", string(body))
}

func prepTestServer() (ts *httptest.Server, teardown func()) {
func prepTestServer(t *testing.T) (ts *httptest.Server, teardown func()) {
eng := store.NewInMemory(time.Second)
srv := Server{
Messager: messager.New(eng, messager.Crypt{Key: "123456789012345678901234567"}, messager.Params{

srv, err := New(
messager.New(eng, messager.Crypt{Key: "123456789012345678901234567"}, messager.Params{
MaxDuration: 10 * time.Hour,
MaxPinAttempts: 3,
}),
PinSize: 5,
MaxPinAttempts: 3,
MaxExpire: 10 * time.Hour,
Version: "1",
}
"1",
Config{
PinSize: 5,
MaxPinAttempts: 3,
MaxExpire: 10 * time.Hour,
})

assert.NoError(t, err)

ts = httptest.NewServer(srv.routes())
return ts, ts.Close
}
32 changes: 16 additions & 16 deletions app/server/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type templateData struct {

// render renders a template
func (s Server) render(w http.ResponseWriter, status int, page, tmplName string, data any) {
ts, ok := s.TemplateCache[page]
ts, ok := s.templateCache[page]
if !ok {
err := fmt.Errorf("the template %s does not exist", page)
log.Printf("[ERROR] %v", err)
Expand Down Expand Up @@ -89,9 +89,9 @@ func (s Server) indexCtrl(w http.ResponseWriter, r *http.Request) { // nolint
data := templateData{
Form: createMsgForm{
Exp: 15,
MaxExp: humanDuration(s.MaxExpire),
MaxExp: humanDuration(s.cfg.MaxExpire),
},
PinSize: s.PinSize,
PinSize: s.cfg.PinSize,
}

s.render(w, http.StatusOK, "home.tmpl.html", baseTmpl, data)
Expand All @@ -113,13 +113,13 @@ func (s Server) generateLinkCtrl(w http.ResponseWriter, r *http.Request) {
form := createMsgForm{
Message: r.PostForm.Get(msgKey),
ExpUnit: r.PostForm.Get(expUnitKey),
MaxExp: humanDuration(s.MaxExpire),
MaxExp: humanDuration(s.cfg.MaxExpire),
}

pinValues := r.Form["pin"]
for _, p := range pinValues {
if validator.Blank(p) || !validator.IsNumber(p) {
form.AddFieldError(pinKey, fmt.Sprintf("Pin must be %d digits long without empty values", s.PinSize))
form.AddFieldError(pinKey, fmt.Sprintf("Pin must be %d digits long without empty values", s.cfg.PinSize))
break
}
}
Expand All @@ -138,12 +138,12 @@ func (s Server) generateLinkCtrl(w http.ResponseWriter, r *http.Request) {
form.Exp = expInt
expDuration := duration(expInt, r.PostFormValue(expUnitKey))

form.CheckField(validator.MaxDuration(expDuration, s.MaxExpire), expKey, fmt.Sprintf("Expire must be less than %s", humanDuration(s.MaxExpire)))
form.CheckField(validator.MaxDuration(expDuration, s.cfg.MaxExpire), expKey, fmt.Sprintf("Expire must be less than %s", humanDuration(s.cfg.MaxExpire)))

if !form.Valid() {
data := templateData{
Form: form,
PinSize: s.PinSize,
PinSize: s.cfg.PinSize,
}

// attach event listeners to pin inputs
Expand All @@ -153,13 +153,13 @@ func (s Server) generateLinkCtrl(w http.ResponseWriter, r *http.Request) {
return
}

msg, err := s.Messager.MakeMessage(expDuration, form.Message, strings.Join(pinValues, ""))
msg, err := s.messager.MakeMessage(expDuration, form.Message, strings.Join(pinValues, ""))
if err != nil {
s.render(w, http.StatusOK, "secure-link.tmpl.html", errorTmpl, err.Error())
return
}

msgURL := fmt.Sprintf("http://%s/message/%s", s.Domain, msg.Key)
msgURL := fmt.Sprintf("http://%s/message/%s", s.cfg.Domain, msg.Key)

s.render(w, http.StatusOK, "secure-link.tmpl.html", "secure-link", msgURL)
}
Expand All @@ -175,7 +175,7 @@ func (s Server) showMessageViewCtrl(w http.ResponseWriter, r *http.Request) {
Form: showMsgForm{
Key: key,
},
PinSize: s.PinSize,
PinSize: s.cfg.PinSize,
}

w.Header().Add("HX-Trigger-After-Swap", "setUpPinInputListeners")
Expand Down Expand Up @@ -208,15 +208,15 @@ func (s Server) loadMessageCtrl(w http.ResponseWriter, r *http.Request) {
pinValues := r.Form["pin"]
for _, p := range pinValues {
if validator.Blank(p) || !validator.IsNumber(p) {
form.AddFieldError(pinKey, fmt.Sprintf("Pin must be %d digits long without empty values", s.PinSize))
form.AddFieldError(pinKey, fmt.Sprintf("Pin must be %d digits long without empty values", s.cfg.PinSize))
break
}
}

if !form.Valid() {
data := templateData{
Form: form,
PinSize: s.PinSize,
PinSize: s.cfg.PinSize,
}

// attach event listeners to pin inputs
Expand All @@ -226,7 +226,7 @@ func (s Server) loadMessageCtrl(w http.ResponseWriter, r *http.Request) {
return
}

msg, err := s.Messager.LoadMessage(form.Key, strings.Join(pinValues, ""))
msg, err := s.messager.LoadMessage(form.Key, strings.Join(pinValues, ""))
if err != nil {
if errors.Is(err, messager.ErrExpired) || errors.Is(err, store.ErrLoadRejected) {
s.render(w, http.StatusOK, "error.tmpl.html", errorTmpl, err.Error())
Expand All @@ -237,7 +237,7 @@ func (s Server) loadMessageCtrl(w http.ResponseWriter, r *http.Request) {

data := templateData{
Form: form,
PinSize: s.PinSize,
PinSize: s.cfg.PinSize,
}
// attach event listeners to pin inputs
w.Header().Add("HX-Trigger-After-Swap", "setUpPinInputListeners")
Expand Down Expand Up @@ -278,8 +278,8 @@ func humanDuration(d time.Duration) string {
}
}

// NewTemplateCache creates a template cache as a map
func NewTemplateCache() (map[string]*template.Template, error) {
// newTemplateCache creates a template cache as a map
func newTemplateCache() (map[string]*template.Template, error) {
cache := map[string]*template.Template{}

pages, err := fs.Glob(ui.Files, "html/*/*.tmpl.html")
Expand Down
2 changes: 1 addition & 1 deletion app/server/web_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func TestTemplates_HumanDuration(t *testing.T) {
}

func TestTemplates_NewTemplateCache(t *testing.T) {
cache, err := NewTemplateCache()
cache, err := newTemplateCache()

assert.NoError(t, err)

Expand Down
1 change: 0 additions & 1 deletion docker-compose-dev.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '2'
services:

secrets:
Expand Down
Loading