bootstrapped
This commit is contained in:
parent
5721ea14a9
commit
db69f3b519
36 changed files with 369 additions and 192 deletions
0
project/.gitignore → bootstrap/.gitignore
vendored
0
project/.gitignore → bootstrap/.gitignore
vendored
|
@ -41,11 +41,14 @@ live/sync_assets:
|
||||||
dev:
|
dev:
|
||||||
@make -j5 live/templ live/server live/tailwind live/sync_assets
|
@make -j5 live/templ live/server live/tailwind live/sync_assets
|
||||||
|
|
||||||
reset:
|
db-status:
|
||||||
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(dsn) goose -dir=$(migrationPath) reset
|
GOOSE_DRIVER=$(DB_DRIVER) GOOSE_DBSTRING=$(DB_NAME) goose status
|
||||||
|
|
||||||
up:
|
db-reset:
|
||||||
@GOOSE_DRIVER=postgres GOOSE_DBSTRING=$(dsn) goose -dir=$(migrationPath) up
|
@GOOSE_DRIVER=$(DB_DRIVER) GOOSE_DBSTRING=$(DB_NAME) goose -dir=$(MIGRATION_DIR) reset
|
||||||
|
|
||||||
seed:
|
db-up:
|
||||||
@go run scripts/seed/main.go
|
@GOOSE_DRIVER=$(DB_DRIVER) GOOSE_DBSTRING=$(DB_NAME) goose -dir=$(MIGRATION_DIR) up
|
||||||
|
|
||||||
|
db-seed:
|
||||||
|
@go run cmd/scripts/seed/main.go
|
3
bootstrap/app/conf/conf.go
Normal file
3
bootstrap/app/conf/conf.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package conf
|
||||||
|
|
||||||
|
// Application config
|
34
bootstrap/app/db/db.go
Normal file
34
bootstrap/app/db/db.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/anthdm/gothkit/pkg/db"
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect/sqlitedialect"
|
||||||
|
"github.com/uptrace/bun/extra/bundebug"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Query *bun.DB
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
config := db.Config{
|
||||||
|
Driver: os.Getenv("DB_DRIVER"),
|
||||||
|
Name: os.Getenv("DB_NAME"),
|
||||||
|
Password: os.Getenv("DB_PASSWORD"),
|
||||||
|
User: os.Getenv("DB_USER"),
|
||||||
|
Host: os.Getenv("DB_HOST"),
|
||||||
|
}
|
||||||
|
db, err := db.New(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
Query = bun.NewDB(db, sqlitedialect.New())
|
||||||
|
if kit.IsDevelopment() {
|
||||||
|
Query.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
||||||
|
}
|
||||||
|
}
|
13
bootstrap/app/handlers/auth.go
Normal file
13
bootstrap/app/handlers/auth.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"example-app/app/types"
|
||||||
|
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleAuthentication(w http.ResponseWriter, r *http.Request) (kit.Auth, error) {
|
||||||
|
return types.AuthUser{}, nil
|
||||||
|
}
|
13
bootstrap/app/handlers/landing.go
Normal file
13
bootstrap/app/handlers/landing.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example-app/app/db"
|
||||||
|
"example-app/app/views/landing"
|
||||||
|
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleLandingIndex(kit *kit.Kit) error {
|
||||||
|
db.Query.NewSelect().Scan(kit.Request.Context())
|
||||||
|
return kit.Render(landing.Index())
|
||||||
|
}
|
61
bootstrap/app/routes.go
Normal file
61
bootstrap/app/routes.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"example-app/app/handlers"
|
||||||
|
"example-app/app/views/errors"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit/middleware"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define your global middleware
|
||||||
|
func InitializeMiddleware(router *chi.Mux) {
|
||||||
|
router.Use(chimiddleware.Logger)
|
||||||
|
router.Use(chimiddleware.Recoverer)
|
||||||
|
router.Use(middleware.WithRequestURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define your routes in here
|
||||||
|
func InitializeRoutes(router *chi.Mux) {
|
||||||
|
// Comment out to configure your authentication
|
||||||
|
authConfig := kit.AuthenticationConfig{
|
||||||
|
AuthFunc: handlers.HandleAuthentication,
|
||||||
|
RedirectURL: "/login",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routes that "might" have an authenticated user
|
||||||
|
router.Group(func(app chi.Router) {
|
||||||
|
app.Use(kit.WithAuthentication(authConfig, false)) // strict set to false
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.Get("/", kit.Handler(handlers.HandleLandingIndex))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Authenticated routes
|
||||||
|
//
|
||||||
|
// Routes that "must" have an authenticated user or else they
|
||||||
|
// will be redirected to the configured redirectURL, set in the
|
||||||
|
// AuthenticationConfig.
|
||||||
|
router.Group(func(app chi.Router) {
|
||||||
|
app.Use(kit.WithAuthentication(authConfig, true)) // strict set to true
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
// app.Get("/path", kit.Handler(myHandler.HandleIndex))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundHandler that will be called when the requested path could
|
||||||
|
// not be found.
|
||||||
|
func NotFoundHandler(kit *kit.Kit) error {
|
||||||
|
return kit.Render(errors.Error404())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorHandler that will be called on errors return from application handlers.
|
||||||
|
func ErrorHandler(kit *kit.Kit, err error) {
|
||||||
|
slog.Error("internal server error", "err", err.Error(), "path", kit.Request.URL.Path)
|
||||||
|
kit.Render(errors.Error500())
|
||||||
|
}
|
14
bootstrap/app/types/auth.go
Normal file
14
bootstrap/app/types/auth.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// AuthUser represents an user that might be authenticated.
|
||||||
|
type AuthUser struct {
|
||||||
|
ID int
|
||||||
|
Email string
|
||||||
|
LoggedIn bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check should return true if the user is authenticated.
|
||||||
|
// See handlers/auth.go.
|
||||||
|
func (user AuthUser) Check() bool {
|
||||||
|
return user.ID > 0 && user.LoggedIn
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"example-app/views/layouts"
|
"example-app/app/views/layouts"
|
||||||
)
|
)
|
||||||
|
|
||||||
templ Error400() {
|
templ Error404() {
|
||||||
@layouts.BaseLayout() {
|
@layouts.BaseLayout() {
|
||||||
<div class="h-screen w-full flex flex-col justify-center align-middle items-center gap-4">
|
<div class="h-screen w-full flex flex-col justify-center align-middle items-center gap-4">
|
||||||
<div class="text-muted-foreground text-5xl font-bold">404</div>
|
<div class="text-muted-foreground text-5xl font-bold">404</div>
|
|
@ -1,6 +1,6 @@
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import "example-app/views/layouts"
|
import "example-app/app/views/layouts"
|
||||||
|
|
||||||
templ Error500() {
|
templ Error500() {
|
||||||
@layouts.BaseLayout() {
|
@layouts.BaseLayout() {
|
18
bootstrap/app/views/landing/index.templ
Normal file
18
bootstrap/app/views/landing/index.templ
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package landing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anthdm/gothkit/pkg/view"
|
||||||
|
"example-app/app/views/layouts"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ Index() {
|
||||||
|
@layouts.App() {
|
||||||
|
<div class="mt-20">
|
||||||
|
<h1 class="text-3xl lg:text-6xl font-bold text-center">Ship stuff with small teams</h1>
|
||||||
|
</div>
|
||||||
|
<div>{ view.URL(ctx).Path }</div>
|
||||||
|
if view.Auth(ctx).Check() {
|
||||||
|
<div>You are authenticated</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package layouts
|
package layouts
|
||||||
|
|
||||||
import "example-app/views/components"
|
import "example-app/app/views/components"
|
||||||
|
|
||||||
templ App() {
|
templ App() {
|
||||||
@BaseLayout() {
|
@BaseLayout() {
|
|
@ -1,33 +1,30 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"example-app/app"
|
||||||
"log/slog"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"example-app/db"
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
db, err := db.New()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = db
|
|
||||||
|
|
||||||
// Routes configuration
|
|
||||||
router := chi.NewMux()
|
router := chi.NewMux()
|
||||||
if true {
|
|
||||||
router.Handle("/*", disableCache(staticDev()))
|
|
||||||
}
|
|
||||||
initializeRoutes(router, db)
|
|
||||||
|
|
||||||
listenAddr := os.Getenv("HTTP_LISTEN_ADDR")
|
app.InitializeMiddleware(router)
|
||||||
slog.Info("application started", "listenAddr", listenAddr)
|
|
||||||
|
if kit.IsDevelopment() {
|
||||||
|
router.Handle("/public/*", disableCache(staticDev()))
|
||||||
|
}
|
||||||
|
|
||||||
|
kit.UseErrorHandler(app.ErrorHandler)
|
||||||
|
router.HandleFunc("/*", kit.Handler(app.NotFoundHandler))
|
||||||
|
|
||||||
|
app.InitializeRoutes(router)
|
||||||
|
|
||||||
|
fmt.Printf("application running in %s at %s\n", kit.Env(), "http://localhost:7331")
|
||||||
|
|
||||||
http.ListenAndServe(os.Getenv("HTTP_LISTEN_ADDR"), router)
|
http.ListenAndServe(os.Getenv("HTTP_LISTEN_ADDR"), router)
|
||||||
}
|
}
|
7
bootstrap/cmd/scripts/seed/main.go
Normal file
7
bootstrap/cmd/scripts/seed/main.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("there are no seeds.")
|
||||||
|
}
|
29
bootstrap/go.mod
Normal file
29
bootstrap/go.mod
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module example-app
|
||||||
|
|
||||||
|
go 1.22.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/anthdm/gothkit v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/a-h/templ v0.2.707 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
|
github.com/uptrace/bun v1.2.1
|
||||||
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.1
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/anthdm/gothkit => ../gothkit
|
33
bootstrap/go.sum
Normal file
33
bootstrap/go.sum
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
||||||
|
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
||||||
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
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/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/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/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk=
|
||||||
|
github.com/uptrace/bun v1.2.1/go.mod h1:cNg+pWBUMmJ8rHnETgf65CEvn3aIKErrwOD6IA8e+Ec=
|
||||||
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1 h1:IprvkIKUjEjvt4VKpcmLpbMIucjrsmUPJOSlg19+a0Q=
|
||||||
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1/go.mod h1:mMQf4NUpgY8bnOanxGmxNiHCdALOggS4cZ3v63a9D/o=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.1 h1:85MYpX3QESYI02YerKxUi1CD9mHuLrc2BXs1eOCtQus=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.1/go.mod h1:sfGKIi0HSGxsTC/sgIHGwpnYduHHYhdMeOIwurgSY+Y=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
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=
|
0
bootstrap/public/favicon.ico
Normal file
0
bootstrap/public/favicon.ico
Normal file
31
gothkit/pkg/db/db.go
Normal file
31
gothkit/pkg/db/db.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DriverSqlite3 = "sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Driver string
|
||||||
|
Name string
|
||||||
|
Host string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg Config) (*sql.DB, error) {
|
||||||
|
switch cfg.Driver {
|
||||||
|
case DriverSqlite3:
|
||||||
|
name := cfg.Name
|
||||||
|
if len(name) == 0 {
|
||||||
|
name = "app_db"
|
||||||
|
}
|
||||||
|
return sql.Open(cfg.Driver, name)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid database driver (%s): currently only sqlite3 is supported", cfg.Driver)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
)
|
)
|
||||||
|
@ -24,15 +25,14 @@ type Auth interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
auth Auth = defaultAuth{}
|
|
||||||
errorHandler = func(kit *Kit, err error) {
|
errorHandler = func(kit *Kit, err error) {
|
||||||
kit.Text(http.StatusInternalServerError, err.Error())
|
kit.Text(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultAuth struct{}
|
type DefaultAuth struct{}
|
||||||
|
|
||||||
func (defaultAuth) Check() bool { return false }
|
func (DefaultAuth) Check() bool { return false }
|
||||||
|
|
||||||
type Kit struct {
|
type Kit struct {
|
||||||
Response http.ResponseWriter
|
Response http.ResponseWriter
|
||||||
|
@ -40,19 +40,28 @@ type Kit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UseErrorHandler(h ErrorHandlerFunc) { errorHandler = h }
|
func UseErrorHandler(h ErrorHandlerFunc) { errorHandler = h }
|
||||||
func SetAuth(a Auth) { auth = a }
|
|
||||||
|
|
||||||
func (kit *Kit) Auth() Auth {
|
func (kit *Kit) Auth() Auth {
|
||||||
value, ok := kit.Request.Context().Value(AuthKey{}).(Auth)
|
value, ok := kit.Request.Context().Value(AuthKey{}).(Auth)
|
||||||
if !ok {
|
if !ok {
|
||||||
slog.Warn("kit authentication not set")
|
slog.Warn("kit authentication not set")
|
||||||
return auth
|
return DefaultAuth{}
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kit *Kit) Redirect(status int, url string) {
|
func (kit *Kit) Redirect(status int, url string) error {
|
||||||
http.Redirect(kit.Response, kit.Request, url, status)
|
http.Redirect(kit.Response, kit.Request, url, status)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kit *Kit) HXRedirect(status int, url string) error {
|
||||||
|
if len(kit.Request.Header.Get("HX-Request")) > 0 {
|
||||||
|
kit.Response.Header().Set("HX-Redirect", url)
|
||||||
|
kit.Response.WriteHeader(http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return kit.Redirect(status, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kit *Kit) JSON(status int, v any) error {
|
func (kit *Kit) JSON(status int, v any) error {
|
||||||
|
@ -123,3 +132,15 @@ func WithAuthentication(config AuthenticationConfig, strict bool) func(http.Hand
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsDevelopment() bool {
|
||||||
|
return os.Getenv("KIT_ENV") == "development"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsProduction() bool {
|
||||||
|
return os.Getenv("KIT_ENV") == "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
func Env() string {
|
||||||
|
return os.Getenv("KIT_ENV")
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
// func Authenticated(next http.Handler) http.Handler {
|
import (
|
||||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
"context"
|
||||||
// kit := &kit.Kit{
|
"net/http"
|
||||||
// Response: w,
|
)
|
||||||
// Request: r,
|
|
||||||
// }
|
|
||||||
// if !kit.Auth().LoggedIn {
|
|
||||||
|
|
||||||
// }
|
type RequestURLKey struct{}
|
||||||
// next.ServeHTTP(w, r.WithContext(ctx))
|
|
||||||
// })
|
func WithRequestURL(next http.Handler) http.Handler {
|
||||||
// }
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), RequestURLKey{}, r.URL)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
31
gothkit/pkg/view/view.go
Normal file
31
gothkit/pkg/view/view.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit"
|
||||||
|
"github.com/anthdm/gothkit/pkg/kit/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth is a view helper function that returns the currency Auth.
|
||||||
|
// If Auth is not set a default auth will be returned
|
||||||
|
func Auth(ctx context.Context) kit.Auth {
|
||||||
|
value, ok := ctx.Value(kit.AuthKey{}).(kit.Auth)
|
||||||
|
if !ok {
|
||||||
|
return kit.DefaultAuth{}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL is a view helper that returns the current URL.
|
||||||
|
// The request path can be acccessed with:
|
||||||
|
// view.URL(ctx).Path
|
||||||
|
func URL(ctx context.Context) *url.URL {
|
||||||
|
value, ok := ctx.Value(middleware.RequestURLKey{}).(*url.URL)
|
||||||
|
if !ok {
|
||||||
|
return &url.URL{}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
|
||||||
|
}
|
15
install.sh
15
install.sh
|
@ -1 +1,14 @@
|
||||||
echo "this is working fine"
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "enter the name of you project: "
|
||||||
|
read varname
|
||||||
|
|
||||||
|
git clone git@github.com:anthdm/gothkit.git _gothkit
|
||||||
|
cd _gothkit
|
||||||
|
mv bootstrap ../$varname
|
||||||
|
cd ..
|
||||||
|
rm -rf _gothkit
|
||||||
|
echo ""
|
||||||
|
echo "project installed successfully"
|
||||||
|
echo "your project folder is available => cd $varname"
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"log/slog"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/anthdm/gothkit/pkg/kit"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
|
|
||||||
"example-app/handlers"
|
|
||||||
"example-app/views/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define your routes in here
|
|
||||||
func initializeRoutes(router *chi.Mux, db *sql.DB) {
|
|
||||||
// Configure the error handler
|
|
||||||
kit.UseErrorHandler(func(kit *kit.Kit, err error) {
|
|
||||||
slog.Error("internal server error", "err", err.Error(), "path", kit.Request.URL.Path)
|
|
||||||
kit.Render(errors.Error500())
|
|
||||||
})
|
|
||||||
|
|
||||||
// Comment out to configure your authentication
|
|
||||||
authConfig := kit.AuthenticationConfig{
|
|
||||||
AuthFunc: handleAuthentication,
|
|
||||||
RedirectURL: "/login",
|
|
||||||
}
|
|
||||||
|
|
||||||
landingHandler := handlers.NewLandingHandler(db)
|
|
||||||
// Routes that "might" have an authenticated user
|
|
||||||
router.Group(func(app chi.Router) {
|
|
||||||
app.Use(kit.WithAuthentication(authConfig, false)) // strict set to false
|
|
||||||
|
|
||||||
// Routes
|
|
||||||
app.Get("/", kit.Handler(landingHandler.HandleIndex))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Routes that "must" have an authenticated user or else they
|
|
||||||
// will be redirected to the configured redirectURL, set in the
|
|
||||||
// AuthenticationConfig.
|
|
||||||
router.Group(func(app chi.Router) {
|
|
||||||
app.Use(kit.WithAuthentication(authConfig, true)) // strict set to true
|
|
||||||
|
|
||||||
// Routes
|
|
||||||
// app.Get("/path", kit.Handler(myHandler.HandleIndex))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthUser struct {
|
|
||||||
ID int
|
|
||||||
Email string
|
|
||||||
LoggedIn bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user AuthUser) Check() bool {
|
|
||||||
return user.ID > 0 && user.LoggedIn
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleAuthentication(w http.ResponseWriter, r *http.Request) (kit.Auth, error) {
|
|
||||||
return AuthUser{}, nil
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DriverSqlite3 = "sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func New() (*sql.DB, error) {
|
|
||||||
driver := os.Getenv("DB_DRIVER")
|
|
||||||
|
|
||||||
switch driver {
|
|
||||||
case DriverSqlite3:
|
|
||||||
name := os.Getenv("DB_NAME")
|
|
||||||
if len(name) == 0 {
|
|
||||||
name = "gothkit"
|
|
||||||
}
|
|
||||||
return sql.Open(driver, name)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid database driver (%s): currently only sqlite3 is supported", driver)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
module example-app
|
|
||||||
|
|
||||||
go 1.22.0
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/anthdm/gothkit v0.0.0-00010101000000-000000000000
|
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/a-h/templ v0.2.707 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
|
||||||
)
|
|
||||||
|
|
||||||
replace github.com/anthdm/gothkit => ../gothkit
|
|
|
@ -1,8 +0,0 @@
|
||||||
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
|
||||||
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
|
||||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
|
||||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
|
||||||
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/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
|
|
@ -1,22 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"example-app/views/landing"
|
|
||||||
|
|
||||||
"github.com/anthdm/gothkit/pkg/kit"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LandingHandler struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLandingHandler(db *sql.DB) *LandingHandler {
|
|
||||||
return &LandingHandler{
|
|
||||||
db: db,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *LandingHandler) HandleIndex(kit *kit.Kit) error {
|
|
||||||
return kit.Render(landing.Index())
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package landing
|
|
||||||
|
|
||||||
import "example-app/views/layouts"
|
|
||||||
|
|
||||||
templ Index() {
|
|
||||||
@layouts.App() {
|
|
||||||
<div class="mt-20">
|
|
||||||
<h1 class="text-3xl lg:text-6xl font-bold text-center">Ship stuff with small teams</h1>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue