refactor: simplified code structure
This commit is contained in:
parent
33b35e6839
commit
e5002d8187
20
cli/serve.go
20
cli/serve.go
|
@ -1,18 +1,20 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tigorlazuardi/redmage/api"
|
||||||
"github.com/tigorlazuardi/redmage/db"
|
"github.com/tigorlazuardi/redmage/db"
|
||||||
"github.com/tigorlazuardi/redmage/db/queries/subreddits"
|
"github.com/tigorlazuardi/redmage/db/queries"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
"github.com/tigorlazuardi/redmage/server"
|
"github.com/tigorlazuardi/redmage/server"
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/api"
|
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/www"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var PublicDir fs.FS = os.DirFS("public")
|
||||||
|
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "Starts the HTTP Server",
|
Short: "Starts the HTTP Server",
|
||||||
|
@ -24,18 +26,14 @@ var serveCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
subreddits := subreddits.New(db)
|
queries := queries.New(db)
|
||||||
|
|
||||||
api := &api.API{
|
api := &api.API{
|
||||||
Subreddits: subreddits,
|
Queries: queries,
|
||||||
Config: cfg,
|
DB: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
www := &www.WWW{
|
server := server.New(cfg, api, PublicDir)
|
||||||
Subreddits: subreddits,
|
|
||||||
Config: cfg,
|
|
||||||
}
|
|
||||||
server := server.New(cfg, api, www)
|
|
||||||
|
|
||||||
exit := make(chan struct{}, 1)
|
exit := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
|
3
main.go
3
main.go
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/tigorlazuardi/redmage/cli"
|
"github.com/tigorlazuardi/redmage/cli"
|
||||||
"github.com/tigorlazuardi/redmage/db"
|
"github.com/tigorlazuardi/redmage/db"
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/www"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed db/migrations/*.sql
|
//go:embed db/migrations/*.sql
|
||||||
|
@ -23,7 +22,7 @@ func main() {
|
||||||
_ = godotenv.Load()
|
_ = godotenv.Load()
|
||||||
db.Migrations = Migrations
|
db.Migrations = Migrations
|
||||||
var err error
|
var err error
|
||||||
www.PublicFS, err = fs.Sub(PublicFS, "public")
|
cli.PublicDir, err = fs.Sub(PublicFS, "public")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(errors.New("failed to create sub filesystem"))
|
panic(errors.New("failed to create sub filesystem"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
||||||
"github.com/tigorlazuardi/redmage/config"
|
|
||||||
"github.com/tigorlazuardi/redmage/db/queries/subreddits"
|
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
type API struct {
|
|
||||||
Subreddits *subreddits.Queries
|
|
||||||
Config *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) Register(router chi.Router) {
|
|
||||||
router.Use(chimiddleware.RequestID)
|
|
||||||
router.Get("/", HealthCheck)
|
|
||||||
router.Get("/health", HealthCheck)
|
|
||||||
router.Route("/subreddits", func(r chi.Router) {
|
|
||||||
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
|
||||||
r.Get("/", api.ListSubreddits)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func HealthCheck(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
rw.Header().Add("Content-Type", "application/json")
|
|
||||||
_, _ = rw.Write([]byte(`{"status":"ok"}`))
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/tigorlazuardi/redmage/db/queries/subreddits"
|
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (api *API) ListSubreddits(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
rw.Header().Add("Content-Type", "application/json")
|
|
||||||
subs, err := api.Subreddits.ListSubreddits(r.Context(), parseListSubredditsQuery(r))
|
|
||||||
if err != nil {
|
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
|
||||||
msg := fmt.Sprintf("failed to list subreddits: %s", err)
|
|
||||||
_ = json.NewEncoder(rw).Encode(map[string]string{"error": msg})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(rw).Encode(subs); err != nil {
|
|
||||||
log.New(r.Context()).Err(err).Error("failed to list subreddits")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseListSubredditsQuery(r *http.Request) subreddits.ListSubredditsParams {
|
|
||||||
params := subreddits.ListSubredditsParams{}
|
|
||||||
params.Limit, _ = strconv.ParseInt(r.URL.Query().Get("limit"), 10, 64)
|
|
||||||
params.Offset, _ = strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64)
|
|
||||||
|
|
||||||
if params.Limit < 1 {
|
|
||||||
params.Limit = 10
|
|
||||||
} else if params.Limit > 100 {
|
|
||||||
params.Limit = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
return params
|
|
||||||
}
|
|
9
server/routes/health_check.go
Normal file
9
server/routes/health_check.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (routes *Routes) HealthCheck(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
_, _ = rw.Write([]byte{})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package www
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (www *WWW) CreateHotReloadRoute() http.HandlerFunc {
|
func (routes *Routes) CreateHotReloadRoute() http.HandlerFunc {
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
knownClients := make(map[string]chan struct{})
|
knownClients := make(map[string]chan struct{})
|
||||||
firstTime := make(chan struct{}, 1)
|
firstTime := make(chan struct{}, 1)
|
17
server/routes/page_home.go
Normal file
17
server/routes/page_home.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
|
"github.com/tigorlazuardi/redmage/views"
|
||||||
|
"github.com/tigorlazuardi/redmage/views/homeview"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (routes *Routes) PageHome(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
vc := views.NewContext(routes.Config, r)
|
||||||
|
if err := homeview.Home(vc).Render(ctx, rw); err != nil {
|
||||||
|
log.New(ctx).Err(err).Error("failed to render home view")
|
||||||
|
}
|
||||||
|
}
|
45
server/routes/routes.go
Normal file
45
server/routes/routes.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/tigorlazuardi/redmage/api"
|
||||||
|
"github.com/tigorlazuardi/redmage/config"
|
||||||
|
"github.com/tigorlazuardi/redmage/server/routes/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Routes struct {
|
||||||
|
API *api.API
|
||||||
|
Config *config.Config
|
||||||
|
PublicDir fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (routes *Routes) Register(router chi.Router) {
|
||||||
|
router.HandleFunc("/ping", routes.HealthCheck)
|
||||||
|
router.HandleFunc("/health", routes.HealthCheck)
|
||||||
|
if routes.Config.Bool("http.hotreload") {
|
||||||
|
router.Get("/hot_reload", routes.CreateHotReloadRoute())
|
||||||
|
}
|
||||||
|
|
||||||
|
router.Group(routes.registerWWWRoutes)
|
||||||
|
router.Route("/api/v1", routes.registerV1APIRoutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (routes *Routes) registerV1APIRoutes(router chi.Router) {
|
||||||
|
router.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
||||||
|
router.Use(chimiddleware.SetHeader("Content-Type", "application/json"))
|
||||||
|
|
||||||
|
router.Get("/subreddits", routes.SubredditsListAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (routes *Routes) registerWWWRoutes(router chi.Router) {
|
||||||
|
router.Mount("/public", http.StripPrefix("/public", http.FileServer(http.FS(routes.PublicDir))))
|
||||||
|
|
||||||
|
router.Group(func(r chi.Router) {
|
||||||
|
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
||||||
|
r.Get("/", routes.PageHome)
|
||||||
|
})
|
||||||
|
}
|
41
server/routes/subreddit_list.go
Normal file
41
server/routes/subreddit_list.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/tigorlazuardi/redmage/api"
|
||||||
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Routes) SubredditsListAPI(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx := req.Context()
|
||||||
|
params := parseSubredditListQuery(req)
|
||||||
|
|
||||||
|
result, err := r.API.ListSubreddits(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_ = json.NewEncoder(rw).Encode(map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(rw).Encode(result); err != nil {
|
||||||
|
log.New(ctx).Err(err).Error("failed to encode subreddits into json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Routes) SubredditsListPage(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSubredditListQuery(req *http.Request) (params api.ListSubredditsParams) {
|
||||||
|
params.Name = req.FormValue("name")
|
||||||
|
params.Limit, _ = strconv.ParseInt(req.FormValue("limit"), 10, 64)
|
||||||
|
if params.Limit < 1 {
|
||||||
|
params.Limit = 10
|
||||||
|
} else if params.Limit > 100 {
|
||||||
|
params.Limit = 100
|
||||||
|
}
|
||||||
|
params.Offset, _ = strconv.ParseInt(req.FormValue("offset"), 10, 64)
|
||||||
|
return params
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
package www
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/tigorlazuardi/redmage/views"
|
|
||||||
"github.com/tigorlazuardi/redmage/views/homeview"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (www *WWW) Home(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
_ = homeview.Home(views.NewContext(www.Config, r)).Render(r.Context(), rw)
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package www
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
||||||
"github.com/tigorlazuardi/redmage/config"
|
|
||||||
"github.com/tigorlazuardi/redmage/db/queries/subreddits"
|
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
var PublicFS = os.DirFS("public")
|
|
||||||
|
|
||||||
type WWW struct {
|
|
||||||
Subreddits *subreddits.Queries
|
|
||||||
Config *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (www *WWW) Register(router chi.Router) {
|
|
||||||
router.Use(chimiddleware.RealIP)
|
|
||||||
router.
|
|
||||||
With(chimiddleware.RequestLogger(middleware.ChiSimpleLogger{})).
|
|
||||||
Mount("/public", http.StripPrefix("/public", http.FileServer(http.FS(PublicFS))))
|
|
||||||
|
|
||||||
if www.Config.Bool("http.hotreload") {
|
|
||||||
log.New(context.Background()).Debug("enabled hot reload")
|
|
||||||
router.
|
|
||||||
With(chimiddleware.RequestLogger(middleware.ChiSimpleLogger{})).
|
|
||||||
Get("/hot_reload", www.CreateHotReloadRoute())
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Group(func(r chi.Router) {
|
|
||||||
r.Use(chimiddleware.RequestID)
|
|
||||||
r.Use(chimiddleware.RequestLogger(middleware.ChiLogger{}))
|
|
||||||
r.Use(chimiddleware.SetHeader("Content-Type", "text/html; utf-8"))
|
|
||||||
r.Get("/", www.Home)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -3,20 +3,22 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/tigorlazuardi/redmage/api"
|
||||||
"github.com/tigorlazuardi/redmage/config"
|
"github.com/tigorlazuardi/redmage/config"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/caller"
|
"github.com/tigorlazuardi/redmage/pkg/caller"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/errs"
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/api"
|
"github.com/tigorlazuardi/redmage/server/routes"
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/www"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
PublicDir fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) Start(exit <-chan struct{}) error {
|
func (srv *Server) Start(exit <-chan struct{}) error {
|
||||||
|
@ -41,11 +43,16 @@ func (srv *Server) Start(exit <-chan struct{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, api *api.API, www *www.WWW) *Server {
|
func New(cfg *config.Config, api *api.API, publicDir fs.FS) *Server {
|
||||||
router := chi.NewRouter()
|
router := chi.NewRouter()
|
||||||
|
|
||||||
router.Route("/api", api.Register)
|
routes := routes.Routes{
|
||||||
router.Route("/", www.Register)
|
API: api,
|
||||||
|
Config: cfg,
|
||||||
|
PublicDir: publicDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.Register(router)
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
|
Loading…
Reference in a new issue