implement proper sever route handling
This commit is contained in:
19
pkg/web/components/issuelist.templ
Normal file
19
pkg/web/components/issuelist.templ
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import "github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
|
|
||||||
|
type IssueListProps struct {
|
||||||
|
Issues []*models.Issue
|
||||||
|
}
|
||||||
|
|
||||||
|
templ IssueList(props IssueListProps) {
|
||||||
|
<ul>
|
||||||
|
for _, issue := range props.Issues {
|
||||||
|
@IssueListItem(issue)
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ IssueListItem(issue *models.Issue) {
|
||||||
|
<li>Issue ID: { issue.ID }, Title: { issue.Title } </li>
|
||||||
|
}
|
||||||
111
pkg/web/components/issuelist_templ.go
Normal file
111
pkg/web/components/issuelist_templ.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package components
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import "github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
|
|
||||||
|
type IssueListProps struct {
|
||||||
|
Issues []*models.Issue
|
||||||
|
}
|
||||||
|
|
||||||
|
func IssueList(props IssueListProps) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<ul>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, issue := range props.Issues {
|
||||||
|
templ_7745c5c3_Err = IssueListItem(issue).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</ul>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func IssueListItem(issue *models.Issue) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var2 == nil {
|
||||||
|
templ_7745c5c3_Var2 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<li>Issue ID: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(issue.ID)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issuelist.templ`, Line: 18, Col: 25}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, ", Title: ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(issue.Title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issuelist.templ`, Line: 18, Col: 49}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</li>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
21
pkg/web/handler/handler.go
Normal file
21
pkg/web/handler/handler.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Pattern string
|
||||||
|
Handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRoutes(svc *service.Services) []Route {
|
||||||
|
var routes []Route
|
||||||
|
|
||||||
|
routes = append(routes, PagesRoutes(svc)...)
|
||||||
|
routes = append(routes, IssuesRoutes(svc)...)
|
||||||
|
|
||||||
|
return routes
|
||||||
|
}
|
||||||
@@ -1,22 +1,66 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IssuesHandler struct {
|
func IssuesRoutes(svc *service.Services) []Route {
|
||||||
Services *service.Services
|
return []Route{
|
||||||
}
|
{Pattern: "/issues", Handler: GetAllIssues(svc)},
|
||||||
|
{Pattern: "POST /create-issue", Handler: CreateIssue(svc)},
|
||||||
func NewIssuesHandler(services *service.Services) *IssuesHandler {
|
|
||||||
return &IssuesHandler{
|
|
||||||
Services: services,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *IssuesHandler) HandleGetAllIssues() http.Handler {
|
func CreateIssue(svc *service.Services) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
})
|
var issue models.Issue
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&issue)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid request payload", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
err = svc.Beads.CreateIssue(r.Context(), &issue, "")
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to create issue: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllIssues(svc *service.Services) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
issues, err := svc.Beads.AllIssues(r.Context())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to retrieve issues", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(issues)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to marshal issues", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(jsonData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
pkg/web/handler/pages.go
Normal file
46
pkg/web/handler/pages.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/components"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/routes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PagesRoutes(svc *service.Services) []Route {
|
||||||
|
return []Route{
|
||||||
|
{Pattern: "/", Handler: IndexHandler(svc)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IndexHandler(svc *service.Services) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
if r.URL.Path != "/" {
|
||||||
|
handleNotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
issues, err := svc.Beads.AllIssues(r.Context())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to retrieve issues",
|
||||||
|
http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
props := routes.IndexProps{
|
||||||
|
Issues: issues,
|
||||||
|
IssueList: components.IssueListProps{
|
||||||
|
Issues: issues,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.Index(props).Render(r.Context(), w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleNotFound(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
http.Error(w, "Page not found", http.StatusNotFound)
|
||||||
|
}
|
||||||
@@ -1,8 +1,19 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
templ Index() {
|
import (
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IndexProps struct {
|
||||||
|
Issues []*models.Issue
|
||||||
|
IssueList components.IssueListProps
|
||||||
|
}
|
||||||
|
|
||||||
|
templ Index(props IndexProps) {
|
||||||
@BaseLayout() {
|
@BaseLayout() {
|
||||||
<h1>Welcome to the Beads Test Application</h1>
|
<h1>Welcome to the Beads Test Application</h1>
|
||||||
<p>Here are current issues:</p>
|
<p>Here are current issues:</p>
|
||||||
|
@components.IssueList(props.IssueList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ package routes
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
func Index() templ.Component {
|
import (
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IndexProps struct {
|
||||||
|
Issues []*models.Issue
|
||||||
|
IssueList components.IssueListProps
|
||||||
|
}
|
||||||
|
|
||||||
|
func Index(props IndexProps) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -45,6 +55,10 @@ func Index() templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
templ_7745c5c3_Err = components.IssueList(props.IssueList).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
templ_7745c5c3_Err = BaseLayout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = BaseLayout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
|||||||
@@ -6,22 +6,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/handler"
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Route struct {
|
func (s *Server) RegisterRoutes(assets embed.FS) http.Handler {
|
||||||
Pattern string
|
|
||||||
Handeler http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) RegisterRoutes(assets embed.FS, routes []Route) http.Handler {
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
s.handleAssets(mux, assets)
|
s.handleAssets(mux, assets)
|
||||||
|
|
||||||
for _, route := range routes {
|
for _, route := range handler.GetRoutes(s.Services) {
|
||||||
mux.Handle(route.Pattern, route.Handeler)
|
mux.Handle(route.Pattern, route.Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := cors.Default().Handler(mux)
|
handler := cors.Default().Handler(mux)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
|
||||||
"embed"
|
"embed"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
Assets embed.FS
|
Assets embed.FS
|
||||||
Routes []Route
|
|
||||||
Services *service.Services
|
Services *service.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,9 +20,11 @@ func NewServer(props Server) *http.Server {
|
|||||||
props.Address = "localhost:8080"
|
props.Address = "localhost:8080"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler := props.RegisterRoutes(props.Assets)
|
||||||
|
|
||||||
return &http.Server{
|
return &http.Server{
|
||||||
Addr: props.Address,
|
Addr: props.Address,
|
||||||
Handler: props.RegisterRoutes(props.Assets, props.Routes),
|
Handler: handler,
|
||||||
IdleTimeout: time.Minute,
|
IdleTimeout: time.Minute,
|
||||||
ReadTimeout: time.Second * 10,
|
ReadTimeout: time.Second * 10,
|
||||||
WriteTimeout: time.Second * 10,
|
WriteTimeout: time.Second * 10,
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/web/handler"
|
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/web/routes"
|
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/web/server"
|
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/web/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebConfig = service.Config
|
type WebConfig = service.Config
|
||||||
@@ -25,16 +22,10 @@ func Run(ctx context.Context, config WebConfig) error {
|
|||||||
|
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
handler := handler.NewIssuesHandler(svc)
|
|
||||||
|
|
||||||
server := server.NewServer(server.Server{
|
server := server.NewServer(server.Server{
|
||||||
Address: config.WebAddress,
|
Address: config.WebAddress,
|
||||||
Assets: assets,
|
Assets: assets,
|
||||||
Services: svc,
|
Services: svc,
|
||||||
Routes: []server.Route{
|
|
||||||
{Pattern: "/", Handeler: templ.Handler(routes.Index())},
|
|
||||||
{Pattern: "/issues", Handeler: handler.HandleGetAllIssues()},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Printf("Starting web server on %s...\n", config.WebAddress)
|
fmt.Printf("Starting web server on %s...\n", config.WebAddress)
|
||||||
|
|||||||
Reference in New Issue
Block a user