feat!: db schema revisions to support note expiration
This commit is contained in:
parent
7026ee4c76
commit
81c2eecd77
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
|
|
||||||
package data
|
package data
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
|
|
||||||
package data
|
package data
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ type Note struct {
|
|||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
CurrentVersion int32 `json:"current_version"`
|
CurrentVersion int32 `json:"current_version"`
|
||||||
LatestVersion int32 `json:"latest_version"`
|
LatestVersion int32 `json:"latest_version"`
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
// source: note_versions.sql
|
// source: note_versions.sql
|
||||||
|
|
||||||
package data
|
package data
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
// source: notes.sql
|
// source: notes.sql
|
||||||
|
|
||||||
package data
|
package data
|
||||||
@ -15,7 +15,7 @@ import (
|
|||||||
const createNote = `-- name: CreateNote :one
|
const createNote = `-- name: CreateNote :one
|
||||||
INSERT INTO notes (user_id)
|
INSERT INTO notes (user_id)
|
||||||
VALUES ($1)
|
VALUES ($1)
|
||||||
RETURNING id, user_id, current_version, latest_version, created_at, updated_at
|
RETURNING id, user_id, current_version, latest_version, expires_at, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CreateNote(ctx context.Context, userID uuid.UUID) (Note, error) {
|
func (q *Queries) CreateNote(ctx context.Context, userID uuid.UUID) (Note, error) {
|
||||||
@ -26,12 +26,23 @@ func (q *Queries) CreateNote(ctx context.Context, userID uuid.UUID) (Note, error
|
|||||||
&i.UserID,
|
&i.UserID,
|
||||||
&i.CurrentVersion,
|
&i.CurrentVersion,
|
||||||
&i.LatestVersion,
|
&i.LatestVersion,
|
||||||
|
&i.ExpiresAt,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteExpiredNotes = `-- name: DeleteExpiredNotes :exec
|
||||||
|
DELETE FROM notes
|
||||||
|
WHERE expires_at < NOW()
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteExpiredNotes(ctx context.Context) error {
|
||||||
|
_, err := q.db.Exec(ctx, deleteExpiredNotes)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const deleteNote = `-- name: DeleteNote :exec
|
const deleteNote = `-- name: DeleteNote :exec
|
||||||
DELETE FROM notes
|
DELETE FROM notes
|
||||||
WHERE id = $1 AND user_id = $2
|
WHERE id = $1 AND user_id = $2
|
||||||
@ -55,6 +66,7 @@ SELECT
|
|||||||
nv.content,
|
nv.content,
|
||||||
nv.version_number,
|
nv.version_number,
|
||||||
nv.created_at AS version_created_at,
|
nv.created_at AS version_created_at,
|
||||||
|
n.expires_at AS note_expires_at,
|
||||||
n.created_at AS note_created_at,
|
n.created_at AS note_created_at,
|
||||||
n.updated_at AS note_updated_at
|
n.updated_at AS note_updated_at
|
||||||
FROM notes n
|
FROM notes n
|
||||||
@ -70,6 +82,7 @@ type GetFullNoteRow struct {
|
|||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
VersionNumber int32 `json:"version_number"`
|
VersionNumber int32 `json:"version_number"`
|
||||||
VersionCreatedAt *time.Time `json:"version_created_at"`
|
VersionCreatedAt *time.Time `json:"version_created_at"`
|
||||||
|
NoteExpiresAt *time.Time `json:"note_expires_at"`
|
||||||
NoteCreatedAt *time.Time `json:"note_created_at"`
|
NoteCreatedAt *time.Time `json:"note_created_at"`
|
||||||
NoteUpdatedAt *time.Time `json:"note_updated_at"`
|
NoteUpdatedAt *time.Time `json:"note_updated_at"`
|
||||||
}
|
}
|
||||||
@ -84,17 +97,64 @@ func (q *Queries) GetFullNote(ctx context.Context, id uuid.UUID) (GetFullNoteRow
|
|||||||
&i.Content,
|
&i.Content,
|
||||||
&i.VersionNumber,
|
&i.VersionNumber,
|
||||||
&i.VersionCreatedAt,
|
&i.VersionCreatedAt,
|
||||||
|
&i.NoteExpiresAt,
|
||||||
&i.NoteCreatedAt,
|
&i.NoteCreatedAt,
|
||||||
&i.NoteUpdatedAt,
|
&i.NoteUpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const listExpiredNotes = `-- name: ListExpiredNotes :many
|
||||||
|
SELECT
|
||||||
|
n.id AS note_id,
|
||||||
|
n.user_id AS owner_id,
|
||||||
|
nv.title,
|
||||||
|
n.expires_at
|
||||||
|
FROM notes n
|
||||||
|
JOIN note_versions nv
|
||||||
|
ON n.id = nv.note_id AND n.current_version = nv.version_number
|
||||||
|
WHERE n.expires_at <= NOW()
|
||||||
|
ORDER BY n.expires_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListExpiredNotesRow struct {
|
||||||
|
NoteID uuid.UUID `json:"note_id"`
|
||||||
|
OwnerID uuid.UUID `json:"owner_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListExpiredNotes(ctx context.Context) ([]ListExpiredNotesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listExpiredNotes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ListExpiredNotesRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i ListExpiredNotesRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.NoteID,
|
||||||
|
&i.OwnerID,
|
||||||
|
&i.Title,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const listNotes = `-- name: ListNotes :many
|
const listNotes = `-- name: ListNotes :many
|
||||||
SELECT
|
SELECT
|
||||||
n.id AS note_id,
|
n.id AS note_id,
|
||||||
n.user_id AS owner_id,
|
n.user_id AS owner_id,
|
||||||
nv.title,
|
nv.title,
|
||||||
|
n.expires_at,
|
||||||
n.updated_at
|
n.updated_at
|
||||||
FROM notes n
|
FROM notes n
|
||||||
JOIN note_versions nv
|
JOIN note_versions nv
|
||||||
@ -114,6 +174,7 @@ type ListNotesRow struct {
|
|||||||
NoteID uuid.UUID `json:"note_id"`
|
NoteID uuid.UUID `json:"note_id"`
|
||||||
OwnerID uuid.UUID `json:"owner_id"`
|
OwnerID uuid.UUID `json:"owner_id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +191,7 @@ func (q *Queries) ListNotes(ctx context.Context, arg ListNotesParams) ([]ListNot
|
|||||||
&i.NoteID,
|
&i.NoteID,
|
||||||
&i.OwnerID,
|
&i.OwnerID,
|
||||||
&i.Title,
|
&i.Title,
|
||||||
|
&i.ExpiresAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -141,3 +203,20 @@ func (q *Queries) ListNotes(ctx context.Context, arg ListNotesParams) ([]ListNot
|
|||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setNoteExpiration = `-- name: SetNoteExpiration :exec
|
||||||
|
UPDATE notes
|
||||||
|
SET expires_at = $1, updated_at = NOW()
|
||||||
|
WHERE id = $2 AND user_id = $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type SetNoteExpirationParams struct {
|
||||||
|
ExpiresAt *time.Time `json:"expires_at"`
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SetNoteExpiration(ctx context.Context, arg SetNoteExpirationParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, setNoteExpiration, arg.ExpiresAt, arg.ID, arg.UserID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
// source: refresh_tokens.sql
|
// source: refresh_tokens.sql
|
||||||
|
|
||||||
package data
|
package data
|
||||||
@ -41,14 +41,17 @@ func (q *Queries) CreateRefreshToken(ctx context.Context, arg CreateRefreshToken
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteExpiredRefreshTokens = `-- name: DeleteExpiredRefreshTokens :exec
|
const deleteExpiredRefreshTokens = `-- name: DeleteExpiredRefreshTokens :execrows
|
||||||
DELETE FROM refresh_tokens
|
DELETE FROM refresh_tokens
|
||||||
WHERE expires_at < NOW()
|
WHERE expires_at < NOW() OR revoked = TRUE
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) DeleteExpiredRefreshTokens(ctx context.Context) error {
|
func (q *Queries) DeleteExpiredRefreshTokens(ctx context.Context) (int64, error) {
|
||||||
_, err := q.db.Exec(ctx, deleteExpiredRefreshTokens)
|
result, err := q.db.Exec(ctx, deleteExpiredRefreshTokens)
|
||||||
return err
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return result.RowsAffected(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRefreshTokenByHash = `-- name: GetRefreshTokenByHash :one
|
const getRefreshTokenByHash = `-- name: GetRefreshTokenByHash :one
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.28.0
|
// sqlc v1.29.0
|
||||||
// source: users.sql
|
// source: users.sql
|
||||||
|
|
||||||
package data
|
package data
|
||||||
|
@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS notes (
|
|||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
current_version INT NOT NULL DEFAULT 1, -- active version (can be historical)
|
current_version INT NOT NULL DEFAULT 1, -- active version (can be historical)
|
||||||
latest_version INT NOT NULL DEFAULT 1, -- highest version number
|
latest_version INT NOT NULL DEFAULT 1, -- highest version number
|
||||||
|
expires_at TIMESTAMPTZ DEFAULT NULL,
|
||||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
);
|
);
|
||||||
@ -45,5 +46,6 @@ CREATE INDEX IF NOT EXISTS idx_refresh_tokens_expires_at ON refresh_tokens(expir
|
|||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_notes_user_updated ON notes(user_id, updated_at DESC);
|
CREATE INDEX IF NOT EXISTS idx_notes_user_updated ON notes(user_id, updated_at DESC);
|
||||||
CREATE INDEX IF NOT EXISTS idx_notes_current_version ON notes(current_version);
|
CREATE INDEX IF NOT EXISTS idx_notes_current_version ON notes(current_version);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_notes_expires_at ON notes(expires_at) WHERE expires_at IS NOT NULL;
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_note_versions_content_hash ON note_versions(note_id, content_hash);
|
CREATE INDEX IF NOT EXISTS idx_note_versions_content_hash ON note_versions(note_id, content_hash);
|
||||||
|
@ -8,6 +8,7 @@ SELECT
|
|||||||
n.id AS note_id,
|
n.id AS note_id,
|
||||||
n.user_id AS owner_id,
|
n.user_id AS owner_id,
|
||||||
nv.title,
|
nv.title,
|
||||||
|
n.expires_at,
|
||||||
n.updated_at
|
n.updated_at
|
||||||
FROM notes n
|
FROM notes n
|
||||||
JOIN note_versions nv
|
JOIN note_versions nv
|
||||||
@ -24,6 +25,7 @@ SELECT
|
|||||||
nv.content,
|
nv.content,
|
||||||
nv.version_number,
|
nv.version_number,
|
||||||
nv.created_at AS version_created_at,
|
nv.created_at AS version_created_at,
|
||||||
|
n.expires_at AS note_expires_at,
|
||||||
n.created_at AS note_created_at,
|
n.created_at AS note_created_at,
|
||||||
n.updated_at AS note_updated_at
|
n.updated_at AS note_updated_at
|
||||||
FROM notes n
|
FROM notes n
|
||||||
@ -34,3 +36,24 @@ WHERE n.id = $1;
|
|||||||
-- name: DeleteNote :exec
|
-- name: DeleteNote :exec
|
||||||
DELETE FROM notes
|
DELETE FROM notes
|
||||||
WHERE id = $1 AND user_id = $2;
|
WHERE id = $1 AND user_id = $2;
|
||||||
|
|
||||||
|
-- name: SetNoteExpiration :exec
|
||||||
|
UPDATE notes
|
||||||
|
SET expires_at = $1, updated_at = NOW()
|
||||||
|
WHERE id = $2 AND user_id = $3;
|
||||||
|
|
||||||
|
-- name: ListExpiredNotes :many
|
||||||
|
SELECT
|
||||||
|
n.id AS note_id,
|
||||||
|
n.user_id AS owner_id,
|
||||||
|
nv.title,
|
||||||
|
n.expires_at
|
||||||
|
FROM notes n
|
||||||
|
JOIN note_versions nv
|
||||||
|
ON n.id = nv.note_id AND n.current_version = nv.version_number
|
||||||
|
WHERE n.expires_at <= NOW()
|
||||||
|
ORDER BY n.expires_at;
|
||||||
|
|
||||||
|
-- name: DeleteExpiredNotes :exec
|
||||||
|
DELETE FROM notes
|
||||||
|
WHERE expires_at < NOW();
|
||||||
|
@ -20,6 +20,6 @@ UPDATE refresh_tokens
|
|||||||
SET revoked = TRUE
|
SET revoked = TRUE
|
||||||
WHERE user_id = $1;
|
WHERE user_id = $1;
|
||||||
|
|
||||||
-- name: DeleteExpiredRefreshTokens :exec
|
-- name: DeleteExpiredRefreshTokens :execrows
|
||||||
DELETE FROM refresh_tokens
|
DELETE FROM refresh_tokens
|
||||||
WHERE expires_at < NOW();
|
WHERE expires_at < NOW() OR revoked = TRUE;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user