Files
im_server/server/db/mysql/tenant_internal_admin_query.go
T

127 lines
3.0 KiB
Go

//go:build mysql
// +build mysql
package mysql
import (
"database/sql"
"encoding/json"
"strings"
"time"
"github.com/jmoiron/sqlx"
t "github.com/tinode/chat/server/store/types"
)
func (a *adapter) TenantInternalAdminQuery(id t.TenantID, query string, countQuery string, args []any, page, perPage int) (*t.InternalAdminQueryResult, error) {
tenantAdp, err := a.routedTenantAdapter(id)
if err != nil {
return nil, t.ErrMalformed
}
if tenantAdp != a {
return tenantAdp.TenantInternalAdminQuery(id, query, countQuery, args, page, perPage)
}
if strings.TrimSpace(query) == "" || strings.TrimSpace(countQuery) == "" {
return nil, t.ErrMalformed
}
if page <= 0 {
page = 1
}
if perPage <= 0 || perPage > 200 {
perPage = 20
}
offset := (page - 1) * perPage
ctx, cancel := a.getContext()
if cancel != nil {
defer cancel()
}
var total int64
if err = a.db.GetContext(ctx, &total, countQuery, args...); err != nil {
return nil, err
}
queryArgs := append(append([]any{}, args...), perPage, offset)
rows, err := a.db.QueryxContext(ctx, query+" LIMIT ? OFFSET ?", queryArgs...)
if err != nil {
return nil, err
}
defer rows.Close()
items, err := scanInternalAdminRows(rows)
if err != nil {
return nil, err
}
return &t.InternalAdminQueryResult{Items: items, Total: total, Page: page, PerPage: perPage}, nil
}
func (a *adapter) TenantInternalAdminQueryOne(id t.TenantID, query string, args []any) (map[string]any, error) {
tenantAdp, err := a.routedTenantAdapter(id)
if err != nil {
return nil, t.ErrMalformed
}
if tenantAdp != a {
return tenantAdp.TenantInternalAdminQueryOne(id, query, args)
}
if strings.TrimSpace(query) == "" {
return nil, t.ErrMalformed
}
ctx, cancel := a.getContext()
if cancel != nil {
defer cancel()
}
rows, err := a.db.QueryxContext(ctx, query+" LIMIT 1", args...)
if err != nil {
return nil, err
}
defer rows.Close()
items, err := scanInternalAdminRows(rows)
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, t.ErrNotFound
}
return items[0], nil
}
func scanInternalAdminRows(rows *sqlx.Rows) ([]map[string]any, error) {
items := []map[string]any{}
for rows.Next() {
row := map[string]any{}
if err := rows.MapScan(row); err != nil {
return nil, err
}
for key, value := range row {
row[key] = normalizeInternalAdminValue(value)
}
items = append(items, row)
}
return items, rows.Err()
}
func normalizeInternalAdminValue(value any) any {
switch v := value.(type) {
case nil:
return nil
case []byte:
raw := strings.TrimSpace(string(v))
if raw == "" {
return ""
}
if (strings.HasPrefix(raw, "{") && strings.HasSuffix(raw, "}")) || (strings.HasPrefix(raw, "[") && strings.HasSuffix(raw, "]")) {
var parsed any
if json.Unmarshal([]byte(raw), &parsed) == nil {
return parsed
}
}
return raw
case time.Time:
return v.UTC().Format(time.RFC3339Nano)
case sql.NullTime:
if v.Valid {
return v.Time.UTC().Format(time.RFC3339Nano)
}
return nil
default:
return value
}
}