diff --git a/.gitignore b/.gitignore index adf8f72..88ebc56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ + + # ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore @@ -21,3 +23,6 @@ # Go workspace file go.work +.vscode +*_templ.go +server \ No newline at end of file diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..f5234ab --- /dev/null +++ b/Justfile @@ -0,0 +1,10 @@ + + +# Generates templ files +gen: + templ generate + +build: + templ generate + go build cmd/server.go + ls -lh server \ No newline at end of file diff --git a/cmd/server.go b/cmd/server.go new file mode 100644 index 0000000..4b350d0 --- /dev/null +++ b/cmd/server.go @@ -0,0 +1,21 @@ +package main + +import ( + "context" + "fmt" + + "git.jamestombleson.com/jtom38/newsbot-portal/internal/config" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/handlers" +) + +func main() { + ctx := context.Background() + + cfg := config.New() + server := handlers.NewServer(ctx, cfg) + + fmt.Println("The server is online and waiting for requests.") + fmt.Printf("http://%v:8082\r\n", cfg.ServerAddress) + + server.Router.Start(":8082") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..64fa1ac --- /dev/null +++ b/go.mod @@ -0,0 +1,35 @@ +module git.jamestombleson.com/jtom38/newsbot-portal + +go 1.22.1 + +require ( + git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240508052157-5b8cf6dfa6cc + github.com/joho/godotenv v1.5.1 + github.com/labstack/echo/v4 v4.12.0 +) + +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/a-h/templ v0.2.680 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/spec v0.20.6 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/swaggo/swag v1.8.12 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.17.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..eb0ceb8 --- /dev/null +++ b/go.sum @@ -0,0 +1,88 @@ +git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240508052157-5b8cf6dfa6cc h1:QhuOntLiCv/kn+ymg/Qw7yKwXX+lPGRjgw8/saqfAeE= +git.jamestombleson.com/jtom38/newsbot-api v0.0.0-20240508052157-5b8cf6dfa6cc/go.mod h1:A3UdJyQ/IEy3utEwJiC4nbi0ohfgrUNRLTei2iZhLLA= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/a-h/templ v0.2.680 h1:TflYFucxp5rmOxAXB9Xy3+QHTk8s8xG9+nCT/cLzjeE= +github.com/a-h/templ v0.2.680/go.mod h1:NQGQOycaPKBxRB14DmAaeIpcGC1AOBPJEMO4ozS7m90= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= +github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= +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/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..d529496 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,49 @@ +package config + +import ( + "log" + "os" + + "github.com/joho/godotenv" +) + +type Configs struct { + ServerAddress string + JwtSecret string + AdminSecret string +} + +func New() Configs { + refreshEnv() + c := getEnvConfig() + return c +} + +func getEnvConfig() Configs { + return Configs{ + ServerAddress: os.Getenv("ServerAddress"), + JwtSecret: os.Getenv("JwtSecret"), + AdminSecret: os.Getenv("AdminSecret"), + } +} + +// Use this when your ConfigClient has been opened for awhile and you want to ensure you have the most recent env changes. +func refreshEnv() { + // Check to see if we have the env file on the system + _, err := os.Stat(".env") + + // We have the file, load it. + if err == nil { + _, err := os.Open(".env") + if err == nil { + loadEnvFile() + } + } +} + +func loadEnvFile() { + err := godotenv.Load() + if err != nil { + log.Fatalln(err) + } +} diff --git a/internal/handlers/handler.go b/internal/handlers/handler.go new file mode 100644 index 0000000..7aa62fe --- /dev/null +++ b/internal/handlers/handler.go @@ -0,0 +1,134 @@ +package handlers + +import ( + "context" + + "github.com/a-h/templ" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + + _ "git.jamestombleson.com/jtom38/newsbot-api/docs" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/config" + //"git.jamestombleson.com/jtom38/newsbot-api/domain" +) + +type Handler struct { + Router *echo.Echo + config config.Configs +} + +const ( + ErrParameterIdMissing = "The requested parameter ID was not found." + ErrParameterMissing = "The requested parameter was not found found:" + ErrUnableToParseId = "Unable to parse the requested ID" + + ErrRecordMissing = "The requested record was not found" + ErrFailedToCreateRecord = "The record was not created due to a database problem" + ErrFailedToUpdateRecord = "The requested record was not updated due to a database problem" + + ErrUserUnknown = "User is unknown" + ErrYouDontOwnTheRecord = "The record requested does not belong to you" + + ResponseMessageSuccess = "Success" +) + +var ( + ErrIdValueMissing string = "id value is missing" + ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct." + ErrNoRecordFound string = "no record was found." + ErrUnableToConvertToJson string = "Unable to convert to json" +) + +func NewServer(ctx context.Context, configs config.Configs) *Handler { + s := &Handler{ + config: configs, + } + + //jwtConfig := echojwt.Config{ + // NewClaimsFunc: func(c echo.Context) jwt.Claims { + // return new(JwtToken) + // }, + // SigningKey: []byte(configs.JwtSecret), + //} + + router := echo.New() + router.Pre(middleware.RemoveTrailingSlash()) + router.Pre(middleware.Logger()) + router.Pre(middleware.Recover()) + + router.GET("/", s.HomeIndex) + + s.Router = router + return s +} + +// This custom Render replaces Echo's echo.Context.Render() with templ's templ.Component.Render(). +func Render(ctx echo.Context, statusCode int, t templ.Component) error { + ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML) + ctx.Response().Writer.WriteHeader(statusCode) + return t.Render(ctx.Request().Context(), ctx.Response().Writer) +} + +/* +func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error { + return c.JSON(HttpStatusCode, domain.BaseResponse{ + Message: errMessage.Error(), + }) +} + +func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error { + return c.JSON(HttpStatusCode, domain.BaseResponse{ + Message: msg, + }) +} + +func (s *Handler) InternalServerErrorResponse(c echo.Context, msg string) error { + return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ + Message: msg, + }) +} + +func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error { + return c.JSON(http.StatusUnauthorized, domain.BaseResponse{ + Message: msg, + }) +} +*/ + +// If the token is not valid then an json error will be returned. +// If the token has the wrong scope, a json error will be returned. +// If the token passes all the checks, it is valid and is returned back to the caller. +//func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) { +// token, err := s.getJwtTokenFromContext(c) +// if err != nil { +// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) +// } +// +// err = token.hasExpired() +// if err != nil { +// return JwtToken{}, errors.New(ErrJwtExpired) +// //s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized) +// } +// +// err = token.hasScope(requiredScope) +// if err != nil { +// return JwtToken{}, errors.New(ErrJwtScopeMissing) +// //s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized) +// } +// +// if token.Iss != s.config.ServerAddress { +// return JwtToken{}, errors.New(ErrJwtInvalidIssuer) +// //s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized) +// } +// +// return token, nil +//} + +//func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 { +// token, err := s.getJwtTokenFromContext(c) +// if err != nil { +// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) +// } +// +// return token.GetUserId() +//} diff --git a/internal/handlers/home.go b/internal/handlers/home.go new file mode 100644 index 0000000..3909431 --- /dev/null +++ b/internal/handlers/home.go @@ -0,0 +1,12 @@ +package handlers + +import ( + "net/http" + + "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/home" + "github.com/labstack/echo/v4" +) + +func (h *Handler) HomeIndex(c echo.Context) error { + return Render(c, http.StatusOK, home.Index()) +} \ No newline at end of file diff --git a/internal/views/home/index.templ b/internal/views/home/index.templ new file mode 100644 index 0000000..6f2ffda --- /dev/null +++ b/internal/views/home/index.templ @@ -0,0 +1,27 @@ +package home + +import "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" + +templ Index() { + @layout.WithTemplate() { +

Welcome to Newsbot

+ +
+

+ News bot is a self hostable solution to aggregating your news. + You can run `Newsbot` as an API +

+
+ + +
+ I started to build this tool to help me avoid sitting on the big platform websites. + I wanted a tool that would work for me, not them. + +
+

+ This project is a passion project of mine as I +

+ + } +} diff --git a/internal/views/layout/withTemplate.templ b/internal/views/layout/withTemplate.templ new file mode 100644 index 0000000..7d25730 --- /dev/null +++ b/internal/views/layout/withTemplate.templ @@ -0,0 +1,48 @@ +package layout + +templ WithTemplate() { + + + + + + + + + + + + + + +
+ { children... } +
+ + +}