mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
initial project structure
This commit is contained in:
commit
a1b7c5921b
13 changed files with 726 additions and 0 deletions
2
.env
Normal file
2
.env
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
DATABASE_URL="user=ruidy database=villafleurie"
|
||||
PORT=8000
|
||||
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
go.work
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
tmp
|
||||
20
Makefile
Normal file
20
Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
NAME=rentease
|
||||
PORT=8000
|
||||
DB_USER=ruidy
|
||||
DB_NAME=villafleurie
|
||||
|
||||
build: templ
|
||||
@docker build -t ${NAME}:latest .
|
||||
run: build
|
||||
@docker run -p ${PORT}:${PORT} -e DATABASE_URL="host=docker.for.mac.host.internal user=${DB_USER} database=${DB_NAME}" -e PORT=${PORT} ${NAME}
|
||||
dev: templ
|
||||
@air cmd/main.go
|
||||
templ: lint
|
||||
@templ generate
|
||||
format:
|
||||
@templ fmt .
|
||||
@go fmt ./...
|
||||
lint: format
|
||||
@golangci-lint run ./...
|
||||
|
||||
.PHONY: build run dev templ format lint
|
||||
18
README.md
Normal file
18
README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# RentEase
|
||||
|
||||
Manage your holiday rental
|
||||
|
||||
## To Do
|
||||
|
||||
- [ ] Create a booking
|
||||
- [ ] Add line items
|
||||
- [ ] Read from the database
|
||||
- [ ] Build the pdf invoice
|
||||
|
||||
## Built With
|
||||
|
||||
- Go
|
||||
- Htmx
|
||||
- Templ
|
||||
- PostgreSQL
|
||||
|
||||
32
go.mod
Normal file
32
go.mod
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
module github.com/rjNemo/rentease
|
||||
|
||||
go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.2.543
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo/v4 v4.11.4
|
||||
gorm.io/driver/postgres v1.5.4
|
||||
gorm.io/gorm v1.25.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.3 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
)
|
||||
65
go.sum
Normal file
65
go.sum
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk=
|
||||
github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA=
|
||||
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/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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
|
||||
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
|
||||
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
||||
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/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/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.7.0/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/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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
||||
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
|
||||
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
|
||||
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
|
||||
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
271
internal/server/handlers.go
Normal file
271
internal/server/handlers.go
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/rjNemo/rentease/internal/views"
|
||||
)
|
||||
|
||||
func (s Server) handleHomePage() echo.HandlerFunc {
|
||||
return func(ctx echo.Context) error {
|
||||
component := views.Index()
|
||||
return s.renderTempl(ctx, http.StatusOK, component)
|
||||
}
|
||||
}
|
||||
|
||||
//func (s Server) handleLoginPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// qs := c.QueryParams()
|
||||
// errs := qs["err"]
|
||||
//
|
||||
// component := views.LoginPage(errs)
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleLogin() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// email := c.FormValue("email")
|
||||
// pwd := c.FormValue("password")
|
||||
//
|
||||
// user, err := s.us.SignIn(email, pwd)
|
||||
// if err != nil {
|
||||
// return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%s?err=invalid+credentials", constants.RouteLogin))
|
||||
// }
|
||||
// if err = writeCookie(c, user.Id, email, user.PaymentValid); err != nil {
|
||||
// return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%s?err=invalid+credentials", constants.RouteLogin))
|
||||
// }
|
||||
// return c.Redirect(http.StatusFound, constants.RouteHome)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleLogout() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// cookie := new(http.Cookie)
|
||||
// cookie.Name = cookieName
|
||||
// cookie.Value = ""
|
||||
// cookie.MaxAge = 0
|
||||
// c.SetCookie(cookie)
|
||||
// return c.Redirect(http.StatusFound, constants.RouteLogin)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleHomePage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// user := c.Get("user").(services.User)
|
||||
//
|
||||
// component := views.Home(&user, s.ms.List(user.Id))
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCreateMeetingNote() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// memberId, err := strconv.Atoi(c.FormValue("member_id"))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// audioFile, err := c.FormFile("audio_file")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// err = s.ms.CreateNote(audioFile, memberId)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// return c.Redirect(http.StatusFound, fmt.Sprintf("%s/%d", constants.RouteTeam, memberId))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleTeamPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// user := c.Get("user").(services.User)
|
||||
//
|
||||
// component := views.Team(&user, s.ms.List(user.Id))
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCreateTeamMember() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// name := c.FormValue("name")
|
||||
// user := c.Get("user").(services.User)
|
||||
// if _, err := s.ms.Create(name, user.Id); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return c.Redirect(http.StatusFound, constants.RouteTeam)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleUpdateTeamMember() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// name := c.FormValue("name")
|
||||
// description := c.FormValue("description")
|
||||
// memberId, _ := strconv.Atoi(c.Param("id"))
|
||||
//
|
||||
// if _, err := s.ms.Update(name, description, memberId); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return c.Redirect(http.StatusFound, fmt.Sprintf("%s/%d", constants.RouteTeam, memberId))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleTeamMemberPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// id, err := strconv.Atoi(c.Param("id"))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// user := c.Get("user").(services.User)
|
||||
//
|
||||
// component := views.TeamMember(&user, s.ms.One(id))
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCheckoutPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// component := views.Checkout(os.Getenv("STRIPE_LOOKUP_KEY"))
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleSuccessPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// stripe.Key = os.Getenv("STRIPE_API_KEY")
|
||||
//
|
||||
// ss, _ := session.Get(c.QueryParam("session_id"), nil)
|
||||
// log.Infof("%+v", ss.Customer)
|
||||
//
|
||||
// uid := c.QueryParam("u")
|
||||
// userId, _ := strconv.Atoi(uid)
|
||||
// err := s.us.SetPaymentStatus(userId, ss.Customer.ID)
|
||||
// if err != nil {
|
||||
// return c.Redirect(302, constants.RouteCancel)
|
||||
// }
|
||||
//
|
||||
// component := views.Success()
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCancelPage() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// component := views.Cancel()
|
||||
// return s.renderTempl(c, http.StatusOK, component)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCreateCheckoutSession() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// stripe.Key = os.Getenv("STRIPE_API_KEY")
|
||||
// lookupKey := c.FormValue("lookup_key")
|
||||
//
|
||||
// params := &stripe.PriceListParams{
|
||||
// LookupKeys: stripe.StringSlice([]string{lookupKey}),
|
||||
// }
|
||||
// i := price.List(params)
|
||||
// sp := new(stripe.Price)
|
||||
// for i.Next() {
|
||||
// p := i.Price()
|
||||
// sp = p
|
||||
// }
|
||||
//
|
||||
// domain := os.Getenv("STRIPE_DOMAIN")
|
||||
// user := c.Get("user").(services.User)
|
||||
// checkoutParams := &stripe.CheckoutSessionParams{
|
||||
// Mode: stripe.String(string(stripe.CheckoutSessionModeSubscription)),
|
||||
// LineItems: []*stripe.CheckoutSessionLineItemParams{
|
||||
// {Price: stripe.String(sp.ID), Quantity: stripe.Int64(1)},
|
||||
// },
|
||||
// SuccessURL: stripe.String(fmt.Sprintf("%s%s?u=%d&session_id={CHECKOUT_SESSION_ID}", domain, constants.RouteSuccess, user.Id)),
|
||||
// CancelURL: stripe.String(fmt.Sprintf("%s/cancel", domain)),
|
||||
// }
|
||||
// s, err := session.New(checkoutParams)
|
||||
// if err != nil {
|
||||
// log.Warnf("session.New %v", err)
|
||||
// }
|
||||
//
|
||||
// return c.Redirect(http.StatusSeeOther, s.URL)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleCreatePortalSession() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// stripe.Key = os.Getenv("STRIPE_API_KEY")
|
||||
// u := c.Get("user").(services.User)
|
||||
// user := s.us.One(u.Id)
|
||||
//
|
||||
// params := &stripe.BillingPortalSessionParams{
|
||||
// Customer: stripe.String(user.PaymentId),
|
||||
// ReturnURL: stripe.String(os.Getenv("STRIPE_DOMAIN")),
|
||||
// }
|
||||
// ps, _ := portalsession.New(params)
|
||||
//
|
||||
// return c.Redirect(http.StatusSeeOther, ps.URL)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s Server) handleWebhook() echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// payload, err := io.ReadAll(c.Request().Body)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, "Error reading request body: %v\n")
|
||||
// }
|
||||
//
|
||||
// endpointSecret := os.Getenv("STRIPE_WEBHOOK_SECRET")
|
||||
// signatureHeader := c.Request().Header.Get("Stripe-Signature")
|
||||
// event, err := webhook.ConstructEvent(payload, signatureHeader, endpointSecret)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, fmt.Sprintf("⚠️ Webhook signature verification failed. %v\n", err))
|
||||
// }
|
||||
// // Unmarshal the event data into an appropriate struct depending on its Type
|
||||
// switch event.Type {
|
||||
// case "customer.subscription.deleted":
|
||||
// var subscription stripe.Subscription
|
||||
// err := json.Unmarshal(event.Data.Raw, &subscription)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, "Error parsing webhook JSON: %v\n")
|
||||
// }
|
||||
// log.Printf("Subscription deleted for %s.", subscription.ID)
|
||||
// // Then define and call a func to handle the deleted subscription.
|
||||
// // handleSubscriptionCanceled(subscription)
|
||||
// case "customer.subscription.updated":
|
||||
// var subscription stripe.Subscription
|
||||
// err := json.Unmarshal(event.Data.Raw, &subscription)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, "Error parsing webhook JSON: %v\n")
|
||||
// }
|
||||
// log.Printf("Subscription updated for %s.", subscription.ID)
|
||||
// // Then define and call a func to handle the successful attachment of a PaymentMethod.
|
||||
// // handleSubscriptionUpdated(subscription)
|
||||
// case "customer.subscription.created":
|
||||
// var subscription stripe.Subscription
|
||||
// err := json.Unmarshal(event.Data.Raw, &subscription)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, "Error parsing webhook JSON: %v\n")
|
||||
// }
|
||||
// log.Printf("Subscription created for %s.", subscription.ID)
|
||||
// // Then define and call a func to handle the successful attachment of a PaymentMethod.
|
||||
// // handleSubscriptionCreated(subscription)
|
||||
// case "customer.subscription.trial_will_end":
|
||||
// var subscription stripe.Subscription
|
||||
// err := json.Unmarshal(event.Data.Raw, &subscription)
|
||||
// if err != nil {
|
||||
// return c.String(http.StatusBadRequest, "Error parsing webhook JSON: %v\n")
|
||||
// }
|
||||
// log.Printf("Subscription trial will end for %s.", subscription.ID)
|
||||
// // Then define and call a func to handle the successful attachment of a PaymentMethod.
|
||||
// // handleSubscriptionTrialWillEnd(subscription)
|
||||
// default:
|
||||
// return c.String(http.StatusBadRequest, "Unhandled event type: %s\n")
|
||||
// }
|
||||
// return c.NoContent(http.StatusOK)
|
||||
// }
|
||||
//}
|
||||
92
internal/server/server.go
Normal file
92
internal/server/server.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Router *echo.Echo
|
||||
//ms *services.MemberService
|
||||
//us *services.UserService
|
||||
addr string
|
||||
}
|
||||
|
||||
func New(db *gorm.DB) *Server {
|
||||
return &Server{
|
||||
Router: echo.New(),
|
||||
//ms: services.NewMemberService(db),
|
||||
//us: services.NewUserService(db),
|
||||
addr: fmt.Sprintf("0.0.0.0:%s", os.Getenv("PORT")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s Server) MountHandlers() {
|
||||
// config
|
||||
s.Router.HideBanner = true
|
||||
s.Router.Debug = strings.ToLower(os.Getenv("DEBUG")) == "true"
|
||||
s.Router.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||
Format: "${time_rfc3339} [${method}: ${status}] ${uri}; ip=${remote_ip}; ${latency_human}; ${user_agent}\n",
|
||||
}))
|
||||
s.Router.HTTPErrorHandler = s.customHTTPErrorHandler
|
||||
// middlewares
|
||||
s.Router.Use(middleware.Recover())
|
||||
s.Router.Use(middleware.Secure())
|
||||
// static assets
|
||||
s.Router.Static("/static", "assets")
|
||||
// landing page
|
||||
s.Router.GET("/", s.handleHomePage())
|
||||
}
|
||||
|
||||
func (s Server) Start() {
|
||||
go func() {
|
||||
if err := s.Router.Start(s.addr); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.Router.Logger.Fatal("shutting down the server")
|
||||
}
|
||||
}()
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
<-quit
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := s.Router.Shutdown(ctx); err != nil {
|
||||
s.Router.Logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Server) renderTempl(c echo.Context, status int, t templ.Component) error {
|
||||
c.Response().Writer.WriteHeader(status)
|
||||
|
||||
err := t.Render(context.Background(), c.Response().Writer)
|
||||
if err != nil {
|
||||
return c.String(http.StatusInternalServerError, "failed to render response template")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Server) customHTTPErrorHandler(err error, c echo.Context) {
|
||||
code := http.StatusInternalServerError
|
||||
var he *echo.HTTPError
|
||||
if errors.As(err, &he) {
|
||||
code = he.Code
|
||||
}
|
||||
s.Router.Logger.Error(err)
|
||||
|
||||
errorPage := fmt.Sprintf("assets/html/HTTP%d.html", code)
|
||||
if err := c.File(errorPage); err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
}
|
||||
27
internal/views/base.templ
Normal file
27
internal/views/base.templ
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package views
|
||||
|
||||
templ BaseLayout() {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Dwight | Assistant to the Manager</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description" content="AI assistant to help you improve your management"/>
|
||||
<link rel="icon" href="/static/img/favicon.svg"/>
|
||||
<link rel="stylesheet" href="/static/css/pico.min.css"/>
|
||||
<link rel="stylesheet" href="/static/css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="container-fluid">
|
||||
<ul>
|
||||
<li><a href="/"><b>🧠 Dwight</b> </a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main class="container">
|
||||
{ children... }
|
||||
</main>
|
||||
<footer class="container-fluid">🧠 Dwight © 2024</footer>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
70
internal/views/base_templ.go
Normal file
70
internal/views/base_templ.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: 0.2.476
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func BaseLayout() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
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 = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var2 := `Dwight | Assistant to the Manager`
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"AI assistant to help you improve your management\"><link rel=\"icon\" href=\"/static/img/favicon.svg\"><link rel=\"stylesheet\" href=\"/static/css/pico.min.css\"><link rel=\"stylesheet\" href=\"/static/css/style.css\"></head><body><nav class=\"container-fluid\"><ul><li><a href=\"/\"><b>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var3 := `🧠 Dwight`
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b></a></li></ul></nav><main class=\"container\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main><footer class=\"container-fluid\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var4 := `🧠 Dwight © 2024`
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</footer></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
7
internal/views/index.templ
Normal file
7
internal/views/index.templ
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package views
|
||||
|
||||
templ Index() {
|
||||
@BaseLayout() {
|
||||
<p>Hi </p>
|
||||
}
|
||||
}
|
||||
59
internal/views/index_templ.go
Normal file
59
internal/views/index_templ.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: 0.2.476
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func Index() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
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_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var3 := `Hi `
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = BaseLayout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
37
main.go
Normal file
37
main.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/rjNemo/rentease/internal/server"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if os.Getenv("ENV") != "PROD" {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
log.Fatalln("Error loading .env file")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("error connecting to the database %s\n", err)
|
||||
}
|
||||
|
||||
//err = db.AutoMigrate(&services.User{}, &models.Member{}, &models.MeetingNote{})
|
||||
//if err != nil {
|
||||
// log.Fatalf("error migrating the database %s\n", err)
|
||||
//}
|
||||
|
||||
s := server.New(db)
|
||||
s.MountHandlers()
|
||||
s.Start()
|
||||
}
|
||||
Loading…
Reference in a new issue