From 51765d6f03a45848b8f997d30d0225ceaa0953b4 Mon Sep 17 00:00:00 2001 From: Lukas SP Date: Sun, 23 Aug 2020 00:32:46 +0200 Subject: [PATCH] Implement basic web server structure and get paste endpoint --- cmd/pasty/main.go | 11 ++----- go.mod | 4 ++- go.sum | 18 ++++++++++ internal/storage/file_driver.go | 3 ++ internal/web/controllers/v1/pastes.go | 47 +++++++++++++++++++++++++++ internal/web/web.go | 29 +++++++++++++++++ 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 internal/web/controllers/v1/pastes.go create mode 100644 internal/web/web.go diff --git a/cmd/pasty/main.go b/cmd/pasty/main.go index 7090a0d..907ac72 100644 --- a/cmd/pasty/main.go +++ b/cmd/pasty/main.go @@ -3,10 +3,8 @@ package main import ( "github.com/Lukaesebrot/pasty/internal/env" "github.com/Lukaesebrot/pasty/internal/storage" + "github.com/Lukaesebrot/pasty/internal/web" "log" - "os" - "os/signal" - "syscall" ) func main() { @@ -22,9 +20,6 @@ func main() { } defer storage.Current.Terminate() - // Wait for the program to exit - // TODO: Replace this through blocking API server - sc := make(chan os.Signal, 1) - signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) - <-sc + // Serve the web server + panic(web.Serve()) } diff --git a/go.mod b/go.mod index 399464a..d85e7ca 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.15 require ( github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b github.com/bwmarrin/snowflake v0.3.0 + github.com/fasthttp/router v1.2.4 github.com/joho/godotenv v1.3.0 - golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect + github.com/klauspost/compress v1.10.11 // indirect + github.com/valyala/fasthttp v1.16.0 ) diff --git a/go.sum b/go.sum index b1f5123..7c98b74 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,33 @@ github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b h1:rcCpjI1OMGtBY8nnBvExeM1pXNoaM35zqmXBGpgJR2o= github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b/go.mod h1:GFtu6vaWaRJV5EvSFaVqgq/3Iq95xyYElBV/aupGzUo= +github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= +github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/fasthttp/router v1.2.4 h1:RBWbCv4vVf+boczSZh/rX9PDSdR9F8I9zSnVJx5YJfU= +github.com/fasthttp/router v1.2.4/go.mod h1:Au2V1CaqqAdzQQcPKrbkFAsImd1aHpadrce21AIPnvE= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= +github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.10.11 h1:K9z59aO18Aywg2b/WSgBaUX99mHy2BES18Cr5lBKZHk= +github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/savsgio/gotils v0.0.0-20200616100644-13ff1fd2c28c h1:KKqhycXW1WVNkX7r4ekTV2gFkbhdyihlWD8c0/FiWmk= +github.com/savsgio/gotils v0.0.0-20200616100644-13ff1fd2c28c/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.15.1/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= +github.com/valyala/fasthttp v1.16.0 h1:9zAqOYLl8Tuy3E5R6ckzGDJ1g8+pw15oQp2iL9Jl6gQ= +github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/storage/file_driver.go b/internal/storage/file_driver.go index 310af97..d85eec8 100644 --- a/internal/storage/file_driver.go +++ b/internal/storage/file_driver.go @@ -31,6 +31,9 @@ func (driver *FileDriver) Get(id snowflake.ID) (*pastes.Paste, error) { // Read the file data, err := ioutil.ReadFile(filepath.Join(driver.FilePath, id.String()+".json")) if err != nil { + if os.IsNotExist(err) { + return nil, nil + } return nil, err } diff --git a/internal/web/controllers/v1/pastes.go b/internal/web/controllers/v1/pastes.go new file mode 100644 index 0000000..797b66f --- /dev/null +++ b/internal/web/controllers/v1/pastes.go @@ -0,0 +1,47 @@ +package v1 + +import ( + "encoding/json" + "github.com/Lukaesebrot/pasty/internal/storage" + "github.com/bwmarrin/snowflake" + "github.com/fasthttp/router" + "github.com/valyala/fasthttp" +) + +// InitializePastesController initializes the '/v1/pastes/*' controller +func InitializePastesController(group *router.Group) { + group.GET("/{id}", v1GetPaste) +} + +// v1GetPaste handles the 'GET /v1/pastes/{id}' endpoint +func v1GetPaste(ctx *fasthttp.RequestCtx) { + // Parse the ID + id, err := snowflake.ParseString(ctx.UserValue("id").(string)) + if err != nil { + ctx.SetStatusCode(fasthttp.StatusBadRequest) + ctx.SetBodyString("invalid ID format") + return + } + + // Retrieve the paste + paste, err := storage.Current.Get(id) + if err != nil { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + ctx.SetBodyString(err.Error()) + return + } + if paste == nil { + ctx.SetStatusCode(fasthttp.StatusNotFound) + ctx.SetBodyString("paste not found") + return + } + + // Respond with the paste + jsonData, err := json.Marshal(paste) + if err != nil { + ctx.SetStatusCode(fasthttp.StatusInternalServerError) + ctx.SetBodyString(err.Error()) + return + } + ctx.SetBody(jsonData) +} diff --git a/internal/web/web.go b/internal/web/web.go new file mode 100644 index 0000000..6708bfb --- /dev/null +++ b/internal/web/web.go @@ -0,0 +1,29 @@ +package web + +import ( + "github.com/Lukaesebrot/pasty/internal/env" + v1 "github.com/Lukaesebrot/pasty/internal/web/controllers/v1" + routing "github.com/fasthttp/router" + "github.com/valyala/fasthttp" +) + +// Serve serves the web server +func Serve() error { + // Create the router + router := routing.New() + + // Route the API endpoints + apiRoute := router.Group("/api") + { + v1Route := apiRoute.Group("/v1") + { + v1.InitializePastesController(v1Route.Group("/pastes")) + } + } + + // TODO: Route the paste endpoints + + // Serve the web server + address := env.Get("WEB_ADDRESS", ":8080") + return fasthttp.ListenAndServe(address, router.Handler) +}