initial commit
This commit is contained in:
89
internal/database/database.go
Normal file
89
internal/database/database.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewClient(pathToDB string) (Client, error) {
|
||||
db, err := sql.Open("sqlite3", pathToDB)
|
||||
if err != nil {
|
||||
return Client{}, err
|
||||
}
|
||||
c := Client{db}
|
||||
err = c.autoMigrate()
|
||||
if err != nil {
|
||||
return Client{}, err
|
||||
}
|
||||
return c, nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) autoMigrate() error {
|
||||
userTable := `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL
|
||||
);
|
||||
`
|
||||
_, err := c.db.Exec(userTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
refreshTokenTable := `
|
||||
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
token TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
revoked_at TIMESTAMP,
|
||||
user_id TEXT NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
);
|
||||
`
|
||||
_, err = c.db.Exec(refreshTokenTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
videoTable := `
|
||||
CREATE TABLE IF NOT EXISTS videos (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
thumbnail_url TEXT,
|
||||
video_url TEXT TEXT,
|
||||
user_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id)
|
||||
);
|
||||
`
|
||||
_, err = c.db.Exec(videoTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Client) Reset() error {
|
||||
if _, err := c.db.Exec("DELETE FROM refresh_tokens"); err != nil {
|
||||
return fmt.Errorf("failed to reset table refresh_tokens: %w", err)
|
||||
}
|
||||
if _, err := c.db.Exec("DELETE FROM users"); err != nil {
|
||||
return fmt.Errorf("failed to reset table users: %w", err)
|
||||
}
|
||||
if _, err := c.db.Exec("DELETE FROM videos"); err != nil {
|
||||
return fmt.Errorf("failed to reset table videos: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
83
internal/database/refresh_tokens.go
Normal file
83
internal/database/refresh_tokens.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type RefreshToken struct {
|
||||
CreateRefreshTokenParams
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
RevokedAt *time.Time `json:"revoked_at"`
|
||||
}
|
||||
|
||||
type CreateRefreshTokenParams struct {
|
||||
Token string `json:"token"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (c Client) CreateRefreshToken(params CreateRefreshTokenParams) (RefreshToken, error) {
|
||||
query := `
|
||||
INSERT INTO refresh_tokens (
|
||||
token,
|
||||
created_at,
|
||||
updated_at,
|
||||
user_id,
|
||||
expires_at
|
||||
) VALUES (?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?)
|
||||
`
|
||||
_, err := c.db.Exec(query, params.Token, params.UserID.String(), params.ExpiresAt)
|
||||
if err != nil {
|
||||
return RefreshToken{}, err
|
||||
}
|
||||
|
||||
return c.GetRefreshToken(params.Token)
|
||||
}
|
||||
|
||||
func (c Client) RevokeRefreshToken(token string) error {
|
||||
query := `
|
||||
UPDATE refresh_tokens
|
||||
SET revoked_at = CURRENT_TIMESTAMP
|
||||
WHERE token = ?
|
||||
`
|
||||
_, err := c.db.Exec(query, token)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Client) GetRefreshToken(token string) (RefreshToken, error) {
|
||||
query := `
|
||||
SELECT token, created_at, updated_at, user_id, expires_at, revoked_at
|
||||
FROM refresh_tokens
|
||||
WHERE token = ?
|
||||
`
|
||||
var rt RefreshToken
|
||||
var userID string
|
||||
err := c.db.QueryRow(query, token).
|
||||
Scan(&rt.Token, &rt.CreatedAt, &rt.UpdatedAt, &userID, &rt.ExpiresAt, &rt.RevokedAt)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return RefreshToken{}, nil
|
||||
}
|
||||
return RefreshToken{}, err
|
||||
}
|
||||
|
||||
rt.UserID, err = uuid.Parse(userID)
|
||||
if err != nil {
|
||||
return RefreshToken{}, err
|
||||
}
|
||||
|
||||
return rt, nil
|
||||
}
|
||||
|
||||
func (c Client) DeleteRefreshToken(token string) error {
|
||||
query := `
|
||||
DELETE FROM refresh_tokens
|
||||
WHERE token = ?
|
||||
`
|
||||
_, err := c.db.Exec(query, token)
|
||||
return err
|
||||
}
|
||||
147
internal/database/users.go
Normal file
147
internal/database/users.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreateUserParams
|
||||
}
|
||||
|
||||
type CreateUserParams struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (c Client) GetUsers() ([]User, error) {
|
||||
query := `
|
||||
SELECT
|
||||
id,
|
||||
email
|
||||
FROM users
|
||||
`
|
||||
|
||||
rows, err := c.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
users := []User{}
|
||||
for rows.Next() {
|
||||
var user User
|
||||
var id string
|
||||
if err := rows.Scan(&id, &user.Email); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.ID, err = uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (c Client) GetUserByEmail(email string) (User, error) {
|
||||
query := `
|
||||
SELECT id, created_at, updated_at, email, password
|
||||
FROM users
|
||||
WHERE email = ?
|
||||
`
|
||||
var user User
|
||||
var id string
|
||||
err := c.db.QueryRow(query, email).Scan(&id, &user.CreatedAt, &user.UpdatedAt, &user.Email, &user.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return User{}, nil
|
||||
}
|
||||
return User{}, err
|
||||
}
|
||||
user.ID, err = uuid.Parse(id)
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (c Client) GetUserByRefreshToken(token string) (*User, error) {
|
||||
query := `
|
||||
SELECT u.id, u.email, u.created_at, u.updated_at, u.password
|
||||
FROM users u
|
||||
JOIN refresh_tokens rt ON u.id = rt.user_id
|
||||
WHERE rt.token = ?
|
||||
`
|
||||
|
||||
var user User
|
||||
var id string
|
||||
err := c.db.QueryRow(query, token).Scan(&id, &user.Email, &user.CreatedAt, &user.UpdatedAt, &user.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
user.ID, err = uuid.Parse(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (c Client) CreateUser(params CreateUserParams) (*User, error) {
|
||||
id := uuid.New()
|
||||
|
||||
query := `
|
||||
INSERT INTO users
|
||||
(id, created_at, updated_at, email, password)
|
||||
VALUES
|
||||
(?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?)
|
||||
`
|
||||
_, err := c.db.Exec(query, id.String(), params.Email, params.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.GetUser(id)
|
||||
}
|
||||
|
||||
func (c Client) GetUser(id uuid.UUID) (*User, error) {
|
||||
query := `
|
||||
SELECT id, created_at, updated_at, email, password
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`
|
||||
var user User
|
||||
var idStr string
|
||||
err := c.db.QueryRow(query, id.String()).Scan(&idStr, &user.CreatedAt, &user.UpdatedAt, &user.Email, &user.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
user.ID, err = uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (c Client) DeleteUser(id uuid.UUID) error {
|
||||
query := `
|
||||
DELETE FROM users
|
||||
WHERE id = ?
|
||||
`
|
||||
_, err := c.db.Exec(query, id.String())
|
||||
return err
|
||||
}
|
||||
155
internal/database/videos.go
Normal file
155
internal/database/videos.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Video struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ThumbnailURL *string `json:"thumbnail_url"`
|
||||
VideoURL *string `json:"video_url"`
|
||||
CreateVideoParams
|
||||
}
|
||||
|
||||
type CreateVideoParams struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
}
|
||||
|
||||
func (c Client) GetVideos(userID uuid.UUID) ([]Video, error) {
|
||||
query := `
|
||||
SELECT
|
||||
id,
|
||||
created_at,
|
||||
updated_at,
|
||||
title,
|
||||
description,
|
||||
thumbnail_url,
|
||||
video_url,
|
||||
user_id
|
||||
FROM videos
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
rows, err := c.db.Query(query, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
videos := []Video{}
|
||||
for rows.Next() {
|
||||
var video Video
|
||||
if err := rows.Scan(
|
||||
&video.ID,
|
||||
&video.CreatedAt,
|
||||
&video.UpdatedAt,
|
||||
&video.Title,
|
||||
&video.Description,
|
||||
&video.ThumbnailURL,
|
||||
&video.VideoURL,
|
||||
&video.UserID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
videos = append(videos, video)
|
||||
}
|
||||
|
||||
return videos, nil
|
||||
}
|
||||
|
||||
func (c Client) CreateVideo(params CreateVideoParams) (Video, error) {
|
||||
id := uuid.New()
|
||||
query := `
|
||||
INSERT INTO videos (
|
||||
id,
|
||||
created_at,
|
||||
updated_at,
|
||||
title,
|
||||
description,
|
||||
user_id
|
||||
) VALUES (?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?, ?)
|
||||
`
|
||||
_, err := c.db.Exec(query, id, params.Title, params.Description, params.UserID)
|
||||
if err != nil {
|
||||
return Video{}, err
|
||||
}
|
||||
|
||||
return c.GetVideo(id)
|
||||
}
|
||||
|
||||
func (c Client) GetVideo(id uuid.UUID) (Video, error) {
|
||||
query := `
|
||||
SELECT
|
||||
id,
|
||||
created_at,
|
||||
updated_at,
|
||||
title,
|
||||
description,
|
||||
thumbnail_url,
|
||||
video_url,
|
||||
user_id
|
||||
FROM videos
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
var video Video
|
||||
err := c.db.QueryRow(query, id).Scan(
|
||||
&video.ID,
|
||||
&video.CreatedAt,
|
||||
&video.UpdatedAt,
|
||||
&video.Title,
|
||||
&video.Description,
|
||||
&video.ThumbnailURL,
|
||||
&video.VideoURL,
|
||||
&video.UserID)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return Video{}, nil
|
||||
}
|
||||
return Video{}, err
|
||||
}
|
||||
|
||||
return video, nil
|
||||
}
|
||||
|
||||
func (c Client) UpdateVideo(video Video) error {
|
||||
query := `
|
||||
UPDATE videos
|
||||
SET
|
||||
title = ?,
|
||||
description = ?,
|
||||
thumbnail_url = ?,
|
||||
video_url = ?,
|
||||
user_id = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
_, err := c.db.Exec(
|
||||
query,
|
||||
video.Title,
|
||||
video.Description,
|
||||
&video.ThumbnailURL,
|
||||
&video.VideoURL,
|
||||
video.UserID,
|
||||
video.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Client) DeleteVideo(id uuid.UUID) error {
|
||||
query := `
|
||||
DELETE FROM videos
|
||||
WHERE id = ?
|
||||
`
|
||||
_, err := c.db.Exec(query, id)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user