features/added-user-tables #3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
.env
|
||||
dev.session.sql
|
||||
__debug_bin
|
||||
|
||||
server
|
||||
.vscode
|
||||
|
||||
# Binaries for programs and plugins
|
||||
|
@ -6,9 +6,12 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
"github.com/pressly/goose/v3"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/docs"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/database"
|
||||
v1 "git.jamestombleson.com/jtom38/newsbot-api/internal/handler/v1"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/handler/v1"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/services"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/services/cron"
|
||||
)
|
||||
@ -17,12 +20,24 @@ import (
|
||||
// @version 0.1
|
||||
// @BasePath /api
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
cfg := services.NewConfig()
|
||||
configs := services.GetEnvConfig()
|
||||
address := cfg.GetConfig(services.ServerAddress)
|
||||
docs.SwaggerInfo.Host = fmt.Sprintf("%v:8081", address)
|
||||
|
||||
ctx := context.Background()
|
||||
db, err := sql.Open("postgres", cfg.GetConfig(services.Sql_Connection_String))
|
||||
db, err := sql.Open("sqlite", "newsbot.db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = goose.SetDialect("sqlite3")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = goose.Up(db, "../internal/database/migrations")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -32,11 +47,11 @@ func main() {
|
||||
c := cron.NewScheduler(ctx)
|
||||
c.Start()
|
||||
|
||||
server := v1.NewServer(ctx, queries)
|
||||
server := v1.NewServer(ctx, queries, configs, db)
|
||||
|
||||
fmt.Println("API is online and waiting for requests.")
|
||||
fmt.Printf("API: http://%v:8081/api\r\n", address)
|
||||
fmt.Printf("Swagger: http://%v:8081/swagger/index.html\r\n", address)
|
||||
fmt.Printf("API: http://%v:8081/api\r\n", configs.ServerAddress)
|
||||
fmt.Printf("Swagger: http://%v:8081/swagger/index.html\r\n", configs.ServerAddress)
|
||||
|
||||
err = http.ListenAndServe(":8081", server.Router)
|
||||
if err != nil {
|
23
go.mod
23
go.mod
@ -4,27 +4,42 @@ go 1.22
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/glebarez/go-sqlite v1.22.0
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-rod/rod v0.107.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/huandu/go-sqlbuilder v1.27.1
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/mmcdole/gofeed v1.1.3
|
||||
github.com/nicklaw5/helix/v2 v2.4.0
|
||||
github.com/pressly/goose/v3 v3.20.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/swaggo/echo-swagger v1.4.1
|
||||
github.com/swaggo/swag v1.8.12
|
||||
golang.org/x/crypto v0.22.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // 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/mfridman/interpolate v0.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sethvargo/go-retry v0.2.4 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/sqlite v1.29.6 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@ -34,7 +49,7 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.6 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lib/pq v1.10.6
|
||||
@ -48,6 +63,6 @@ require (
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
59
go.sum
59
go.sum
@ -12,8 +12,12 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
@ -29,11 +33,21 @@ github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrK
|
||||
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-rod/rod v0.107.1 h1:wRxTTAXJ0JUnoSGcyGAOubpdrToWIKPCnLu3av8EDFY=
|
||||
github.com/go-rod/rod v0.107.1/go.mod h1:Au6ufsz7KyXUJVnw6Ljs1nFpsopy+9AJ/lBwGauYBVg=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c=
|
||||
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
|
||||
github.com/huandu/go-sqlbuilder v1.27.1 h1:7UU/3EMIQYYX8wn+L7BNcGVz1aEs5TPNOVFd7ryrPos=
|
||||
github.com/huandu/go-sqlbuilder v1.27.1/go.mod h1:nUVmMitjOmn/zacMLXT0d3Yd3RHoO2K+vy906JzqxMI=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@ -62,6 +76,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||
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/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||
github.com/mmcdole/gofeed v1.1.3 h1:pdrvMb18jMSLidGp8j0pLvc9IGziX4vbmvVqmLH6z8o=
|
||||
github.com/mmcdole/gofeed v1.1.3/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE=
|
||||
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
|
||||
@ -73,18 +89,27 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nicklaw5/helix/v2 v2.4.0 h1:ZvqCKVqza1eJYyqgTRrZ/xjDq0w/EQVFNkN067Utls0=
|
||||
github.com/nicklaw5/helix/v2 v2.4.0/go.mod h1:0ONzvVi1cH+k3a7EDIFNNqxfW0podhf+CqlmFvuexq8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pressly/goose/v3 v3.20.0 h1:uPJdOxF/Ipj7ABVNOAMJXSxwFXZGwMGHNqjC8e61VA0=
|
||||
github.com/pressly/goose/v3 v3.20.0/go.mod h1:BRfF2GcG4FTG12QfdBVy3q1yveaf4ckL9vWwEcIO3lA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec=
|
||||
github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
@ -110,17 +135,21 @@ github.com/ysmood/gson v0.7.2 h1:1iWUvpi5DPvd2j59W7ifRPR9DiAZ3Ga+fmMl1mJrRbM=
|
||||
github.com/ysmood/gson v0.7.2/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
|
||||
github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw=
|
||||
github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -135,8 +164,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
@ -148,3 +177,17 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
|
||||
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
|
@ -1,72 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
CREATE TABLE Articles (
|
||||
ID uuid PRIMARY KEY,
|
||||
SourceId uuid NOT null,
|
||||
Tags TEXT NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
PubDate timestamp NOT NULL,
|
||||
Video TEXT,
|
||||
VideoHeight int NOT NULL,
|
||||
VideoWidth int NOT NULL,
|
||||
Thumbnail TEXT NOT NULL,
|
||||
Description TEXT NOT NULL,
|
||||
AuthorName TEXT,
|
||||
AuthorImage TEXT
|
||||
);
|
||||
|
||||
CREATE Table DiscordQueue (
|
||||
ID uuid PRIMARY KEY,
|
||||
ArticleId uuid NOT NULL
|
||||
);
|
||||
|
||||
CREATE Table DiscordWebHooks (
|
||||
ID uuid PRIMARY KEY,
|
||||
Name TEXT NOT NULL, -- Defines webhook purpose
|
||||
Key TEXT,
|
||||
Url TEXT NOT NULL, -- Webhook Url
|
||||
Server TEXT NOT NULL, -- Defines the server its bound it. Used for refrence
|
||||
Channel TEXT NOT NULL, -- Defines the channel its bound to. Used for refrence
|
||||
Enabled BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE Table Icons (
|
||||
ID uuid PRIMARY Key,
|
||||
FileName TEXT NOT NULL,
|
||||
Site TEXT NOT NULL
|
||||
);
|
||||
|
||||
Create Table Settings (
|
||||
ID uuid PRIMARY Key,
|
||||
Key TEXT NOT NULL, -- How you search for a entry
|
||||
Value TEXT NOT NULL, -- The value for one
|
||||
Options TEXT -- any notes about the entry
|
||||
);
|
||||
|
||||
Create Table Sources (
|
||||
ID uuid PRIMARY Key,
|
||||
Site TEXT NOT NULL, -- Vanity name
|
||||
Name TEXT NOT NULL, -- Defines the name of the source. IE: dadjokes
|
||||
Source TEXT NOT NULL, -- Defines the service that will use this reocrd. IE reddit or youtube
|
||||
Type TEXT NOT NULL, -- Defines what kind of feed this is. feed, user, tag
|
||||
Value TEXT,
|
||||
Enabled BOOLEAN NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
Tags TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
Drop Table Articles;
|
||||
Drop Table DiscordQueue;
|
||||
Drop Table DiscordWebHooks;
|
||||
Drop Table Icons;
|
||||
Drop Table Settings;
|
||||
Drop Table Sources;
|
||||
-- +goose StatementEnd
|
@ -1,50 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
|
||||
-- Enable UUID's
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- Final Fantasy XIV Entries
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'ffxiv', 'Final Fantasy XIV - NA', 'ffxiv', 'scrape', 'a', TRUE, 'https://na.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, na, lodestone');
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'ffxiv', 'Final Fantasy XIV - JP', 'ffxiv', 'scrape', 'a', FALSE, 'https://jp.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, jp, lodestone');
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'ffxiv', 'Final Fantasy XIV - EU', 'ffxiv', 'scrape', 'a', FALSE, 'https://eu.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, eu, lodestone');
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'ffxiv', 'Final Fantasy XIV - FR', 'ffxiv', 'scrape', 'a', FALSE, 'https://fr.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, fr, lodestone');
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'ffxiv', 'Final Fantasy XIV - DE', 'ffxiv', 'scrape', 'a', FALSE, 'https://de.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, de, lodestone');
|
||||
|
||||
-- Reddit Entries
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'reddit', 'dadjokes', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/dadjokes', 'reddit, dadjokes');
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'reddit', 'steamdeck', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/steamdeck', 'reddit, steam deck, steam, deck');
|
||||
|
||||
-- Youtube Entries
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'youtube', 'Game Grumps', 'youtube', 'feed', 'a', TRUE, 'https://www.youtube.com/user/GameGrumps', 'youtube, game grumps, game, grumps');
|
||||
|
||||
-- RSS Entries
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'steampowered', 'steam deck', 'rss', 'feed', 'a', TRUE, 'https://store.steampowered.com/feeds/news/app/1675200/?cc=US&l=english&snr=1_2108_9__2107', 'rss, steampowered, steam, deck, steam deck');
|
||||
|
||||
-- Twitch Entries
|
||||
INSERT INTO sources VALUES
|
||||
(uuid_generate_v4(), 'twitch', 'Nintendo', 'twitch', 'api', 'a', TRUE, 'https://twitch.tv/nintendo', 'twitch, nintendo');
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
--SELECT 'down SQL query';
|
||||
|
||||
DELETE FROM sources where source = 'reddit' and name = 'dadjokes';
|
||||
DELETE FROM sources where source = 'reddit' and name = 'steamdeck';
|
||||
DELETE FROM sources where source = 'ffxiv';
|
||||
DELETE FROM sources WHERE source = 'twitch' and name = 'Nintendo';
|
||||
DELETE FROM sources WHERE source = 'youtube' and name = 'Game Grumps';
|
||||
DELETE FROM SOURCES WHERE source = 'rss' and name = 'steam deck';
|
||||
-- +goose StatementEnd
|
@ -1,21 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
Create TABLE Subscriptions (
|
||||
ID uuid Primary Key,
|
||||
DiscordWebHookID uuid Not Null,
|
||||
SourceID uuid Not Null
|
||||
);
|
||||
|
||||
ALTER TABLE discordwebhooks drop COLUMN Name;
|
||||
ALTER TABLE discordwebhooks drop COLUMN Key;
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
SELECT 'down SQL query';
|
||||
Drop Table Subscriptions;
|
||||
ALTER TABLE discordwebhooks Add COLUMN Name TEXT;
|
||||
--ALTER TABLE discordwebhooks Add COLUMN Key TEXT;
|
||||
-- +goose StatementEnd
|
@ -1,11 +0,0 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
ALTER TABLE sources Add COLUMN Deleted BOOLEAN;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
SELECT 'down SQL query';
|
||||
ALTER TABLE sources Drop Deleted Deleted BOOLEAN;
|
||||
-- +goose StatementEnd
|
121
internal/database/migrations/20240425083756_init.sql
Normal file
121
internal/database/migrations/20240425083756_init.sql
Normal file
@ -0,0 +1,121 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
CREATE TABLE Articles (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
LastUpdated DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
SourceId NUMBER NOT NULL,
|
||||
Tags TEXT NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
PubDate DATETIME NOT NULL,
|
||||
Video TEXT,
|
||||
VideoHeight int NOT NULL,
|
||||
VideoWidth int NOT NULL,
|
||||
Thumbnail TEXT NOT NULL,
|
||||
Description TEXT NOT NULL,
|
||||
AuthorName TEXT,
|
||||
AuthorImageUrl TEXT
|
||||
);
|
||||
|
||||
CREATE Table DiscordQueue (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
ArticleId NUMBER NOT NULL,
|
||||
SourceId NUMBER NOT NULL
|
||||
);
|
||||
|
||||
CREATE Table DiscordWebHooks (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
--Name TEXT NOT NULL, -- Defines webhook purpose
|
||||
--Key TEXT,
|
||||
Url TEXT NOT NULL, -- Webhook Url
|
||||
Server TEXT NOT NULL, -- Defines the server its bound it. Used for refrence
|
||||
Channel TEXT NOT NULL, -- Defines the channel its bound to. Used for refrence
|
||||
Enabled BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE Table Icons (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
FileName TEXT NOT NULL,
|
||||
Site TEXT NOT NULL
|
||||
);
|
||||
|
||||
Create Table Settings (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
Key TEXT NOT NULL, -- How you search for a entry
|
||||
Value TEXT NOT NULL, -- The value for one
|
||||
Options TEXT -- any notes about the entry
|
||||
);
|
||||
|
||||
CREATE Table Sources (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
Site TEXT NOT NULL, -- Vanity name
|
||||
Name TEXT NOT NULL, -- Defines the name of the source. IE: dadjokes
|
||||
Source TEXT NOT NULL, -- Defines the service that will use this reocrd. IE reddit or youtube
|
||||
Type TEXT NOT NULL, -- Defines what kind of feed this is. feed, user, tag
|
||||
Value TEXT,
|
||||
Enabled BOOLEAN NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
Tags TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE Subscriptions (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
DiscordWebHookId NUMBER NOT NULL,
|
||||
SourceId NUMBER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE Users (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
Name TEXT NOT NULL,
|
||||
Hash TEXT NOT NULL,
|
||||
Scopes TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE RefreshTokens (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
Username TEXT NOT NULL,
|
||||
Token TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
Drop Table Articles;
|
||||
Drop Table DiscordQueue;
|
||||
Drop Table DiscordWebHooks;
|
||||
Drop Table Icons;
|
||||
Drop Table Settings;
|
||||
Drop Table Sources;
|
||||
DROP TABLE Subscriptions;
|
||||
DROP TABLE Users;
|
||||
DROP TABLE RefreshTokens;
|
||||
-- +goose StatementEnd
|
50
internal/database/migrations/20240425092459_seed.sql
Normal file
50
internal/database/migrations/20240425092459_seed.sql
Normal file
@ -0,0 +1,50 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
|
||||
-- Enable UUID's
|
||||
--CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- Final Fantasy XIV Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - NA', 'ffxiv', 'scrape', 'a', TRUE, 'https://na.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, na, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - JP', 'ffxiv', 'scrape', 'a', FALSE, 'https://jp.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, jp, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - EU', 'ffxiv', 'scrape', 'a', FALSE, 'https://eu.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, eu, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - FR', 'ffxiv', 'scrape', 'a', FALSE, 'https://fr.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, fr, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - DE', 'ffxiv', 'scrape', 'a', FALSE, 'https://de.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, de, lodestone');
|
||||
|
||||
-- Reddit Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'reddit', 'dadjokes', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/dadjokes', 'reddit, dadjokes');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'reddit', 'steamdeck', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/steamdeck', 'reddit, steam deck, steam, deck');
|
||||
|
||||
-- Youtube Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'youtube', 'Game Grumps', 'youtube', 'feed', 'a', TRUE, 'https://www.youtube.com/user/GameGrumps', 'youtube, game grumps, game, grumps');
|
||||
|
||||
-- RSS Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'steampowered', 'steam deck', 'rss', 'feed', 'a', TRUE, 'https://store.steampowered.com/feeds/news/app/1675200/?cc=US&l=english&snr=1_2108_9__2107', 'rss, steampowered, steam, deck, steam deck');
|
||||
|
||||
-- Twitch Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'twitch', 'Nintendo', 'twitch', 'api', 'a', TRUE, 'https://twitch.tv/nintendo', 'twitch, nintendo');
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
--SELECT 'down SQL query';
|
||||
|
||||
DELETE FROM sources where source = 'reddit' and name = 'dadjokes';
|
||||
DELETE FROM sources where source = 'reddit' and name = 'steamdeck';
|
||||
DELETE FROM sources where source = 'ffxiv';
|
||||
DELETE FROM sources WHERE source = 'twitch' and name = 'Nintendo';
|
||||
DELETE FROM sources WHERE source = 'youtube' and name = 'Game Grumps';
|
||||
DELETE FROM SOURCES WHERE source = 'rss' and name = 'steam deck';
|
||||
-- +goose StatementEnd
|
@ -1 +1,86 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type ArticleDto struct {
|
||||
ID int64 `json:"id"`
|
||||
SourceID int64 `json:"sourceId"`
|
||||
Tags string `json:"tags"`
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
PubDate time.Time `json:"pubDate"`
|
||||
Video string `json:"video"`
|
||||
VideoHeight uint16 `json:"videoHeight"`
|
||||
VideoWidth uint16 `json:"videoWidth"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Description string `json:"description"`
|
||||
AuthorName string `json:"authorName"`
|
||||
AuthorImage string `json:"authorImage"`
|
||||
}
|
||||
|
||||
type DiscordQueueDto struct {
|
||||
//CreatedAt time.Time `json:"createdAt"`
|
||||
//UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID int64 `json:"id"`
|
||||
ArticleId int64 `json:"articleId"`
|
||||
SourceId int64 `json:"sourceId"`
|
||||
}
|
||||
|
||||
type DiscordWebHookDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Url string `json:"url"`
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type IconDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
Site string `json:"site"`
|
||||
}
|
||||
|
||||
type SettingDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Options string `json:"options"`
|
||||
}
|
||||
|
||||
type SubscriptionDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
SourceID int64 `json:"sourceId"`
|
||||
SourceType string `json:"sourceType"`
|
||||
SourceName string `json:"sourceName"`
|
||||
DiscordID int64 `json:"discordId"`
|
||||
DiscordName string `json:"discordName"`
|
||||
}
|
||||
|
||||
type SourceDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
Site string `json:"site"`
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Url string `json:"url"`
|
||||
Tags string `json:"tags"`
|
||||
}
|
||||
|
@ -4,87 +4,105 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Articles represents the model for an Article
|
||||
type Articles struct {
|
||||
ID uint `json:"ID"`
|
||||
SourceID uint `json:"sourceId"`
|
||||
Tags string `json:"tags"`
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
PubDate time.Time `json:"pubdate"`
|
||||
Video string `json:"video"`
|
||||
VideoHeight uint16 `json:"videoHeight"`
|
||||
VideoWidth uint16 `json:"videoWidth"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Description string `json:"description"`
|
||||
AuthorName string `json:"authorName"`
|
||||
AuthorImage string `json:"authorImage"`
|
||||
type ArticleEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
LastUpdated time.Time
|
||||
DeletedAt time.Time
|
||||
SourceID int64
|
||||
Tags string
|
||||
Title string
|
||||
Url string
|
||||
PubDate time.Time
|
||||
Thumbnail string
|
||||
Description string
|
||||
AuthorName string
|
||||
AuthorImageUrl string
|
||||
}
|
||||
|
||||
type DiscordQueue struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
ArticleId string `json:"articleId"`
|
||||
type DiscordQueueEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
LastUpdated time.Time
|
||||
DeletedAt time.Time
|
||||
ArticleId int64
|
||||
SourceId int64
|
||||
}
|
||||
|
||||
type DiscordWebHooks struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Url string `json:"url"`
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
Enabled bool `json:"enabled"`
|
||||
type DiscordWebHookEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Name string
|
||||
Key string
|
||||
Url string
|
||||
Server string
|
||||
Channel string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type Icons struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
|
||||
FileName string `json:"fileName"`
|
||||
Site string `json:"site"`
|
||||
type IconEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
FileName string
|
||||
Site string
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Options string `json:"options"`
|
||||
type SettingEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Key string
|
||||
Value string
|
||||
Options string
|
||||
}
|
||||
|
||||
type Sources struct {
|
||||
ID uint `json:"ID"`
|
||||
Site string `json:"site"`
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Url string `json:"url"`
|
||||
Tags string `json:"tags"`
|
||||
type SourceEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Site string
|
||||
Name string
|
||||
Source string
|
||||
Type string
|
||||
Value string
|
||||
Enabled bool
|
||||
Url string
|
||||
Tags string
|
||||
}
|
||||
|
||||
type SourceLinks struct {
|
||||
ID uint `json:"ID"`
|
||||
CreatedAt time.Time `json:"CreatedAt"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
DeletedAt time.Time `json:"DeletedAt"`
|
||||
|
||||
SourceID uint `json:"sourceId"`
|
||||
SourceType string `json:"sourceType"`
|
||||
SourceName string `json:"sourceName"`
|
||||
DiscordID uint `json:"discordId"`
|
||||
DiscordName string `json:"discordName"`
|
||||
type SubscriptionEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
SourceID int64
|
||||
SourceType string
|
||||
SourceName string
|
||||
DiscordID int64
|
||||
DiscordName string
|
||||
}
|
||||
|
||||
type UserEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Username string
|
||||
Hash string
|
||||
Scopes string
|
||||
}
|
||||
|
||||
type RefreshTokenEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Username string
|
||||
Token string
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ type Handler struct {
|
||||
Router *echo.Echo
|
||||
Db *database.Queries
|
||||
dto *dto.DtoClient
|
||||
//ctx *context.Context
|
||||
config services.Configs
|
||||
sqlConnection *sql.DB
|
||||
}
|
||||
|
||||
const (
|
||||
@ -39,11 +40,12 @@ var (
|
||||
ErrUnableToConvertToJson string = "Unable to convert to json"
|
||||
)
|
||||
|
||||
func NewServer(ctx context.Context, db *database.Queries) *Handler {
|
||||
func NewServer(ctx context.Context, db *database.Queries, configs services.Configs, conn *sql.DB) *Handler {
|
||||
s := &Handler{
|
||||
//ctx: &ctx,
|
||||
Db: db,
|
||||
dto: dto.NewDtoClient(db),
|
||||
config: configs,
|
||||
sqlConnection: conn,
|
||||
}
|
||||
|
||||
db, err := openDatabase(ctx)
|
||||
@ -137,15 +139,3 @@ func (s *Handler) WriteError(c echo.Context, errMessage string, HttpStatusCode i
|
||||
Message: errMessage,
|
||||
})
|
||||
}
|
||||
|
||||
//func (s *Handler) WriteJson(w http.ResponseWriter, model interface{}) {
|
||||
// w.Header().Set(HeaderContentType, ApplicationJson)
|
||||
//
|
||||
// bres, err := json.Marshal(model)
|
||||
// if err != nil {
|
||||
// s.WriteError(w, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// w.Write(bres)
|
||||
//}
|
||||
|
119
internal/repository/refreshTokens.go
Normal file
119
internal/repository/refreshTokens.go
Normal file
@ -0,0 +1,119 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
refreshTokenTableName = "RefreshTokens"
|
||||
)
|
||||
|
||||
type RefreshTokenTable interface {
|
||||
Create(username string, token string) (int64, error)
|
||||
GetByUsername(name string) (domain.RefreshTokenEntity, error)
|
||||
DeleteById(id int64) (int64, error)
|
||||
}
|
||||
|
||||
type RefreshTokenRepository struct {
|
||||
connection *sql.DB
|
||||
}
|
||||
|
||||
func NewRefreshTokenRepository(conn *sql.DB) RefreshTokenRepository {
|
||||
return RefreshTokenRepository{
|
||||
connection: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) Create(username string, token string) (int64, error) {
|
||||
dt := time.Now()
|
||||
builder := sqlbuilder.NewInsertBuilder()
|
||||
builder.InsertInto(refreshTokenTableName)
|
||||
builder.Cols("Username", "Token", "CreatedAt", "UpdatedAt")
|
||||
builder.Values(username, token, dt, dt)
|
||||
query, args := builder.Build()
|
||||
|
||||
_, err := rt.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) GetByUsername(name string) (domain.RefreshTokenEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*").From(refreshTokenTableName).Where(
|
||||
builder.E("Username", name),
|
||||
)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := rt.connection.Query(query, args...)
|
||||
if err != nil {
|
||||
return domain.RefreshTokenEntity{}, err
|
||||
}
|
||||
|
||||
data := rt.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.RefreshTokenEntity{}, errors.New("no token found for user")
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) DeleteById(id int64) (int64, error) {
|
||||
builder := sqlbuilder.NewDeleteBuilder()
|
||||
builder.DeleteFrom(refreshTokenTableName)
|
||||
builder.Where(
|
||||
builder.EQ("Id", id),
|
||||
)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := rt.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return rows.RowsAffected()
|
||||
}
|
||||
|
||||
func (rd RefreshTokenRepository) processRows(rows *sql.Rows) []domain.RefreshTokenEntity {
|
||||
items := []domain.RefreshTokenEntity{}
|
||||
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var username string
|
||||
var token string
|
||||
var createdAt time.Time
|
||||
var updatedAt time.Time
|
||||
var deletedAt sql.NullTime
|
||||
|
||||
err := rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &username, &token)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
item := domain.RefreshTokenEntity{
|
||||
ID: id,
|
||||
Username: username,
|
||||
Token: token,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
if (deletedAt.Valid) {
|
||||
item.DeletedAt = deletedAt.Time
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
//func (rt RefreshTokenRepository) Delete()
|
92
internal/repository/refreshTokens_test.go
Normal file
92
internal/repository/refreshTokens_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
|
||||
)
|
||||
|
||||
func TestRefreshTokenCreate(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repository.NewRefreshTokenRepository(conn)
|
||||
rows, err := client.Create("tester", "BadTokenDontUse")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
t.Log("expected one row to come back but got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshTokenGetByUsername(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repository.NewRefreshTokenRepository(conn)
|
||||
rows, err := client.Create("tester", "BadTokenDoNotUse")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if rows != 1 {
|
||||
t.Log("expected a row to be added but not the wrong value back")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
model, err := client.GetByUsername("tester")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if model.Username != "tester" {
|
||||
t.Log("got the wrong user back")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshTokenDeleteById(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repository.NewRefreshTokenRepository(conn)
|
||||
created, err := client.Create("tester", "BadTokenDoNotUse")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if created != 1 {
|
||||
t.Log("Unexpected number back for rows created")
|
||||
}
|
||||
|
||||
model, err := client.GetByUsername("tester")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
updated, err := client.DeleteById(model.ID)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if updated != 1 {
|
||||
t.Log("deleted the wrong number of records")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
164
internal/repository/users.go
Normal file
164
internal/repository/users.go
Normal file
@ -0,0 +1,164 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
TableName string = "users"
|
||||
ErrUserNotFound string = "requested user was not found"
|
||||
)
|
||||
|
||||
type IUserTable interface {
|
||||
GetByName(name string) (domain.UserEntity, error)
|
||||
Create(name, password, scope string) (int64, error)
|
||||
Update(id int, entity domain.UserEntity) error
|
||||
UpdatePassword(name, password string) error
|
||||
CheckUserHash(name, password string) error
|
||||
UpdateScopes(name, scope string) error
|
||||
}
|
||||
|
||||
// Creates a new instance of UserRepository with the bound sql
|
||||
func NewUserRepository(conn *sql.DB) UserRepository {
|
||||
return UserRepository{
|
||||
connection: conn,
|
||||
}
|
||||
}
|
||||
|
||||
type UserRepository struct {
|
||||
connection *sql.DB
|
||||
}
|
||||
|
||||
func (ur UserRepository) GetByName(name string) (domain.UserEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*").From("users").Where(
|
||||
builder.E("Name", name),
|
||||
)
|
||||
query, args := builder.Build()
|
||||
|
||||
rows, err := ur.connection.Query(query, args...)
|
||||
if err != nil {
|
||||
return domain.UserEntity{}, err
|
||||
}
|
||||
|
||||
data := ur.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.UserEntity{}, errors.New(ErrUserNotFound)
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) Create(name, password, scope string) (int64, error) {
|
||||
passwordBytes := []byte(password)
|
||||
hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
dt := time.Now()
|
||||
queryBuilder := sqlbuilder.NewInsertBuilder()
|
||||
queryBuilder.InsertInto("users")
|
||||
queryBuilder.Cols("Name", "Hash", "UpdatedAt", "CreatedAt", "Scopes")
|
||||
queryBuilder.Values(name, string(hash), dt, dt, scope)
|
||||
query, args := queryBuilder.Build()
|
||||
|
||||
_, err = ur.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) Update(id int, entity domain.UserEntity) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (ur UserRepository) UpdatePassword(name, password string) error {
|
||||
_, err := ur.GetByName(name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
queryBuilder := sqlbuilder.NewUpdateBuilder()
|
||||
queryBuilder.Update(TableName)
|
||||
//queryBuilder.Set
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the hash matches what we have in the database, an error will not be returned.
|
||||
// If the user does not exist or the hash does not match, an error will be returned
|
||||
func (ur UserRepository) CheckUserHash(name, password string) error {
|
||||
record, err := ur.GetByName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(record.Hash), []byte(password))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) UpdateScopes(name, scope string) error {
|
||||
builder := sqlbuilder.NewUpdateBuilder()
|
||||
builder.Update("users")
|
||||
builder.Set(
|
||||
builder.Assign("Scopes", scope),
|
||||
)
|
||||
builder.Where(
|
||||
builder.Equal("Name", name),
|
||||
)
|
||||
query, args := builder.Build()
|
||||
|
||||
_, err := ur.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) processRows(rows *sql.Rows) []domain.UserEntity {
|
||||
items := []domain.UserEntity{}
|
||||
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var username string
|
||||
var hash string
|
||||
var createdAt time.Time
|
||||
var updatedAt time.Time
|
||||
var deletedAt sql.NullTime
|
||||
var scopes string
|
||||
err := rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &username, &hash, &scopes)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
item := domain.UserEntity{
|
||||
ID: id,
|
||||
UpdatedAt: updatedAt,
|
||||
Username: username,
|
||||
Hash: hash,
|
||||
Scopes: scopes,
|
||||
CreatedAt: createdAt,
|
||||
}
|
||||
if deletedAt.Valid {
|
||||
item.DeletedAt = deletedAt.Time
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
88
internal/repository/users_test.go
Normal file
88
internal/repository/users_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
|
||||
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
func TestCanCreateNewUser(t *testing.T) {
|
||||
//t.Log(time.Now().String())
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
repo := repository.NewUserRepository(db)
|
||||
updated, err := repo.Create("testing", "NotSecure", "placeholder")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
log.Println(updated)
|
||||
}
|
||||
|
||||
func TestCanFindUserInTable(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
log.Println("unable to open connection")
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
repo := repository.NewUserRepository(db)
|
||||
updated, err := repo.Create("testing", "NotSecure", "placeholder")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if updated != 1 {
|
||||
t.Log("expected a row to come back")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
user, err := repo.GetByName("testing")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
log.Println(user)
|
||||
}
|
||||
|
||||
func TestCheckUserHash(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
log.Println("unable to open connection")
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
repo := repository.NewUserRepository(db)
|
||||
repo.CheckUserHash("testing", "NotSecure")
|
||||
}
|
||||
|
||||
func setupInMemoryDb() (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = goose.SetDialect("sqlite3")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = goose.Up(db, "../database/migrations")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
@ -32,6 +32,26 @@ const (
|
||||
FEATURE_ENABLE_FFXIV_BACKEND = "FEATURE_ENABLE_FFXIV_BACKEND"
|
||||
)
|
||||
|
||||
type Configs struct {
|
||||
ServerAddress string
|
||||
|
||||
RedditEnabled bool
|
||||
RedditPullTop bool
|
||||
RedditPullHot bool
|
||||
RedditPullNsfw bool
|
||||
|
||||
YoutubeEnabled bool
|
||||
YoutubeDebug bool
|
||||
|
||||
TwitchEnabled bool
|
||||
TwitchClientId string
|
||||
TwitchClientSecret string
|
||||
TwitchMonitorClips bool
|
||||
TwitchMonitorVOD bool
|
||||
|
||||
FfxivEnabled bool
|
||||
}
|
||||
|
||||
type ConfigClient struct{}
|
||||
|
||||
func NewConfig() ConfigClient {
|
||||
@ -41,6 +61,38 @@ func NewConfig() ConfigClient {
|
||||
return c
|
||||
}
|
||||
|
||||
func GetEnvConfig() Configs {
|
||||
return Configs{
|
||||
ServerAddress: os.Getenv(ServerAddress),
|
||||
|
||||
RedditEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_REDDIT_BACKEND)),
|
||||
RedditPullTop: processBoolConfig(os.Getenv(REDDIT_PULL_TOP)),
|
||||
RedditPullHot: processBoolConfig(os.Getenv(REDDIT_PULL_HOT)),
|
||||
RedditPullNsfw: processBoolConfig(os.Getenv(REDDIT_PULL_NSFW)),
|
||||
|
||||
YoutubeEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_YOUTUBE_BACKEND)),
|
||||
YoutubeDebug: processBoolConfig(os.Getenv(YOUTUBE_DEBUG)),
|
||||
|
||||
TwitchEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_TWITCH_BACKEND)),
|
||||
TwitchClientId: os.Getenv(TWITCH_CLIENT_ID),
|
||||
TwitchClientSecret: os.Getenv(TWITCH_CLIENT_SECRET),
|
||||
TwitchMonitorClips: processBoolConfig(TWITCH_MONITOR_CLIPS),
|
||||
TwitchMonitorVOD: processBoolConfig(os.Getenv(TWITCH_MONITOR_VOD)),
|
||||
|
||||
FfxivEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_FFXIV_BACKEND)),
|
||||
}
|
||||
}
|
||||
|
||||
// This will parse a string and convert it to a bool.
|
||||
// If it runs into any errors, it will default to false
|
||||
func processBoolConfig(value string) bool {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (cc *ConfigClient) GetConfig(key string) string {
|
||||
res, filled := os.LookupEnv(key)
|
||||
if !filled {
|
||||
|
@ -10,10 +10,10 @@ import (
|
||||
)
|
||||
|
||||
type rssClient struct {
|
||||
SourceRecord domain.Sources
|
||||
SourceRecord domain.SourceEntity
|
||||
}
|
||||
|
||||
func NewRssClient(sourceRecord domain.Sources) rssClient {
|
||||
func NewRssClient(sourceRecord domain.SourceEntity) rssClient {
|
||||
client := rssClient{
|
||||
SourceRecord: sourceRecord,
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/services/input"
|
||||
)
|
||||
|
||||
var rssRecord = domain.Sources{
|
||||
var rssRecord = domain.SourceEntity{
|
||||
ID: 1,
|
||||
Name: "ArsTechnica",
|
||||
Url: "https://feeds.arstechnica.com/arstechnica/index",
|
||||
|
Loading…
Reference in New Issue
Block a user