package repository import ( "context" "haixun-backend/internal/library/clock" "haixun-backend/internal/library/crypto" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" "haixun-backend/internal/model/threads_account/domain/entity" domrepo "haixun-backend/internal/model/threads_account/domain/repository" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type secretsMongoRepository struct { collection *mongo.Collection cipher *crypto.Cipher } func NewSecretsMongoRepository(db *mongo.Database, cipher *crypto.Cipher) domrepo.SecretsRepository { if db == nil { return &secretsMongoRepository{cipher: cipher} } return &secretsMongoRepository{collection: db.Collection(entity.SecretsCollectionName), cipher: cipher} } func (r *secretsMongoRepository) EnsureIndexes(ctx context.Context) error { if r.collection == nil { return nil } _, err := r.collection.Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{Key: "_id", Value: 1}}, }) return err } func (r *secretsMongoRepository) FindByAccountID(ctx context.Context, accountID string) (*entity.Secrets, error) { if r.collection == nil { return nil, app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } var out entity.Secrets err := r.collection.FindOne(ctx, bson.M{"_id": accountID}).Decode(&out) if err == mongo.ErrNoDocuments { return nil, nil } if err != nil { return nil, err } if r.cipher != nil && out.BrowserStorageState != "" { plain, decErr := r.cipher.Decrypt(out.BrowserStorageState) if decErr != nil { return nil, decErr } out.BrowserStorageState = plain } return &out, nil } func (r *secretsMongoRepository) SaveBrowserStorageState(ctx context.Context, accountID, storageState string) (*entity.Secrets, error) { if r.collection == nil { return nil, app.For(code.ThreadsAccount).DBUnavailable("Mongo is not configured") } now := clock.NowUnixNano() storedValue := storageState if r.cipher != nil { enc, encErr := r.cipher.Encrypt(storageState) if encErr != nil { return nil, encErr } storedValue = enc } var out entity.Secrets err := r.collection.FindOneAndUpdate( ctx, bson.M{"_id": accountID}, bson.M{ "$set": bson.M{ "browser_storage_state": storedValue, "update_at": now, }, "$setOnInsert": bson.M{"_id": accountID}, }, options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After), ).Decode(&out) if err != nil { return nil, err } // Return plaintext to callers regardless of at-rest encryption. out.BrowserStorageState = storageState return &out, nil }