Update structure to domain driven architecture.
This commit is contained in:
@@ -10,15 +10,15 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nfeeder/internal/db"
|
"nfeeder/db"
|
||||||
"nfeeder/internal/web"
|
"nfeeder/web"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// @Logger
|
// INFO: Logger
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
var handler slog.Handler
|
var handler slog.Handler
|
||||||
if os.Getenv("ENV") == "production" {
|
if os.Getenv("ENV") == "production" {
|
||||||
@@ -31,7 +31,7 @@ func main() {
|
|||||||
ctx, ctxCancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, ctxCancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
defer ctxCancel()
|
defer ctxCancel()
|
||||||
|
|
||||||
// @DB
|
// INFO:DB
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
connStr := os.Getenv("DATABASE_URL")
|
connStr := os.Getenv("DATABASE_URL")
|
||||||
if connStr == "" {
|
if connStr == "" {
|
||||||
@@ -67,7 +67,7 @@ func main() {
|
|||||||
// Create Store sqlc wrapper
|
// Create Store sqlc wrapper
|
||||||
store := db.NewStore(pool)
|
store := db.NewStore(pool)
|
||||||
|
|
||||||
// @Server
|
// INFO: Server
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
jwtSecret := os.Getenv("JWT_SECRET")
|
jwtSecret := os.Getenv("JWT_SECRET")
|
||||||
if jwtSecret == "" {
|
if jwtSecret == "" {
|
||||||
@@ -87,7 +87,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// @GracefulShutdown
|
// INFO: GracefulShutdown
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
logger.Warn("shutdown signal received", "signal", "SIGTERM/Interrupt")
|
logger.Warn("shutdown signal received", "signal", "SIGTERM/Interrupt")
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type model struct {
|
||||||
|
name string
|
||||||
|
sayHello bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func initialModel() model {
|
||||||
|
return model{
|
||||||
|
name: "jason",
|
||||||
|
sayHello: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Init() tea.Cmd {
|
||||||
|
// any init commands like fetching data etc
|
||||||
|
// to populate model if need be
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update loop like a game
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
|
||||||
|
// Handle key presses
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
// Quit the app
|
||||||
|
case "ctrl+c", "q":
|
||||||
|
return m, tea.Quit
|
||||||
|
|
||||||
|
// Toggle the greeting
|
||||||
|
case "enter", " ":
|
||||||
|
m.sayHello = !m.sayHello
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// View: Returns a string representing the UI. like a render method
|
||||||
|
func (m model) View() tea.View {
|
||||||
|
s := "nfeeder Dev Console\n"
|
||||||
|
s += "-------------------\n\n"
|
||||||
|
|
||||||
|
if m.sayHello {
|
||||||
|
s += fmt.Sprintf("Hello, %s! Nice to see you.\n\n", m.name)
|
||||||
|
} else {
|
||||||
|
s += "Press ENTER to say hello.\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "Press 'q' to quit.\n"
|
||||||
|
|
||||||
|
return tea.NewView(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
p := tea.NewProgram(initialModel())
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Printf("Alas, there's been an error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ module nfeeder
|
|||||||
go 1.26.1
|
go 1.26.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
charm.land/bubbletea/v2 v2.0.6
|
||||||
github.com/go-chi/chi/v5 v5.2.5
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
github.com/jackc/pgx/v5 v5.9.1
|
github.com/jackc/pgx/v5 v5.9.1
|
||||||
@@ -10,9 +11,23 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.7 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
golang.org/x/text v0.36.0 // indirect
|
golang.org/x/text v0.36.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,25 @@
|
|||||||
|
charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo=
|
||||||
|
charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 h1:Q9fO0y1Zo5KB/5Vu8JZoLGm1N3RzF9bNj3Ao3xoR+Ac=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f h1:UytXHv0UxnsDFmL/7Z9Q5SBYPwSuRLXHbwx+6LycZ2w=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -13,17 +35,31 @@ github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc=
|
|||||||
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||||
|
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||||
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||||
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
# 1. Register a new unique user
|
|
||||||
POST http://localhost:3333/register
|
|
||||||
[FormParams]
|
|
||||||
email: test_user@debian.org
|
|
||||||
password: supersecretpassword
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Captures]
|
|
||||||
access_token: jsonpath "$.access_token"
|
|
||||||
refresh_token: jsonpath "$.refresh_token"
|
|
||||||
|
|
||||||
|
|
||||||
# 2. Access a protected route with the first token
|
|
||||||
GET http://localhost:3333/home
|
|
||||||
Authorization: Bearer {{access_token}}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.status" == "authenticated"
|
|
||||||
|
|
||||||
|
|
||||||
# 3. Refresh the tokens
|
|
||||||
POST http://localhost:3333/refresh
|
|
||||||
Content-Type: application/json
|
|
||||||
{
|
|
||||||
"refresh_token": "{{refresh_token}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Captures]
|
|
||||||
# Overwrite with the fresh tokens
|
|
||||||
next_access_token: jsonpath "$.access_token"
|
|
||||||
next_refresh_token: jsonpath "$.refresh_token"
|
|
||||||
|
|
||||||
[Asserts]
|
|
||||||
# Now compare the two distinct variable names
|
|
||||||
variable "next_refresh_token" != "{{refresh_token}}"
|
|
||||||
|
|
||||||
# 4. Access the protected route again with the NEW access token
|
|
||||||
GET http://localhost:3333/home
|
|
||||||
Authorization: Bearer {{next_access_token}}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.status" == "authenticated"
|
|
||||||
|
|
||||||
# Log out user to clean table of tokens etc
|
|
||||||
POST http://localhost:3333/logout
|
|
||||||
Content-Type: application/json
|
|
||||||
{
|
|
||||||
"refresh_token": "{{next_refresh_token}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.message" == "logout success"
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Check accessing protected route with an invalid token gives a 401
|
|
||||||
GET http://localhost:3333/home
|
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
|
|
||||||
|
|
||||||
HTTP 401
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.error" == "unauthorized"
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
POST http://localhost:3333/login
|
|
||||||
[FormParams]
|
|
||||||
email: test_user@debian.org
|
|
||||||
password: supersecretpassword
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Captures]
|
|
||||||
access_token: jsonpath "$.access_token"
|
|
||||||
refresh_token: jsonpath "$.refresh_token"
|
|
||||||
|
|
||||||
# Check the logged in use can access the protected route
|
|
||||||
GET http://localhost:3333/home
|
|
||||||
Authorization: Bearer {{access_token}}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.status" == "authenticated"
|
|
||||||
|
|
||||||
# Log out user to clean table of tokens etc
|
|
||||||
POST http://localhost:3333/logout
|
|
||||||
Content-Type: application/json
|
|
||||||
{
|
|
||||||
"refresh_token": "{{refresh_token}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTP 200
|
|
||||||
[Asserts]
|
|
||||||
jsonpath "$.message" == "logout success"
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "nfeeder/tui/pages"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sessionState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
LoginView sessionState = iota
|
||||||
|
FeedView
|
||||||
|
HelpView
|
||||||
|
)
|
||||||
|
|
||||||
|
type MainModel struct {
|
||||||
|
state sessionState
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
|
||||||
|
// Sub-models
|
||||||
|
loginPage pages.LoginModel
|
||||||
|
feedPage pages.FeedModel
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO CLEANUP: Test route for now
|
||||||
|
func (s *Server) handleHome() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 1. Grab the userID from the context (placed there by s.hasAuth)
|
||||||
|
userID, ok := userIDFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
// Technically never happen if the middleware is working...
|
||||||
|
log.Printf("UNAUTHORIZED!")
|
||||||
|
http.Error(w, "User not found in context", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Respond with something that proves it works
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"message": "Welcome to the nfeeder API!",
|
||||||
|
"user_id": userID,
|
||||||
|
"status": "authenticated",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"nfeeder/internal/db"
|
"nfeeder/db"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
)
|
)
|
||||||
@@ -5,11 +5,13 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"nfeeder/internal/db"
|
"nfeeder/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ISSUER = "nfeeder-app"
|
const (
|
||||||
var MAX_USER_SESSIONS = 3
|
MAX_USER_SESSIONS = 3
|
||||||
|
ISSUER = "nfeeder-app"
|
||||||
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
Reference in New Issue
Block a user