Fixes and improvements

This commit is contained in:
Shtorm
2026-07-16 11:16:10 +03:00
parent a27453e4f7
commit 4624fd51b1
28 changed files with 1178 additions and 1474 deletions

195
common/sql/filter.go Normal file
View File

@@ -0,0 +1,195 @@
package sql
import (
"encoding/json"
"slices"
"strconv"
"github.com/huandu/go-sqlbuilder"
"github.com/sagernet/sing/common/byteformats"
E "github.com/sagernet/sing/common/exceptions"
)
type Filter func(sb *sqlbuilder.SelectBuilder, value []string) error
func EqualFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.Equal(field, value[0]))
return nil
}
}
func EqualOrNullFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.Or(sb.Equal(field, value[0]), sb.IsNull(field)))
return nil
}
}
func GreaterThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.GreaterThan(field, value[0]))
return nil
}
}
func LessThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.LessThan(field, value[0]))
return nil
}
}
func GreaterEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.GreaterEqualThan(field, value[0]))
return nil
}
}
func LessEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
sb.Where(sb.LessEqualThan(field, value[0]))
return nil
}
}
func SpeedGreaterEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
bytesSpeed, err := json.Marshal(value[0])
if err != nil {
return err
}
speed := &byteformats.NetworkBytesCompat{}
err = speed.UnmarshalJSON(bytesSpeed)
if err != nil {
return err
}
sb.Where(sb.GreaterEqualThan(field, speed.Value()))
return nil
}
}
func SpeedLessEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
bytesSpeed, err := json.Marshal(value[0])
if err != nil {
return err
}
speed := &byteformats.NetworkBytesCompat{}
err = speed.UnmarshalJSON(bytesSpeed)
if err != nil {
return err
}
sb.Where(sb.LessEqualThan(field, speed.Value()))
return nil
}
}
func ExistsAndWhereInFilter(subqueryFactory func() *sqlbuilder.SelectBuilder, field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
values := make([]interface{}, len(value))
for i, v := range value {
values[i] = v
}
subquery := subqueryFactory()
subquery.Where(subquery.In(field, values...))
sb.Where(sb.Exists(subquery))
return nil
}
}
func InFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
if len(value) == 0 {
return nil
}
values := make([]interface{}, len(value))
for i, v := range value {
values[i] = v
}
sb.Where(sb.In(field, values...))
return nil
}
}
func SortAscFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func SortDescFilter(columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, err := isValidSortColumn(value[0], columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
func ReplacedSortAscFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByAsc(column)
return nil
}
}
func ReplacedSortDescFilter(replace map[string]string, columns []string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
column, ok := replace[value[0]]
if !ok {
column = value[0]
}
column, err := isValidSortColumn(column, columns)
if err != nil {
return err
}
sb.OrderByDesc(column)
return nil
}
}
func LimitFilter() Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
limit, err := strconv.Atoi(value[0])
if err != nil {
return err
}
sb.Limit(limit)
return nil
}
}
func OffsetFilter() Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
offset, err := strconv.Atoi(value[0])
if err != nil {
return err
}
sb.Offset(offset)
return nil
}
}
func isValidSortColumn(column string, columns []string) (string, error) {
if slices.Contains(columns, column) {
return column, nil
}
return "", E.New("invalid sort column \"", column, "\"")
}

29
common/sql/json.go Normal file
View File

@@ -0,0 +1,29 @@
package sql
import (
"encoding/json"
"fmt"
)
type SliceJSON[T any] []T
func (s *SliceJSON[T]) Scan(src interface{}) error {
if src == nil {
*s = nil
return nil
}
var data []byte
switch v := src.(type) {
case []byte:
data = v
case string:
data = []byte(v)
default:
return fmt.Errorf("sliceJSON.Scan: unsupported type %T", src)
}
if len(data) == 0 {
*s = nil
return nil
}
return json.Unmarshal(data, (*[]T)(s))
}

53
common/sql/timestamp.go Normal file
View File

@@ -0,0 +1,53 @@
package sql
import (
"time"
"github.com/huandu/go-sqlbuilder"
E "github.com/sagernet/sing/common/exceptions"
)
const timestampFormat = "2006-01-02 15:04:05.000"
func FormatTimestamp(t time.Time) string {
return t.UTC().Format(timestampFormat)
}
func ParseTimestamp(value string) (time.Time, error) {
return time.ParseInLocation(timestampFormat, value, time.UTC)
}
func ParseFilterTimestamp(value string) (time.Time, error) {
if t, err := time.Parse(time.RFC3339Nano, value); err == nil {
return t, nil
}
if t, err := time.Parse(time.RFC3339, value); err == nil {
return t, nil
}
if t, err := ParseTimestamp(value); err == nil {
return t, nil
}
return time.Time{}, E.New("invalid timestamp: ", value)
}
func TimestampGreaterEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
t, err := ParseFilterTimestamp(value[0])
if err != nil {
return err
}
sb.Where(sb.GreaterEqualThan(field, FormatTimestamp(t)))
return nil
}
}
func TimestampLessEqualThanFilter(field string) Filter {
return func(sb *sqlbuilder.SelectBuilder, value []string) error {
t, err := ParseFilterTimestamp(value[0])
if err != nil {
return err
}
sb.Where(sb.LessEqualThan(field, FormatTimestamp(t)))
return nil
}
}