mirror of
https://git.swurl.xyz/swirl/link.git
synced 2023-11-08 10:44:52 +02:00
Feat(*): Retry for error case, Go 1.17 beta compiler.
This commit is contained in:
parent
e39a843f18
commit
15d3d2837f
2 changed files with 51 additions and 26 deletions
67
main.go
67
main.go
|
@ -40,13 +40,29 @@ import (
|
||||||
//go:embed index.html
|
//go:embed index.html
|
||||||
var indexTemplate string
|
var indexTemplate string
|
||||||
|
|
||||||
|
type Retry struct {
|
||||||
|
retryAttemptCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Retry) Do(f func() error) (err error) {
|
||||||
|
for i := 0; i < r.retryAttemptCount; i++ {
|
||||||
|
err = f()
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
*gorm.DB
|
*gorm.DB
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
hashSeed string
|
hashSeed string
|
||||||
|
retry Retry
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDB(l *log.Logger, dbFilePath, hashSeed string) (DB, error) {
|
func NewDB(l *log.Logger, dbFilePath, hashSeed string, retry Retry) (DB, error) {
|
||||||
_, err := os.Stat(dbFilePath)
|
_, err := os.Stat(dbFilePath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
err := ioutil.WriteFile(dbFilePath, []byte{}, 0600)
|
err := ioutil.WriteFile(dbFilePath, []byte{}, 0600)
|
||||||
|
@ -61,7 +77,7 @@ func NewDB(l *log.Logger, dbFilePath, hashSeed string) (DB, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DB{}, err
|
return DB{}, err
|
||||||
}
|
}
|
||||||
return DB{db, l, hashSeed}, db.AutoMigrate(&Link{})
|
return DB{db, l, hashSeed, retry}, db.AutoMigrate(&Link{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Link struct {
|
type Link struct {
|
||||||
|
@ -71,10 +87,15 @@ type Link struct {
|
||||||
Del string `gorm:"unique"`
|
Del string `gorm:"unique"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db DB) getHashShortLink(s fmt.Stringer) string {
|
func (db DB) getHashShortLink(s fmt.Stringer) (string, error) {
|
||||||
h := maphash.Hash{}
|
var (
|
||||||
h.WriteString(s.String())
|
h = maphash.Hash{}
|
||||||
return strings.TrimSpace(strings.TrimLeft(fmt.Sprintf("%#x\n", h.Sum64()), "0x"))
|
_, err = h.WriteString(s.String())
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(strings.TrimLeft(fmt.Sprintf("%#x\n", h.Sum64()), "0x")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db DB) getHashDeleteKey(s fmt.Stringer) string {
|
func (db DB) getHashDeleteKey(s fmt.Stringer) string {
|
||||||
|
@ -82,20 +103,20 @@ func (db DB) getHashDeleteKey(s fmt.Stringer) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db DB) NewLink(u *url.URL) (Link, error) {
|
func (db DB) NewLink(u *url.URL) (Link, error) {
|
||||||
return db.NewLinkWithShortLink(u, db.getHashShortLink(u))
|
h, err := db.getHashShortLink(u)
|
||||||
}
|
|
||||||
|
|
||||||
func (db DB) NewLinkWithShortLink(u *url.URL, hash string) (Link, error) {
|
|
||||||
var (
|
|
||||||
link = Link{Big: u.String(), Smol: hash, Del: db.getHashDeleteKey(u)}
|
|
||||||
err = db.Create(&link).Error
|
|
||||||
)
|
|
||||||
// TODO: If error due to unique issue should attempt to retry a few times.
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.log.Println(err)
|
|
||||||
return Link{}, err
|
return Link{}, err
|
||||||
}
|
}
|
||||||
return link, nil
|
return db.NewLinkWithShortLink(u, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db DB) NewLinkWithShortLink(u *url.URL, hash string) (link Link, err error) {
|
||||||
|
// Retry for unique errors.
|
||||||
|
err = db.retry.Do(func() error {
|
||||||
|
link = Link{Big: u.String(), Smol: hash, Del: db.getHashDeleteKey(u)}
|
||||||
|
return db.Create(&link).Error
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db DB) GetLink(smol string) (l Link, e error) {
|
func (db DB) GetLink(smol string) (l Link, e error) {
|
||||||
|
@ -130,12 +151,12 @@ func NewController(logger *log.Logger, db DB, demo bool, url, copy string, tmpl
|
||||||
func (c controller) Err(rw http.ResponseWriter, r *http.Request, err error) {
|
func (c controller) Err(rw http.ResponseWriter, r *http.Request, err error) {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
rw.WriteHeader(http.StatusNotFound)
|
rw.WriteHeader(http.StatusNotFound)
|
||||||
rw.Write([]byte(err.Error()))
|
fmt.Fprintf(rw, "%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.log.Println(err)
|
c.log.Println(err)
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
rw.Write([]byte(err.Error()))
|
fmt.Fprintf(rw, "%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -184,7 +205,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if u.Scheme != "http" && u.Scheme != "https" {
|
if u.Scheme != "http" && u.Scheme != "https" {
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
rw.Write([]byte("URL must contain scheme. E.G. missing `http://` or `https://`."))
|
fmt.Fprintf(rw, "URL must contain scheme. E.G. missing `http://` or `https://`.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
@ -203,7 +224,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
rw.Header().Set("X-Delete-With", link.Del)
|
rw.Header().Set("X-Delete-With", link.Del)
|
||||||
rw.WriteHeader(http.StatusFound)
|
rw.WriteHeader(http.StatusFound)
|
||||||
rw.Write([]byte(fmt.Sprintf("%s/%s", c.url, link.Smol)))
|
fmt.Fprintf(rw, "%s/%s", c.url, link.Smol)
|
||||||
return
|
return
|
||||||
|
|
||||||
case http.MethodDelete:
|
case http.MethodDelete:
|
||||||
|
@ -214,7 +235,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
if len(b) < 1 {
|
if len(b) < 1 {
|
||||||
rw.WriteHeader(http.StatusBadRequest)
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
rw.Write([]byte("Must include deletion key in DELETE body."))
|
fmt.Fprintf(rw, "Must include deletion key in DELETE body.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
@ -254,7 +275,7 @@ func main() {
|
||||||
if *v {
|
if *v {
|
||||||
applicationLogger = log.New(os.Stdout, logPrefix, 0)
|
applicationLogger = log.New(os.Stdout, logPrefix, 0)
|
||||||
}
|
}
|
||||||
db, err := NewDB(applicationLogger, *dbFilePath, *hashSeed)
|
db, err := NewDB(applicationLogger, *dbFilePath, *hashSeed, Retry{3})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
startupLogger.Fatal(err)
|
startupLogger.Fatal(err)
|
||||||
return
|
return
|
||||||
|
|
10
makefile
10
makefile
|
@ -1,11 +1,12 @@
|
||||||
BIN=out
|
BIN=out
|
||||||
CC=go1.16
|
CC=go1.17beta1
|
||||||
|
|
||||||
all: setup vendor gen build
|
all: setup vendor gen build
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
@env GOOD=off go get golang.org/dl/go1.16
|
@env GOOD=off go get golang.org/dl/$(CC)
|
||||||
@env GOOD=off $(CC) download
|
@env GOOD=off $(CC) download
|
||||||
|
#@env GO111MODULE=off $(CC) get github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
|
||||||
vendor: go.mod go.sum
|
vendor: go.mod go.sum
|
||||||
@$(CC) mod tidy
|
@$(CC) mod tidy
|
||||||
|
@ -20,9 +21,12 @@ gen:
|
||||||
test:
|
test:
|
||||||
@env $(ENV) $(CC) test ./... -cover -count 1
|
@env $(ENV) $(CC) test ./... -cover -count 1
|
||||||
|
|
||||||
run: gen build
|
run: lint build
|
||||||
@clear
|
@clear
|
||||||
@env $(ENV) ./$(BIN) -v -demo -copy "2021 i@fsh.ee" -url https://dev.fsh.ee -port 8080 -db /tmp/link_test_db_1.sql -seed secret
|
@env $(ENV) ./$(BIN) -v -demo -copy "2021 i@fsh.ee" -url https://dev.fsh.ee -port 8080 -db /tmp/link_test_db_1.sql -seed secret
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
@find . -type f | grep -E '(.*)\.(go|html)' | entr -cr make run
|
@find . -type f | grep -E '(.*)\.(go|html)' | entr -cr make run
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@golangci-lint run ./...
|
||||||
|
|
Loading…
Reference in a new issue