5 changed files with 230 additions and 0 deletions
@ -1,4 +1,12 @@
@@ -1,4 +1,12 @@
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= |
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= |
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= |
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= |
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= |
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= |
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= |
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= |
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d h1:dOMI4+zEbDI37KGb0TI44GUAwxHF9cMsIoDTJ7UmgfU= |
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s= |
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= |
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= |
||||
|
||||
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
package db |
||||
|
||||
import ( |
||||
"context" |
||||
"database/sql" |
||||
"errors" |
||||
"time" |
||||
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql" |
||||
) |
||||
|
||||
type Progress struct { |
||||
ID string |
||||
UserID string |
||||
LevelID string |
||||
Status string |
||||
CompletedAt sql.NullTime |
||||
AttemptData sql.NullString |
||||
Stars sql.NullInt64 |
||||
CreatedAt time.Time |
||||
UpdatedAt time.Time |
||||
} |
||||
|
||||
type ProgressClient struct { |
||||
db *sql.DB |
||||
} |
||||
|
||||
func NewProgressClient(db *sql.DB) *ProgressClient { |
||||
return &ProgressClient{db: db} |
||||
} |
||||
|
||||
func (c *ProgressClient) UpsertProgress(ctx context.Context, p *Progress) error { |
||||
_, err := c.db.ExecContext(ctx, ` |
||||
INSERT INTO progress (id, user_id, level_id, status, completed_at, attempt_data, stars) |
||||
VALUES (?, ?, ?, ?, ?, ?, ?) |
||||
ON CONFLICT(user_id, level_id) DO UPDATE SET |
||||
status = excluded.status, |
||||
completed_at = excluded.completed_at, |
||||
attempt_data = excluded.attempt_data, |
||||
stars = excluded.stars, |
||||
updated_at = CURRENT_TIMESTAMP |
||||
`, p.ID, p.UserID, p.LevelID, p.Status, p.CompletedAt, p.AttemptData, p.Stars) |
||||
return err |
||||
} |
||||
|
||||
func (c *ProgressClient) GetProgressByUser(ctx context.Context, userID string) ([]*Progress, error) { |
||||
rows, err := c.db.QueryContext(ctx, ` |
||||
SELECT id, user_id, level_id, status, completed_at, attempt_data, stars, created_at, updated_at |
||||
FROM progress |
||||
WHERE user_id = ? |
||||
`, userID) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer rows.Close() |
||||
|
||||
var progressList []*Progress |
||||
for rows.Next() { |
||||
var p Progress |
||||
err := rows.Scan(&p.ID, &p.UserID, &p.LevelID, &p.Status, &p.CompletedAt, &p.AttemptData, &p.Stars, &p.CreatedAt, &p.UpdatedAt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
progressList = append(progressList, &p) |
||||
} |
||||
|
||||
return progressList, nil |
||||
} |
||||
|
||||
func (c *ProgressClient) GetProgress(ctx context.Context, userID, levelID string) (*Progress, error) { |
||||
row := c.db.QueryRowContext(ctx, ` |
||||
SELECT id, user_id, level_id, status, completed_at, attempt_data, stars, created_at, updated_at |
||||
FROM progress |
||||
WHERE user_id = ? AND level_id = ? |
||||
`, userID, levelID) |
||||
|
||||
var p Progress |
||||
err := row.Scan(&p.ID, &p.UserID, &p.LevelID, &p.Status, &p.CompletedAt, &p.AttemptData, &p.Stars, &p.CreatedAt, &p.UpdatedAt) |
||||
if err != nil { |
||||
if errors.Is(err, sql.ErrNoRows) { |
||||
return nil, nil |
||||
} |
||||
return nil, err |
||||
} |
||||
return &p, nil |
||||
} |
||||
|
||||
func (c *ProgressClient) DeleteProgress(ctx context.Context, userID, levelID string) error { |
||||
_, err := c.db.ExecContext(ctx, ` |
||||
DELETE FROM progress |
||||
WHERE user_id = ? AND level_id = ? |
||||
`, userID, levelID) |
||||
return err |
||||
} |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
package db |
||||
|
||||
import ( |
||||
"context" |
||||
"database/sql" |
||||
"time" |
||||
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql" |
||||
) |
||||
|
||||
type Subscription struct { |
||||
ID string |
||||
UserID string |
||||
StripeID string |
||||
Status string |
||||
StartedAt time.Time |
||||
EndsAt sql.NullTime |
||||
} |
||||
|
||||
type SubscriptionClient struct { |
||||
db *sql.DB |
||||
} |
||||
|
||||
func NewSubscriptionClient(db *sql.DB) *SubscriptionClient { |
||||
return &SubscriptionClient{db: db} |
||||
} |
||||
|
||||
// Insert or update subscription
|
||||
func (c *SubscriptionClient) Upsert(ctx context.Context, s *Subscription) error { |
||||
_, err := c.db.ExecContext(ctx, ` |
||||
INSERT INTO subscriptions (id, user_id, stripe_id, status, started_at, ends_at) |
||||
VALUES (?, ?, ?, ?, ?, ?) |
||||
ON CONFLICT(id) DO UPDATE SET |
||||
status = excluded.status, |
||||
started_at = excluded.started_at, |
||||
ends_at = excluded.ends_at |
||||
`, s.ID, s.UserID, s.StripeID, s.Status, s.StartedAt, s.EndsAt) |
||||
return err |
||||
} |
||||
|
||||
// Get by user ID
|
||||
func (c *SubscriptionClient) GetByUserID(ctx context.Context, userID string) (*Subscription, error) { |
||||
row := c.db.QueryRowContext(ctx, ` |
||||
SELECT id, user_id, stripe_id, status, started_at, ends_at |
||||
FROM subscriptions |
||||
WHERE user_id = ? |
||||
`, userID) |
||||
|
||||
var s Subscription |
||||
err := row.Scan(&s.ID, &s.UserID, &s.StripeID, &s.Status, &s.StartedAt, &s.EndsAt) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &s, nil |
||||
} |
||||
|
||||
// Delete by user ID
|
||||
func (c *SubscriptionClient) DeleteByUserID(ctx context.Context, userID string) error { |
||||
_, err := c.db.ExecContext(ctx, ` |
||||
DELETE FROM subscriptions |
||||
WHERE user_id = ? |
||||
`, userID) |
||||
return err |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
package db |
||||
|
||||
import ( |
||||
"context" |
||||
"database/sql" |
||||
"time" |
||||
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql" |
||||
) |
||||
|
||||
type User struct { |
||||
ID string |
||||
GitHubID int64 |
||||
GitHubLogin string |
||||
AvatarURL string |
||||
Email string |
||||
CreatedAt time.Time |
||||
UpdatedAt time.Time |
||||
} |
||||
|
||||
type UserClient struct { |
||||
db *sql.DB |
||||
} |
||||
|
||||
func NewUserClient(db *sql.DB) *UserClient { |
||||
return &UserClient{db: db} |
||||
} |
||||
|
||||
func (uc *UserClient) UpsertUser(ctx context.Context, user User) error { |
||||
_, err := uc.db.ExecContext(ctx, ` |
||||
INSERT INTO users (id, github_id, github_login, avatar_url, email) |
||||
VALUES (?, ?, ?, ?, ?) |
||||
ON CONFLICT(github_id) DO UPDATE SET |
||||
github_login = excluded.github_login, |
||||
avatar_url = excluded.avatar_url, |
||||
email = excluded.email, |
||||
updated_at = CURRENT_TIMESTAMP |
||||
`, user.ID, user.GitHubID, user.GitHubLogin, user.AvatarURL, user.Email) |
||||
return err |
||||
} |
||||
|
||||
func (uc *UserClient) GetUserByGitHubID(ctx context.Context, githubID int64) (*User, error) { |
||||
row := uc.db.QueryRowContext(ctx, ` |
||||
SELECT id, github_id, github_login, avatar_url, email, created_at, updated_at |
||||
FROM users WHERE github_id = ? |
||||
`, githubID) |
||||
|
||||
var user User |
||||
err := row.Scan( |
||||
&user.ID, &user.GitHubID, &user.GitHubLogin, |
||||
&user.AvatarURL, &user.Email, &user.CreatedAt, &user.UpdatedAt, |
||||
) |
||||
if err == sql.ErrNoRows { |
||||
return nil, nil |
||||
} |
||||
return &user, err |
||||
} |
||||
Loading…
Reference in new issue