@@ -34,10 +34,10 @@ type AttendanceSearchDoc struct {
|
||||
CheckinAt time.Time `json:"checkin_at"`
|
||||
}
|
||||
|
||||
func (self *Attendance) GetAttendance(userId, eventId uuid.UUID) (*Attendance, error) {
|
||||
func (self *Attendance) GetAttendance(ctx context.Context, userId, eventId uuid.UUID) (*Attendance, error) {
|
||||
var checkin Attendance
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Where("user_id = ? AND event_id = ?", userId, eventId).
|
||||
First(&checkin).Error
|
||||
|
||||
@@ -57,10 +57,10 @@ type AttendanceUsers struct {
|
||||
CheckinAt time.Time `json:"checkin_at"`
|
||||
}
|
||||
|
||||
func (self *Attendance) GetUsersByEventID(eventID uuid.UUID) (*[]AttendanceUsers, error) {
|
||||
func (self *Attendance) GetUsersByEventID(ctx context.Context, eventID uuid.UUID) (*[]AttendanceUsers, error) {
|
||||
var result []AttendanceUsers
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Model(&Attendance{}).
|
||||
Select("user_id, checkin_at").
|
||||
Where("event_id = ?", eventID).
|
||||
@@ -75,10 +75,10 @@ type AttendanceEvent struct {
|
||||
CheckinAt time.Time `json:"checkin_at"`
|
||||
}
|
||||
|
||||
func (self *Attendance) GetEventsByUserID(userID uuid.UUID) (*[]AttendanceEvent, error) {
|
||||
func (self *Attendance) GetEventsByUserID(ctx context.Context, userID uuid.UUID) (*[]AttendanceEvent, error) {
|
||||
var result []AttendanceEvent
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Model(&Attendance{}).
|
||||
Select("event_id, checkin_at").
|
||||
Where("user_id = ?", userID).
|
||||
@@ -88,12 +88,12 @@ func (self *Attendance) GetEventsByUserID(userID uuid.UUID) (*[]AttendanceEvent,
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (self *Attendance) Create() error {
|
||||
func (self *Attendance) Create(ctx context.Context) error {
|
||||
self.UUID = uuid.New()
|
||||
self.AttendanceId = uuid.New()
|
||||
|
||||
// DB transaction for strong consistency
|
||||
err := Database.Transaction(func(tx *gorm.DB) error {
|
||||
err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&self).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,17 +103,17 @@ func (self *Attendance) Create() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.UpdateSearchIndex(); err != nil {
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Attendance) Update(attendanceId uuid.UUID, checkinTime *time.Time) (*Attendance, error) {
|
||||
func (self *Attendance) Update(ctx context.Context, attendanceId uuid.UUID, checkinTime *time.Time) (*Attendance, error) {
|
||||
var attendance Attendance
|
||||
|
||||
err := Database.Transaction(func(tx *gorm.DB) error {
|
||||
err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// Lock the row for update
|
||||
if err := tx.
|
||||
Where("attendance_id = ?", attendanceId).
|
||||
@@ -148,32 +148,32 @@ func (self *Attendance) Update(attendanceId uuid.UUID, checkinTime *time.Time) (
|
||||
}
|
||||
|
||||
// Sync to MeiliSearch (eventual consistency)
|
||||
if err := attendance.UpdateSearchIndex(); err != nil {
|
||||
if err := attendance.UpdateSearchIndex(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &attendance, nil
|
||||
}
|
||||
|
||||
func (self *Attendance) SearchUsersByEvent(eventID string) (*meilisearch.SearchResponse, error) {
|
||||
func (self *Attendance) SearchUsersByEvent(ctx context.Context, eventID string) (*meilisearch.SearchResponse, error) {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
|
||||
return index.Search("", &meilisearch.SearchRequest{
|
||||
return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Filter: "event_id = \"" + eventID + "\"",
|
||||
Sort: []string{"checkin_at:asc"},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *Attendance) SearchEventsByUser(userID string) (*meilisearch.SearchResponse, error) {
|
||||
func (self *Attendance) SearchEventsByUser(ctx context.Context, userID string) (*meilisearch.SearchResponse, error) {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
|
||||
return index.Search("", &meilisearch.SearchRequest{
|
||||
return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Filter: "user_id = \"" + userID + "\"",
|
||||
Sort: []string{"checkin_at:asc"},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *Attendance) UpdateSearchIndex() error {
|
||||
func (self *Attendance) UpdateSearchIndex(ctx context.Context) error {
|
||||
doc := AttendanceSearchDoc{
|
||||
AttendanceId: self.AttendanceId.String(),
|
||||
EventId: self.EventId.String(),
|
||||
@@ -188,21 +188,20 @@ func (self *Attendance) UpdateSearchIndex() error {
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocuments([]AttendanceSearchDoc{doc}, opts); err != nil {
|
||||
if _, err := index.UpdateDocumentsWithContext(ctx, []AttendanceSearchDoc{doc}, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Attendance) DeleteSearchIndex() error {
|
||||
func (self *Attendance) DeleteSearchIndex(ctx context.Context) error {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
_, err := index.DeleteDocument(self.AttendanceId.String(), nil)
|
||||
_, err := index.DeleteDocumentWithContext(ctx, self.AttendanceId.String(), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *Attendance) GenCheckinCode(eventId uuid.UUID) (*string, error) {
|
||||
ctx := context.Background()
|
||||
func (self *Attendance) GenCheckinCode(ctx context.Context, eventId uuid.UUID) (*string, error) {
|
||||
ttl := viper.GetDuration("ttl.checkin_code_ttl")
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
@@ -223,9 +222,7 @@ func (self *Attendance) GenCheckinCode(eventId uuid.UUID) (*string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Attendance) VerifyCheckinCode(checkinCode string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func (self *Attendance) VerifyCheckinCode(ctx context.Context, checkinCode string) error {
|
||||
val, err := Redis.Get(ctx, "checkin_code:"+checkinCode).Result()
|
||||
if err != nil {
|
||||
return errors.New("[Attendance Data] invalid or expired checkin code")
|
||||
@@ -250,13 +247,13 @@ func (self *Attendance) VerifyCheckinCode(checkinCode string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
attendanceData, err := self.GetAttendance(userId, eventId)
|
||||
attendanceData, err := self.GetAttendance(ctx, userId, eventId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time := time.Now()
|
||||
_, err = self.Update(attendanceData.AttendanceId, &time)
|
||||
_, err = self.Update(ctx, attendanceData.AttendanceId, &time)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -22,9 +23,9 @@ type Client struct {
|
||||
RedirectUri datatypes.JSON `json:"redirect_uri" gorm:"type:json;not null"`
|
||||
}
|
||||
|
||||
func (self *Client) GetClientByClientId(clientId string) (*Client, error) {
|
||||
func (self *Client) GetClientByClientId(ctx context.Context, clientId string) (*Client, error) {
|
||||
var client Client
|
||||
if err := Database.
|
||||
if err := Database.WithContext(ctx).
|
||||
Where("client_id = ?", clientId).
|
||||
First(&client).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -44,7 +45,7 @@ type ClientParams struct {
|
||||
RedirectUri []string
|
||||
}
|
||||
|
||||
func (self *Client) Create(params *ClientParams) (*Client, error) {
|
||||
func (self *Client) Create(ctx context.Context, params *ClientParams) (*Client, error) {
|
||||
jsonRedirectUri, err := json.Marshal(params.RedirectUri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -69,7 +70,7 @@ func (self *Client) Create(params *ClientParams) (*Client, error) {
|
||||
RedirectUri: jsonRedirectUri,
|
||||
}
|
||||
|
||||
if err := Database.Create(&client).Error; err != nil {
|
||||
if err := Database.WithContext(ctx).Create(&client).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
package drivers
|
||||
|
||||
import "github.com/meilisearch/meilisearch-go"
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
)
|
||||
|
||||
func MeiliSearch(dsn MeiliDSN) meilisearch.ServiceManager {
|
||||
otelTransport := otelhttp.NewTransport(http.DefaultTransport)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: otelTransport,
|
||||
}
|
||||
|
||||
return meilisearch.New(dsn.Host,
|
||||
meilisearch.WithAPIKey(dsn.ApiKey),
|
||||
meilisearch.WithCustomClient(httpClient),
|
||||
meilisearch.WithContentEncoding(
|
||||
meilisearch.GzipEncoding,
|
||||
meilisearch.BestCompression,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"nixcn-cms/config"
|
||||
"nixcn-cms/logger"
|
||||
"strings"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/plugin/opentelemetry/tracing"
|
||||
)
|
||||
|
||||
func SplitHostPort(url string) (host, port string) {
|
||||
@@ -19,6 +22,11 @@ func SplitHostPort(url string) (host, port string) {
|
||||
func Postgres(dsn ExternalDSN) (*gorm.DB, error) {
|
||||
host, port := SplitHostPort(dsn.Host)
|
||||
conn := "host=" + host + " user=" + dsn.Username + " password=" + dsn.Password + " dbname=" + dsn.Name + " port=" + port + " sslmode=disable TimeZone=" + config.TZ()
|
||||
db, err := gorm.Open(postgres.Open(conn), &gorm.Config{})
|
||||
db, err := gorm.Open(postgres.Open(conn), &gorm.Config{Logger: logger.GormLogger()})
|
||||
|
||||
// Use otel gorm plugin
|
||||
if err := db.Use(tracing.NewPlugin()); err != nil {
|
||||
slog.Error("[Database] Error starting otel plugin!", "err", err)
|
||||
}
|
||||
return db, err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package drivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/redis/go-redis/extra/redisotel/v9"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
@@ -15,7 +17,17 @@ func Redis(dsn RedisDSN) (redis.UniversalClient, error) {
|
||||
Password: dsn.Password,
|
||||
DB: dsn.DB,
|
||||
})
|
||||
|
||||
if err := redisotel.InstrumentMetrics(rdb); err != nil {
|
||||
slog.Error("[Redis] Error starting otel metrics plugin!", "err", err)
|
||||
}
|
||||
|
||||
if err := redisotel.InstrumentTracing(rdb); err != nil {
|
||||
slog.Error("[Redis] Error starting otel tracing plugin!", "err", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Ping Redis
|
||||
_, err := rdb.Ping(ctx).Result()
|
||||
return rdb, err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
@@ -31,10 +32,10 @@ type EventSearchDoc struct {
|
||||
EndTime time.Time `json:"end_time"`
|
||||
}
|
||||
|
||||
func (self *Event) GetEventById(eventId uuid.UUID) (*Event, error) {
|
||||
func (self *Event) GetEventById(ctx context.Context, eventId uuid.UUID) (*Event, error) {
|
||||
var event Event
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Where("event_id = ?", eventId).
|
||||
First(&event).Error
|
||||
|
||||
@@ -48,9 +49,9 @@ func (self *Event) GetEventById(eventId uuid.UUID) (*Event, error) {
|
||||
return &event, nil
|
||||
}
|
||||
|
||||
func (self *Event) UpdateEventById(eventId uuid.UUID) error {
|
||||
func (self *Event) UpdateEventById(ctx context.Context, eventId uuid.UUID) error {
|
||||
// DB transaction
|
||||
if err := Database.Transaction(func(tx *gorm.DB) error {
|
||||
if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// Update by business key
|
||||
if err := tx.
|
||||
Model(&Event{}).
|
||||
@@ -68,19 +69,19 @@ func (self *Event) UpdateEventById(eventId uuid.UUID) error {
|
||||
}
|
||||
|
||||
// Sync search index
|
||||
if err := self.UpdateSearchIndex(); err != nil {
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Event) Create() error {
|
||||
func (self *Event) Create(ctx context.Context) error {
|
||||
self.UUID = uuid.New()
|
||||
self.EventId = uuid.New()
|
||||
|
||||
// DB transaction only
|
||||
if err := Database.Transaction(func(tx *gorm.DB) error {
|
||||
if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(self).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,7 +91,7 @@ func (self *Event) Create() error {
|
||||
}
|
||||
|
||||
// Search index (eventual consistency)
|
||||
if err := self.UpdateSearchIndex(); err != nil {
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
// TODO: async retry / log
|
||||
return err
|
||||
}
|
||||
@@ -98,20 +99,20 @@ func (self *Event) Create() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Event) GetFullTable() (*[]Event, error) {
|
||||
func (self *Event) GetFullTable(ctx context.Context) (*[]Event, error) {
|
||||
var events []Event
|
||||
err := Database.Find(&events).Error
|
||||
err := Database.WithContext(ctx).Find(&events).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &events, err
|
||||
}
|
||||
|
||||
func (self *Event) FastListEvents(limit, offset int64) (*[]EventSearchDoc, error) {
|
||||
func (self *Event) FastListEvents(ctx context.Context, limit, offset int64) (*[]EventSearchDoc, error) {
|
||||
index := MeiliSearch.Index("event")
|
||||
|
||||
// Fast read from MeiliSearch (no DB involved)
|
||||
result, err := index.Search("", &meilisearch.SearchRequest{
|
||||
result, err := index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
@@ -127,7 +128,7 @@ func (self *Event) FastListEvents(limit, offset int64) (*[]EventSearchDoc, error
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
func (self *Event) UpdateSearchIndex() error {
|
||||
func (self *Event) UpdateSearchIndex(ctx context.Context) error {
|
||||
doc := EventSearchDoc{
|
||||
EventId: self.EventId.String(),
|
||||
Name: self.Name,
|
||||
@@ -143,15 +144,15 @@ func (self *Event) UpdateSearchIndex() error {
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocuments([]EventSearchDoc{doc}, opts); err != nil {
|
||||
if _, err := index.UpdateDocumentsWithContext(ctx, []EventSearchDoc{doc}, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Event) DeleteSearchIndex() error {
|
||||
func (self *Event) DeleteSearchIndex(ctx context.Context) error {
|
||||
index := MeiliSearch.Index("event")
|
||||
_, err := index.DeleteDocument(self.EventId.String(), nil)
|
||||
_, err := index.DeleteDocumentWithContext(ctx, self.EventId.String(), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
37
data/user.go
37
data/user.go
@@ -1,6 +1,8 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
@@ -31,10 +33,10 @@ type UserSearchDoc struct {
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
func (self *User) GetByEmail(email string) (*User, error) {
|
||||
func (self *User) GetByEmail(ctx context.Context, email string) (*User, error) {
|
||||
var user User
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Where("email = ?", email).
|
||||
First(&user).Error
|
||||
|
||||
@@ -48,10 +50,10 @@ func (self *User) GetByEmail(email string) (*User, error) {
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (self *User) GetByUserId(userId uuid.UUID) (*User, error) {
|
||||
func (self *User) GetByUserId(ctx context.Context, userId uuid.UUID) (*User, error) {
|
||||
var user User
|
||||
|
||||
err := Database.
|
||||
err := Database.WithContext(ctx).
|
||||
Where("user_id = ?", userId).
|
||||
First(&user).Error
|
||||
|
||||
@@ -65,12 +67,12 @@ func (self *User) GetByUserId(userId uuid.UUID) (*User, error) {
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (self *User) Create() error {
|
||||
func (self *User) Create(ctx context.Context) error {
|
||||
self.UUID = uuid.New()
|
||||
self.UserId = uuid.New()
|
||||
|
||||
// DB transaction only
|
||||
if err := Database.Transaction(func(tx *gorm.DB) error {
|
||||
if err := Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(self).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -80,7 +82,7 @@ func (self *User) Create() error {
|
||||
}
|
||||
|
||||
// Search index (eventual consistency)
|
||||
if err := self.UpdateSearchIndex(); err != nil {
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
// TODO: async retry / log
|
||||
return err
|
||||
}
|
||||
@@ -88,8 +90,8 @@ func (self *User) Create() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *User) UpdateByUserID(userId uuid.UUID) error {
|
||||
return Database.Transaction(func(tx *gorm.DB) error {
|
||||
func (self *User) UpdateByUserID(ctx context.Context, userId uuid.UUID) error {
|
||||
return Database.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&User{}).Where("user_id = ?", userId).Updates(&self).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,20 +99,20 @@ func (self *User) UpdateByUserID(userId uuid.UUID) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (self *User) GetFullTable() (*[]User, error) {
|
||||
func (self *User) GetFullTable(ctx context.Context) (*[]User, error) {
|
||||
var users []User
|
||||
err := Database.Find(&users).Error
|
||||
err := Database.WithContext(ctx).Find(&users).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &users, nil
|
||||
}
|
||||
|
||||
func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) {
|
||||
func (self *User) FastListUsers(ctx context.Context, limit, offset int64) (*[]UserSearchDoc, error) {
|
||||
index := MeiliSearch.Index("user")
|
||||
|
||||
// Fast read from MeiliSearch, no DB involved
|
||||
result, err := index.Search("", &meilisearch.SearchRequest{
|
||||
result, err := index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
})
|
||||
@@ -126,7 +128,7 @@ func (self *User) FastListUsers(limit, offset int64) (*[]UserSearchDoc, error) {
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
func (self *User) UpdateSearchIndex() error {
|
||||
func (self *User) UpdateSearchIndex(ctx context.Context) error {
|
||||
doc := UserSearchDoc{
|
||||
UserId: self.UserId.String(),
|
||||
Email: self.Email,
|
||||
@@ -142,7 +144,8 @@ func (self *User) UpdateSearchIndex() error {
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocuments(
|
||||
if _, err := index.UpdateDocumentsWithContext(
|
||||
ctx,
|
||||
[]UserSearchDoc{doc},
|
||||
opts,
|
||||
); err != nil {
|
||||
@@ -152,8 +155,8 @@ func (self *User) UpdateSearchIndex() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *User) DeleteSearchIndex() error {
|
||||
func (self *User) DeleteSearchIndex(ctx context.Context) error {
|
||||
index := MeiliSearch.Index("user")
|
||||
_, err := index.DeleteDocument(self.UserId.String(), nil)
|
||||
_, err := index.DeleteDocumentWithContext(ctx, self.UserId.String(), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user