first commit

This commit is contained in:
domrichardson
2026-03-24 16:03:04 +00:00
commit df40cc57e1
80 changed files with 16766 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
package database
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"github.com/noteapp/backend/internal/domain/entities"
)
// AccountRecoveryRepository implements account recovery operations
type AccountRecoveryRepository struct {
collection *mongo.Collection
}
type featureFlagSettings struct {
ID string `bson:"_id"`
Flags entities.FeatureFlags `bson:"flags"`
UpdatedAt time.Time `bson:"updated_at"`
}
// FeatureFlagRepository implements app-wide feature flag operations.
type FeatureFlagRepository struct {
collection *mongo.Collection
}
// NewFeatureFlagRepository creates a new feature flag repository.
func NewFeatureFlagRepository(db *mongo.Database) *FeatureFlagRepository {
return &FeatureFlagRepository{
collection: db.Collection("app_settings"),
}
}
// GetFeatureFlags returns persisted feature flags or defaults when not set.
func (r *FeatureFlagRepository) GetFeatureFlags(ctx context.Context) (*entities.FeatureFlags, error) {
var settings featureFlagSettings
err := r.collection.FindOne(ctx, bson.M{"_id": "feature_flags"}).Decode(&settings)
if err != nil {
if err == mongo.ErrNoDocuments {
return entities.NewDefaultFeatureFlags(), nil
}
return nil, err
}
flags := settings.Flags
return &flags, nil
}
// UpdateFeatureFlags persists feature flags.
func (r *FeatureFlagRepository) UpdateFeatureFlags(ctx context.Context, flags *entities.FeatureFlags) error {
if flags == nil {
flags = entities.NewDefaultFeatureFlags()
}
now := time.Now()
_, err := r.collection.UpdateOne(
ctx,
bson.M{"_id": "feature_flags"},
bson.M{
"$set": bson.M{
"flags": flags,
"updated_at": now,
},
},
options.UpdateOne().SetUpsert(true),
)
return err
}
// NewAccountRecoveryRepository creates a new recovery repository
func NewAccountRecoveryRepository(db *mongo.Database) *AccountRecoveryRepository {
return &AccountRecoveryRepository{
collection: db.Collection("account_recovery"),
}
}
// CreateRecovery creates a new recovery token
func (r *AccountRecoveryRepository) CreateRecovery(ctx context.Context, recovery *entities.AccountRecovery) error {
recovery.ID = bson.NewObjectID()
recovery.CreatedAt = time.Now()
_, err := r.collection.InsertOne(ctx, recovery)
return err
}
// GetRecoveryByToken retrieves a recovery record by token
func (r *AccountRecoveryRepository) GetRecoveryByToken(ctx context.Context, token string) (*entities.AccountRecovery, error) {
var recovery entities.AccountRecovery
err := r.collection.FindOne(ctx, bson.M{
"token": token,
"expires_at": bson.M{"$gt": time.Now()},
"used_at": bson.M{"$exists": false},
}).Decode(&recovery)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, errors.New("recovery token not found or expired")
}
return nil, err
}
return &recovery, nil
}
// MarkRecoveryUsed marks a recovery token as used
func (r *AccountRecoveryRepository) MarkRecoveryUsed(ctx context.Context, id bson.ObjectID) error {
now := time.Now()
_, err := r.collection.UpdateOne(ctx, bson.M{"_id": id}, bson.M{
"$set": bson.M{"used_at": now},
})
return err
}
// NoteRevisionRepository implements note revision operations
type NoteRevisionRepository struct {
collection *mongo.Collection
}
// NewNoteRevisionRepository creates a new revision repository
func NewNoteRevisionRepository(db *mongo.Database) *NoteRevisionRepository {
return &NoteRevisionRepository{
collection: db.Collection("note_revisions"),
}
}
// CreateRevision creates a new note revision
func (r *NoteRevisionRepository) CreateRevision(ctx context.Context, revision *entities.NoteRevision) error {
revision.ID = bson.NewObjectID()
revision.CreatedAt = time.Now()
_, err := r.collection.InsertOne(ctx, revision)
return err
}
// GetRevisionsByNoteID retrieves all revisions for a note
func (r *NoteRevisionRepository) GetRevisionsByNoteID(ctx context.Context, noteID bson.ObjectID) ([]*entities.NoteRevision, error) {
var revisions []*entities.NoteRevision
cursor, err := r.collection.Find(ctx, bson.M{"note_id": noteID})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
if err = cursor.All(ctx, &revisions); err != nil {
return nil, err
}
return revisions, nil
}
// GetRevisionByID retrieves a specific revision
func (r *NoteRevisionRepository) GetRevisionByID(ctx context.Context, id bson.ObjectID) (*entities.NoteRevision, error) {
var revision entities.NoteRevision
err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&revision)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, errors.New("revision not found")
}
return nil, err
}
return &revision, nil
}
// AuthProviderRepository implements auth provider operations
type AuthProviderRepository struct {
collection *mongo.Collection
}
// NewAuthProviderRepository creates a new provider repository
func NewAuthProviderRepository(db *mongo.Database) *AuthProviderRepository {
return &AuthProviderRepository{
collection: db.Collection("auth_providers"),
}
}
// CreateProvider creates a new provider
func (r *AuthProviderRepository) CreateProvider(ctx context.Context, provider *entities.AuthProvider) error {
provider.ID = bson.NewObjectID()
provider.CreatedAt = time.Now()
provider.UpdatedAt = time.Now()
_, err := r.collection.InsertOne(ctx, provider)
return err
}
// GetProviderByID retrieves a provider by ID
func (r *AuthProviderRepository) GetProviderByID(ctx context.Context, id bson.ObjectID) (*entities.AuthProvider, error) {
var provider entities.AuthProvider
err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&provider)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, errors.New("provider not found")
}
return nil, err
}
return &provider, nil
}
// GetAllProviders retrieves all active providers
func (r *AuthProviderRepository) GetAllProviders(ctx context.Context) ([]*entities.AuthProvider, error) {
var providers []*entities.AuthProvider
cursor, err := r.collection.Find(ctx, bson.M{"is_active": true})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
if err = cursor.All(ctx, &providers); err != nil {
return nil, err
}
return providers, nil
}
// UpdateProvider updates a provider
func (r *AuthProviderRepository) UpdateProvider(ctx context.Context, provider *entities.AuthProvider) error {
provider.UpdatedAt = time.Now()
_, err := r.collection.ReplaceOne(ctx, bson.M{"_id": provider.ID}, provider)
return err
}
// DeleteProvider deletes a provider
func (r *AuthProviderRepository) DeleteProvider(ctx context.Context, id bson.ObjectID) error {
_, err := r.collection.DeleteOne(ctx, bson.M{"_id": id})
return err
}
// UserProviderLinkRepository implements user provider link operations
type UserProviderLinkRepository struct {
collection *mongo.Collection
}
// NewUserProviderLinkRepository creates a new link repository
func NewUserProviderLinkRepository(db *mongo.Database) *UserProviderLinkRepository {
return &UserProviderLinkRepository{
collection: db.Collection("user_provider_links"),
}
}
// CreateLink creates a new user provider link
func (r *UserProviderLinkRepository) CreateLink(ctx context.Context, link *entities.UserProviderLink) error {
link.ID = bson.NewObjectID()
link.LinkedAt = time.Now()
_, err := r.collection.InsertOne(ctx, link)
return err
}
// GetLink retrieves a user provider link
func (r *UserProviderLinkRepository) GetLink(ctx context.Context, userID, providerID bson.ObjectID) (*entities.UserProviderLink, error) {
var link entities.UserProviderLink
err := r.collection.FindOne(ctx, bson.M{
"user_id": userID,
"provider_id": providerID,
}).Decode(&link)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, errors.New("link not found")
}
return nil, err
}
return &link, nil
}
// GetLinkByProviderUserID retrieves a link by provider user ID
func (r *UserProviderLinkRepository) GetLinkByProviderUserID(ctx context.Context, providerID bson.ObjectID, providerUserID string) (*entities.UserProviderLink, error) {
var link entities.UserProviderLink
err := r.collection.FindOne(ctx, bson.M{
"provider_id": providerID,
"provider_user_id": providerUserID,
}).Decode(&link)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, errors.New("link not found")
}
return nil, err
}
return &link, nil
}
// GetUserLinks retrieves all provider links for a user
func (r *UserProviderLinkRepository) GetUserLinks(ctx context.Context, userID bson.ObjectID) ([]*entities.UserProviderLink, error) {
var links []*entities.UserProviderLink
cursor, err := r.collection.Find(ctx, bson.M{"user_id": userID})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
if err = cursor.All(ctx, &links); err != nil {
return nil, err
}
return links, nil
}
// UpdateLink updates a provider link
func (r *UserProviderLinkRepository) UpdateLink(ctx context.Context, link *entities.UserProviderLink) error {
_, err := r.collection.ReplaceOne(ctx, bson.M{"_id": link.ID}, link)
return err
}
// DeleteLink deletes a provider link
func (r *UserProviderLinkRepository) DeleteLink(ctx context.Context, id bson.ObjectID) error {
_, err := r.collection.DeleteOne(ctx, bson.M{"_id": id})
return err
}