From 47058dd866b5f0a33e80edf831739156dcf3ab07 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sun, 26 May 2024 07:52:29 -0700 Subject: [PATCH] sqlc removed and adding sessionToken to jwt --- docs/docs.go | 42 + docs/swagger.json | 42 + docs/swagger.yaml | 26 + domain/responses.go | 1 + internal/database/db.go | 31 - internal/database/dto.go | 45 - .../20240521194914_session_token.sql | 9 + internal/database/models.go | 73 - internal/database/query.sql.go | 1308 ----------------- internal/database/schema/query.sql | 215 --- internal/database/schema/schema.sql | 61 - internal/entity/entity.go | 15 +- internal/handler/v1/jwt.go | 7 +- internal/handler/v1/{auth.go => users.go} | 50 +- internal/repository/users.go | 53 +- internal/repository/users_test.go | 4 +- internal/repositoryServices/userService.go | 27 +- internal/services/cron/scheduler.go | 4 +- internal/services/output/discordwebhook.go | 7 +- .../services/output/discordwebhook_test.go | 13 +- 20 files changed, 250 insertions(+), 1783 deletions(-) delete mode 100644 internal/database/db.go delete mode 100644 internal/database/dto.go create mode 100644 internal/database/migrations/20240521194914_session_token.sql delete mode 100644 internal/database/models.go delete mode 100644 internal/database/query.sql.go delete mode 100644 internal/database/schema/query.sql delete mode 100644 internal/database/schema/schema.sql rename internal/handler/v1/{auth.go => users.go} (86%) diff --git a/docs/docs.go b/docs/docs.go index f7ce7b5..acb6b6e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -996,6 +996,45 @@ const docTemplate = `{ } } }, + "/v1/users/refresh/sessionToken": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Revokes the current session token and replaces it with a new one.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + } + } + } + }, "/v1/users/refreshToken": { "post": { "security": [ @@ -1311,6 +1350,9 @@ const docTemplate = `{ "refreshToken": { "type": "string" }, + "sessionToken": { + "type": "string" + }, "token": { "type": "string" }, diff --git a/docs/swagger.json b/docs/swagger.json index 89172d1..56e2d20 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -987,6 +987,45 @@ } } }, + "/v1/users/refresh/sessionToken": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Revokes the current session token and replaces it with a new one.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.BaseResponse" + } + } + } + } + }, "/v1/users/refreshToken": { "post": { "security": [ @@ -1302,6 +1341,9 @@ "refreshToken": { "type": "string" }, + "sessionToken": { + "type": "string" + }, "token": { "type": "string" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9eddc06..3076d08 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -84,6 +84,8 @@ definitions: type: string refreshToken: type: string + sessionToken: + type: string token: type: string type: @@ -750,6 +752,30 @@ paths: summary: Logs into the API and returns a bearer token if successful tags: - Users + /v1/users/refresh/sessionToken: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.BaseResponse' + security: + - Bearer: [] + summary: Revokes the current session token and replaces it with a new one. + tags: + - Users /v1/users/refreshToken: post: parameters: diff --git a/domain/responses.go b/domain/responses.go index 1ee7a22..fc7c4a3 100644 --- a/domain/responses.go +++ b/domain/responses.go @@ -10,6 +10,7 @@ type LoginResponse struct { Token string `json:"token"` Type string `json:"type"` RefreshToken string `json:"refreshToken"` + SessionToken string `json:"sessionToken"` } type ArticleResponse struct { diff --git a/internal/database/db.go b/internal/database/db.go deleted file mode 100644 index fa4f825..0000000 --- a/internal/database/db.go +++ /dev/null @@ -1,31 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.16.0 - -package database - -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/internal/database/dto.go b/internal/database/dto.go deleted file mode 100644 index 7b4427e..0000000 --- a/internal/database/dto.go +++ /dev/null @@ -1,45 +0,0 @@ -package database - -import ( - "strings" - - "github.com/google/uuid" -) - -type SourceDto struct { - ID uuid.UUID `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"` - Deleted bool `json:"deleted"` -} - -func ConvertToSourceDto(i Source) SourceDto { - var deleted bool - if !i.Deleted.Valid { - deleted = true - } - - return SourceDto{ - ID: i.ID, - Site: i.Site, - Name: i.Name, - Source: i.Source, - Type: i.Type, - Value: i.Value.String, - Enabled: i.Enabled, - Url: i.Url, - Tags: splitTags(i.Tags), - Deleted: deleted, - } -} - -func splitTags(t string) []string { - items := strings.Split(t, ", ") - return items -} diff --git a/internal/database/migrations/20240521194914_session_token.sql b/internal/database/migrations/20240521194914_session_token.sql new file mode 100644 index 0000000..bda5063 --- /dev/null +++ b/internal/database/migrations/20240521194914_session_token.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE Users ADD SessionToken TEXT; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE Users DROP SessionToken; +-- +goose StatementEnd diff --git a/internal/database/models.go b/internal/database/models.go deleted file mode 100644 index b743218..0000000 --- a/internal/database/models.go +++ /dev/null @@ -1,73 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.16.0 - -package database - -import ( - "database/sql" - "time" - - "github.com/google/uuid" -) - -type Article struct { - ID uuid.UUID - Sourceid uuid.UUID - Tags string - Title string - Url string - Pubdate time.Time - Video sql.NullString - Videoheight int32 - Videowidth int32 - Thumbnail string - Description string - Authorname sql.NullString - Authorimage sql.NullString -} - -type Discordqueue struct { - ID uuid.UUID - Articleid uuid.UUID -} - -type Discordwebhook struct { - ID uuid.UUID - Url string - Server string - Channel string - Enabled bool -} - -type Icon struct { - ID uuid.UUID - Filename string - Site string -} - -type Setting struct { - ID uuid.UUID - Key string - Value string - Options sql.NullString -} - -type Source struct { - ID uuid.UUID - Site string - Name string - Source string - Type string - Value sql.NullString - Enabled bool - Url string - Tags string - Deleted sql.NullBool -} - -type Subscription struct { - ID uuid.UUID - Discordwebhookid uuid.UUID - Sourceid uuid.UUID -} diff --git a/internal/database/query.sql.go b/internal/database/query.sql.go deleted file mode 100644 index 97dfe45..0000000 --- a/internal/database/query.sql.go +++ /dev/null @@ -1,1308 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.16.0 -// source: query.sql - -package database - -import ( - "context" - "database/sql" - "time" - - "github.com/google/uuid" -) - -const createArticle = `-- name: CreateArticle :exec -INSERT INTO Articles -(ID, SourceId, Tags, Title, Url, PubDate, Video, VideoHeight, VideoWidth, Thumbnail, Description, AuthorName, AuthorImage) -Values -($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) -` - -type CreateArticleParams struct { - ID uuid.UUID - Sourceid uuid.UUID - Tags string - Title string - Url string - Pubdate time.Time - Video sql.NullString - Videoheight int32 - Videowidth int32 - Thumbnail string - Description string - Authorname sql.NullString - Authorimage sql.NullString -} - -func (q *Queries) CreateArticle(ctx context.Context, arg CreateArticleParams) error { - _, err := q.db.ExecContext(ctx, createArticle, - arg.ID, - arg.Sourceid, - arg.Tags, - arg.Title, - arg.Url, - arg.Pubdate, - arg.Video, - arg.Videoheight, - arg.Videowidth, - arg.Thumbnail, - arg.Description, - arg.Authorname, - arg.Authorimage, - ) - return err -} - -const createDiscordQueue = `-- name: CreateDiscordQueue :exec -Insert into DiscordQueue -(ID, ArticleId) -Values -($1, $2) -` - -type CreateDiscordQueueParams struct { - ID uuid.UUID - Articleid uuid.UUID -} - -// DiscordQueue -func (q *Queries) CreateDiscordQueue(ctx context.Context, arg CreateDiscordQueueParams) error { - _, err := q.db.ExecContext(ctx, createDiscordQueue, arg.ID, arg.Articleid) - return err -} - -const createDiscordWebHook = `-- name: CreateDiscordWebHook :exec -Insert Into DiscordWebHooks -(ID, Url, Server, Channel, Enabled) -Values -($1, $2, $3, $4, $5) -` - -type CreateDiscordWebHookParams struct { - ID uuid.UUID - Url string - Server string - Channel string - Enabled bool -} - -// DiscordWebHooks -func (q *Queries) CreateDiscordWebHook(ctx context.Context, arg CreateDiscordWebHookParams) error { - _, err := q.db.ExecContext(ctx, createDiscordWebHook, - arg.ID, - arg.Url, - arg.Server, - arg.Channel, - arg.Enabled, - ) - return err -} - -const createIcon = `-- name: CreateIcon :exec - -INSERT INTO Icons -(ID, FileName, Site) -VALUES -($1,$2,$3) -` - -type CreateIconParams struct { - ID uuid.UUID - Filename string - Site string -} - -// Icons -func (q *Queries) CreateIcon(ctx context.Context, arg CreateIconParams) error { - _, err := q.db.ExecContext(ctx, createIcon, arg.ID, arg.Filename, arg.Site) - return err -} - -const createSettings = `-- name: CreateSettings :one - -Insert Into settings -(ID, Key, Value, OPTIONS) -Values -($1,$2,$3,$4) -RETURNING id, key, value, options -` - -type CreateSettingsParams struct { - ID uuid.UUID - Key string - Value string - Options sql.NullString -} - -// Settings -func (q *Queries) CreateSettings(ctx context.Context, arg CreateSettingsParams) (Setting, error) { - row := q.db.QueryRowContext(ctx, createSettings, - arg.ID, - arg.Key, - arg.Value, - arg.Options, - ) - var i Setting - err := row.Scan( - &i.ID, - &i.Key, - &i.Value, - &i.Options, - ) - return i, err -} - -const createSource = `-- name: CreateSource :exec - -Insert Into Sources -(ID, Site, Name, Source, Type, Value, Enabled, Url, Tags) -Values -($1,$2,$3,$4,$5,$6,$7,$8,$9) -` - -type CreateSourceParams struct { - ID uuid.UUID - Site string - Name string - Source string - Type string - Value sql.NullString - Enabled bool - Url string - Tags string -} - -// Sources -func (q *Queries) CreateSource(ctx context.Context, arg CreateSourceParams) error { - _, err := q.db.ExecContext(ctx, createSource, - arg.ID, - arg.Site, - arg.Name, - arg.Source, - arg.Type, - arg.Value, - arg.Enabled, - arg.Url, - arg.Tags, - ) - return err -} - -const createSubscription = `-- name: CreateSubscription :exec - -Insert Into subscriptions (ID, DiscordWebHookId, SourceId) Values ($1, $2, $3) -` - -type CreateSubscriptionParams struct { - ID uuid.UUID - Discordwebhookid uuid.UUID - Sourceid uuid.UUID -} - -// Subscriptions -func (q *Queries) CreateSubscription(ctx context.Context, arg CreateSubscriptionParams) error { - _, err := q.db.ExecContext(ctx, createSubscription, arg.ID, arg.Discordwebhookid, arg.Sourceid) - return err -} - -const deleteDiscordQueueItem = `-- name: DeleteDiscordQueueItem :exec -Delete From DiscordQueue Where ID = $1 -` - -func (q *Queries) DeleteDiscordQueueItem(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteDiscordQueueItem, id) - return err -} - -const deleteDiscordWebHooks = `-- name: DeleteDiscordWebHooks :exec -Delete From discordwebhooks Where ID = $1 -` - -func (q *Queries) DeleteDiscordWebHooks(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteDiscordWebHooks, id) - return err -} - -const deleteIcon = `-- name: DeleteIcon :exec -Delete From Icons where ID = $1 -` - -func (q *Queries) DeleteIcon(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteIcon, id) - return err -} - -const deleteSetting = `-- name: DeleteSetting :exec -Delete From settings Where ID = $1 -` - -func (q *Queries) DeleteSetting(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteSetting, id) - return err -} - -const deleteSource = `-- name: DeleteSource :exec -UPDATE Sources Set Disabled = TRUE where id = $1 -` - -func (q *Queries) DeleteSource(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteSource, id) - return err -} - -const deleteSubscription = `-- name: DeleteSubscription :exec -Delete From subscriptions Where id = $1 -` - -func (q *Queries) DeleteSubscription(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, deleteSubscription, id) - return err -} - -const disableDiscordWebHook = `-- name: DisableDiscordWebHook :exec -Update discordwebhooks Set Enabled = FALSE where ID = $1 -` - -func (q *Queries) DisableDiscordWebHook(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, disableDiscordWebHook, id) - return err -} - -const disableSource = `-- name: DisableSource :exec -Update Sources Set Enabled = FALSE where ID = $1 -` - -func (q *Queries) DisableSource(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, disableSource, id) - return err -} - -const enableDiscordWebHook = `-- name: EnableDiscordWebHook :exec -Update discordwebhooks Set Enabled = TRUE where ID = $1 -` - -func (q *Queries) EnableDiscordWebHook(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, enableDiscordWebHook, id) - return err -} - -const enableSource = `-- name: EnableSource :exec -Update Sources Set Enabled = TRUE where ID = $1 -` - -func (q *Queries) EnableSource(ctx context.Context, id uuid.UUID) error { - _, err := q.db.ExecContext(ctx, enableSource, id) - return err -} - -const getArticleByID = `-- name: GetArticleByID :one -Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage from Articles -WHERE ID = $1 LIMIT 1 -` - -// Articles -func (q *Queries) GetArticleByID(ctx context.Context, id uuid.UUID) (Article, error) { - row := q.db.QueryRowContext(ctx, getArticleByID, id) - var i Article - err := row.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ) - return i, err -} - -const getArticleByUrl = `-- name: GetArticleByUrl :one -Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage from Articles -Where Url = $1 LIMIT 1 -` - -func (q *Queries) GetArticleByUrl(ctx context.Context, url string) (Article, error) { - row := q.db.QueryRowContext(ctx, getArticleByUrl, url) - var i Article - err := row.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ) - return i, err -} - -const getArticlesBySource = `-- name: GetArticlesBySource :many -select articles.id, sourceid, articles.tags, title, articles.url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage, sources.id, site, name, source, type, value, enabled, sources.url, sources.tags, deleted from articles -INNER join sources on articles.sourceid=Sources.ID -where site = $1 -` - -type GetArticlesBySourceRow struct { - ID uuid.UUID - Sourceid uuid.UUID - Tags string - Title string - Url string - Pubdate time.Time - Video sql.NullString - Videoheight int32 - Videowidth int32 - Thumbnail string - Description string - Authorname sql.NullString - Authorimage sql.NullString - ID_2 uuid.UUID - Site string - Name string - Source string - Type string - Value sql.NullString - Enabled bool - Url_2 string - Tags_2 string - Deleted sql.NullBool -} - -func (q *Queries) GetArticlesBySource(ctx context.Context, site string) ([]GetArticlesBySourceRow, error) { - rows, err := q.db.QueryContext(ctx, getArticlesBySource, site) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetArticlesBySourceRow - for rows.Next() { - var i GetArticlesBySourceRow - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - &i.ID_2, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url_2, - &i.Tags_2, - &i.Deleted, - ); 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 getArticlesBySourceName = `-- name: GetArticlesBySourceName :many -select -articles.ID, articles.SourceId, articles.Tags, articles.Title, articles.Url, articles.PubDate, articles.Video, articles.VideoHeight, articles.VideoWidth, articles.Thumbnail, articles.Description, articles.AuthorName, articles.AuthorImage, sources.source, sources.name -From articles -Left Join sources -On articles.sourceid = sources.id -Where name = $1 -` - -type GetArticlesBySourceNameRow struct { - ID uuid.UUID - Sourceid uuid.UUID - Tags string - Title string - Url string - Pubdate time.Time - Video sql.NullString - Videoheight int32 - Videowidth int32 - Thumbnail string - Description string - Authorname sql.NullString - Authorimage sql.NullString - Source sql.NullString - Name sql.NullString -} - -func (q *Queries) GetArticlesBySourceName(ctx context.Context, name string) ([]GetArticlesBySourceNameRow, error) { - rows, err := q.db.QueryContext(ctx, getArticlesBySourceName, name) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetArticlesBySourceNameRow - for rows.Next() { - var i GetArticlesBySourceNameRow - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - &i.Source, - &i.Name, - ); 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 getDiscordQueueByID = `-- name: GetDiscordQueueByID :one -Select id, articleid from DiscordQueue -Where ID = $1 LIMIT 1 -` - -func (q *Queries) GetDiscordQueueByID(ctx context.Context, id uuid.UUID) (Discordqueue, error) { - row := q.db.QueryRowContext(ctx, getDiscordQueueByID, id) - var i Discordqueue - err := row.Scan(&i.ID, &i.Articleid) - return i, err -} - -const getDiscordWebHookByUrl = `-- name: GetDiscordWebHookByUrl :one -Select id, url, server, channel, enabled From DiscordWebHooks Where url = $1 -` - -func (q *Queries) GetDiscordWebHookByUrl(ctx context.Context, url string) (Discordwebhook, error) { - row := q.db.QueryRowContext(ctx, getDiscordWebHookByUrl, url) - var i Discordwebhook - err := row.Scan( - &i.ID, - &i.Url, - &i.Server, - &i.Channel, - &i.Enabled, - ) - return i, err -} - -const getDiscordWebHooksByID = `-- name: GetDiscordWebHooksByID :one -Select id, url, server, channel, enabled from DiscordWebHooks -Where ID = $1 LIMIT 1 -` - -func (q *Queries) GetDiscordWebHooksByID(ctx context.Context, id uuid.UUID) (Discordwebhook, error) { - row := q.db.QueryRowContext(ctx, getDiscordWebHooksByID, id) - var i Discordwebhook - err := row.Scan( - &i.ID, - &i.Url, - &i.Server, - &i.Channel, - &i.Enabled, - ) - return i, err -} - -const getDiscordWebHooksByServerAndChannel = `-- name: GetDiscordWebHooksByServerAndChannel :many -SELECT id, url, server, channel, enabled FROM DiscordWebHooks -WHERE Server = $1 and Channel = $2 -` - -type GetDiscordWebHooksByServerAndChannelParams struct { - Server string - Channel string -} - -func (q *Queries) GetDiscordWebHooksByServerAndChannel(ctx context.Context, arg GetDiscordWebHooksByServerAndChannelParams) ([]Discordwebhook, error) { - rows, err := q.db.QueryContext(ctx, getDiscordWebHooksByServerAndChannel, arg.Server, arg.Channel) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Discordwebhook - for rows.Next() { - var i Discordwebhook - if err := rows.Scan( - &i.ID, - &i.Url, - &i.Server, - &i.Channel, - &i.Enabled, - ); 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 getIconByID = `-- name: GetIconByID :one -Select id, filename, site FROM Icons -Where ID = $1 Limit 1 -` - -func (q *Queries) GetIconByID(ctx context.Context, id uuid.UUID) (Icon, error) { - row := q.db.QueryRowContext(ctx, getIconByID, id) - var i Icon - err := row.Scan(&i.ID, &i.Filename, &i.Site) - return i, err -} - -const getIconBySite = `-- name: GetIconBySite :one -Select id, filename, site FROM Icons -Where Site = $1 Limit 1 -` - -func (q *Queries) GetIconBySite(ctx context.Context, site string) (Icon, error) { - row := q.db.QueryRowContext(ctx, getIconBySite, site) - var i Icon - err := row.Scan(&i.ID, &i.Filename, &i.Site) - return i, err -} - -const getSettingByID = `-- name: GetSettingByID :one -Select id, key, value, options From settings -Where ID = $1 Limit 1 -` - -func (q *Queries) GetSettingByID(ctx context.Context, id uuid.UUID) (Setting, error) { - row := q.db.QueryRowContext(ctx, getSettingByID, id) - var i Setting - err := row.Scan( - &i.ID, - &i.Key, - &i.Value, - &i.Options, - ) - return i, err -} - -const getSettingByKey = `-- name: GetSettingByKey :one -Select id, key, value, options From settings Where -Key = $1 Limit 1 -` - -func (q *Queries) GetSettingByKey(ctx context.Context, key string) (Setting, error) { - row := q.db.QueryRowContext(ctx, getSettingByKey, key) - var i Setting - err := row.Scan( - &i.ID, - &i.Key, - &i.Value, - &i.Options, - ) - return i, err -} - -const getSettingByValue = `-- name: GetSettingByValue :one -Select id, key, value, options From settings Where -Value = $1 Limit 1 -` - -func (q *Queries) GetSettingByValue(ctx context.Context, value string) (Setting, error) { - row := q.db.QueryRowContext(ctx, getSettingByValue, value) - var i Setting - err := row.Scan( - &i.ID, - &i.Key, - &i.Value, - &i.Options, - ) - return i, err -} - -const getSourceByID = `-- name: GetSourceByID :one -Select id, site, name, source, type, value, enabled, url, tags, deleted From Sources where ID = $1 Limit 1 -` - -func (q *Queries) GetSourceByID(ctx context.Context, id uuid.UUID) (Source, error) { - row := q.db.QueryRowContext(ctx, getSourceByID, id) - var i Source - err := row.Scan( - &i.ID, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url, - &i.Tags, - &i.Deleted, - ) - return i, err -} - -const getSourceByName = `-- name: GetSourceByName :one -Select id, site, name, source, type, value, enabled, url, tags, deleted from Sources where name = $1 Limit 1 -` - -func (q *Queries) GetSourceByName(ctx context.Context, name string) (Source, error) { - row := q.db.QueryRowContext(ctx, getSourceByName, name) - var i Source - err := row.Scan( - &i.ID, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url, - &i.Tags, - &i.Deleted, - ) - return i, err -} - -const getSourceByNameAndSource = `-- name: GetSourceByNameAndSource :one -Select id, site, name, source, type, value, enabled, url, tags, deleted from Sources WHERE name = $1 and source = $2 -` - -type GetSourceByNameAndSourceParams struct { - Name string - Source string -} - -func (q *Queries) GetSourceByNameAndSource(ctx context.Context, arg GetSourceByNameAndSourceParams) (Source, error) { - row := q.db.QueryRowContext(ctx, getSourceByNameAndSource, arg.Name, arg.Source) - var i Source - err := row.Scan( - &i.ID, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url, - &i.Tags, - &i.Deleted, - ) - return i, err -} - -const getSubscriptionsByDiscordWebHookId = `-- name: GetSubscriptionsByDiscordWebHookId :many -Select id, discordwebhookid, sourceid from subscriptions Where discordwebhookid = $1 -` - -func (q *Queries) GetSubscriptionsByDiscordWebHookId(ctx context.Context, discordwebhookid uuid.UUID) ([]Subscription, error) { - rows, err := q.db.QueryContext(ctx, getSubscriptionsByDiscordWebHookId, discordwebhookid) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Subscription - for rows.Next() { - var i Subscription - if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); 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 getSubscriptionsBySourceID = `-- name: GetSubscriptionsBySourceID :many -Select id, discordwebhookid, sourceid From subscriptions Where sourceid = $1 -` - -func (q *Queries) GetSubscriptionsBySourceID(ctx context.Context, sourceid uuid.UUID) ([]Subscription, error) { - rows, err := q.db.QueryContext(ctx, getSubscriptionsBySourceID, sourceid) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Subscription - for rows.Next() { - var i Subscription - if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); 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 listArticles = `-- name: ListArticles :many -Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage From articles -Order By PubDate DESC -offset $2 -fetch next $1 rows only -` - -type ListArticlesParams struct { - Limit int32 - Offset int32 -} - -func (q *Queries) ListArticles(ctx context.Context, arg ListArticlesParams) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listArticles, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listArticlesByDate = `-- name: ListArticlesByDate :many -Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage From articles -ORDER BY pubdate desc -Limit $1 -` - -func (q *Queries) ListArticlesByDate(ctx context.Context, limit int32) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listArticlesByDate, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listArticlesByPage = `-- name: ListArticlesByPage :many -select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage from articles -order by pubdate desc -offset $2 -fetch next $1 rows only -` - -type ListArticlesByPageParams struct { - Limit int32 - Offset int32 -} - -func (q *Queries) ListArticlesByPage(ctx context.Context, arg ListArticlesByPageParams) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listArticlesByPage, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listArticlesBySourceId = `-- name: ListArticlesBySourceId :many -Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage From articles -Where sourceid = $1 -Limit 50 -` - -func (q *Queries) ListArticlesBySourceId(ctx context.Context, sourceid uuid.UUID) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listArticlesBySourceId, sourceid) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listDiscordQueueItems = `-- name: ListDiscordQueueItems :many -Select id, articleid from DiscordQueue LIMIT $1 -` - -func (q *Queries) ListDiscordQueueItems(ctx context.Context, limit int32) ([]Discordqueue, error) { - rows, err := q.db.QueryContext(ctx, listDiscordQueueItems, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Discordqueue - for rows.Next() { - var i Discordqueue - if err := rows.Scan(&i.ID, &i.Articleid); 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 listDiscordWebHooksByServer = `-- name: ListDiscordWebHooksByServer :many -Select id, url, server, channel, enabled From DiscordWebHooks -Where Server = $1 -` - -func (q *Queries) ListDiscordWebHooksByServer(ctx context.Context, server string) ([]Discordwebhook, error) { - rows, err := q.db.QueryContext(ctx, listDiscordWebHooksByServer, server) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Discordwebhook - for rows.Next() { - var i Discordwebhook - if err := rows.Scan( - &i.ID, - &i.Url, - &i.Server, - &i.Channel, - &i.Enabled, - ); 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 listDiscordWebhooks = `-- name: ListDiscordWebhooks :many -Select id, url, server, channel, enabled From discordwebhooks LIMIT $1 -` - -func (q *Queries) ListDiscordWebhooks(ctx context.Context, limit int32) ([]Discordwebhook, error) { - rows, err := q.db.QueryContext(ctx, listDiscordWebhooks, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Discordwebhook - for rows.Next() { - var i Discordwebhook - if err := rows.Scan( - &i.ID, - &i.Url, - &i.Server, - &i.Channel, - &i.Enabled, - ); 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 listNewArticlesBySourceId = `-- name: ListNewArticlesBySourceId :many -SELECT id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage FROM articles -Where sourceid = $1 -ORDER BY pubdate desc -offset $3 -fetch next $2 rows only -` - -type ListNewArticlesBySourceIdParams struct { - Sourceid uuid.UUID - Limit int32 - Offset int32 -} - -func (q *Queries) ListNewArticlesBySourceId(ctx context.Context, arg ListNewArticlesBySourceIdParams) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listNewArticlesBySourceId, arg.Sourceid, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listOldestArticlesBySourceId = `-- name: ListOldestArticlesBySourceId :many -SELECT id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage FROM articles -Where sourceid = $1 -ORDER BY pubdate asc -offset $3 -fetch next $2 rows only -` - -type ListOldestArticlesBySourceIdParams struct { - Sourceid uuid.UUID - Limit int32 - Offset int32 -} - -func (q *Queries) ListOldestArticlesBySourceId(ctx context.Context, arg ListOldestArticlesBySourceIdParams) ([]Article, error) { - rows, err := q.db.QueryContext(ctx, listOldestArticlesBySourceId, arg.Sourceid, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Article - for rows.Next() { - var i Article - if err := rows.Scan( - &i.ID, - &i.Sourceid, - &i.Tags, - &i.Title, - &i.Url, - &i.Pubdate, - &i.Video, - &i.Videoheight, - &i.Videowidth, - &i.Thumbnail, - &i.Description, - &i.Authorname, - &i.Authorimage, - ); 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 listSources = `-- name: ListSources :many -Select id, site, name, source, type, value, enabled, url, tags, deleted From Sources Limit $1 -` - -func (q *Queries) ListSources(ctx context.Context, limit int32) ([]Source, error) { - rows, err := q.db.QueryContext(ctx, listSources, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Source - for rows.Next() { - var i Source - if err := rows.Scan( - &i.ID, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url, - &i.Tags, - &i.Deleted, - ); 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 listSourcesBySource = `-- name: ListSourcesBySource :many -Select id, site, name, source, type, value, enabled, url, tags, deleted From Sources where Source = $1 -` - -func (q *Queries) ListSourcesBySource(ctx context.Context, source string) ([]Source, error) { - rows, err := q.db.QueryContext(ctx, listSourcesBySource, source) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Source - for rows.Next() { - var i Source - if err := rows.Scan( - &i.ID, - &i.Site, - &i.Name, - &i.Source, - &i.Type, - &i.Value, - &i.Enabled, - &i.Url, - &i.Tags, - &i.Deleted, - ); 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 listSubscriptions = `-- name: ListSubscriptions :many -Select id, discordwebhookid, sourceid From subscriptions Limit $1 -` - -func (q *Queries) ListSubscriptions(ctx context.Context, limit int32) ([]Subscription, error) { - rows, err := q.db.QueryContext(ctx, listSubscriptions, limit) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Subscription - for rows.Next() { - var i Subscription - if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); 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 listSubscriptionsBySourceId = `-- name: ListSubscriptionsBySourceId :many -Select id, discordwebhookid, sourceid From subscriptions where sourceid = $1 -` - -func (q *Queries) ListSubscriptionsBySourceId(ctx context.Context, sourceid uuid.UUID) ([]Subscription, error) { - rows, err := q.db.QueryContext(ctx, listSubscriptionsBySourceId, sourceid) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Subscription - for rows.Next() { - var i Subscription - if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); 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 querySubscriptions = `-- name: QuerySubscriptions :one -Select id, discordwebhookid, sourceid From subscriptions Where discordwebhookid = $1 and sourceid = $2 Limit 1 -` - -type QuerySubscriptionsParams struct { - Discordwebhookid uuid.UUID - Sourceid uuid.UUID -} - -func (q *Queries) QuerySubscriptions(ctx context.Context, arg QuerySubscriptionsParams) (Subscription, error) { - row := q.db.QueryRowContext(ctx, querySubscriptions, arg.Discordwebhookid, arg.Sourceid) - var i Subscription - err := row.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid) - return i, err -} diff --git a/internal/database/schema/query.sql b/internal/database/schema/query.sql deleted file mode 100644 index f65ed96..0000000 --- a/internal/database/schema/query.sql +++ /dev/null @@ -1,215 +0,0 @@ -/* Articles */ --- name: GetArticleByID :one -Select * from Articles -WHERE ID = $1 LIMIT 1; - --- name: GetArticleByUrl :one -Select * from Articles -Where Url = $1 LIMIT 1; - --- name: ListArticles :many -Select * From articles -Order By PubDate DESC -offset $2 -fetch next $1 rows only; - --- name: ListArticlesByDate :many -Select * From articles -ORDER BY pubdate desc -Limit $1; - --- name: GetArticlesBySource :many -select * from articles -INNER join sources on articles.sourceid=Sources.ID -where site = $1; - --- name: ListNewArticlesBySourceId :many -SELECT * FROM articles -Where sourceid = $1 -ORDER BY pubdate desc -offset $3 -fetch next $2 rows only; - --- name: ListOldestArticlesBySourceId :many -SELECT * FROM articles -Where sourceid = $1 -ORDER BY pubdate asc -offset $3 -fetch next $2 rows only; - - --- name: ListArticlesBySourceId :many -Select * From articles -Where sourceid = $1 -Limit 50; - --- name: GetArticlesBySourceName :many -select -articles.ID, articles.SourceId, articles.Tags, articles.Title, articles.Url, articles.PubDate, articles.Video, articles.VideoHeight, articles.VideoWidth, articles.Thumbnail, articles.Description, articles.AuthorName, articles.AuthorImage, sources.source, sources.name -From articles -Left Join sources -On articles.sourceid = sources.id -Where name = $1; - --- name: ListArticlesByPage :many -select * from articles -order by pubdate desc -offset $2 -fetch next $1 rows only; - --- name: CreateArticle :exec -INSERT INTO Articles -(ID, SourceId, Tags, Title, Url, PubDate, Video, VideoHeight, VideoWidth, Thumbnail, Description, AuthorName, AuthorImage) -Values -($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13); - - -/* DiscordQueue */ --- name: CreateDiscordQueue :exec -Insert into DiscordQueue -(ID, ArticleId) -Values -($1, $2); - --- name: GetDiscordQueueByID :one -Select * from DiscordQueue -Where ID = $1 LIMIT 1; - --- name: DeleteDiscordQueueItem :exec -Delete From DiscordQueue Where ID = $1; - --- name: ListDiscordQueueItems :many -Select * from DiscordQueue LIMIT $1; - -/* DiscordWebHooks */ --- name: CreateDiscordWebHook :exec -Insert Into DiscordWebHooks -(ID, Url, Server, Channel, Enabled) -Values -($1, $2, $3, $4, $5); - --- name: GetDiscordWebHooksByID :one -Select * from DiscordWebHooks -Where ID = $1 LIMIT 1; - --- name: ListDiscordWebHooksByServer :many -Select * From DiscordWebHooks -Where Server = $1; - --- name: GetDiscordWebHooksByServerAndChannel :many -SELECT * FROM DiscordWebHooks -WHERE Server = $1 and Channel = $2; - --- name: GetDiscordWebHookByUrl :one -Select * From DiscordWebHooks Where url = $1; - --- name: ListDiscordWebhooks :many -Select * From discordwebhooks LIMIT $1; - --- name: DeleteDiscordWebHooks :exec -Delete From discordwebhooks Where ID = $1; - --- name: DisableDiscordWebHook :exec -Update discordwebhooks Set Enabled = FALSE where ID = $1; - --- name: EnableDiscordWebHook :exec -Update discordwebhooks Set Enabled = TRUE where ID = $1; - -/* Icons */ - --- name: CreateIcon :exec -INSERT INTO Icons -(ID, FileName, Site) -VALUES -($1,$2,$3); - --- name: GetIconByID :one -Select * FROM Icons -Where ID = $1 Limit 1; - --- name: GetIconBySite :one -Select * FROM Icons -Where Site = $1 Limit 1; - --- name: DeleteIcon :exec -Delete From Icons where ID = $1; - -/* Settings */ - --- name: CreateSettings :one -Insert Into settings -(ID, Key, Value, OPTIONS) -Values -($1,$2,$3,$4) -RETURNING *; - --- name: GetSettingByID :one -Select * From settings -Where ID = $1 Limit 1; - --- name: GetSettingByKey :one -Select * From settings Where -Key = $1 Limit 1; - --- name: GetSettingByValue :one -Select * From settings Where -Value = $1 Limit 1; - --- name: DeleteSetting :exec -Delete From settings Where ID = $1; - -/* Sources */ - --- name: CreateSource :exec -Insert Into Sources -(ID, Site, Name, Source, Type, Value, Enabled, Url, Tags) -Values -($1,$2,$3,$4,$5,$6,$7,$8,$9); - --- name: GetSourceByID :one -Select * From Sources where ID = $1 Limit 1; - --- name: GetSourceByName :one -Select * from Sources where name = $1 Limit 1; - --- name: GetSourceByNameAndSource :one -Select * from Sources WHERE name = $1 and source = $2; - --- name: ListSources :many -Select * From Sources Limit $1; - --- name: ListSourcesBySource :many -Select * From Sources where Source = $1; - --- name: DeleteSource :exec -UPDATE Sources Set Disabled = TRUE where id = $1; - --- name: DisableSource :exec -Update Sources Set Enabled = FALSE where ID = $1; - --- name: EnableSource :exec -Update Sources Set Enabled = TRUE where ID = $1; - - -/* Subscriptions */ - --- name: CreateSubscription :exec -Insert Into subscriptions (ID, DiscordWebHookId, SourceId) Values ($1, $2, $3); - --- name: ListSubscriptions :many -Select * From subscriptions Limit $1; - --- name: ListSubscriptionsBySourceId :many -Select * From subscriptions where sourceid = $1; - --- name: QuerySubscriptions :one -Select * From subscriptions Where discordwebhookid = $1 and sourceid = $2 Limit 1; - --- name: GetSubscriptionsBySourceID :many -Select * From subscriptions Where sourceid = $1; - --- name: GetSubscriptionsByDiscordWebHookId :many -Select * from subscriptions Where discordwebhookid = $1; - --- name: DeleteSubscription :exec -Delete From subscriptions Where id = $1; \ No newline at end of file diff --git a/internal/database/schema/schema.sql b/internal/database/schema/schema.sql deleted file mode 100644 index 280f877..0000000 --- a/internal/database/schema/schema.sql +++ /dev/null @@ -1,61 +0,0 @@ -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, - 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, - Value TEXT NOT NULL, - Options TEXT -); - -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, - Deleted BOOLEAN -); - -/* This table is used to track what the Web Hook wants to have sent by Source */; -Create TABLE Subscriptions ( - ID uuid Primary Key, - DiscordWebHookID uuid Not Null, - SourceID uuid Not Null -); diff --git a/internal/entity/entity.go b/internal/entity/entity.go index 107a9d6..bb8d5a5 100644 --- a/internal/entity/entity.go +++ b/internal/entity/entity.go @@ -121,13 +121,14 @@ type UserSourceSubscriptionEntity struct { } type UserEntity struct { - ID int64 - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt time.Time - Username string - Hash string - Scopes string + ID int64 + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time + Username string + Hash string + Scopes string + SessionToken string } type RefreshTokenEntity struct { diff --git a/internal/handler/v1/jwt.go b/internal/handler/v1/jwt.go index fba6795..d234694 100644 --- a/internal/handler/v1/jwt.go +++ b/internal/handler/v1/jwt.go @@ -83,11 +83,11 @@ func (j JwtToken) hasScope(scope string) error { return errors.New(ErrJwtScopeMissing) } -func (h *Handler) generateJwt(username, issuer string, userScopes []string, userId int64) (string, error) { - return h.generateJwtWithExp(username, issuer, userScopes, userId, time.Now().Add(10*time.Minute)) +func (h *Handler) generateJwt(username, issuer, sessionToken string, userScopes []string, userId int64) (string, error) { + return h.generateJwtWithExp(username, issuer, sessionToken, userScopes, userId, time.Now().Add(10*time.Minute)) } -func (h *Handler) generateJwtWithExp(username, issuer string, userScopes []string, userId int64, expiresAt time.Time) (string, error) { +func (h *Handler) generateJwtWithExp(username, issuer, sessionToken string, userScopes []string, userId int64, expiresAt time.Time) (string, error) { secret := []byte(h.config.JwtSecret) // Anyone who wants to decrypt the key needs to use the same method @@ -98,6 +98,7 @@ func (h *Handler) generateJwtWithExp(username, issuer string, userScopes []strin claims["username"] = username claims["iss"] = issuer claims["userId"] = userId + claims["sessionToken"] = sessionToken var scopes []string scopes = append(scopes, userScopes...) diff --git a/internal/handler/v1/auth.go b/internal/handler/v1/users.go similarity index 86% rename from internal/handler/v1/auth.go rename to internal/handler/v1/users.go index 61a9ad6..8179bbb 100644 --- a/internal/handler/v1/auth.go +++ b/internal/handler/v1/users.go @@ -8,6 +8,7 @@ import ( "git.jamestombleson.com/jtom38/newsbot-api/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/repository" + "github.com/google/uuid" "github.com/labstack/echo/v4" ) @@ -48,7 +49,7 @@ func (h *Handler) AuthRegister(c echo.Context) error { return h.WriteError(c, err, http.StatusInternalServerError) } - _, err = h.repo.Users.Create(c.Request().Context(), username, password, domain.ScopeArticleRead) + _, err = h.repo.Users.Create(c.Request().Context(), username, password, "", domain.ScopeArticleRead) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -91,8 +92,12 @@ func (h *Handler) AuthLogin(c echo.Context) error { // TODO think about moving this down some? expiresAt := time.Now().Add(time.Hour * 48) userScopes := strings.Split(user.Scopes, ",") + sessionToken, err := uuid.NewV7() + if err != nil { + return h.InternalServerErrorResponse(c, err.Error()) + } - jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, userScopes, user.ID, expiresAt) + jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, sessionToken.String(), userScopes, user.ID, expiresAt) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -109,6 +114,7 @@ func (h *Handler) AuthLogin(c echo.Context) error { Token: jwt, Type: "Bearer", RefreshToken: refresh, + SessionToken: sessionToken.String(), }) } @@ -124,8 +130,12 @@ func (h *Handler) createAdminToken(c echo.Context, password string) error { } var userScopes []string userScopes = append(userScopes, domain.ScopeAll) + sessionToken, err := uuid.NewV7() + if err != nil { + return h.InternalServerErrorResponse(c, err.Error()) + } - token, err := h.generateJwt("admin", h.config.ServerAddress, userScopes, -1) + token, err := h.generateJwt("admin", h.config.ServerAddress, sessionToken.String(), userScopes, -1) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -173,7 +183,7 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error { } userScopes := strings.Split(user.Scopes, ",") - jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, userScopes, user.ID, time.Now().Add(time.Hour*48)) + jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, "", userScopes, user.ID, time.Now().Add(time.Hour*48)) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -198,10 +208,10 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error { // @Param request body domain.UpdateScopesRequest true "body" // @Tags Users // @Accept json -// @Produce json +// @Produce json // @Success 200 {object} domain.BaseResponse -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.BaseResponse +// @Failure 500 {object} domain.BaseResponse // @Security Bearer func (h *Handler) AddScopes(c echo.Context) error { _, err := h.ValidateJwtToken(c, domain.ScopeAll) @@ -231,7 +241,7 @@ func (h *Handler) AddScopes(c echo.Context) error { // @Tags Users // @Accept json // @Produce json -// @Success 200 {object} domain.BaseResponse +// @Success 200 {object} domain.BaseResponse // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse // @Security Bearer @@ -250,7 +260,6 @@ func (h *Handler) RemoveScopes(c echo.Context) error { err = (&echo.DefaultBinder{}).BindBody(c, &request) if err != nil { h.WriteError(c, err, http.StatusBadRequest) - } err = h.repo.Users.RemoveScopes(c.Request().Context(), request.Username, request.Scopes) @@ -262,3 +271,26 @@ func (h *Handler) RemoveScopes(c echo.Context) error { Message: "OK", }) } + +// @Summary Revokes the current session token and replaces it with a new one. +// @Router /v1/users/refresh/sessionToken [post] +// @Tags Users +// @Accept json +// @Produce json +// @Success 200 {object} domain.BaseResponse +// @Failure 400 {object} domain.BaseResponse +// @Failure 500 {object} domain.BaseResponse +// @Security Bearer +func (h *Handler) LogoutEverywhere(c echo.Context) error { + token, err := h.getJwtTokenFromContext(c) + if err != nil { + return h.WriteError(c, err, http.StatusUnauthorized) + } + + err = token.IsValid(domain.ScopeAll) + if err != nil { + return h.WriteError(c, err, http.StatusUnauthorized) + } + + return c.String(http.StatusInternalServerError, "Not Implemented") +} diff --git a/internal/repository/users.go b/internal/repository/users.go index fbad225..2c052b2 100644 --- a/internal/repository/users.go +++ b/internal/repository/users.go @@ -14,17 +14,18 @@ import ( ) const ( - TableName string = "users" + usersTableName string = "users" ErrUserNotFound string = "requested user was not found" ) type Users interface { GetByName(ctx context.Context, name string) (entity.UserEntity, error) - Create(ctx context.Context, name, password, scope string) (int64, error) + Create(ctx context.Context, name, password, sessionTOken, scope string) (int64, error) Update(ctx context.Context, id int, entity entity.UserEntity) error UpdatePassword(ctx context.Context, name, password string) error CheckUserHash(ctx context.Context, name, password string) error UpdateScopes(ctx context.Context, name, scope string) error + UpdateSessionToken(ctx context.Context, name, sessionToken string) (int64, error) } // Creates a new instance of UserRepository with the bound sql @@ -58,7 +59,7 @@ func (ur userRepository) GetByName(ctx context.Context, name string) (entity.Use return data[0], nil } -func (ur userRepository) Create(ctx context.Context, name, password, scope string) (int64, error) { +func (ur userRepository) Create(ctx context.Context, name, password, sessionToken, scope string) (int64, error) { passwordBytes := []byte(password) hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost) if err != nil { @@ -68,8 +69,8 @@ func (ur userRepository) Create(ctx context.Context, name, password, scope strin dt := time.Now() queryBuilder := sqlbuilder.NewInsertBuilder() queryBuilder.InsertInto("users") - queryBuilder.Cols("Name", "Hash", "UpdatedAt", "CreatedAt", "DeletedAt", "Scopes") - queryBuilder.Values(name, string(hash), dt, dt, time.Time{}, scope) + queryBuilder.Cols("Name", "Hash", "UpdatedAt", "CreatedAt", "DeletedAt", "Scopes", "SessionToken") + queryBuilder.Values(name, string(hash), dt, dt, time.Time{}, scope, sessionToken) query, args := queryBuilder.Build() _, err = ur.connection.ExecContext(ctx, query, args...) @@ -91,11 +92,35 @@ func (ur userRepository) UpdatePassword(ctx context.Context, name, password stri } queryBuilder := sqlbuilder.NewUpdateBuilder() - queryBuilder.Update(TableName) + queryBuilder.Update(usersTableName) //queryBuilder.Set return nil } +func (ur userRepository) UpdateSessionToken(ctx context.Context, name, sessionToken string) (int64, error) { + _, err := ur.GetByName(ctx, name) + if err != nil { + return 0, err + } + + q := sqlbuilder.NewUpdateBuilder() + q.Update(usersTableName) + q.Set( + q.Equal("SessionToken", sessionToken), + ) + q.Where( + q.Equal("Name", name), + ) + + query, args := q.Build() + rowsUpdates, err := ur.connection.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return rowsUpdates.RowsAffected() +} + // 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(ctx context.Context, name, password string) error { @@ -141,18 +166,20 @@ func (ur userRepository) processRows(rows *sql.Rows) []entity.UserEntity { var updatedAt time.Time var deletedAt sql.NullTime var scopes string - err := rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &username, &hash, &scopes) + var sessionToken string + err := rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &username, &hash, &scopes, &sessionToken) if err != nil { fmt.Println(err) } item := entity.UserEntity{ - ID: id, - UpdatedAt: updatedAt, - Username: username, - Hash: hash, - Scopes: scopes, - CreatedAt: createdAt, + ID: id, + UpdatedAt: updatedAt, + Username: username, + Hash: hash, + Scopes: scopes, + CreatedAt: createdAt, + SessionToken: sessionToken, } if deletedAt.Valid { item.DeletedAt = deletedAt.Time diff --git a/internal/repository/users_test.go b/internal/repository/users_test.go index 84fe23a..40d6102 100644 --- a/internal/repository/users_test.go +++ b/internal/repository/users_test.go @@ -21,7 +21,7 @@ func TestCanCreateNewUser(t *testing.T) { defer db.Close() repo := repository.NewUserRepository(db) - updated, err := repo.Create(context.Background(), "testing", "NotSecure", "placeholder") + updated, err := repo.Create(context.Background(), "testing", "NotSecure", "sessionToken", "placeholder") if err != nil { log.Println(err) t.FailNow() @@ -38,7 +38,7 @@ func TestCanFindUserInTable(t *testing.T) { defer db.Close() repo := repository.NewUserRepository(db) - updated, err := repo.Create(context.Background(), "testing", "NotSecure", "placeholder") + updated, err := repo.Create(context.Background(), "testing", "NotSecure", "sessionToken", "placeholder") if err != nil { t.Log(err) t.FailNow() diff --git a/internal/repositoryServices/userService.go b/internal/repositoryServices/userService.go index 2bd52d8..4f7acdf 100644 --- a/internal/repositoryServices/userService.go +++ b/internal/repositoryServices/userService.go @@ -4,11 +4,13 @@ import ( "context" "database/sql" "errors" + "fmt" "strings" "git.jamestombleson.com/jtom38/newsbot-api/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/entity" "git.jamestombleson.com/jtom38/newsbot-api/internal/repository" + "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) @@ -25,7 +27,8 @@ type UserServices interface { GetUser(ctx context.Context, username string) (entity.UserEntity, error) AddScopes(ctx context.Context, username string, scopes []string) error RemoveScopes(ctx context.Context, username string, scopes []string) error - Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error) + Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error) + NewSessionToken(ctx context.Context, name string) error CheckPasswordForRequirements(password string) error } @@ -125,16 +128,34 @@ func (us UserService) doesScopeExist(scopes []string, target string) bool { return false } -func (us UserService) Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error) { +func (us UserService) Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error) { err := us.CheckPasswordForRequirements(password) if err != nil { return entity.UserEntity{}, err } - us.repo.Create(ctx, name, password, domain.ScopeArticleRead) + us.repo.Create(ctx, name, password, sessionToken, domain.ScopeArticleRead) return entity.UserEntity{}, nil } +func (us UserService) NewSessionToken(ctx context.Context, name string) error { + token, err := uuid.NewV7() + if err != nil { + return err + } + + rows, err := us.repo.UpdateSessionToken(ctx, name, token.String()) + if err != nil { + return err + } + + if rows != 1 { + return fmt.Errorf("UserService.NewSessionToken %w", err) + } + + return nil +} + func (us UserService) CheckPasswordForRequirements(password string) error { err := us.checkPasswordLength(password) if err != nil { diff --git a/internal/services/cron/scheduler.go b/internal/services/cron/scheduler.go index 41d3e75..4450ba9 100644 --- a/internal/services/cron/scheduler.go +++ b/internal/services/cron/scheduler.go @@ -7,12 +7,12 @@ import ( _ "github.com/lib/pq" "github.com/robfig/cron/v3" - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + //"git.jamestombleson.com/jtom38/newsbot-api/internal/database" "git.jamestombleson.com/jtom38/newsbot-api/internal/services" ) type Cron struct { - Db *database.Queries + //Db *database.Queries ctx context.Context timer *cron.Cron repo services.RepositoryService diff --git a/internal/services/output/discordwebhook.go b/internal/services/output/discordwebhook.go index f6a5c7e..4b7bc58 100644 --- a/internal/services/output/discordwebhook.go +++ b/internal/services/output/discordwebhook.go @@ -8,7 +8,8 @@ import ( "net/http" "strings" - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + "git.jamestombleson.com/jtom38/newsbot-api/internal/entity" + //"git.jamestombleson.com/jtom38/newsbot-api/internal/database" ) type discordField struct { @@ -63,11 +64,11 @@ const ( type Discord struct { Subscriptions []string - article database.Article + article entity.ArticleEntity Message *DiscordMessage } -func NewDiscordWebHookMessage(Article database.Article) Discord { +func NewDiscordWebHookMessage(Article entity.ArticleEntity) Discord { return Discord{ article: Article, } diff --git a/internal/services/output/discordwebhook_test.go b/internal/services/output/discordwebhook_test.go index 2628d7a..0896699 100644 --- a/internal/services/output/discordwebhook_test.go +++ b/internal/services/output/discordwebhook_test.go @@ -5,22 +5,19 @@ import ( "strings" "testing" - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" + //"git.jamestombleson.com/jtom38/newsbot-api/internal/database" + "git.jamestombleson.com/jtom38/newsbot-api/internal/entity" "git.jamestombleson.com/jtom38/newsbot-api/internal/services/output" - "github.com/google/uuid" "github.com/joho/godotenv" ) var ( - article database.Article = database.Article{ - ID: uuid.New(), - Sourceid: uuid.New(), + article entity.ArticleEntity = entity.ArticleEntity{ + ID: 999, + SourceID: 1, Tags: "unit, testing", Title: "Demo", Url: "https://github.com/jtom38/newsbot.collector.api", - //Pubdate: time.Now(), - Videoheight: 0, - Videowidth: 0, Description: "Hello World", } blank string = ""