Skip to content

Commit

Permalink
chore: add database
Browse files Browse the repository at this point in the history
  • Loading branch information
katallaxie committed Feb 8, 2024
1 parent 4ff6785 commit b66926d
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 729 deletions.
154 changes: 93 additions & 61 deletions adapters/adapter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package adapters

import (
"context"
"encoding/gob"
"errors"
"time"
Expand Down Expand Up @@ -34,78 +35,94 @@ const (
AccountTypeWebAuthn AccountType = "webauthn"
)

// Account represents an account.
// Account ...
type Account struct {
ID uuid.UUID `json:"id"`
Type AccountType `json:"type"`
Provider string `json:"provider"`
ProviderAccountID string `json:"provider_account_id"`
RefreshToken string `json:"refresh_token"`
AccessToken string `json:"access_token"`
ExpiresAt time.Time `json:"expires_at"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
IDToken string `json:"id_token"`
SessionState string `json:"session_state"`
UserID uuid.UUID `json:"user_id"`
RawData map[string]interface{} `json:"raw_data"`
}

// User represents a user.
ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;column:id;default:gen_random_uuid();"`
Type AccountType `json:"type"`
Provider string `json:"provider"`
ProviderAccountID *string `json:"provider_account_id"`
RefreshToken *string `json:"refresh_token"`
AccessToken *string `json:"access_token"`
ExpiresAt *time.Time `json:"expires_at"`
TokenType *string `json:"token_type"`
Scope *string `json:"scope"`
IDToken *string `json:"id_token"`
SessionState string `json:"session_state"`
UserID *uuid.UUID `json:"user_id"`
User User `json:"user" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"`
}

// User ...
type User struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
EmailVerified string `json:"email_verified"`
Image string `json:"image"`
RawData map[string]interface{} `json:"raw_data"`
ID uuid.UUID `json:"id" gorm:"primaryKey;unique;type:uuid;column:id;default:gen_random_uuid()"`
Name string `json:"name"`
Email string `json:"email"`
EmailVerified *bool `json:"email_verified"`
Image *string `json:"image"`
Accounts []Account `json:"accounts" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
Sessions []Session `json:"sessions" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"`
}

// Session represents a session.
// Session ...
type Session struct {
ID uuid.UUID `json:"id"`
ID uuid.UUID `json:"id" gorm:"primaryKey;unique;type:uuid;column:id;default:gen_random_uuid()"`
ExpiresAt time.Time `json:"expires_at"`
SessionToken string `json:"session_token"`
UserID uuid.UUID `json:"user_id"`
User User `json:"user" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time
}

// VerificationToken represents a verification token.
// VerificationToken ...
type VerificationToken struct {
Token string `json:"token"`
Token string `json:"token" gorm:"primaryKey"`
Identifier string `json:"identifier"`
ExpiresAt time.Time `json:"expires_at"`

CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"`
}

// Adapter is an interface that defines the methods for interacting with the underlying data storage.
type Adapter interface {
// CreateAccount creates a new account.
CreateUser(user *User) (*User, error)
// CreateUser creates a new user.
CreateUser(ctx context.Context, user User) (User, error)
// GetUser retrieves a user by ID.
GetUser(ID string) (*User, error)
GetUser(id uuid.UUID) (User, error)
// GetUserByEmail retrieves a user by email.
GetUserByEmail(email string) (*User, error)
// GetUserByAccount retrieves a user by account.
GetUserByAccount(provider string, providerAccountID string) (*User, error)
GetUserByEmail(email string) (User, error)
// UpdateUser updates a user.
UpdateUser(user *User) (*User, error)
UpdateUser(user User) (User, error)
// DeleteUser deletes a user by ID.
DeleteUser(ID string) error
DeleteUser(id uuid.UUID) error
// LinkAccount links an account to a user.
LinkAccount(accountID, userID uuid.UUID) error
// UnlinkAccount unlinks an account from a user.
UnlinkAccount(accountID, userID uuid.UUID) error
// CreateSession creates a new session.
CreateSession(session *Session) (*Session, error)
CreateSession(ctx context.Context, userID uuid.UUID, expires time.Time) (Session, error)
// GetSession retrieves a session by session token.
GetSession(sessionToken string) (*Session, error)
GetSession(sessionToken string) (Session, error)
// UpdateSession updates a session.
UpdateSession(session *Session) (*Session, error)
UpdateSession(session Session) (Session, error)
// DeleteSession deletes a session by session token.
DeleteSession(sessionToken string) error
// CreateVerificationToken creates a new verification token.
CreateVerificationToken(verficationToken *VerificationToken) (*VerificationToken, error)
CreateVerificationToken(verficationToken VerificationToken) (VerificationToken, error)
// UseVerficationToken uses a verification token.
UseVerficationToken(identifier string, token string) (*VerificationToken, error)
UseVerficationToken(identifier string, token string) (VerificationToken, error)
}

var _ Adapter = (*UnimplementedAdapter)(nil)
Expand All @@ -114,32 +131,32 @@ var _ Adapter = (*UnimplementedAdapter)(nil)
type UnimplementedAdapter struct{}

// CreateUser creates a new user.
func (a *UnimplementedAdapter) CreateUser(user *User) (*User, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) CreateUser(_ context.Context, user User) (User, error) {
return User{}, ErrUnimplemented
}

// GetUser retrieves a user by ID.
func (a *UnimplementedAdapter) GetUser(id string) (*User, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) GetUser(id uuid.UUID) (User, error) {
return User{}, ErrUnimplemented
}

// GetUserByEmail retrieves a user by email.
func (a *UnimplementedAdapter) GetUserByEmail(email string) (*User, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) GetUserByEmail(email string) (User, error) {
return User{}, ErrUnimplemented
}

// GetUserByAccount retrieves a user by account.
func (a *UnimplementedAdapter) GetUserByAccount(provider string, providerAccountID string) (*User, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) GetUserByAccount(provider string, providerAccountID string) (User, error) {
return User{}, ErrUnimplemented
}

// UpdateUser updates a user.
func (a *UnimplementedAdapter) UpdateUser(user *User) (*User, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) UpdateUser(user User) (User, error) {
return User{}, ErrUnimplemented
}

// DeleteUser deletes a user by ID.
func (a *UnimplementedAdapter) DeleteUser(id string) error {
func (a *UnimplementedAdapter) DeleteUser(id uuid.UUID) error {
return ErrUnimplemented
}

Expand All @@ -154,18 +171,18 @@ func (a *UnimplementedAdapter) UnlinkAccount(accountID, userID uuid.UUID) error
}

// CreateSession creates a new session.
func (a *UnimplementedAdapter) CreateSession(session *Session) (*Session, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) CreateSession(ctx context.Context, userID uuid.UUID, expires time.Time) (Session, error) {
return Session{}, ErrUnimplemented
}

// GetSession retrieves a session by session token.
func (a *UnimplementedAdapter) GetSession(sessionToken string) (*Session, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) GetSession(sessionToken string) (Session, error) {
return Session{}, ErrUnimplemented
}

// UpdateSession updates a session.
func (a *UnimplementedAdapter) UpdateSession(session *Session) (*Session, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) UpdateSession(session Session) (Session, error) {
return Session{}, ErrUnimplemented
}

// DeleteSession deletes a session by session token.
Expand All @@ -174,11 +191,26 @@ func (a *UnimplementedAdapter) DeleteSession(sessionToken string) error {
}

// CreateVerificationToken creates a new verification token.
func (a *UnimplementedAdapter) CreateVerificationToken(verficationToken *VerificationToken) (*VerificationToken, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) CreateVerificationToken(verficationToken VerificationToken) (VerificationToken, error) {
return VerificationToken{}, ErrUnimplemented
}

// UseVerficationToken uses a verification token.
func (a *UnimplementedAdapter) UseVerficationToken(identifier string, token string) (*VerificationToken, error) {
return nil, ErrUnimplemented
func (a *UnimplementedAdapter) UseVerficationToken(identifier string, token string) (VerificationToken, error) {
return VerificationToken{}, ErrUnimplemented
}

// GetAccount retrieve by provider and provider account ID.
func (a *UnimplementedAdapter) GetAccount(provider string, providerAccountID string) (Account, error) {
return Account{}, ErrUnimplemented
}

// StringPtr returns a pointer to the string value passed in.
func StringPtr(s string) *string {
return &s
}

// TimePtr returns a pointer to the time value passed in.
func TimePtr(t time.Time) *time.Time {
return &t
}
110 changes: 41 additions & 69 deletions adapters/gorm/gorm.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gorm_adapter

import (
"context"
"time"

"github.com/zeiss/fiber-goth/adapters"
Expand All @@ -9,13 +10,13 @@ import (
"gorm.io/gorm"
)

// RunMigrations ...
// RunMigrations is a helper function to run the migrations for the database.
func RunMigrations(db *gorm.DB) error {
err := db.AutoMigrate(
&Account{},
&User{},
&Session{},
&VerificationToken{},
&adapters.Account{},
&adapters.User{},
&adapters.Session{},
&adapters.VerificationToken{},
)
if err != nil {
return err
Expand All @@ -42,78 +43,49 @@ func New(db *gorm.DB) (*gormAdapter, error) {
return &gormAdapter{db, adapters.UnimplementedAdapter{}}, nil
}

// Account ...
type Account struct {
ID uuid.UUID `gorm:"primaryKey;type:uuid;column:id;default:gen_random_uuid();"`
Type string
Provider string
ProviderAccountID *string
RefreshToken *string
AccessToken *string
ExpiresAt *time.Time
TokenType *string
Scope *string
IDToken *string
SessionState string
UserID uuid.UUID
User User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
// CreateUser ...
func (a *gormAdapter) CreateUser(ctx context.Context, user adapters.User) (adapters.User, error) {
err := a.db.WithContext(ctx).FirstOrCreate(&user).Error
if err != nil {
return adapters.User{}, err
}

// User ...
type User struct {
ID uuid.UUID ` gorm:"primaryKey;unique;type:uuid;column:id;default:gen_random_uuid()"`
Name string
Email string
EmailVerified *string
Image *string

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
return user, nil
}

// Session ...
type Session struct {
ID uuid.UUID `gorm:"primaryKey;unique;type:uuid;column:id;default:gen_random_uuid()"`
ExpiresAt time.Time
SessionToken string
UserID uuid.UUID
User User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
// GetUser ...
func (a *gormAdapter) GetUser(id uuid.UUID) (adapters.User, error) {
var user adapters.User
err := a.db.Preload("Accounts").Where("id = ?", id).First(&user).Error
if err != nil {
return adapters.User{}, err
}

return user, nil
}

// VerificationToken ...
type VerificationToken struct {
Token string `gorm:"primaryKey"`
Identifier string
ExpiresAt time.Time
// CreateSession ...
func (a *gormAdapter) CreateSession(ctx context.Context, userID uuid.UUID, expires time.Time) (adapters.Session, error) {
session := adapters.Session{UserID: userID, SessionToken: uuid.NewString()}
err := a.db.WithContext(ctx).Create(&session).Error
if err != nil {
return adapters.Session{}, err
}

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
return session, nil
}

// CreateUser ...
func (a *gormAdapter) CreateUser(user *adapters.User) (*adapters.User, error) {
u := User{
Name: user.Name,
Email: user.Email,
EmailVerified: &user.EmailVerified,
Image: &user.Image,
}
u.ID = uuid.New()
// DeleteUser ...
func (a *gormAdapter) DeleteUser(id uuid.UUID) error {
return a.db.Where("id = ?", id).Delete(&adapters.User{}).Error
}

err := a.db.Create(&u).Error
if err != nil {
return nil, err
}
// LinkAccount ...
func (a *gormAdapter) LinkAccount(accountID, userID uuid.UUID) error {
return a.db.Model(&adapters.Account{}).Where("id = ?", accountID).Update("user_id", userID).Error
}

return user, nil
// DeleteSession ...
func (a *gormAdapter) DeleteSession(sessionToken string) error {
return a.db.Where("session_token = ?", sessionToken).Delete(&adapters.Session{}).Error
}
Loading

0 comments on commit b66926d

Please sign in to comment.