127 lines
3.0 KiB
Go
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
|
|
}
|
|
}
|