diff --git a/cmd/server/main.go b/cmd/server/main.go index e4039c5..0ab9f83 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,34 +6,43 @@ import ( "git.tijl.dev/tijl/tijl.dev/internal/assets" "git.tijl.dev/tijl/tijl.dev/internal/config" "git.tijl.dev/tijl/tijl.dev/internal/i18n" + "git.tijl.dev/tijl/tijl.dev/modules/logger" "git.tijl.dev/tijl/tijl.dev/static" "git.tijl.dev/tijl/tijl.dev/views" + "github.com/gofiber/contrib/fiberzerolog" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/template/html/v2" - "github.com/mikhail-bigun/fiberlogrus" - log "github.com/sirupsen/logrus" ) func main() { - log.SetLevel(log.DebugLevel) + // Load config config.Load() + // Load assets assets.Load() + // Load translations i18n.Load() - engine := html.NewFileSystem(http.FS(views.Embed), ".html") + // Todo load db, migrations and plugins + // Init templating engine + engine := html.NewFileSystem(http.FS(views.Embed), ".html") engine.AddFunc("icon", assets.Svg) + // Init fiber app := fiber.New(fiber.Config{ Views: engine, DisableStartupMessage: true, }) + app.Use(fiberzerolog.New(fiberzerolog.Config{ + Logger: &log.Logger, + })) - app.Use(fiberlogrus.New()) - + /* + Routes + */ app.Get("/", func(c *fiber.Ctx) error { data := getCommon(c) data["Title"] = i18n.Translate(c, "home") @@ -70,18 +79,24 @@ func main() { return nil }) + // Static routes app.Use("/static", filesystem.New(filesystem.Config{ Root: http.FS(static.Embed), })) + // 404 app.Use(func(c *fiber.Ctx) error { data := getCommon(c) return c.Render("404", data, "layouts/base") }) - log.Fatal(app.Listen(":3000")) + // Listen web server + if err := app.Listen(":3000"); err != nil { + log.Fatal().Err(err).Msg("Fiber app error") + } } +// Common functions for in templating func getCommon(c *fiber.Ctx) fiber.Map { return fiber.Map{ "Path": c.Path(), diff --git a/go.mod b/go.mod index a9c687f..eda027a 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,16 @@ require ( github.com/gofiber/fiber/v2 v2.52.5 github.com/gofiber/template/html/v2 v2.1.2 github.com/mikhail-bigun/fiberlogrus v0.1.3 + github.com/rs/zerolog v1.33.0 github.com/sirupsen/logrus v1.9.3 + github.com/sqlc-dev/pqtype v0.3.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/gofiber/contrib/fiberzerolog v1.0.2 // indirect github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 04c1a89..e5a2e68 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,15 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofiber/contrib/fiberzerolog v1.0.2 h1:LMa/luarQVeINoRwZLHtLQYepLPDIwUNB5OmdZKk+s8= +github.com/gofiber/contrib/fiberzerolog v1.0.2/go.mod h1:aTPsgArSgxRWcUeJ/K6PiICz3mbQENR1QOR426QwOoQ= github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= @@ -24,19 +28,26 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib 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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mikhail-bigun/fiberlogrus v0.1.3 h1:2aVtFSfMr/T8J2p4228TwV6txvUEOQxKlu5LpcKyym0= github.com/mikhail-bigun/fiberlogrus v0.1.3/go.mod h1:Tt0FrmLd2maF8VSHsx1pfiWNRTrjyBrKfDA2JW5hvmY= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sqlc-dev/pqtype v0.3.0 h1:b09TewZ3cSnO5+M1Kqq05y0+OjqIptxELaSayg7bmqk= +github.com/sqlc-dev/pqtype v0.3.0/go.mod h1:oyUjp5981ctiL9UYvj1bVvCKi8OXkCa0u645hce7CAs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -54,6 +65,7 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/assets/svg.go b/internal/assets/svg.go index 057b5c8..b65e6fc 100644 --- a/internal/assets/svg.go +++ b/internal/assets/svg.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - log "github.com/sirupsen/logrus" + "git.tijl.dev/tijl/tijl.dev/modules/logger" ) var SVGData map[string]string @@ -18,13 +18,13 @@ func loadSVGs() { files, err := filepath.Glob(filepath.Join(dir, "*.svg")) if err != nil { - log.Fatalf("Error loading SVG files: %v", err) + log.Fatal().Err(err) } for _, file := range files { data, err := os.ReadFile(file) if err != nil { - log.Warnf("Error reading SVG file %s: %v", file, err) + log.Fatal().Err(err) continue } filename := filepath.Base(file) @@ -32,7 +32,7 @@ func loadSVGs() { SVGData[key] = string(data) } - log.Debug("Loaded SVG files") + log.Debug().Msg("Loaded SVG files") } func Svg(name string) template.HTML { diff --git a/internal/config/config.go b/internal/config/config.go index 2e97b4e..5ae463f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,7 +3,7 @@ package config import ( "os" - log "github.com/sirupsen/logrus" + log "git.tijl.dev/tijl/tijl.dev/modules/logger" "gopkg.in/yaml.v3" ) @@ -17,15 +17,15 @@ func Load() { indexFile, err := os.ReadFile(configPath) if err != nil { - log.Fatal(err) + log.Fatal().Err(err) } err = yaml.Unmarshal(indexFile, &Config) if err != nil { - log.Fatal(err) + log.Fatal().Err(err) } - log.Debug("loaded config") + log.Debug().Msg("loaded config") } type ConfigType struct { diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go index 7140363..7b31cb0 100644 --- a/internal/i18n/i18n.go +++ b/internal/i18n/i18n.go @@ -2,11 +2,11 @@ package i18n import ( "encoding/json" - "github.com/gofiber/fiber/v2" "os" "path/filepath" - log "github.com/sirupsen/logrus" + log "git.tijl.dev/tijl/tijl.dev/modules/logger" + "github.com/gofiber/fiber/v2" ) var translations map[string]map[string]string @@ -19,7 +19,7 @@ func Load() { dir := "locales" files, err := filepath.Glob(filepath.Join(dir, "*.json")) if err != nil { - log.Fatalf("Error loading language files: %v", err) + log.Fatal().Err(err) } for _, file := range files { @@ -28,20 +28,20 @@ func Load() { file, err := os.Open(file) if err != nil { - log.Errorf("Error opening translation file %s: %v", file, err) + log.Error().Err(err) continue } defer file.Close() var messages map[string]string if err := json.NewDecoder(file).Decode(&messages); err != nil { - log.Errorf("Error decoding translation file %s: %v", file, err) + log.Error().Err(err) continue } translations[lang] = messages } - log.Debug("Loaded translations") + log.Debug().Msg("Loaded translations") } func Translate(c *fiber.Ctx, key string) string { diff --git a/internal/oidc/oidc.go b/internal/oidc/oidc.go index 9d19b3d..f659050 100644 --- a/internal/oidc/oidc.go +++ b/internal/oidc/oidc.go @@ -1,7 +1,5 @@ package oidc -import "github.com/coreos/go-oidc/v3/oidc" - func Setup() { } diff --git a/justfile b/justfile index 99ddd4a..32eb621 100644 --- a/justfile +++ b/justfile @@ -7,8 +7,11 @@ npm-build-css: npm-build: npm run build +generate-sqlc: + sqlc generate + go-build: go build -o tijl.dev cmd/server/main.go -build: npm-build-css npm-build go-build +build: npm-build-css npm-build generate-sqlc go-build diff --git a/migrations/00000001_init.up.sql b/migrations/00000001_init.up.sql new file mode 100644 index 0000000..41b0eb3 --- /dev/null +++ b/migrations/00000001_init.up.sql @@ -0,0 +1,30 @@ +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + uid VARCHAR UNIQUE NOT NULL, -- username as unique identifier + email VARCHAR UNIQUE, + full_name VARCHAR, + displayname VARCHAR, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE sessions ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR, + token VARCHAR NOT NULL UNIQUE, + password VARCHAR, + last_activity TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expires TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) +); + +CREATE TABLE session_ips ( + id SERIAL PRIMARY KEY, + session_id INTEGER NOT NULL, + ip_address INET NOT NULL, + access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (session_id) REFERENCES sessions (id) +); + diff --git a/migrations/0000001_init.down.sql b/migrations/0000001_init.down.sql new file mode 100644 index 0000000..c76ca32 --- /dev/null +++ b/migrations/0000001_init.down.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS session_ips; +DROP TABLE IF EXISTS sessions; +DROP TABLE IF EXISTS users; diff --git a/migrations/migrations.go b/migrations/migrations.go new file mode 100644 index 0000000..6784cac --- /dev/null +++ b/migrations/migrations.go @@ -0,0 +1,6 @@ +package migrations + +import "embed" + +//go:embed * +var Embed embed.FS diff --git a/modules/db/db.go b/modules/db/db.go new file mode 100644 index 0000000..41b7a34 --- /dev/null +++ b/modules/db/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package db + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/modules/db/models.go b/modules/db/models.go new file mode 100644 index 0000000..c1d553e --- /dev/null +++ b/modules/db/models.go @@ -0,0 +1,40 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package db + +import ( + "database/sql" + "time" + + "github.com/sqlc-dev/pqtype" +) + +type Session struct { + ID int32 + UserID int32 + Title sql.NullString + Token string + Password sql.NullString + LastActivity sql.NullTime + Expires sql.NullTime + CreatedAt time.Time +} + +type SessionIp struct { + ID int32 + SessionID int32 + IpAddress pqtype.Inet + AccessTime sql.NullTime +} + +type User struct { + ID int32 + Uid string + Email sql.NullString + FullName sql.NullString + Displayname sql.NullString + CreatedAt time.Time + UpdatedAt sql.NullTime +} diff --git a/modules/db/sessions.sql b/modules/db/sessions.sql new file mode 100644 index 0000000..ca3c7f0 --- /dev/null +++ b/modules/db/sessions.sql @@ -0,0 +1,18 @@ +-- name: GetSesssion :one +SELECT * FROM sessions WHERE token = $1; + +-- name: GetSessions :many +SELECT * FROM sessions WHERE user_id = $1 ORDER BY $2; + +-- name: GetActiveSessions :many +SELECT * FROM sessions WHERE user_id = $1 AND (expires > CURRENT_TIMESTAMP OR expires IS NULL) ORDER BY $2; + +-- name: CreateSession :exec +INSERT INTO sessions (user_id, title, token) VALUES ($1, $2, $3); + +-- name: QuickUpdateSession :exec +UPDATE sessions SET last_activity = GETDATE() WHERE id = $1; + +-- name: ExpireSession :exec +UPDATE sessions SET expires = 1 WHERE id = $1; + diff --git a/modules/db/sessions.sql.go b/modules/db/sessions.sql.go new file mode 100644 index 0000000..c52f3fe --- /dev/null +++ b/modules/db/sessions.sql.go @@ -0,0 +1,146 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: sessions.sql + +package db + +import ( + "context" + "database/sql" +) + +const createSession = `-- name: CreateSession :exec +INSERT INTO sessions (user_id, title, token) VALUES ($1, $2, $3) +` + +type CreateSessionParams struct { + UserID int32 + Title sql.NullString + Token string +} + +func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) error { + _, err := q.db.ExecContext(ctx, createSession, arg.UserID, arg.Title, arg.Token) + return err +} + +const expireSession = `-- name: ExpireSession :exec +UPDATE sessions SET expires = 1 WHERE id = $1 +` + +func (q *Queries) ExpireSession(ctx context.Context, id int32) error { + _, err := q.db.ExecContext(ctx, expireSession, id) + return err +} + +const getActiveSessions = `-- name: GetActiveSessions :many +SELECT id, user_id, title, token, password, last_activity, expires, created_at FROM sessions WHERE user_id = $1 AND (expires > CURRENT_TIMESTAMP OR expires IS NULL) ORDER BY $2 +` + +type GetActiveSessionsParams struct { + UserID int32 + Column2 interface{} +} + +func (q *Queries) GetActiveSessions(ctx context.Context, arg GetActiveSessionsParams) ([]Session, error) { + rows, err := q.db.QueryContext(ctx, getActiveSessions, arg.UserID, arg.Column2) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Session + for rows.Next() { + var i Session + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Token, + &i.Password, + &i.LastActivity, + &i.Expires, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getSessions = `-- name: GetSessions :many +SELECT id, user_id, title, token, password, last_activity, expires, created_at FROM sessions WHERE user_id = $1 ORDER BY $2 +` + +type GetSessionsParams struct { + UserID int32 + Column2 interface{} +} + +func (q *Queries) GetSessions(ctx context.Context, arg GetSessionsParams) ([]Session, error) { + rows, err := q.db.QueryContext(ctx, getSessions, arg.UserID, arg.Column2) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Session + for rows.Next() { + var i Session + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Token, + &i.Password, + &i.LastActivity, + &i.Expires, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getSesssion = `-- name: GetSesssion :one +SELECT id, user_id, title, token, password, last_activity, expires, created_at FROM sessions WHERE token = $1 +` + +func (q *Queries) GetSesssion(ctx context.Context, token string) (Session, error) { + row := q.db.QueryRowContext(ctx, getSesssion, token) + var i Session + err := row.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Token, + &i.Password, + &i.LastActivity, + &i.Expires, + &i.CreatedAt, + ) + return i, err +} + +const quickUpdateSession = `-- name: QuickUpdateSession :exec +UPDATE sessions SET last_activity = GETDATE() WHERE id = $1 +` + +func (q *Queries) QuickUpdateSession(ctx context.Context, id int32) error { + _, err := q.db.ExecContext(ctx, quickUpdateSession, id) + return err +} diff --git a/modules/db/users.sql b/modules/db/users.sql new file mode 100644 index 0000000..19337e3 --- /dev/null +++ b/modules/db/users.sql @@ -0,0 +1,25 @@ +-- name: GetUser :one +SELECT * FROM users WHERE uid = $1 LIMIT 1; + +-- name: GetUserUid :one +SELECT uid FROM users WHERE id = $1 LIMIT 1; + +-- name: GetUserById :one +SELECT * FROM users WHERE id = $1 LIMIT 1; + +-- name: DeleteUser :exec +DELETE FROM users WHERE uid = $1; + +-- name: CreateUser :exec +INSERT INTO users (uid, email, full_name, displayname) +VALUES ($1, $2, $3, $4) +RETURNING id; + +-- name: UpdateUserData :exec +UPDATE users +SET email = COALESCE($2, email), + full_name = COALESCE($3, full_name), + displayname = COALESCE($4, displayname), + updated_at = CURRENT_TIMESTAMP +WHERE uid = $1; + diff --git a/modules/db/users.sql.go b/modules/db/users.sql.go new file mode 100644 index 0000000..d7ccf1b --- /dev/null +++ b/modules/db/users.sql.go @@ -0,0 +1,118 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: users.sql + +package db + +import ( + "context" + "database/sql" +) + +const createUser = `-- name: CreateUser :exec +INSERT INTO users (uid, email, full_name, displayname) +VALUES ($1, $2, $3, $4) +RETURNING id +` + +type CreateUserParams struct { + Uid string + Email sql.NullString + FullName sql.NullString + Displayname sql.NullString +} + +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) error { + _, err := q.db.ExecContext(ctx, createUser, + arg.Uid, + arg.Email, + arg.FullName, + arg.Displayname, + ) + return err +} + +const deleteUser = `-- name: DeleteUser :exec +DELETE FROM users WHERE uid = $1 +` + +func (q *Queries) DeleteUser(ctx context.Context, uid string) error { + _, err := q.db.ExecContext(ctx, deleteUser, uid) + return err +} + +const getUser = `-- name: GetUser :one +SELECT id, uid, email, full_name, displayname, created_at, updated_at FROM users WHERE uid = $1 LIMIT 1 +` + +func (q *Queries) GetUser(ctx context.Context, uid string) (User, error) { + row := q.db.QueryRowContext(ctx, getUser, uid) + var i User + err := row.Scan( + &i.ID, + &i.Uid, + &i.Email, + &i.FullName, + &i.Displayname, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getUserById = `-- name: GetUserById :one +SELECT id, uid, email, full_name, displayname, created_at, updated_at FROM users WHERE id = $1 LIMIT 1 +` + +func (q *Queries) GetUserById(ctx context.Context, id int32) (User, error) { + row := q.db.QueryRowContext(ctx, getUserById, id) + var i User + err := row.Scan( + &i.ID, + &i.Uid, + &i.Email, + &i.FullName, + &i.Displayname, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getUserUid = `-- name: GetUserUid :one +SELECT uid FROM users WHERE id = $1 LIMIT 1 +` + +func (q *Queries) GetUserUid(ctx context.Context, id int32) (string, error) { + row := q.db.QueryRowContext(ctx, getUserUid, id) + var uid string + err := row.Scan(&uid) + return uid, err +} + +const updateUserData = `-- name: UpdateUserData :exec +UPDATE users +SET email = COALESCE($2, email), + full_name = COALESCE($3, full_name), + displayname = COALESCE($4, displayname), + updated_at = CURRENT_TIMESTAMP +WHERE uid = $1 +` + +type UpdateUserDataParams struct { + Uid string + Email sql.NullString + FullName sql.NullString + Displayname sql.NullString +} + +func (q *Queries) UpdateUserData(ctx context.Context, arg UpdateUserDataParams) error { + _, err := q.db.ExecContext(ctx, updateUserData, + arg.Uid, + arg.Email, + arg.FullName, + arg.Displayname, + ) + return err +} diff --git a/modules/logger/logger.go b/modules/logger/logger.go new file mode 100644 index 0000000..f36cee4 --- /dev/null +++ b/modules/logger/logger.go @@ -0,0 +1,35 @@ +package log + +import ( + "os" + + "github.com/rs/zerolog" +) + +var ( + Logger zerolog.Logger +) + +func init() { + Logger = zerolog.New(os.Stderr).With().Timestamp().Logger() +} + +func Info() *zerolog.Event { + return Logger.Info() +} + +func Error() *zerolog.Event { + return Logger.Error() +} + +func Debug() *zerolog.Event { + return Logger.Debug() +} + +func Fatal() *zerolog.Event { + return Logger.Fatal() +} + +func SetLevel(level zerolog.Level) { + Logger = Logger.Level(level) +} diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..a525185 --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,10 @@ +version: "2" +sql: + - engine: "postgresql" + queries: "modules/db/*.sql" + schema: "migrations/*.sql" + gen: + go: + package: "db" + out: "modules/db/" + sql_package: "database/sql" diff --git a/views/index.html b/views/index.html index 2128b25..5e79ea2 100644 --- a/views/index.html +++ b/views/index.html @@ -1,87 +1,3 @@

Welcome to My Go App

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

-

This is the homepage.

{{.T.about}} diff --git a/views/layouts/base.html b/views/layouts/base.html index beec45e..c65476f 100644 --- a/views/layouts/base.html +++ b/views/layouts/base.html @@ -3,7 +3,7 @@ - +