feat: implement core backend architecture and project management services for the History API
Build and Release / release (push) Successful in 1m33s
Build and Release / release (push) Successful in 1m33s
This commit is contained in:
+9
-4
@@ -16,6 +16,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -114,16 +115,20 @@ func StartServer() {
|
||||
Singleton = serverHttp
|
||||
|
||||
done := make(chan bool, 1)
|
||||
go gracefulShutdown(serverHttp, done)
|
||||
|
||||
err = serverHttp.App.Listen(fmt.Sprintf("%s:%s", serverIp, httpPort))
|
||||
err = serverHttp.App.Listen(
|
||||
fmt.Sprintf("%s:%s", serverIp, httpPort),
|
||||
fiber.ListenConfig{
|
||||
DisableStartupMessage: config.GetBoolConfigWithDefault("DISABLE_STARTUP_MESSAGE", false),
|
||||
EnablePrefork: config.GetBoolConfigWithDefault("ENABLE_PREFORK", false),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Error().Msgf("Error: app failed to start on port %s, %v", httpPort, err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Run graceful shutdown in a separate goroutine
|
||||
go gracefulShutdown(serverHttp, done)
|
||||
|
||||
// Wait for the graceful shutdown to complete
|
||||
<-done
|
||||
log.Info().Msg("Graceful shutdown complete.")
|
||||
|
||||
+40
-14
@@ -10,8 +10,11 @@ import (
|
||||
"history-api/internal/services"
|
||||
"history-api/pkg/ai"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/jsonx"
|
||||
"history-api/pkg/storage"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
swagger "github.com/gofiber/contrib/v3/swaggerui"
|
||||
@@ -19,6 +22,7 @@ import (
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/middleware/compress"
|
||||
"github.com/gofiber/fiber/v3/middleware/cors"
|
||||
"github.com/gofiber/fiber/v3/middleware/pprof"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -35,20 +39,29 @@ type FiberServer struct {
|
||||
func NewHttpServer() *FiberServer {
|
||||
server := &FiberServer{
|
||||
App: fiber.New(fiber.Config{
|
||||
ServerHeader: "http-server",
|
||||
AppName: "http-server",
|
||||
ServerHeader: "http-server",
|
||||
AppName: "http-server",
|
||||
BodyLimit: config.GetIntConfigWithDefault("FIBER_BODY_LIMIT", 0),
|
||||
Concurrency: config.GetIntConfigWithDefault("FIBER_CONCURRENCY", 0),
|
||||
ReadBufferSize: config.GetIntConfigWithDefault("FIBER_READ_BUFFER_SIZE", 0),
|
||||
WriteBufferSize: config.GetIntConfigWithDefault("FIBER_WRITE_BUFFER_SIZE", 0),
|
||||
ReduceMemoryUsage: config.GetBoolConfigWithDefault("FIBER_REDUCE_MEMORY_USAGE", false),
|
||||
JSONEncoder: jsonx.Marshal,
|
||||
JSONDecoder: jsonx.Unmarshal,
|
||||
}),
|
||||
}
|
||||
cfg := swagger.Config{
|
||||
BasePath: "/",
|
||||
FileContent: docs.SwaggerJSON,
|
||||
Path: "swagger",
|
||||
Title: "Swagger API Docs",
|
||||
if !config.GetBoolConfigWithDefault("DISABLE_SWAGGER", false) {
|
||||
cfg := swagger.Config{
|
||||
BasePath: "/",
|
||||
FileContent: docs.SwaggerJSON,
|
||||
Path: "swagger",
|
||||
Title: "Swagger API Docs",
|
||||
}
|
||||
|
||||
server.App.Use(swagger.New(cfg))
|
||||
}
|
||||
|
||||
server.App.Use(swagger.New(cfg))
|
||||
|
||||
if os.Getenv("DISABLE_REQUEST_LOG") != "true" {
|
||||
if !config.GetBoolConfigWithDefault("DISABLE_REQUEST_LOG", false) {
|
||||
logger := zerolog.New(zerolog.ConsoleWriter{
|
||||
Out: os.Stderr,
|
||||
TimeFormat: time.RFC3339,
|
||||
@@ -60,6 +73,11 @@ func NewHttpServer() *FiberServer {
|
||||
return server
|
||||
}
|
||||
|
||||
func isTilePath(c fiber.Ctx) bool {
|
||||
path := c.Path()
|
||||
return strings.HasPrefix(path, "/tiles/") || strings.HasPrefix(path, "/raster-tiles/")
|
||||
}
|
||||
|
||||
func (s *FiberServer) SetupServer(
|
||||
poolPg *pgxpool.Pool,
|
||||
sqlTile *sql.DB,
|
||||
@@ -88,10 +106,18 @@ func (s *FiberServer) SetupServer(
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
|
||||
// Apply Compress middleware (Brotli/Gzip/Deflate on-the-fly)
|
||||
s.App.Use(compress.New(compress.Config{
|
||||
Level: compress.LevelBestSpeed, // Optimize for high throughput and low CPU usage
|
||||
}))
|
||||
if !config.GetBoolConfigWithDefault("DISABLE_RESPONSE_COMPRESSION", false) {
|
||||
s.App.Use(compress.New(compress.Config{
|
||||
Level: compress.LevelBestSpeed,
|
||||
Next: isTilePath,
|
||||
}))
|
||||
}
|
||||
|
||||
if config.GetBoolConfigWithDefault("ENABLE_PPROF", false) {
|
||||
s.App.Use(pprof.New(pprof.Config{
|
||||
Prefix: config.GetConfigWithDefault("PPROF_PREFIX", ""),
|
||||
}))
|
||||
}
|
||||
|
||||
// repo setup
|
||||
userRepo := repositories.NewUserRepository(poolPg, redis)
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/email"
|
||||
json "history-api/pkg/jsonx"
|
||||
_ "history-api/pkg/log"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -87,7 +87,7 @@ func runSingleWorker(ctx context.Context, rdb *redis.Client, consumerID int) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if taskType == constants.TaskTypeAdminUserAction.String() {
|
||||
var data models.AdminUserActionPayload
|
||||
if err := json.Unmarshal([]byte(payloadStr), &data); err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/database"
|
||||
json "history-api/pkg/jsonx"
|
||||
_ "history-api/pkg/log"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
_ "history-api/pkg/log"
|
||||
"history-api/pkg/storage"
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
DROP INDEX IF EXISTS idx_geometries_active_project_id_id_desc;
|
||||
DROP INDEX IF EXISTS idx_geometries_active_id_desc;
|
||||
DROP INDEX IF EXISTS idx_entities_active_project_id_id_desc;
|
||||
DROP INDEX IF EXISTS idx_entities_active_id_desc;
|
||||
DROP INDEX IF EXISTS idx_wikis_active_project_id_id_desc;
|
||||
DROP INDEX IF EXISTS idx_wikis_active_id_desc;
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE INDEX IF NOT EXISTS idx_wikis_active_id_desc
|
||||
ON wikis (id DESC)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wikis_active_project_id_id_desc
|
||||
ON wikis (project_id, id DESC)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_entities_active_id_desc
|
||||
ON entities (id DESC)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_entities_active_project_id_id_desc
|
||||
ON entities (project_id, id DESC)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_geometries_active_id_desc
|
||||
ON geometries (id DESC)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_geometries_active_project_id_id_desc
|
||||
ON geometries (project_id, id DESC)
|
||||
WHERE is_deleted = false;
|
||||
@@ -91,7 +91,8 @@ WHERE g.is_deleted = false
|
||||
sqlc.narg('has_bound')::boolean = true OR
|
||||
g.bound_with IS NULL
|
||||
)
|
||||
ORDER BY g.id DESC;
|
||||
ORDER BY g.id DESC
|
||||
LIMIT NULLIF(sqlc.arg('limit_count')::int, 0);
|
||||
|
||||
-- name: SearchGeometriesByEntityName :many
|
||||
WITH matched_entities AS (
|
||||
|
||||
@@ -7,6 +7,8 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.18
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.17
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
|
||||
github.com/bytedance/sonic v1.15.2
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/glebarez/go-sqlite v1.22.0
|
||||
github.com/go-co-op/gocron/v2 v2.21.2
|
||||
github.com/go-playground/validator/v10 v10.30.2
|
||||
@@ -31,15 +33,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/ai v0.7.0 // indirect
|
||||
cloud.google.com/go/aiplatform v1.125.0 // indirect
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/iam v1.11.0 // indirect
|
||||
cloud.google.com/go/longrunning v1.0.0 // indirect
|
||||
cloud.google.com/go/vertexai v0.12.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
@@ -57,7 +53,9 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect
|
||||
github.com/aws/smithy-go v1.25.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
@@ -89,7 +87,6 @@ require (
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/gofiber/schema v1.7.1 // indirect
|
||||
github.com/gofiber/utils/v2 v2.0.6 // indirect
|
||||
github.com/google/generative-ai-go v0.15.1 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.16 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.21.0 // indirect
|
||||
@@ -98,6 +95,7 @@ require (
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
@@ -107,6 +105,7 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.71.0 // indirect
|
||||
gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181 // indirect
|
||||
@@ -115,21 +114,18 @@ require (
|
||||
gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 // indirect
|
||||
gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/grpc v1.81.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/ai v0.7.0 h1:P6+b5p4gXlza5E+u7uvcgYlzZ7103ACg70YdZeC6oGE=
|
||||
cloud.google.com/go/ai v0.7.0/go.mod h1:7ozuEcraovh4ABsPbrec3o4LmFl9HigNI3D5haxYeQo=
|
||||
cloud.google.com/go/aiplatform v1.125.0 h1:QUGv+XaHN9wcWdb0/J0NFIcaP/veQSvDcqg4GH6QiP4=
|
||||
cloud.google.com/go/aiplatform v1.125.0/go.mod h1:yWTZiCunYDnyxeWWD14tDo6+BMlvAUCC5VxuxhvbrVI=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.11.0 h1:KieQ9Pb+LLPak1O3Rv3GgCxhnmkYf7Xyh0P5HfF1jFM=
|
||||
cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4=
|
||||
cloud.google.com/go/longrunning v1.0.0 h1:lwzWEYD8+NkYV7dhexOz6kmlvajZA70+bW/xMhRVVdY=
|
||||
cloud.google.com/go/longrunning v1.0.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM=
|
||||
cloud.google.com/go/vertexai v0.12.0 h1:zTadEo/CtsoyRXNx3uGCncoWAP1H2HakGqwznt+iMo8=
|
||||
cloud.google.com/go/vertexai v0.12.0/go.mod h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8=
|
||||
entgo.io/ent v0.14.3 h1:wokAV/kIlH9TeklJWGGS7AYJdVckr0DloWjIcO9iIIQ=
|
||||
entgo.io/ent v0.14.3/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
@@ -64,11 +52,18 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
|
||||
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
@@ -77,11 +72,6 @@ github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
|
||||
@@ -172,8 +162,6 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/generative-ai-go v0.15.1 h1:n8aQUpvhPOlGVuM2DRkJ2jvx04zpp42B778AROJa+pQ=
|
||||
github.com/google/generative-ai-go v0.15.1/go.mod h1:AAucpWZjXsDKhQYWvCYuP6d0yB1kX998pJlOW1rAesw=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
@@ -229,8 +217,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkoukk/tiktoken-go v0.1.8 h1:85ENo+3FpWgAACBaEUVp+lctuTcYUO7BtmfhlN/QTRo=
|
||||
github.com/pkoukk/tiktoken-go v0.1.8/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -249,10 +235,16 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/shamaton/msgpack/v3 v3.1.2 h1:d5gWAIyMU4M0WgDjz6IFSCuXJUA2dFwRHBpDclE8CLw=
|
||||
github.com/shamaton/msgpack/v3 v3.1.2/go.mod h1:DcQG8jrdrQCIxr3HlMYkiXdMhK+KfN2CitkyzsQV4uc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
@@ -263,6 +255,8 @@ github.com/tmc/langchaingo v0.1.14 h1:o1qWBPigAIuFvrG6cjTFo0cZPFEZ47ZqpOYMjM15yZ
|
||||
github.com/tmc/langchaingo v0.1.14/go.mod h1:aKKYXYoqhIDEv7WKdpnnCLRaqXic69cX9MnDUk72378=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ=
|
||||
github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk=
|
||||
@@ -324,6 +318,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
@@ -348,10 +344,6 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.274.0 h1:aYhycS5QQCwxHLwfEHRRLf9yNsfvp1JadKKWBE54RFA=
|
||||
google.golang.org/api v0.274.0/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew=
|
||||
google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa h1:mfj8IS4EA4VAR9a6QDVxTQkLY64iBybb5QI1B4pXrpE=
|
||||
google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:fuT7yonGw1Iq2oa+YC0fyqPPQJkgo/54gPNC6VitOkI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
|
||||
@@ -3,13 +3,13 @@ package controllers
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/models"
|
||||
"history-api/internal/services"
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
"history-api/pkg/validator"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -111,9 +111,6 @@ func (h *EntityController) IsExistEntitySlug(c fiber.Ctx) error {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /entities [get]
|
||||
func (h *EntityController) SearchEntities(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
dto := &request.SearchEntityDto{}
|
||||
if err := validator.ValidateQueryDto(c, dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||
@@ -122,7 +119,7 @@ func (h *EntityController) SearchEntities(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
res, err := h.service.SearchEntities(ctx, dto)
|
||||
res, err := h.service.SearchEntities(c.Context(), dto)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
@@ -172,4 +169,3 @@ func (h *EntityController) GetEntitiesByGeometryIDs(c fiber.Ctx) error {
|
||||
Data: res,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -57,9 +57,6 @@ func (h *GeometryController) GetGeometryById(c fiber.Ctx) error {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /geometries [get]
|
||||
func (h *GeometryController) SearchGeometries(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
dto := &request.SearchGeometryDto{}
|
||||
if err := validator.ValidateQueryDto(c, dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||
@@ -68,7 +65,7 @@ func (h *GeometryController) SearchGeometries(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
res, err := h.service.SearchGeometries(ctx, dto)
|
||||
res, err := h.service.SearchGeometries(c.Context(), dto)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
|
||||
@@ -63,8 +63,9 @@ func (ctrl *goongController) Proxy(c fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
for k, v := range c.GetReqHeaders() {
|
||||
reqHeaders := c.GetReqHeaders()
|
||||
headers := make(map[string]string, len(reqHeaders))
|
||||
for k, v := range reqHeaders {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
@@ -29,10 +27,7 @@ func NewRasterTileController(svc services.RasterTileService) *RasterTileControll
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /raster-tiles/metadata [get]
|
||||
func (h *RasterTileController) GetMetadata(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := h.service.GetMetadata(ctx)
|
||||
res, err := h.service.GetMetadata(c.Context())
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
@@ -59,9 +54,6 @@ func (h *RasterTileController) GetMetadata(c fiber.Ctx) error {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /raster-tiles/{z}/{x}/{y} [get]
|
||||
func (h *RasterTileController) GetTile(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
z, x, y, pErr := h.parseTileParams(c)
|
||||
if pErr != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||
@@ -70,7 +62,7 @@ func (h *RasterTileController) GetTile(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
data, headers, err := h.service.GetTile(ctx, z, x, y)
|
||||
tile, err := h.service.GetTile(c.Context(), z, x, y)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
@@ -78,11 +70,14 @@ func (h *RasterTileController) GetTile(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
c.Set(k, v)
|
||||
if tile.ContentType != "" {
|
||||
c.Set(fiber.HeaderContentType, tile.ContentType)
|
||||
}
|
||||
if tile.CacheControl != "" {
|
||||
c.Set(fiber.HeaderCacheControl, tile.CacheControl)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).Send(data)
|
||||
return c.Status(fiber.StatusOK).Send(tile.Data)
|
||||
}
|
||||
|
||||
func (h *RasterTileController) parseTileParams(c fiber.Ctx) (int, int, int, error) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
@@ -29,10 +27,7 @@ func NewTileController(svc services.TileService) *TileController {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /tiles/metadata [get]
|
||||
func (h *TileController) GetMetadata(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := h.service.GetMetadata(ctx)
|
||||
res, err := h.service.GetMetadata(c.Context())
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
@@ -59,9 +54,6 @@ func (h *TileController) GetMetadata(c fiber.Ctx) error {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /tiles/{z}/{x}/{y} [get]
|
||||
func (h *TileController) GetTile(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
z, x, y, pErr := h.parseTileParams(c)
|
||||
if pErr != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||
@@ -70,7 +62,7 @@ func (h *TileController) GetTile(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
data, headers, err := h.service.GetTile(ctx, z, x, y)
|
||||
tile, err := h.service.GetTile(c.Context(), z, x, y)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
@@ -78,11 +70,17 @@ func (h *TileController) GetTile(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
c.Set(k, v)
|
||||
if tile.ContentType != "" {
|
||||
c.Set(fiber.HeaderContentType, tile.ContentType)
|
||||
}
|
||||
if tile.ContentEncoding != "" {
|
||||
c.Set(fiber.HeaderContentEncoding, tile.ContentEncoding)
|
||||
}
|
||||
if tile.CacheControl != "" {
|
||||
c.Set(fiber.HeaderCacheControl, tile.CacheControl)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).Send(data)
|
||||
return c.Status(fiber.StatusOK).Send(tile.Data)
|
||||
}
|
||||
|
||||
func (h *TileController) parseTileParams(c fiber.Ctx) (int, int, int, error) {
|
||||
|
||||
@@ -111,9 +111,6 @@ func (h *WikiController) IsExistWikiSlug(c fiber.Ctx) error {
|
||||
// @Failure 500 {object} response.CommonResponse
|
||||
// @Router /wikis [get]
|
||||
func (h *WikiController) SearchWikis(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
dto := &request.SearchWikiDto{}
|
||||
if err := validator.ValidateQueryDto(c, dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(response.CommonResponse{
|
||||
@@ -122,7 +119,7 @@ func (h *WikiController) SearchWikis(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
res, err := h.service.SearchWikis(ctx, dto)
|
||||
res, err := h.service.SearchWikis(c.Context(), dto)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
|
||||
@@ -5,6 +5,7 @@ type SearchGeometryDto struct {
|
||||
MinLat *float64 `json:"min_lat" query:"min_lat" validate:"required,gte=-90,lte=90"`
|
||||
MaxLng *float64 `json:"max_lng" query:"max_lng" validate:"required,gte=-180,lte=180"`
|
||||
MaxLat *float64 `json:"max_lat" query:"max_lat" validate:"required,gte=-90,lte=90"`
|
||||
Limit int `json:"limit" query:"limit" validate:"omitempty,min=1,max=100"`
|
||||
TimePoint *int32 `json:"time" query:"time" validate:"omitempty,number"`
|
||||
TimeRange *int32 `json:"time_range" query:"time_range" validate:"omitempty,number"`
|
||||
EntityID *string `json:"entity_id" query:"entity_id" validate:"omitempty,uuid"`
|
||||
@@ -13,7 +14,7 @@ type SearchGeometryDto struct {
|
||||
}
|
||||
|
||||
type SearchGeometriesByEntityNameDto struct {
|
||||
Name string `json:"name" query:"name" validate:"required,max=255"`
|
||||
Name string `json:"name" query:"name" validate:"required,max=255"`
|
||||
Cursor string `json:"cursor" query:"cursor" validate:"omitempty,uuid"`
|
||||
Limit int `json:"limit" query:"limit" validate:"omitempty,min=1,max=100"`
|
||||
}
|
||||
|
||||
@@ -545,6 +545,7 @@ WHERE g.is_deleted = false
|
||||
g.bound_with IS NULL
|
||||
)
|
||||
ORDER BY g.id DESC
|
||||
LIMIT NULLIF($10::int, 0)
|
||||
`
|
||||
|
||||
type SearchGeometriesParams struct {
|
||||
@@ -557,6 +558,7 @@ type SearchGeometriesParams struct {
|
||||
TimeRange pgtype.Int4 `json:"time_range"`
|
||||
EntityID pgtype.UUID `json:"entity_id"`
|
||||
HasBound pgtype.Bool `json:"has_bound"`
|
||||
LimitCount int32 `json:"limit_count"`
|
||||
}
|
||||
|
||||
type SearchGeometriesRow struct {
|
||||
@@ -587,6 +589,7 @@ func (q *Queries) SearchGeometries(ctx context.Context, arg SearchGeometriesPara
|
||||
arg.TimeRange,
|
||||
arg.EntityID,
|
||||
arg.HasBound,
|
||||
arg.LimitCount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -34,7 +34,7 @@ func (b *BattleReplayEntity) ToResponse() *response.BattleReplayResponse {
|
||||
}
|
||||
|
||||
func BattleReplaysEntityToResponse(bs []*BattleReplayEntity) []*response.BattleReplayResponse {
|
||||
out := make([]*response.BattleReplayResponse, 0)
|
||||
out := make([]*response.BattleReplayResponse, 0, len(bs))
|
||||
if bs == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
+24
-24
@@ -6,17 +6,17 @@ import (
|
||||
)
|
||||
|
||||
type EntityEntity struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Status *int16 `json:"status"`
|
||||
TimeStart *int32 `json:"time_start"`
|
||||
TimeEnd *int32 `json:"time_end"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Status *int16 `json:"status"`
|
||||
TimeStart *int32 `json:"time_start"`
|
||||
TimeEnd *int32 `json:"time_end"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt *time.Time `json:"created_at"`
|
||||
UpdatedAt *time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (e *EntityEntity) ToResponse() *response.EntityResponse {
|
||||
@@ -24,25 +24,25 @@ func (e *EntityEntity) ToResponse() *response.EntityResponse {
|
||||
return nil
|
||||
}
|
||||
return &response.EntityResponse{
|
||||
ID: e.ID,
|
||||
Name: e.Name,
|
||||
Slug: e.Slug,
|
||||
Description: e.Description,
|
||||
ProjectID: e.ProjectID,
|
||||
Status: e.Status,
|
||||
TimeStart: e.TimeStart,
|
||||
TimeEnd: e.TimeEnd,
|
||||
IsDeleted: e.IsDeleted,
|
||||
CreatedAt: e.CreatedAt,
|
||||
UpdatedAt: e.UpdatedAt,
|
||||
ID: e.ID,
|
||||
Name: e.Name,
|
||||
Slug: e.Slug,
|
||||
Description: e.Description,
|
||||
ProjectID: e.ProjectID,
|
||||
Status: e.Status,
|
||||
TimeStart: e.TimeStart,
|
||||
TimeEnd: e.TimeEnd,
|
||||
IsDeleted: e.IsDeleted,
|
||||
CreatedAt: e.CreatedAt,
|
||||
UpdatedAt: e.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func EntitiesEntityToResponse(es []*EntityEntity) []*response.EntityResponse {
|
||||
out := make([]*response.EntityResponse, 0)
|
||||
if es == nil {
|
||||
return out
|
||||
return []*response.EntityResponse{}
|
||||
}
|
||||
out := make([]*response.EntityResponse, 0, len(es))
|
||||
for _, e := range es {
|
||||
if e == nil {
|
||||
continue
|
||||
|
||||
@@ -52,10 +52,10 @@ func (g *GeometryEntity) ToResponse() *response.GeometryResponse {
|
||||
}
|
||||
|
||||
func GeometriesEntityToResponse(gs []*GeometryEntity) []*response.GeometryResponse {
|
||||
out := make([]*response.GeometryResponse, 0)
|
||||
if gs == nil {
|
||||
return out
|
||||
return []*response.GeometryResponse{}
|
||||
}
|
||||
out := make([]*response.GeometryResponse, 0, len(gs))
|
||||
for _, g := range gs {
|
||||
if g == nil {
|
||||
continue
|
||||
|
||||
@@ -90,7 +90,7 @@ func (e *MediaEntity) ToSimpleEntity() *MediaSimpleEntity {
|
||||
}
|
||||
|
||||
func MediaEntitiesToResponse(entities []*MediaEntity) []*response.MediaResponse {
|
||||
responses := make([]*response.MediaResponse, 0)
|
||||
responses := make([]*response.MediaResponse, 0, len(entities))
|
||||
if entities == nil {
|
||||
return responses
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func MediaEntitiesToResponse(entities []*MediaEntity) []*response.MediaResponse
|
||||
}
|
||||
|
||||
func MediaEntitiesToStorageEntity(entities []*MediaEntity) []*MediaStorageEntity {
|
||||
responses := make([]*MediaStorageEntity, 0)
|
||||
responses := make([]*MediaStorageEntity, 0, len(entities))
|
||||
if entities == nil {
|
||||
return responses
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func MediaEntitiesToStorageEntity(entities []*MediaEntity) []*MediaStorageEntity
|
||||
}
|
||||
|
||||
func MediaSimpleEntitiesToResponse(entities []*MediaSimpleEntity) []*response.MediaSimpleResponse {
|
||||
responses := make([]*response.MediaSimpleResponse, 0)
|
||||
responses := make([]*response.MediaSimpleResponse, 0, len(entities))
|
||||
if entities == nil {
|
||||
return responses
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -127,7 +127,7 @@ func (p *ProjectEntity) ToResponse() *response.ProjectResponse {
|
||||
}
|
||||
|
||||
func ProjectsEntityToResponse(projects []*ProjectEntity) []*response.ProjectResponse {
|
||||
out := make([]*response.ProjectResponse, 0)
|
||||
out := make([]*response.ProjectResponse, 0, len(projects))
|
||||
if projects == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (r *RoleEntity) ToRoleSimple() *RoleSimple {
|
||||
}
|
||||
|
||||
func RolesEntityToResponse(rs []*RoleEntity) []*response.RoleResponse {
|
||||
out := make([]*response.RoleResponse, 0)
|
||||
out := make([]*response.RoleResponse, 0, len(rs))
|
||||
if rs == nil {
|
||||
return out
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func RolesEntityToResponse(rs []*RoleEntity) []*response.RoleResponse {
|
||||
}
|
||||
|
||||
func RolesEntityToRoleConstant(rs []*RoleSimple) []constants.RoleType {
|
||||
out := make([]constants.RoleType, 0)
|
||||
out := make([]constants.RoleType, 0, len(rs))
|
||||
if rs == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -65,7 +65,7 @@ func (s *SubmissionEntity) ToResponse() *response.SubmissionResponse {
|
||||
}
|
||||
|
||||
func SubmissionsEntityToResponse(submissions []*SubmissionEntity) []*response.SubmissionResponse {
|
||||
out := make([]*response.SubmissionResponse, 0)
|
||||
out := make([]*response.SubmissionResponse, 0, len(submissions))
|
||||
if submissions == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"history-api/internal/dtos/response"
|
||||
json "history-api/pkg/jsonx"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ func (u *UserEntity) ToResponse() *response.UserResponse {
|
||||
}
|
||||
|
||||
func UsersEntityToResponse(users []*UserEntity) []*response.UserResponse {
|
||||
out := make([]*response.UserResponse, 0)
|
||||
out := make([]*response.UserResponse, 0, len(users))
|
||||
if users == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -77,7 +77,7 @@ func (u *UserVerificationEntity) ToResponse() *response.UserVerificationResponse
|
||||
}
|
||||
|
||||
func UserVerificationsEntitiesToResponse(entities []*UserVerificationEntity) []*response.UserVerificationResponse {
|
||||
responses := make([]*response.UserVerificationResponse, 0)
|
||||
responses := make([]*response.UserVerificationResponse, 0, len(entities))
|
||||
if entities == nil {
|
||||
return responses
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ func (w *WikiEntity) ToResponse() *response.WikiResponse {
|
||||
if w == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var contentSample []response.WikiContentSample
|
||||
|
||||
contentSample := make([]response.WikiContentSample, 0, len(w.ContentSample))
|
||||
for _, c := range w.ContentSample {
|
||||
contentSample = append(contentSample, response.WikiContentSample{
|
||||
ID: c.ID,
|
||||
@@ -49,10 +49,10 @@ func (w *WikiEntity) ToResponse() *response.WikiResponse {
|
||||
}
|
||||
|
||||
func WikisEntityToResponse(ws []*WikiEntity) []*response.WikiResponse {
|
||||
out := make([]*response.WikiResponse, 0)
|
||||
if ws == nil {
|
||||
return out
|
||||
return []*response.WikiResponse{}
|
||||
}
|
||||
out := make([]*response.WikiResponse, 0, len(ws))
|
||||
for _, w := range ws {
|
||||
if w == nil {
|
||||
continue
|
||||
|
||||
@@ -2,8 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -66,14 +65,14 @@ func (r *battleReplayRepository) getByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("battle_replay:id:%s", id)
|
||||
keys[i] = cache.Key("battle_replay:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var items []*models.BattleReplayEntity
|
||||
missingToCache := make(map[string]any)
|
||||
items := make([]*models.BattleReplayEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -84,7 +83,7 @@ func (r *battleReplayRepository) getByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.BattleReplayEntity)
|
||||
dbMap := make(map[string]*models.BattleReplayEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetBattleReplaysByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -121,7 +120,7 @@ func (r *battleReplayRepository) GetByIDs(ctx context.Context, ids []string) ([]
|
||||
}
|
||||
|
||||
func (r *battleReplayRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.BattleReplayEntity, error) {
|
||||
cacheId := fmt.Sprintf("battle_replay:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("battle_replay:id", convert.UUIDToString(id))
|
||||
var item models.BattleReplayEntity
|
||||
err := r.c.Get(ctx, cacheId, &item)
|
||||
if err == nil {
|
||||
@@ -141,7 +140,7 @@ func (r *battleReplayRepository) GetByID(ctx context.Context, id pgtype.UUID) (*
|
||||
}
|
||||
|
||||
func (r *battleReplayRepository) GetByGeometryID(ctx context.Context, geometryID pgtype.UUID) ([]*models.BattleReplayEntity, error) {
|
||||
cacheKey := fmt.Sprintf("battle_replay:geometry:%s", convert.UUIDToString(geometryID))
|
||||
cacheKey := cache.Key("battle_replay:geometry", convert.UUIDToString(geometryID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -156,15 +155,15 @@ func (r *battleReplayRepository) GetByGeometryID(ctx context.Context, geometryID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var items []*models.BattleReplayEntity
|
||||
var ids []string
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.BattleReplayEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := r.rowToEntity(row)
|
||||
ids = append(ids, item.ID)
|
||||
items = append(items, item)
|
||||
itemToCache[fmt.Sprintf("battle_replay:id:%s", item.ID)] = item
|
||||
itemToCache[cache.Key("battle_replay:id", item.ID)] = item
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
@@ -180,7 +179,7 @@ func (r *battleReplayRepository) GetByGeometryIDs(ctx context.Context, geometryI
|
||||
return []*models.BattleReplayEntity{}, nil
|
||||
}
|
||||
|
||||
var pgIds []pgtype.UUID
|
||||
pgIds := make([]pgtype.UUID, 0, len(geometryIDs))
|
||||
for _, id := range geometryIDs {
|
||||
pgId := pgtype.UUID{}
|
||||
if err := pgId.Scan(id); err == nil {
|
||||
@@ -193,13 +192,13 @@ func (r *battleReplayRepository) GetByGeometryIDs(ctx context.Context, geometryI
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var items []*models.BattleReplayEntity
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.BattleReplayEntity, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := r.rowToEntity(row)
|
||||
items = append(items, item)
|
||||
itemToCache[fmt.Sprintf("battle_replay:id:%s", item.ID)] = item
|
||||
itemToCache[cache.Key("battle_replay:id", item.ID)] = item
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
@@ -210,7 +209,7 @@ func (r *battleReplayRepository) GetByGeometryIDs(ctx context.Context, geometryI
|
||||
}
|
||||
|
||||
func (r *battleReplayRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.BattleReplayEntity, error) {
|
||||
cacheKey := fmt.Sprintf("battle_replay:project:%s", convert.UUIDToString(projectID))
|
||||
cacheKey := cache.Key("battle_replay:project", convert.UUIDToString(projectID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -225,15 +224,15 @@ func (r *battleReplayRepository) GetByProjectID(ctx context.Context, projectID p
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var items []*models.BattleReplayEntity
|
||||
var ids []string
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.BattleReplayEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := r.rowToEntity(row)
|
||||
ids = append(ids, item.ID)
|
||||
items = append(items, item)
|
||||
itemToCache[fmt.Sprintf("battle_replay:id:%s", item.ID)] = item
|
||||
itemToCache[cache.Key("battle_replay:id", item.ID)] = item
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
@@ -252,8 +251,8 @@ func (r *battleReplayRepository) Create(ctx context.Context, params sqlc.CreateB
|
||||
|
||||
entity := r.rowToEntity(row)
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("battle_replay:project:%s", entity.ProjectID))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("battle_replay:geometry:%s", entity.GeometryID))
|
||||
_ = r.c.Del(ctx, cache.Key("battle_replay:project", entity.ProjectID))
|
||||
_ = r.c.Del(ctx, cache.Key("battle_replay:geometry", entity.GeometryID))
|
||||
|
||||
return entity, nil
|
||||
}
|
||||
@@ -265,7 +264,7 @@ func (r *battleReplayRepository) Update(ctx context.Context, params sqlc.UpdateB
|
||||
}
|
||||
|
||||
entity := r.rowToEntity(row)
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("battle_replay:id:%s", entity.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("battle_replay:id", entity.ID))
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
@@ -274,7 +273,7 @@ func (r *battleReplayRepository) Delete(ctx context.Context, id pgtype.UUID) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("battle_replay:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("battle_replay:id", convert.UUIDToString(id)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -286,7 +285,7 @@ func (r *battleReplayRepository) DeleteByIDs(ctx context.Context, ids []pgtype.U
|
||||
if len(ids) > 0 {
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("battle_replay:id:%s", convert.UUIDToString(id))
|
||||
keys[i] = cache.Key("battle_replay:id", convert.UUIDToString(id))
|
||||
}
|
||||
_ = r.c.Del(ctx, keys...)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
@@ -39,9 +38,7 @@ func NewChatRepository(db *pgxpool.Pool, c cache.Cache) ChatRepository {
|
||||
}
|
||||
|
||||
func (r *chatRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:query:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *chatRepository) getConversationsByIDsWithFallback(ctx context.Context, ids []string) ([]*models.ConversationEntity, error) {
|
||||
@@ -50,14 +47,14 @@ func (r *chatRepository) getConversationsByIDsWithFallback(ctx context.Context,
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("conversation:id:%s", id)
|
||||
keys[i] = cache.Key("conversation:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var results []*models.ConversationEntity
|
||||
missingToCache := make(map[string]any)
|
||||
results := make([]*models.ConversationEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -68,7 +65,7 @@ func (r *chatRepository) getConversationsByIDsWithFallback(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.ConversationEntity)
|
||||
dbMap := make(map[string]*models.ConversationEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetConversationsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -114,14 +111,14 @@ func (r *chatRepository) getMessagesByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("message:id:%s", id)
|
||||
keys[i] = cache.Key("message:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var results []*models.MessageEntity
|
||||
missingToCache := make(map[string]any)
|
||||
results := make([]*models.MessageEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -132,7 +129,7 @@ func (r *chatRepository) getMessagesByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.MessageEntity)
|
||||
dbMap := make(map[string]*models.MessageEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetMessagesByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -176,14 +173,14 @@ func (r *chatRepository) getChatbotHistoriesByIDsWithFallback(ctx context.Contex
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("chatbot_history:id:%s", id)
|
||||
keys[i] = cache.Key("chatbot_history:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var results []*models.ChatbotHistoryEntity
|
||||
missingToCache := make(map[string]any)
|
||||
results := make([]*models.ChatbotHistoryEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -194,7 +191,7 @@ func (r *chatRepository) getChatbotHistoriesByIDsWithFallback(ctx context.Contex
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.ChatbotHistoryEntity)
|
||||
dbMap := make(map[string]*models.ChatbotHistoryEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetChatbotHistoriesByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -263,7 +260,7 @@ func (r *chatRepository) UpdateConversationStatus(ctx context.Context, params sq
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("conversation:id:%s", entity.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("conversation:id", entity.ID))
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
@@ -299,9 +296,9 @@ func (r *chatRepository) GetMessagesByConversation(ctx context.Context, params s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []*models.MessageEntity
|
||||
var ids []string
|
||||
toCache := make(map[string]any)
|
||||
results := make([]*models.MessageEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
toCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := &models.MessageEntity{
|
||||
@@ -313,7 +310,7 @@ func (r *chatRepository) GetMessagesByConversation(ctx context.Context, params s
|
||||
}
|
||||
ids = append(ids, item.ID)
|
||||
results = append(results, item)
|
||||
toCache[fmt.Sprintf("message:id:%s", item.ID)] = item
|
||||
toCache[cache.Key("message:id", item.ID)] = item
|
||||
}
|
||||
|
||||
if len(toCache) > 0 {
|
||||
@@ -341,7 +338,7 @@ func (r *chatRepository) CreateChatbotHistory(ctx context.Context, params sqlc.C
|
||||
go func() {
|
||||
userId := convert.UUIDToString(params.UserID)
|
||||
if userId != "" {
|
||||
_ = r.c.DelByPattern(context.Background(), fmt.Sprintf("chatbot_history:userId:%s:*", userId))
|
||||
_ = r.c.DelByPattern(context.Background(), "chatbot_history:userId:"+userId+":*")
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -349,12 +346,9 @@ func (r *chatRepository) CreateChatbotHistory(ctx context.Context, params sqlc.C
|
||||
}
|
||||
|
||||
func (r *chatRepository) GetChatbotHistory(ctx context.Context, params sqlc.GetChatbotHistoryParams) ([]*models.ChatbotHistoryEntity, error) {
|
||||
queryKey := fmt.Sprintf(
|
||||
"chatbot_history:userId:%s:limit:%d:cursor:%s",
|
||||
convert.UUIDToString(params.UserID),
|
||||
params.Limit,
|
||||
convert.UUIDToString(params.CursorID),
|
||||
)
|
||||
queryKey := "chatbot_history:userId:" + convert.UUIDToString(params.UserID) +
|
||||
":limit:" + strconv.Itoa(int(params.Limit)) +
|
||||
":cursor:" + convert.UUIDToString(params.CursorID)
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, queryKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -369,9 +363,9 @@ func (r *chatRepository) GetChatbotHistory(ctx context.Context, params sqlc.GetC
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []*models.ChatbotHistoryEntity
|
||||
var ids []string
|
||||
toCache := make(map[string]any)
|
||||
results := make([]*models.ChatbotHistoryEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
toCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := &models.ChatbotHistoryEntity{
|
||||
@@ -383,7 +377,7 @@ func (r *chatRepository) GetChatbotHistory(ctx context.Context, params sqlc.GetC
|
||||
}
|
||||
ids = append(ids, item.ID)
|
||||
results = append(results, item)
|
||||
toCache[fmt.Sprintf("chatbot_history:id:%s", item.ID)] = item
|
||||
toCache[cache.Key("chatbot_history:id", item.ID)] = item
|
||||
}
|
||||
|
||||
if len(toCache) > 0 {
|
||||
|
||||
@@ -2,14 +2,13 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
"history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -62,7 +61,7 @@ func (r *commitRepository) Create(ctx context.Context, params sqlc.CreateCommitP
|
||||
}
|
||||
|
||||
func (r *commitRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.CommitEntity, error) {
|
||||
cacheId := fmt.Sprintf("commit:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("commit:id", convert.UUIDToString(id))
|
||||
var commit models.CommitEntity
|
||||
err := r.c.Get(ctx, cacheId, &commit)
|
||||
if err == nil {
|
||||
@@ -92,9 +91,7 @@ func (r *commitRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models
|
||||
}
|
||||
|
||||
func (r *commitRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:query:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *commitRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.CommitEntity, error) {
|
||||
@@ -103,14 +100,14 @@ func (r *commitRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("commit:id:%s", id)
|
||||
keys[i] = cache.Key("commit:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var commits []*models.CommitEntity
|
||||
missingToCache := make(map[string]any)
|
||||
commits := make([]*models.CommitEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -121,7 +118,7 @@ func (r *commitRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.CommitEntity)
|
||||
dbMap := make(map[string]*models.CommitEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetCommitsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -144,7 +141,7 @@ func (r *commitRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
for i, b := range raws {
|
||||
if len(b) > 0 {
|
||||
var c models.CommitEntity
|
||||
if err := json.Unmarshal(b, &c); err == nil {
|
||||
if err := jsonx.Unmarshal(b, &c); err == nil {
|
||||
commits = append(commits, &c)
|
||||
}
|
||||
} else {
|
||||
@@ -163,7 +160,7 @@ func (r *commitRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
}
|
||||
|
||||
func (r *commitRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.CommitEntity, error) {
|
||||
queryKey := fmt.Sprintf("commit:project:%s", convert.UUIDToString(projectID))
|
||||
queryKey := cache.Key("commit:project", convert.UUIDToString(projectID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, queryKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -180,7 +177,7 @@ func (r *commitRepository) GetByProjectID(ctx context.Context, projectID pgtype.
|
||||
|
||||
entities := make([]*models.CommitEntity, 0, len(rows))
|
||||
ids := make([]string, len(rows))
|
||||
commitToCache := make(map[string]any)
|
||||
commitToCache := make(map[string]any, len(rows))
|
||||
for i, row := range rows {
|
||||
item := &models.CommitEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
@@ -194,7 +191,7 @@ func (r *commitRepository) GetByProjectID(ctx context.Context, projectID pgtype.
|
||||
}
|
||||
entities = append(entities, item)
|
||||
ids[i] = item.ID
|
||||
commitToCache[fmt.Sprintf("commit:id:%s", item.ID)] = item
|
||||
commitToCache[cache.Key("commit:id", item.ID)] = item
|
||||
}
|
||||
if len(commitToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, commitToCache, constants.NormalCacheDuration)
|
||||
@@ -217,9 +214,9 @@ func (r *commitRepository) Search(ctx context.Context, params sqlc.SearchCommits
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var commits []*models.CommitEntity
|
||||
var ids []string
|
||||
commitToCache := make(map[string]any)
|
||||
commits := make([]*models.CommitEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
commitToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
commit := &models.CommitEntity{
|
||||
@@ -234,7 +231,7 @@ func (r *commitRepository) Search(ctx context.Context, params sqlc.SearchCommits
|
||||
}
|
||||
ids = append(ids, commit.ID)
|
||||
commits = append(commits, commit)
|
||||
commitToCache[fmt.Sprintf("commit:id:%s", commit.ID)] = commit
|
||||
commitToCache[cache.Key("commit:id", commit.ID)] = commit
|
||||
}
|
||||
|
||||
if len(commitToCache) > 0 {
|
||||
@@ -253,7 +250,7 @@ func (r *commitRepository) UpdateSnapshot(ctx context.Context, id pgtype.UUID, s
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.c.Del(ctx, fmt.Sprintf("commit:id:%s", convert.UUIDToString(id)))
|
||||
r.c.Del(ctx, cache.Key("commit:id", convert.UUIDToString(id)))
|
||||
|
||||
return &models.CommitEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -51,9 +49,7 @@ func (r *entityRepository) WithTx(tx pgx.Tx) EntityRepository {
|
||||
}
|
||||
|
||||
func (r *entityRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *entityRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.EntityEntity, error) {
|
||||
@@ -62,14 +58,14 @@ func (r *entityRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("entity:id:%s", id)
|
||||
keys[i] = cache.Key("entity:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var entities []*models.EntityEntity
|
||||
missingToCache := make(map[string]any)
|
||||
entities := make([]*models.EntityEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -80,7 +76,7 @@ func (r *entityRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.EntityEntity)
|
||||
dbMap := make(map[string]*models.EntityEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetEntitiesByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -129,7 +125,7 @@ func (r *entityRepository) GetByIDs(ctx context.Context, ids []string) ([]*model
|
||||
}
|
||||
|
||||
func (r *entityRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.EntityEntity, error) {
|
||||
cacheId := fmt.Sprintf("entity:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("entity:id", convert.UUIDToString(id))
|
||||
var entity models.EntityEntity
|
||||
err := r.c.Get(ctx, cacheId, &entity)
|
||||
if err == nil {
|
||||
@@ -175,9 +171,9 @@ func (r *entityRepository) Search(ctx context.Context, params sqlc.SearchEntitie
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var entities []*models.EntityEntity
|
||||
var ids []string
|
||||
entityToCache := make(map[string]any)
|
||||
entities := make([]*models.EntityEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
entityToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
entity := &models.EntityEntity{
|
||||
@@ -195,7 +191,7 @@ func (r *entityRepository) Search(ctx context.Context, params sqlc.SearchEntitie
|
||||
}
|
||||
ids = append(ids, entity.ID)
|
||||
entities = append(entities, entity)
|
||||
entityToCache[fmt.Sprintf("entity:id:%s", entity.ID)] = entity
|
||||
entityToCache[cache.Key("entity:id", entity.ID)] = entity
|
||||
}
|
||||
|
||||
if len(entityToCache) > 0 {
|
||||
@@ -248,8 +244,7 @@ func (r *entityRepository) Update(ctx context.Context, params sqlc.UpdateEntityP
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("entity:id:%s", entity.ID))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("entity:slug:%s", entity.Slug))
|
||||
_ = r.c.Del(ctx, cache.Key("entity:id", entity.ID), cache.Key("entity:slug", entity.Slug))
|
||||
return &entity, nil
|
||||
}
|
||||
|
||||
@@ -258,12 +253,12 @@ func (r *entityRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("entity:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("entity:id", convert.UUIDToString(id)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *entityRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.EntityEntity, error) {
|
||||
cacheKey := fmt.Sprintf("entity:project:%s", convert.UUIDToString(projectID))
|
||||
cacheKey := cache.Key("entity:project", convert.UUIDToString(projectID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -278,9 +273,9 @@ func (r *entityRepository) GetByProjectID(ctx context.Context, projectID pgtype.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entities []*models.EntityEntity
|
||||
var ids []string
|
||||
entityToCache := make(map[string]any)
|
||||
entities := make([]*models.EntityEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
entityToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
entity := &models.EntityEntity{
|
||||
@@ -298,7 +293,7 @@ func (r *entityRepository) GetByProjectID(ctx context.Context, projectID pgtype.
|
||||
}
|
||||
ids = append(ids, entity.ID)
|
||||
entities = append(entities, entity)
|
||||
entityToCache[fmt.Sprintf("entity:id:%s", entity.ID)] = entity
|
||||
entityToCache[cache.Key("entity:id", entity.ID)] = entity
|
||||
}
|
||||
|
||||
if len(entityToCache) > 0 {
|
||||
@@ -317,7 +312,7 @@ func (r *entityRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID) e
|
||||
if len(ids) > 0 {
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("entity:id:%s", convert.UUIDToString(id))
|
||||
keys[i] = cache.Key("entity:id", convert.UUIDToString(id))
|
||||
}
|
||||
_ = r.c.Del(ctx, keys...)
|
||||
}
|
||||
@@ -325,7 +320,7 @@ func (r *entityRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID) e
|
||||
}
|
||||
|
||||
func (r *entityRepository) GetBySlug(ctx context.Context, slug string) (*models.EntityEntity, error) {
|
||||
cacheKey := fmt.Sprintf("entity:slug:%s", slug)
|
||||
cacheKey := cache.Key("entity:slug", slug)
|
||||
var entity models.EntityEntity
|
||||
err := r.c.Get(ctx, cacheKey, &entity)
|
||||
if err == nil {
|
||||
@@ -362,13 +357,13 @@ func (r *entityRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*m
|
||||
}
|
||||
keys := make([]string, len(slugs))
|
||||
for i, slug := range slugs {
|
||||
keys[i] = fmt.Sprintf("entity:slug:%s", slug)
|
||||
keys[i] = cache.Key("entity:slug", slug)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var entities []*models.EntityEntity
|
||||
missingToCache := make(map[string]any)
|
||||
var missingSlugs []string
|
||||
entities := make([]*models.EntityEntity, 0, len(slugs))
|
||||
missingToCache := make(map[string]any, len(slugs))
|
||||
missingSlugs := make([]string, 0, len(slugs))
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
@@ -376,7 +371,7 @@ func (r *entityRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*m
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.EntityEntity)
|
||||
dbMap := make(map[string]*models.EntityEntity, len(missingSlugs))
|
||||
if len(missingSlugs) > 0 {
|
||||
dbRows, err := r.q.GetEntitiesBySlugs(ctx, missingSlugs)
|
||||
if err == nil {
|
||||
@@ -427,13 +422,13 @@ func (r *entityRepository) GetEntityIDsByGeometryIDs(ctx context.Context, geomet
|
||||
|
||||
keys := make([]string, len(geometryIDs))
|
||||
for i, id := range geometryIDs {
|
||||
keys[i] = fmt.Sprintf("entity_geometries:geometry:%s", id)
|
||||
keys[i] = cache.Key("entity_geometries:geometry", id)
|
||||
}
|
||||
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
result := make(map[string][]string)
|
||||
var missingGeometryIDs []string
|
||||
var missingPgIDs []pgtype.UUID
|
||||
result := make(map[string][]string, len(geometryIDs))
|
||||
missingGeometryIDs := make([]string, 0, len(geometryIDs))
|
||||
missingPgIDs := make([]pgtype.UUID, 0, len(geometryIDs))
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) > 0 {
|
||||
@@ -456,7 +451,7 @@ func (r *entityRepository) GetEntityIDsByGeometryIDs(ctx context.Context, geomet
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbMap := make(map[string][]string)
|
||||
dbMap := make(map[string][]string, len(missingGeometryIDs))
|
||||
for _, id := range missingGeometryIDs {
|
||||
dbMap[id] = []string{}
|
||||
}
|
||||
@@ -466,10 +461,10 @@ func (r *entityRepository) GetEntityIDsByGeometryIDs(ctx context.Context, geomet
|
||||
dbMap[gID] = append(dbMap[gID], eID)
|
||||
}
|
||||
|
||||
missingToCache := make(map[string]any)
|
||||
missingToCache := make(map[string]any, len(dbMap))
|
||||
for gID, eIDs := range dbMap {
|
||||
result[gID] = eIDs
|
||||
missingToCache[fmt.Sprintf("entity_geometries:geometry:%s", gID)] = eIDs
|
||||
missingToCache[cache.Key("entity_geometries:geometry", gID)] = eIDs
|
||||
}
|
||||
if len(missingToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
@@ -60,9 +58,7 @@ func (r *geometryRepository) WithTx(tx pgx.Tx) GeometryRepository {
|
||||
}
|
||||
|
||||
func (r *geometryRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *geometryRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.GeometryEntity, error) {
|
||||
@@ -71,14 +67,14 @@ func (r *geometryRepository) getByIDsWithFallback(ctx context.Context, ids []str
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("geometry:id:%s", id)
|
||||
keys[i] = cache.Key("geometry:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var geometries []*models.GeometryEntity
|
||||
missingToCache := make(map[string]any)
|
||||
geometries := make([]*models.GeometryEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -89,7 +85,7 @@ func (r *geometryRepository) getByIDsWithFallback(ctx context.Context, ids []str
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.GeometryEntity)
|
||||
dbMap := make(map[string]*models.GeometryEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetGeometriesByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -143,7 +139,7 @@ func (r *geometryRepository) GetByIDs(ctx context.Context, ids []string) ([]*mod
|
||||
}
|
||||
|
||||
func (r *geometryRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.GeometryEntity, error) {
|
||||
cacheId := fmt.Sprintf("geometry:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("geometry:id", convert.UUIDToString(id))
|
||||
var geometry models.GeometryEntity
|
||||
err := r.c.Get(ctx, cacheId, &geometry)
|
||||
if err == nil {
|
||||
@@ -194,9 +190,9 @@ func (r *geometryRepository) Search(ctx context.Context, params sqlc.SearchGeome
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var geometries []*models.GeometryEntity
|
||||
var ids []string
|
||||
geometryToCache := make(map[string]any)
|
||||
geometries := make([]*models.GeometryEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
geometryToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
geometry := &models.GeometryEntity{
|
||||
@@ -219,7 +215,7 @@ func (r *geometryRepository) Search(ctx context.Context, params sqlc.SearchGeome
|
||||
}
|
||||
ids = append(ids, geometry.ID)
|
||||
geometries = append(geometries, geometry)
|
||||
geometryToCache[fmt.Sprintf("geometry:id:%s", geometry.ID)] = geometry
|
||||
geometryToCache[cache.Key("geometry:id", geometry.ID)] = geometry
|
||||
}
|
||||
|
||||
if len(geometryToCache) > 0 {
|
||||
@@ -281,7 +277,7 @@ func (r *geometryRepository) Update(ctx context.Context, params sqlc.UpdateGeome
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("geometry:id:%s", geometry.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("geometry:id", geometry.ID))
|
||||
return &geometry, nil
|
||||
}
|
||||
|
||||
@@ -290,7 +286,7 @@ func (r *geometryRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("geometry:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("geometry:id", convert.UUIDToString(id)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -311,7 +307,7 @@ func (r *geometryRepository) BulkDeleteEntityGeometriesByEntityId(ctx context.Co
|
||||
}
|
||||
|
||||
func (r *geometryRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.GeometryEntity, error) {
|
||||
cacheKey := fmt.Sprintf("geometry:project:%s", convert.UUIDToString(projectID))
|
||||
cacheKey := cache.Key("geometry:project", convert.UUIDToString(projectID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -326,9 +322,9 @@ func (r *geometryRepository) GetByProjectID(ctx context.Context, projectID pgtyp
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var geometries []*models.GeometryEntity
|
||||
var ids []string
|
||||
geometryToCache := make(map[string]any)
|
||||
geometries := make([]*models.GeometryEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
geometryToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
geometry := &models.GeometryEntity{
|
||||
@@ -351,7 +347,7 @@ func (r *geometryRepository) GetByProjectID(ctx context.Context, projectID pgtyp
|
||||
}
|
||||
ids = append(ids, geometry.ID)
|
||||
geometries = append(geometries, geometry)
|
||||
geometryToCache[fmt.Sprintf("geometry:id:%s", geometry.ID)] = geometry
|
||||
geometryToCache[cache.Key("geometry:id", geometry.ID)] = geometry
|
||||
}
|
||||
|
||||
if len(geometryToCache) > 0 {
|
||||
@@ -363,7 +359,7 @@ func (r *geometryRepository) GetByProjectID(ctx context.Context, projectID pgtyp
|
||||
}
|
||||
|
||||
func (r *geometryRepository) GetGeometriesByBoundWith(ctx context.Context, boundWith pgtype.UUID) ([]*models.GeometryEntity, error) {
|
||||
cacheKey := fmt.Sprintf("geometry:bound_with:%s", convert.UUIDToString(boundWith))
|
||||
cacheKey := cache.Key("geometry:bound_with", convert.UUIDToString(boundWith))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -378,9 +374,9 @@ func (r *geometryRepository) GetGeometriesByBoundWith(ctx context.Context, bound
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var geometries []*models.GeometryEntity
|
||||
var ids []string
|
||||
geometryToCache := make(map[string]any)
|
||||
geometries := make([]*models.GeometryEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
geometryToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
geometry := &models.GeometryEntity{
|
||||
@@ -403,7 +399,7 @@ func (r *geometryRepository) GetGeometriesByBoundWith(ctx context.Context, bound
|
||||
}
|
||||
ids = append(ids, geometry.ID)
|
||||
geometries = append(geometries, geometry)
|
||||
geometryToCache[fmt.Sprintf("geometry:id:%s", geometry.ID)] = geometry
|
||||
geometryToCache[cache.Key("geometry:id", geometry.ID)] = geometry
|
||||
}
|
||||
|
||||
if len(geometryToCache) > 0 {
|
||||
@@ -421,7 +417,7 @@ func (r *geometryRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID)
|
||||
if len(ids) > 0 {
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("geometry:id:%s", convert.UUIDToString(id))
|
||||
keys[i] = cache.Key("geometry:id", convert.UUIDToString(id))
|
||||
}
|
||||
_ = r.c.Del(ctx, keys...)
|
||||
}
|
||||
@@ -445,15 +441,15 @@ func (r *geometryRepository) getSearchByIDsWithFallback(ctx context.Context, pai
|
||||
}
|
||||
keys := make([]string, len(pairs))
|
||||
for i, pair := range pairs {
|
||||
keys[i] = fmt.Sprintf("geometry:search:item:%s", pair)
|
||||
keys[i] = cache.Key("geometry:search:item", pair)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var results []*models.EntityGeometriesSearchEntity
|
||||
missingToCache := make(map[string]any)
|
||||
results := make([]*models.EntityGeometriesSearchEntity, 0, len(pairs))
|
||||
missingToCache := make(map[string]any, len(pairs))
|
||||
|
||||
var missingEntityIds []pgtype.UUID
|
||||
var missingGeometryIds []pgtype.UUID
|
||||
missingEntityIds := make([]pgtype.UUID, 0, len(pairs))
|
||||
missingGeometryIds := make([]pgtype.UUID, 0, len(pairs))
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
@@ -471,7 +467,7 @@ func (r *geometryRepository) getSearchByIDsWithFallback(ctx context.Context, pai
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.EntityGeometriesSearchEntity)
|
||||
dbMap := make(map[string]*models.EntityGeometriesSearchEntity, len(missingEntityIds))
|
||||
if len(missingEntityIds) > 0 {
|
||||
dbRows, err := r.q.GetEntityGeometriesByPairs(ctx, sqlc.GetEntityGeometriesByPairsParams{
|
||||
EntityIds: missingEntityIds,
|
||||
@@ -490,7 +486,7 @@ func (r *geometryRepository) getSearchByIDsWithFallback(ctx context.Context, pai
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
}
|
||||
key := fmt.Sprintf("%s:%s", item.EntityID, item.GeometryID)
|
||||
key := cache.Key(item.EntityID, item.GeometryID)
|
||||
dbMap[key] = item
|
||||
}
|
||||
}
|
||||
@@ -533,9 +529,9 @@ func (r *geometryRepository) SearchByEntityName(ctx context.Context, params sqlc
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var geometries []*models.EntityGeometriesSearchEntity
|
||||
var pairs []string
|
||||
geometryToCache := make(map[string]any)
|
||||
geometries := make([]*models.EntityGeometriesSearchEntity, 0, len(rows))
|
||||
pairs := make([]string, 0, len(rows))
|
||||
geometryToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := &models.EntityGeometriesSearchEntity{
|
||||
@@ -549,10 +545,10 @@ func (r *geometryRepository) SearchByEntityName(ctx context.Context, params sqlc
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
}
|
||||
pair := fmt.Sprintf("%s:%s", item.EntityID, item.GeometryID)
|
||||
pair := cache.Key(item.EntityID, item.GeometryID)
|
||||
geometries = append(geometries, item)
|
||||
pairs = append(pairs, pair)
|
||||
geometryToCache[fmt.Sprintf("geometry:search:item:%s", pair)] = item
|
||||
geometryToCache[cache.Key("geometry:search:item", pair)] = item
|
||||
}
|
||||
|
||||
if len(geometryToCache) > 0 {
|
||||
|
||||
@@ -2,14 +2,12 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -47,9 +45,7 @@ func (r *mediaRepository) WithTx(tx pgx.Tx) MediaRepository {
|
||||
}
|
||||
|
||||
func (r *mediaRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *mediaRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.MediaEntity, error) {
|
||||
@@ -58,14 +54,14 @@ func (r *mediaRepository) getByIDsWithFallback(ctx context.Context, ids []string
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("media:id:%s", id)
|
||||
keys[i] = cache.Key("media:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var medias []*models.MediaEntity
|
||||
missingMediasToCache := make(map[string]any)
|
||||
medias := make([]*models.MediaEntity, 0, len(ids))
|
||||
missingMediasToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -76,7 +72,7 @@ func (r *mediaRepository) getByIDsWithFallback(ctx context.Context, ids []string
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.MediaEntity)
|
||||
dbMap := make(map[string]*models.MediaEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetMediaByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -123,7 +119,7 @@ func (r *mediaRepository) GetByIDs(ctx context.Context, ids []string) ([]*models
|
||||
}
|
||||
|
||||
func (r *mediaRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.MediaEntity, error) {
|
||||
cacheId := fmt.Sprintf("media:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("media:id", convert.UUIDToString(id))
|
||||
var media models.MediaEntity
|
||||
err := r.c.Get(ctx, cacheId, &media)
|
||||
if err == nil {
|
||||
@@ -165,7 +161,7 @@ func (r *mediaRepository) Create(ctx context.Context, params sqlc.CreateMediaPar
|
||||
_ = r.c.DelByPattern(bgCtx, "media:count*")
|
||||
}()
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("media:userId:%s", convert.UUIDToString(params.UserID)))
|
||||
_ = r.c.Del(ctx, cache.Key("media:userId", convert.UUIDToString(params.UserID)))
|
||||
|
||||
media := models.MediaEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
@@ -188,7 +184,7 @@ func (r *mediaRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheId := fmt.Sprintf("media:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("media:id", convert.UUIDToString(id))
|
||||
_ = r.c.Del(ctx, cacheId)
|
||||
go func() {
|
||||
_ = r.c.DelByPattern(context.Background(), "media:count*")
|
||||
@@ -206,7 +202,7 @@ func (r *mediaRepository) BulkDelete(ctx context.Context, ids []pgtype.UUID) err
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("media:id:%s", convert.UUIDToString(id))
|
||||
keys[i] = cache.Key("media:id", convert.UUIDToString(id))
|
||||
}
|
||||
_ = r.c.Del(ctx, keys...)
|
||||
|
||||
@@ -237,9 +233,9 @@ func (r *mediaRepository) Search(ctx context.Context, params sqlc.SearchMediasPa
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var medias []*models.MediaEntity
|
||||
var ids []string
|
||||
mediasToCache := make(map[string]any)
|
||||
medias := make([]*models.MediaEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
mediasToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
media := &models.MediaEntity{
|
||||
@@ -256,7 +252,7 @@ func (r *mediaRepository) Search(ctx context.Context, params sqlc.SearchMediasPa
|
||||
ids = append(ids, media.ID)
|
||||
medias = append(medias, media)
|
||||
|
||||
mediasToCache[fmt.Sprintf("media:id:%s", media.ID)] = media
|
||||
mediasToCache[cache.Key("media:id", media.ID)] = media
|
||||
}
|
||||
|
||||
if len(mediasToCache) > 0 {
|
||||
@@ -284,7 +280,7 @@ func (r *mediaRepository) Count(ctx context.Context, params sqlc.CountMediasPara
|
||||
}
|
||||
|
||||
func (r *mediaRepository) GetByUserID(ctx context.Context, userId pgtype.UUID) ([]*models.MediaEntity, error) {
|
||||
queryKey := fmt.Sprintf("media:userId:%s", convert.UUIDToString(userId))
|
||||
queryKey := cache.Key("media:userId", convert.UUIDToString(userId))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, queryKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -307,9 +303,9 @@ func (r *mediaRepository) GetByUserID(ctx context.Context, userId pgtype.UUID) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var medias []*models.MediaEntity
|
||||
var ids []string
|
||||
mediasToCache := make(map[string]any)
|
||||
medias := make([]*models.MediaEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
mediasToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
media := &models.MediaEntity{
|
||||
@@ -326,7 +322,7 @@ func (r *mediaRepository) GetByUserID(ctx context.Context, userId pgtype.UUID) (
|
||||
ids = append(ids, media.ID)
|
||||
medias = append(medias, media)
|
||||
|
||||
mediasToCache[fmt.Sprintf("media:id:%s", media.ID)] = media
|
||||
mediasToCache[cache.Key("media:id", media.ID)] = media
|
||||
}
|
||||
|
||||
if len(mediasToCache) > 0 {
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -53,9 +51,7 @@ func (r *projectRepository) WithTx(tx pgx.Tx) ProjectRepository {
|
||||
}
|
||||
}
|
||||
func (r *projectRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *projectRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.ProjectEntity, error) {
|
||||
@@ -64,14 +60,14 @@ func (r *projectRepository) getByIDsWithFallback(ctx context.Context, ids []stri
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("project:id:%s", id)
|
||||
keys[i] = cache.Key("project:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var projects []*models.ProjectEntity
|
||||
missingToCache := make(map[string]any)
|
||||
projects := make([]*models.ProjectEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -82,7 +78,7 @@ func (r *projectRepository) getByIDsWithFallback(ctx context.Context, ids []stri
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.ProjectEntity)
|
||||
dbMap := make(map[string]*models.ProjectEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetProjectsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -134,7 +130,7 @@ func (r *projectRepository) GetByIDs(ctx context.Context, ids []string) ([]*mode
|
||||
}
|
||||
|
||||
func (r *projectRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.ProjectEntity, error) {
|
||||
cacheId := fmt.Sprintf("project:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("project:id", convert.UUIDToString(id))
|
||||
var project models.ProjectEntity
|
||||
err := r.c.Get(ctx, cacheId, &project)
|
||||
if err == nil {
|
||||
@@ -185,9 +181,9 @@ func (r *projectRepository) GetByUserID(ctx context.Context, params sqlc.GetProj
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var projects []*models.ProjectEntity
|
||||
var ids []string
|
||||
projectToCache := make(map[string]any)
|
||||
projects := make([]*models.ProjectEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
projectToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
project := &models.ProjectEntity{
|
||||
@@ -209,7 +205,7 @@ func (r *projectRepository) GetByUserID(ctx context.Context, params sqlc.GetProj
|
||||
|
||||
ids = append(ids, project.ID)
|
||||
projects = append(projects, project)
|
||||
projectToCache[fmt.Sprintf("project:id:%s", project.ID)] = project
|
||||
projectToCache[cache.Key("project:id", project.ID)] = project
|
||||
}
|
||||
|
||||
if len(projectToCache) > 0 {
|
||||
@@ -235,9 +231,9 @@ func (r *projectRepository) Search(ctx context.Context, params sqlc.SearchProjec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var projects []*models.ProjectEntity
|
||||
var ids []string
|
||||
projectToCache := make(map[string]any)
|
||||
projects := make([]*models.ProjectEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
projectToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
project := &models.ProjectEntity{
|
||||
@@ -259,7 +255,7 @@ func (r *projectRepository) Search(ctx context.Context, params sqlc.SearchProjec
|
||||
|
||||
ids = append(ids, project.ID)
|
||||
projects = append(projects, project)
|
||||
projectToCache[fmt.Sprintf("project:id:%s", project.ID)] = project
|
||||
projectToCache[cache.Key("project:id", project.ID)] = project
|
||||
}
|
||||
|
||||
if len(projectToCache) > 0 {
|
||||
@@ -340,7 +336,7 @@ func (r *projectRepository) Update(ctx context.Context, params sqlc.UpdateProjec
|
||||
_ = project.ParseSubmissions(row.Submissions)
|
||||
_ = project.ParseMembers(row.Members)
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", project.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", project.ID))
|
||||
return &project, nil
|
||||
}
|
||||
|
||||
@@ -349,7 +345,7 @@ func (r *projectRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", convert.UUIDToString(id)))
|
||||
go func() {
|
||||
bgCtx := context.Background()
|
||||
_ = r.c.DelByPattern(bgCtx, "project:search*")
|
||||
@@ -367,8 +363,8 @@ func (r *projectRepository) AddMember(ctx context.Context, params sqlc.AddProjec
|
||||
go func() {
|
||||
_ = r.c.DelByPattern(context.Background(), "project:user*")
|
||||
}()
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, cache.Key2("project:perm", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -380,8 +376,8 @@ func (r *projectRepository) UpdateMemberRole(ctx context.Context, params sqlc.Up
|
||||
go func() {
|
||||
_ = r.c.DelByPattern(context.Background(), "project:user*")
|
||||
}()
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, cache.Key2("project:perm", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -393,13 +389,13 @@ func (r *projectRepository) RemoveMember(ctx context.Context, params sqlc.Remove
|
||||
go func() {
|
||||
_ = r.c.DelByPattern(context.Background(), "project:user*")
|
||||
}()
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", convert.UUIDToString(params.ProjectID)))
|
||||
_ = r.c.Del(ctx, cache.Key2("project:perm", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *projectRepository) CheckPermission(ctx context.Context, params sqlc.CheckProjectPermissionParams) (int16, error) {
|
||||
cacheKey := fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID))
|
||||
cacheKey := cache.Key2("project:perm", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID))
|
||||
var role int16
|
||||
if err := r.c.Get(ctx, cacheKey, &role); err == nil {
|
||||
return role, nil
|
||||
@@ -420,9 +416,9 @@ func (r *projectRepository) ChangeOwner(ctx context.Context, params sqlc.ChangeP
|
||||
return err
|
||||
}
|
||||
projectID := convert.UUIDToString(params.ID)
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", projectID))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", projectID))
|
||||
go func() {
|
||||
_ = r.c.DelByPattern(context.Background(), fmt.Sprintf("project:perm:%s:*", projectID))
|
||||
_ = r.c.DelByPattern(context.Background(), "project:perm:"+projectID+":*")
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@@ -432,6 +428,6 @@ func (r *projectRepository) UpdateLatestCommit(ctx context.Context, params sqlc.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ID)))
|
||||
_ = r.c.Del(ctx, cache.Key("project:id", convert.UUIDToString(params.ID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,17 +6,23 @@ import (
|
||||
"fmt"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const rasterTileCacheDuration = 5 * time.Minute
|
||||
|
||||
type RasterTileRepository interface {
|
||||
GetMetadata(ctx context.Context) (map[string]string, error)
|
||||
GetTile(ctx context.Context, z, x, y int) ([]byte, string, error)
|
||||
}
|
||||
|
||||
type rasterTileRepository struct {
|
||||
db *sql.DB
|
||||
c cache.Cache
|
||||
db *sql.DB
|
||||
c cache.Cache
|
||||
metadataMu sync.RWMutex
|
||||
metadata map[string]string
|
||||
}
|
||||
|
||||
func NewRasterTileRepository(db *sql.DB, c cache.Cache) RasterTileRepository {
|
||||
@@ -27,11 +33,27 @@ func NewRasterTileRepository(db *sql.DB, c cache.Cache) RasterTileRepository {
|
||||
}
|
||||
|
||||
func (r *rasterTileRepository) GetMetadata(ctx context.Context) (map[string]string, error) {
|
||||
r.metadataMu.RLock()
|
||||
if r.metadata != nil {
|
||||
metadata := r.metadata
|
||||
r.metadataMu.RUnlock()
|
||||
return metadata, nil
|
||||
}
|
||||
r.metadataMu.RUnlock()
|
||||
|
||||
r.metadataMu.Lock()
|
||||
defer r.metadataMu.Unlock()
|
||||
|
||||
if r.metadata != nil {
|
||||
return r.metadata, nil
|
||||
}
|
||||
|
||||
cacheId := "rasterTile:metadata"
|
||||
|
||||
var cached map[string]string
|
||||
err := r.c.Get(ctx, cacheId, &cached)
|
||||
if err == nil {
|
||||
r.metadata = cached
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
@@ -41,7 +63,7 @@ func (r *rasterTileRepository) GetMetadata(ctx context.Context) (map[string]stri
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
metadata := make(map[string]string)
|
||||
metadata := make(map[string]string, 8)
|
||||
|
||||
for rows.Next() {
|
||||
var name, value string
|
||||
@@ -50,8 +72,12 @@ func (r *rasterTileRepository) GetMetadata(ctx context.Context) (map[string]stri
|
||||
}
|
||||
metadata[name] = value
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, metadata, constants.NormalCacheDuration)
|
||||
r.metadata = metadata
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
@@ -61,12 +87,14 @@ func (r *rasterTileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte
|
||||
return nil, "", fmt.Errorf("invalid tile coordinates")
|
||||
}
|
||||
|
||||
cacheId := fmt.Sprintf("rasterTile:%d:%d:%d", z, x, y)
|
||||
cacheId := "rasterTile:raw:" + strconv.Itoa(z) + ":" + strconv.Itoa(x) + ":" + strconv.Itoa(y)
|
||||
|
||||
var cached []byte
|
||||
err := r.c.Get(ctx, cacheId, &cached)
|
||||
cached, err := r.c.GetRawClient().Get(ctx, cacheId).Bytes()
|
||||
if err == nil {
|
||||
meta, _ := r.GetMetadata(ctx)
|
||||
meta, err := r.GetMetadata(ctx)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return cached, meta["format"], nil
|
||||
}
|
||||
|
||||
@@ -92,7 +120,7 @@ func (r *rasterTileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, tileData, 5*time.Minute)
|
||||
_ = r.c.GetRawClient().Set(ctx, cacheId, tileData, rasterTileCacheDuration).Err()
|
||||
|
||||
return tileData, meta["format"], nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -52,9 +50,7 @@ func (r *roleRepository) WithTx(tx pgx.Tx) RoleRepository {
|
||||
}
|
||||
|
||||
func (r *roleRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *roleRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.RoleEntity, error) {
|
||||
@@ -63,14 +59,14 @@ func (r *roleRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("role:id:%s", id)
|
||||
keys[i] = cache.Key("role:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var roles []*models.RoleEntity
|
||||
missingRolesToCache := make(map[string]any)
|
||||
roles := make([]*models.RoleEntity, 0, len(ids))
|
||||
missingRolesToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -81,7 +77,7 @@ func (r *roleRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.RoleEntity)
|
||||
dbMap := make(map[string]*models.RoleEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetRolesByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -124,7 +120,7 @@ func (r *roleRepository) GetByIDs(ctx context.Context, ids []string) ([]*models.
|
||||
}
|
||||
|
||||
func (r *roleRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.RoleEntity, error) {
|
||||
cacheId := fmt.Sprintf("role:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("role:id", convert.UUIDToString(id))
|
||||
var role models.RoleEntity
|
||||
err := r.c.Get(ctx, cacheId, &role)
|
||||
if err == nil {
|
||||
@@ -150,7 +146,7 @@ func (r *roleRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.R
|
||||
}
|
||||
|
||||
func (r *roleRepository) GetByName(ctx context.Context, name string) (*models.RoleEntity, error) {
|
||||
cacheId := fmt.Sprintf("role:name:%s", name)
|
||||
cacheId := cache.Key("role:name", name)
|
||||
var role models.RoleEntity
|
||||
err := r.c.Get(ctx, cacheId, &role)
|
||||
if err == nil {
|
||||
@@ -208,7 +204,7 @@ func (r *roleRepository) Update(ctx context.Context, params sqlc.UpdateRoleParam
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("role:id:%s", convert.UUIDToString(row.ID)), fmt.Sprintf("role:name:%s", row.Name))
|
||||
_ = r.c.Del(ctx, cache.Key("role:id", convert.UUIDToString(row.ID)), cache.Key("role:name", row.Name))
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
@@ -236,9 +232,9 @@ func (r *roleRepository) All(ctx context.Context) ([]*models.RoleEntity, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var roles []*models.RoleEntity
|
||||
var ids []string
|
||||
roleToCache := make(map[string]any)
|
||||
roles := make([]*models.RoleEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
roleToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
role := &models.RoleEntity{
|
||||
@@ -251,7 +247,7 @@ func (r *roleRepository) All(ctx context.Context) ([]*models.RoleEntity, error)
|
||||
ids = append(ids, role.ID)
|
||||
roles = append(roles, role)
|
||||
|
||||
roleToCache[fmt.Sprintf("role:id:%s", role.ID)] = role
|
||||
roleToCache[cache.Key("role:id", role.ID)] = role
|
||||
}
|
||||
|
||||
if len(roleToCache) > 0 {
|
||||
@@ -273,7 +269,7 @@ func (r *roleRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("role:id:%s", role.ID), fmt.Sprintf("role:name:%s", role.Name))
|
||||
_ = r.c.Del(ctx, cache.Key("role:id", role.ID), cache.Key("role:name", role.Name))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -282,7 +278,7 @@ func (r *roleRepository) Restore(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("role:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("role:id", convert.UUIDToString(id)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
@@ -47,9 +45,7 @@ func (r *statisticRepository) WithTx(tx pgx.Tx) StatisticRepository {
|
||||
}
|
||||
|
||||
func (r *statisticRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func mapToEntity(row sqlc.SystemStatistic) *models.StatisticEntity {
|
||||
@@ -84,14 +80,14 @@ func (r *statisticRepository) getByIDsWithFallback(ctx context.Context, ids []st
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("statistic:id:%s", id)
|
||||
keys[i] = cache.Key("statistic:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var stats []*models.StatisticEntity
|
||||
missingStatsToCache := make(map[string]any)
|
||||
stats := make([]*models.StatisticEntity, 0, len(ids))
|
||||
missingStatsToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -102,7 +98,7 @@ func (r *statisticRepository) getByIDsWithFallback(ctx context.Context, ids []st
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.StatisticEntity)
|
||||
dbMap := make(map[string]*models.StatisticEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetSystemStatisticsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -151,15 +147,15 @@ func (r *statisticRepository) Search(ctx context.Context, params sqlc.SearchSyst
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ids []string
|
||||
statsToCache := make(map[string]any)
|
||||
var stats []*models.StatisticEntity
|
||||
ids := make([]string, 0, len(rows))
|
||||
statsToCache := make(map[string]any, len(rows))
|
||||
stats := make([]*models.StatisticEntity, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
entity := mapToEntity(row)
|
||||
ids = append(ids, entity.ID)
|
||||
stats = append(stats, entity)
|
||||
statsToCache[fmt.Sprintf("statistic:id:%s", entity.ID)] = entity
|
||||
statsToCache[cache.Key("statistic:id", entity.ID)] = entity
|
||||
}
|
||||
|
||||
if len(statsToCache) > 0 {
|
||||
@@ -171,7 +167,7 @@ func (r *statisticRepository) Search(ctx context.Context, params sqlc.SearchSyst
|
||||
}
|
||||
|
||||
func (r *statisticRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.StatisticEntity, error) {
|
||||
cacheId := fmt.Sprintf("statistic:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("statistic:id", convert.UUIDToString(id))
|
||||
var stat models.StatisticEntity
|
||||
err := r.c.Get(ctx, cacheId, &stat)
|
||||
if err == nil {
|
||||
@@ -194,7 +190,7 @@ func (r *statisticRepository) GetByID(ctx context.Context, id pgtype.UUID) (*mod
|
||||
|
||||
func (r *statisticRepository) GetByDate(ctx context.Context, date time.Time) (*models.StatisticEntity, error) {
|
||||
dateStr := date.Format("2006-01-02")
|
||||
cacheId := fmt.Sprintf("statistic:date:%s", dateStr)
|
||||
cacheId := cache.Key("statistic:date", dateStr)
|
||||
|
||||
var stat models.StatisticEntity
|
||||
err := r.c.Get(ctx, cacheId, &stat)
|
||||
@@ -213,7 +209,7 @@ func (r *statisticRepository) GetByDate(ctx context.Context, date time.Time) (*m
|
||||
entity := mapToEntity(row)
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, entity, constants.NormalCacheDuration)
|
||||
_ = r.c.Set(ctx, fmt.Sprintf("statistic:id:%s", entity.ID), entity, constants.NormalCacheDuration)
|
||||
_ = r.c.Set(ctx, cache.Key("statistic:id", entity.ID), entity, constants.NormalCacheDuration)
|
||||
|
||||
return entity, nil
|
||||
}
|
||||
@@ -231,12 +227,10 @@ func (r *statisticRepository) Upsert(ctx context.Context, date time.Time) (*mode
|
||||
_ = r.c.DelByPattern(bgCtx, "statistic:search*")
|
||||
_ = r.c.Del(
|
||||
bgCtx,
|
||||
fmt.Sprintf("statistic:id:%s", entity.ID),
|
||||
fmt.Sprintf("statistic:date:%s", date.Format("2006-01-02")),
|
||||
cache.Key("statistic:id", entity.ID),
|
||||
cache.Key("statistic:date", date.Format("2006-01-02")),
|
||||
)
|
||||
}()
|
||||
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -48,7 +46,7 @@ func (r *submissionRepository) WithTx(tx pgx.Tx) SubmissionRepository {
|
||||
}
|
||||
|
||||
func (r *submissionRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.SubmissionEntity, error) {
|
||||
cacheId := fmt.Sprintf("submission:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("submission:id", convert.UUIDToString(id))
|
||||
var submission models.SubmissionEntity
|
||||
err := r.c.Get(ctx, cacheId, &submission)
|
||||
if err == nil {
|
||||
@@ -82,9 +80,7 @@ func (r *submissionRepository) GetByID(ctx context.Context, id pgtype.UUID) (*mo
|
||||
}
|
||||
|
||||
func (r *submissionRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *submissionRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.SubmissionEntity, error) {
|
||||
@@ -93,14 +89,14 @@ func (r *submissionRepository) getByIDsWithFallback(ctx context.Context, ids []s
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("submission:id:%s", id)
|
||||
keys[i] = cache.Key("submission:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var submission []*models.SubmissionEntity
|
||||
missingSubmisstionToCache := make(map[string]any)
|
||||
submission := make([]*models.SubmissionEntity, 0, len(ids))
|
||||
missingSubmisstionToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -111,7 +107,7 @@ func (r *submissionRepository) getByIDsWithFallback(ctx context.Context, ids []s
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.SubmissionEntity)
|
||||
dbMap := make(map[string]*models.SubmissionEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetSubmissionsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -164,7 +160,7 @@ func (r *submissionRepository) GetByIDs(ctx context.Context, ids []string) ([]*m
|
||||
}
|
||||
|
||||
func (r *submissionRepository) Search(ctx context.Context, params sqlc.SearchSubmissionsParams) ([]*models.SubmissionEntity, error) {
|
||||
queryKey := r.generateQueryKey("verification:search", params)
|
||||
queryKey := r.generateQueryKey("submission:search", params)
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, queryKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -187,9 +183,9 @@ func (r *submissionRepository) Search(ctx context.Context, params sqlc.SearchSub
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var items []*models.SubmissionEntity
|
||||
var ids []string
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.SubmissionEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
entity := &models.SubmissionEntity{
|
||||
@@ -213,7 +209,7 @@ func (r *submissionRepository) Search(ctx context.Context, params sqlc.SearchSub
|
||||
ids = append(ids, entity.ID)
|
||||
items = append(items, entity)
|
||||
|
||||
itemToCache[fmt.Sprintf("submission:id:%s", entity.ID)] = entity
|
||||
itemToCache[cache.Key("submission:id", entity.ID)] = entity
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
@@ -308,7 +304,7 @@ func (r *submissionRepository) Update(ctx context.Context, params sqlc.UpdateSub
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("submission:id:%s", submission.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("submission:id", submission.ID))
|
||||
|
||||
return &submission, nil
|
||||
}
|
||||
@@ -319,7 +315,7 @@ func (r *submissionRepository) Delete(ctx context.Context, id pgtype.UUID) error
|
||||
return err
|
||||
}
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("submission:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("submission:id", convert.UUIDToString(id)))
|
||||
go func() {
|
||||
bgCtx := context.Background()
|
||||
_ = r.c.DelByPattern(bgCtx, "submission:search*")
|
||||
|
||||
@@ -6,17 +6,23 @@ import (
|
||||
"fmt"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const tileCacheDuration = 5 * time.Minute
|
||||
|
||||
type TileRepository interface {
|
||||
GetMetadata(ctx context.Context) (map[string]string, error)
|
||||
GetTile(ctx context.Context, z, x, y int) ([]byte, string, bool, error)
|
||||
}
|
||||
|
||||
type tileRepository struct {
|
||||
db *sql.DB
|
||||
c cache.Cache
|
||||
db *sql.DB
|
||||
c cache.Cache
|
||||
metadataMu sync.RWMutex
|
||||
metadata map[string]string
|
||||
}
|
||||
|
||||
func NewTileRepository(db *sql.DB, c cache.Cache) TileRepository {
|
||||
@@ -27,11 +33,27 @@ func NewTileRepository(db *sql.DB, c cache.Cache) TileRepository {
|
||||
}
|
||||
|
||||
func (r *tileRepository) GetMetadata(ctx context.Context) (map[string]string, error) {
|
||||
r.metadataMu.RLock()
|
||||
if r.metadata != nil {
|
||||
metadata := r.metadata
|
||||
r.metadataMu.RUnlock()
|
||||
return metadata, nil
|
||||
}
|
||||
r.metadataMu.RUnlock()
|
||||
|
||||
r.metadataMu.Lock()
|
||||
defer r.metadataMu.Unlock()
|
||||
|
||||
if r.metadata != nil {
|
||||
return r.metadata, nil
|
||||
}
|
||||
|
||||
cacheId := "tile:metadata"
|
||||
|
||||
var cached map[string]string
|
||||
err := r.c.Get(ctx, cacheId, &cached)
|
||||
if err == nil {
|
||||
r.metadata = cached
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
@@ -41,7 +63,7 @@ func (r *tileRepository) GetMetadata(ctx context.Context) (map[string]string, er
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
metadata := make(map[string]string)
|
||||
metadata := make(map[string]string, 8)
|
||||
|
||||
for rows.Next() {
|
||||
var name, value string
|
||||
@@ -50,8 +72,12 @@ func (r *tileRepository) GetMetadata(ctx context.Context) (map[string]string, er
|
||||
}
|
||||
metadata[name] = value
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, metadata, constants.NormalCacheDuration)
|
||||
r.metadata = metadata
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
@@ -61,13 +87,16 @@ func (r *tileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte, stri
|
||||
return nil, "", false, fmt.Errorf("invalid tile coordinates")
|
||||
}
|
||||
|
||||
cacheId := fmt.Sprintf("tile:%d:%d:%d", z, x, y)
|
||||
cacheId := "tile:raw:" + strconv.Itoa(z) + ":" + strconv.Itoa(x) + ":" + strconv.Itoa(y)
|
||||
|
||||
var cached []byte
|
||||
err := r.c.Get(ctx, cacheId, &cached)
|
||||
cached, err := r.c.GetRawClient().Get(ctx, cacheId).Bytes()
|
||||
if err == nil {
|
||||
meta, _ := r.GetMetadata(ctx)
|
||||
return cached, meta["format"], meta["format"] == "pbf", nil
|
||||
meta, err := r.GetMetadata(ctx)
|
||||
if err != nil {
|
||||
return nil, "", false, err
|
||||
}
|
||||
format := meta["format"]
|
||||
return cached, format, format == "pbf", nil
|
||||
}
|
||||
|
||||
// XYZ -> TMS
|
||||
@@ -92,7 +121,7 @@ func (r *tileRepository) GetTile(ctx context.Context, z, x, y int) ([]byte, stri
|
||||
return nil, "", false, err
|
||||
}
|
||||
|
||||
_ = r.c.Set(ctx, cacheId, tileData, 5*time.Minute)
|
||||
_ = r.c.GetRawClient().Set(ctx, cacheId, tileData, tileCacheDuration).Err()
|
||||
|
||||
return tileData, meta["format"], meta["format"] == "pbf", nil
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TokenRepository interface {
|
||||
@@ -33,23 +33,27 @@ func NewTokenRepository(c cache.Cache) TokenRepository {
|
||||
}
|
||||
}
|
||||
|
||||
func tokenTypeValue(t constants.TokenType) string {
|
||||
return strconv.Itoa(int(t.Value()))
|
||||
}
|
||||
|
||||
func (t *tokenRepository) CreateVerified(ctx context.Context, email string, tokenType constants.TokenType, id string) error {
|
||||
cacheKey := fmt.Sprintf("token:verified:%d:%s:%s", tokenType.Value(), email, id)
|
||||
cacheKey := cache.Key2("token:verified:"+tokenTypeValue(tokenType), email, id)
|
||||
return t.c.Set(ctx, cacheKey, true, constants.TokenVerifiedDuration)
|
||||
}
|
||||
|
||||
func (t *tokenRepository) DeleteVerified(ctx context.Context, email string, tokenType constants.TokenType, id string) error {
|
||||
cacheKey := fmt.Sprintf("token:verified:%d:%s:%s", tokenType.Value(), email, id)
|
||||
cacheKey := cache.Key2("token:verified:"+tokenTypeValue(tokenType), email, id)
|
||||
return t.c.Del(ctx, cacheKey)
|
||||
}
|
||||
func (t *tokenRepository) CheckVerified(ctx context.Context, email string, tokenType constants.TokenType, id string) (bool, error) {
|
||||
cacheKey := fmt.Sprintf("token:verified:%d:%s:%s", tokenType.Value(), email, id)
|
||||
cacheKey := cache.Key2("token:verified:"+tokenTypeValue(tokenType), email, id)
|
||||
exists, err := t.c.Exists(ctx, cacheKey)
|
||||
return exists, err
|
||||
}
|
||||
|
||||
func (t *tokenRepository) CreateUploadToken(ctx context.Context, userId string, token *models.TokenUploadEntity) error {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s:%s", constants.TokenTypeUpload.Value(), userId, token.ID)
|
||||
cacheKey := cache.Key2("token:"+tokenTypeValue(constants.TokenTypeUpload), userId, token.ID)
|
||||
err := t.c.Set(ctx, cacheKey, token, constants.TokenUploadDuration)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -58,7 +62,7 @@ func (t *tokenRepository) CreateUploadToken(ctx context.Context, userId string,
|
||||
}
|
||||
|
||||
func (t *tokenRepository) GetUploadToken(ctx context.Context, userId string, id string) (*models.TokenUploadEntity, error) {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s:%s", constants.TokenTypeUpload.Value(), userId, id)
|
||||
cacheKey := cache.Key2("token:"+tokenTypeValue(constants.TokenTypeUpload), userId, id)
|
||||
var token models.TokenUploadEntity
|
||||
err := t.c.Get(ctx, cacheKey, &token)
|
||||
if err != nil {
|
||||
@@ -68,35 +72,35 @@ func (t *tokenRepository) GetUploadToken(ctx context.Context, userId string, id
|
||||
}
|
||||
|
||||
func (t *tokenRepository) DeleteUploadToken(ctx context.Context, userId string, id string) error {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s:%s", constants.TokenTypeUpload.Value(), userId, id)
|
||||
cacheKey := cache.Key2("token:"+tokenTypeValue(constants.TokenTypeUpload), userId, id)
|
||||
return t.c.Del(ctx, cacheKey)
|
||||
}
|
||||
|
||||
func (t *tokenRepository) CheckCooldown(ctx context.Context, email string, tokenType constants.TokenType) (bool, error) {
|
||||
cacheKey := fmt.Sprintf("token:cooldown:%d:%s", tokenType.Value(), email)
|
||||
cacheKey := cache.Key("token:cooldown:"+tokenTypeValue(tokenType), email)
|
||||
exists, err := t.c.Exists(ctx, cacheKey)
|
||||
return exists, err
|
||||
}
|
||||
|
||||
func (t *tokenRepository) Create(ctx context.Context, token *models.TokenEntity) error {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s", token.TokenType.Value(), token.Email)
|
||||
cacheKey := cache.Key("token:"+tokenTypeValue(token.TokenType), token.Email)
|
||||
err := t.c.Set(ctx, cacheKey, token, constants.TokenExpirationDuration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cooldownKey := fmt.Sprintf("token:cooldown:%d:%s", token.TokenType.Value(), token.Email)
|
||||
cooldownKey := cache.Key("token:cooldown:"+tokenTypeValue(token.TokenType), token.Email)
|
||||
return t.c.Set(ctx, cooldownKey, true, constants.TokenCooldownDuration)
|
||||
}
|
||||
|
||||
func (t *tokenRepository) Delete(ctx context.Context, email string, tokenType constants.TokenType) error {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s", tokenType.Value(), email)
|
||||
cooldownKey := fmt.Sprintf("token:cooldown:%d:%s", tokenType.Value(), email)
|
||||
cacheKey := cache.Key("token:"+tokenTypeValue(tokenType), email)
|
||||
cooldownKey := cache.Key("token:cooldown:"+tokenTypeValue(tokenType), email)
|
||||
_ = t.c.Del(ctx, cooldownKey)
|
||||
return t.c.Del(ctx, cacheKey)
|
||||
}
|
||||
|
||||
func (t *tokenRepository) Get(ctx context.Context, email string, tokenType constants.TokenType) (*models.TokenEntity, error) {
|
||||
cacheKey := fmt.Sprintf("token:%d:%s", tokenType.Value(), email)
|
||||
cacheKey := cache.Key("token:"+tokenTypeValue(tokenType), email)
|
||||
var token models.TokenEntity
|
||||
err := t.c.Get(ctx, cacheKey, &token)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"time"
|
||||
@@ -25,7 +24,7 @@ func NewUsageRepository(c cache.Cache) UsageRepository {
|
||||
|
||||
func (r *usageRepository) getUsageKey(userID string) string {
|
||||
dateStr := time.Now().Format("20060102")
|
||||
return fmt.Sprintf("usage:ai:%s:%s", userID, dateStr)
|
||||
return cache.Key2("usage:ai", userID, dateStr)
|
||||
}
|
||||
|
||||
func (r *usageRepository) GetAIUsage(ctx context.Context, userID string) (int, error) {
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -54,9 +52,7 @@ func (r *userRepository) WithTx(tx pgx.Tx) UserRepository {
|
||||
}
|
||||
|
||||
func (r *userRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *userRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.UserEntity, error) {
|
||||
@@ -65,14 +61,14 @@ func (r *userRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("user:id:%s", id)
|
||||
keys[i] = cache.Key("user:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var users []*models.UserEntity
|
||||
missingUsersToCache := make(map[string]any)
|
||||
users := make([]*models.UserEntity, 0, len(ids))
|
||||
missingUsersToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -83,7 +79,7 @@ func (r *userRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.UserEntity)
|
||||
dbMap := make(map[string]*models.UserEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetUsersByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -126,7 +122,7 @@ func (r *userRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
|
||||
func (r *userRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.UserEntity, error) {
|
||||
cacheId := fmt.Sprintf("user:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("user:id", convert.UUIDToString(id))
|
||||
var user models.UserEntity
|
||||
err := r.c.Get(ctx, cacheId, &user)
|
||||
if err == nil {
|
||||
@@ -164,7 +160,7 @@ func (r *userRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.U
|
||||
}
|
||||
|
||||
func (r *userRepository) GetByIDWithoutDeleted(ctx context.Context, id pgtype.UUID) (*models.UserEntity, error) {
|
||||
cacheId := fmt.Sprintf("user:deleted:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("user:deleted:id", convert.UUIDToString(id))
|
||||
var user models.UserEntity
|
||||
err := r.c.Get(ctx, cacheId, &user)
|
||||
if err == nil {
|
||||
@@ -202,7 +198,7 @@ func (r *userRepository) GetByIDWithoutDeleted(ctx context.Context, id pgtype.UU
|
||||
}
|
||||
|
||||
func (r *userRepository) GetByEmail(ctx context.Context, email string) (*models.UserEntity, error) {
|
||||
cacheId := fmt.Sprintf("user:email:%s", email)
|
||||
cacheId := cache.Key("user:email", email)
|
||||
|
||||
var user models.UserEntity
|
||||
err := r.c.Get(ctx, cacheId, &user)
|
||||
@@ -287,7 +283,7 @@ func (r *userRepository) UpdateProfile(ctx context.Context, params sqlc.UpdateUs
|
||||
}
|
||||
|
||||
user.Profile = &profile
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("user:email:%s", user.Email), fmt.Sprintf("user:id:%s", user.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("user:email", user.Email), cache.Key("user:id", user.ID))
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@@ -326,9 +322,9 @@ func (r *userRepository) Search(ctx context.Context, params sqlc.SearchUsersPara
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var users []*models.UserEntity
|
||||
var ids []string
|
||||
usersToCache := make(map[string]any)
|
||||
users := make([]*models.UserEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
usersToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
user := &models.UserEntity{
|
||||
@@ -352,7 +348,7 @@ func (r *userRepository) Search(ctx context.Context, params sqlc.SearchUsersPara
|
||||
|
||||
users = append(users, user)
|
||||
ids = append(ids, user.ID)
|
||||
usersToCache[fmt.Sprintf("user:id:%s", user.ID)] = user
|
||||
usersToCache[cache.Key("user:id", user.ID)] = user
|
||||
}
|
||||
|
||||
if len(usersToCache) > 0 {
|
||||
@@ -392,9 +388,9 @@ func (r *userRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
|
||||
_ = r.c.Del(
|
||||
ctx,
|
||||
fmt.Sprintf("user:id:%s", user.ID),
|
||||
fmt.Sprintf("user:email:%s", user.Email),
|
||||
fmt.Sprintf("user:token:%s", user.ID),
|
||||
cache.Key("user:id", user.ID),
|
||||
cache.Key("user:email", user.Email),
|
||||
cache.Key("user:token", user.ID),
|
||||
)
|
||||
go func() {
|
||||
bgCtx := context.Background()
|
||||
@@ -411,9 +407,9 @@ func (r *userRepository) Restore(ctx context.Context, id pgtype.UUID) error {
|
||||
}
|
||||
_ = r.c.Del(
|
||||
ctx,
|
||||
fmt.Sprintf("user:deleted:id:%s", convert.UUIDToString(id)),
|
||||
fmt.Sprintf("user:email:%s", convert.UUIDToString(id)),
|
||||
fmt.Sprintf("user:id:%s", convert.UUIDToString(id)),
|
||||
cache.Key("user:deleted:id", convert.UUIDToString(id)),
|
||||
cache.Key("user:email", convert.UUIDToString(id)),
|
||||
cache.Key("user:id", convert.UUIDToString(id)),
|
||||
)
|
||||
go func() {
|
||||
bgCtx := context.Background()
|
||||
@@ -424,7 +420,7 @@ func (r *userRepository) Restore(ctx context.Context, id pgtype.UUID) error {
|
||||
}
|
||||
|
||||
func (r *userRepository) GetTokenVersion(ctx context.Context, id pgtype.UUID) (int32, error) {
|
||||
cacheId := fmt.Sprintf("user:token:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("user:token", convert.UUIDToString(id))
|
||||
var token int32
|
||||
err := r.c.Get(ctx, cacheId, &token)
|
||||
if err == nil {
|
||||
@@ -445,7 +441,7 @@ func (r *userRepository) UpdateTokenVersion(ctx context.Context, params sqlc.Upd
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("user:token:%s", convert.UUIDToString(params.ID)))
|
||||
_ = r.c.Del(ctx, cache.Key("user:token", convert.UUIDToString(params.ID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -458,7 +454,7 @@ func (r *userRepository) UpdatePassword(ctx context.Context, params sqlc.UpdateU
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("user:email:%s", user.Email), fmt.Sprintf("user:id:%s", user.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("user:email", user.Email), cache.Key("user:id", user.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -474,6 +470,6 @@ func (r *userRepository) UpdateRefreshToken(ctx context.Context, params sqlc.Upd
|
||||
|
||||
user.RefreshToken = convert.TextToString(params.RefreshToken)
|
||||
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("user:email:%s", user.Email), fmt.Sprintf("user:id:%s", user.ID))
|
||||
_ = r.c.Del(ctx, cache.Key("user:email", user.Email), cache.Key("user:id", user.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/gen/sqlc"
|
||||
"history-api/internal/models"
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -49,13 +47,11 @@ func (v *verificationRepository) WithTx(tx pgx.Tx) VerificationRepository {
|
||||
}
|
||||
|
||||
func (v *verificationRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (v *verificationRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.UserVerificationEntity, error) {
|
||||
cacheId := fmt.Sprintf("verification:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("verification:id", convert.UUIDToString(id))
|
||||
var verification models.UserVerificationEntity
|
||||
err := v.c.Get(ctx, cacheId, &verification)
|
||||
if err == nil {
|
||||
@@ -101,14 +97,14 @@ func (v *verificationRepository) getByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("verification:id:%s", id)
|
||||
keys[i] = cache.Key("verification:id", id)
|
||||
}
|
||||
raws := v.c.MGet(ctx, keys...)
|
||||
|
||||
var verification []*models.UserVerificationEntity
|
||||
missingVerificationToCache := make(map[string]any)
|
||||
verification := make([]*models.UserVerificationEntity, 0, len(ids))
|
||||
missingVerificationToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -119,7 +115,7 @@ func (v *verificationRepository) getByIDsWithFallback(ctx context.Context, ids [
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.UserVerificationEntity)
|
||||
dbMap := make(map[string]*models.UserVerificationEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := v.q.GetUserVerificationsByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -212,7 +208,7 @@ func (v *verificationRepository) Create(ctx context.Context, params sqlc.CreateU
|
||||
_ = v.c.DelByPattern(bgCtx, "verification:count*")
|
||||
}()
|
||||
|
||||
_ = v.c.Del(ctx, fmt.Sprintf("verification:userId:%s", convert.UUIDToString(params.UserID)))
|
||||
_ = v.c.Del(ctx, cache.Key("verification:userId", convert.UUIDToString(params.UserID)))
|
||||
|
||||
return &verification, nil
|
||||
}
|
||||
@@ -222,7 +218,7 @@ func (v *verificationRepository) UpdateStatus(ctx context.Context, params sqlc.U
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = v.c.Del(ctx, fmt.Sprintf("verification:id:%s", convert.UUIDToString(params.ID)))
|
||||
_ = v.c.Del(ctx, cache.Key("verification:id", convert.UUIDToString(params.ID)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -232,7 +228,7 @@ func (v *verificationRepository) Delete(ctx context.Context, id pgtype.UUID) err
|
||||
return err
|
||||
}
|
||||
|
||||
_ = v.c.Del(ctx, fmt.Sprintf("verification:id:%s", convert.UUIDToString(id)))
|
||||
_ = v.c.Del(ctx, cache.Key("verification:id", convert.UUIDToString(id)))
|
||||
go func() {
|
||||
_ = v.c.DelByPattern(context.Background(), "verification:count*")
|
||||
}()
|
||||
@@ -249,13 +245,13 @@ func (v *verificationRepository) BulkVerificationMediaByMediaId(ctx context.Cont
|
||||
return nil
|
||||
}
|
||||
|
||||
listCacheId := make([]string, 0)
|
||||
listCacheId := make([]string, 0, len(ids))
|
||||
for _, it := range ids {
|
||||
id := convert.UUIDToString(it)
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
listCacheId = append(listCacheId, fmt.Sprintf("verification:id:%s", id))
|
||||
listCacheId = append(listCacheId, cache.Key("verification:id", id))
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -276,7 +272,7 @@ func (v *verificationRepository) DeleteVerificationMedia(ctx context.Context, pa
|
||||
}
|
||||
|
||||
func (v *verificationRepository) GetByUserID(ctx context.Context, userId pgtype.UUID) ([]*models.UserVerificationEntity, error) {
|
||||
queryKey := fmt.Sprintf("verification:userId:%s", convert.UUIDToString(userId))
|
||||
queryKey := cache.Key("verification:userId", convert.UUIDToString(userId))
|
||||
var cachedIDs []string
|
||||
err := v.c.Get(ctx, queryKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -299,9 +295,9 @@ func (v *verificationRepository) GetByUserID(ctx context.Context, userId pgtype.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var items []*models.UserVerificationEntity
|
||||
var ids []string
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.UserVerificationEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
verification := &models.UserVerificationEntity{
|
||||
@@ -329,7 +325,7 @@ func (v *verificationRepository) GetByUserID(ctx context.Context, userId pgtype.
|
||||
ids = append(ids, verification.ID)
|
||||
items = append(items, verification)
|
||||
|
||||
itemToCache[fmt.Sprintf("verification:id:%s", verification.ID)] = verification
|
||||
itemToCache[cache.Key("verification:id", verification.ID)] = verification
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
@@ -365,9 +361,9 @@ func (v *verificationRepository) Search(ctx context.Context, params sqlc.SearchU
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var items []*models.UserVerificationEntity
|
||||
var ids []string
|
||||
itemToCache := make(map[string]any)
|
||||
items := make([]*models.UserVerificationEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
itemToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
verification := &models.UserVerificationEntity{
|
||||
@@ -396,7 +392,7 @@ func (v *verificationRepository) Search(ctx context.Context, params sqlc.SearchU
|
||||
ids = append(ids, verification.ID)
|
||||
items = append(items, verification)
|
||||
|
||||
itemToCache[fmt.Sprintf("verification:id:%s", verification.ID)] = verification
|
||||
itemToCache[cache.Key("verification:id", verification.ID)] = verification
|
||||
}
|
||||
|
||||
if len(itemToCache) > 0 {
|
||||
|
||||
@@ -2,9 +2,7 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -60,9 +58,7 @@ func (r *wikiRepository) WithTx(tx pgx.Tx) WikiRepository {
|
||||
}
|
||||
|
||||
func (r *wikiRepository) generateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.WikiEntity, error) {
|
||||
@@ -71,14 +67,14 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("wiki:id:%s", id)
|
||||
keys[i] = cache.Key("wiki:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var wikis []*models.WikiEntity
|
||||
missingToCache := make(map[string]any)
|
||||
wikis := make([]*models.WikiEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -89,7 +85,7 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.WikiEntity)
|
||||
dbMap := make(map[string]*models.WikiEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetWikisByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -148,7 +144,7 @@ func (r *wikiRepository) GetByIDs(ctx context.Context, ids []string) ([]*models.
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.WikiEntity, error) {
|
||||
cacheId := fmt.Sprintf("wiki:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("wiki:id", convert.UUIDToString(id))
|
||||
var wiki models.WikiEntity
|
||||
err := r.c.Get(ctx, cacheId, &wiki)
|
||||
if err == nil {
|
||||
@@ -173,6 +169,7 @@ func (r *wikiRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.W
|
||||
|
||||
samples, err := r.q.GetWikiContentByWikiID(ctx, row.ID)
|
||||
if err == nil {
|
||||
wiki.ContentSample = make([]models.WikiContentSample, 0, len(samples))
|
||||
for _, sample := range samples {
|
||||
wiki.ContentSample = append(wiki.ContentSample, models.WikiContentSample{
|
||||
ID: convert.UUIDToString(sample.ID),
|
||||
@@ -202,11 +199,11 @@ func (r *wikiRepository) Search(ctx context.Context, params sqlc.SearchWikisPara
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var wikis []*models.WikiEntity
|
||||
var ids []string
|
||||
var pgIds []pgtype.UUID
|
||||
wikiMap := make(map[string]*models.WikiEntity)
|
||||
wikiToCache := make(map[string]any)
|
||||
wikis := make([]*models.WikiEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
pgIds := make([]pgtype.UUID, 0, len(rows))
|
||||
wikiMap := make(map[string]*models.WikiEntity, len(rows))
|
||||
wikiToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
wiki := &models.WikiEntity{
|
||||
@@ -241,7 +238,7 @@ func (r *wikiRepository) Search(ctx context.Context, params sqlc.SearchWikisPara
|
||||
}
|
||||
|
||||
for _, wiki := range wikis {
|
||||
wikiToCache[fmt.Sprintf("wiki:id:%s", wiki.ID)] = wiki
|
||||
wikiToCache[cache.Key("wiki:id", wiki.ID)] = wiki
|
||||
}
|
||||
if len(wikiToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, wikiToCache, constants.NormalCacheDuration)
|
||||
@@ -284,8 +281,7 @@ func (r *wikiRepository) Update(ctx context.Context, params sqlc.UpdateWikiParam
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wiki.ID))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("wiki:slug:%s", wiki.Slug))
|
||||
_ = r.c.Del(ctx, cache.Key("wiki:id", wiki.ID), cache.Key("wiki:slug", wiki.Slug))
|
||||
return &wiki, nil
|
||||
}
|
||||
|
||||
@@ -294,7 +290,7 @@ func (r *wikiRepository) Delete(ctx context.Context, id pgtype.UUID) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("wiki:id:%s", convert.UUIDToString(id)))
|
||||
_ = r.c.Del(ctx, cache.Key("wiki:id", convert.UUIDToString(id)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -315,7 +311,7 @@ func (r *wikiRepository) BulkDeleteEntityWikisByEntityId(ctx context.Context, en
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UUID) ([]*models.WikiEntity, error) {
|
||||
cacheKey := fmt.Sprintf("wiki:project:%s", convert.UUIDToString(projectID))
|
||||
cacheKey := cache.Key("wiki:project", convert.UUIDToString(projectID))
|
||||
var cachedIDs []string
|
||||
err := r.c.Get(ctx, cacheKey, &cachedIDs)
|
||||
if err == nil {
|
||||
@@ -330,11 +326,11 @@ func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UU
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var wikis []*models.WikiEntity
|
||||
var ids []string
|
||||
var pgIds []pgtype.UUID
|
||||
wikiMap := make(map[string]*models.WikiEntity)
|
||||
wikiToCache := make(map[string]any)
|
||||
wikis := make([]*models.WikiEntity, 0, len(rows))
|
||||
ids := make([]string, 0, len(rows))
|
||||
pgIds := make([]pgtype.UUID, 0, len(rows))
|
||||
wikiMap := make(map[string]*models.WikiEntity, len(rows))
|
||||
wikiToCache := make(map[string]any, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
wiki := &models.WikiEntity{
|
||||
@@ -369,7 +365,7 @@ func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UU
|
||||
}
|
||||
|
||||
for _, wiki := range wikis {
|
||||
wikiToCache[fmt.Sprintf("wiki:id:%s", wiki.ID)] = wiki
|
||||
wikiToCache[cache.Key("wiki:id", wiki.ID)] = wiki
|
||||
}
|
||||
if len(wikiToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, wikiToCache, constants.NormalCacheDuration)
|
||||
@@ -387,7 +383,7 @@ func (r *wikiRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID) err
|
||||
if len(ids) > 0 {
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("wiki:id:%s", convert.UUIDToString(id))
|
||||
keys[i] = cache.Key("wiki:id", convert.UUIDToString(id))
|
||||
}
|
||||
_ = r.c.Del(ctx, keys...)
|
||||
}
|
||||
@@ -406,7 +402,7 @@ func (r *wikiRepository) DeleteEntityWiki(ctx context.Context, entityID pgtype.U
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetBySlug(ctx context.Context, slug string) (*models.WikiEntity, error) {
|
||||
cacheKey := fmt.Sprintf("wiki:slug:%s", slug)
|
||||
cacheKey := cache.Key("wiki:slug", slug)
|
||||
var wiki models.WikiEntity
|
||||
err := r.c.Get(ctx, cacheKey, &wiki)
|
||||
if err == nil {
|
||||
@@ -431,6 +427,7 @@ func (r *wikiRepository) GetBySlug(ctx context.Context, slug string) (*models.Wi
|
||||
|
||||
samples, err := r.q.GetWikiContentByWikiID(ctx, row.ID)
|
||||
if err == nil {
|
||||
wiki.ContentSample = make([]models.WikiContentSample, 0, len(samples))
|
||||
for _, sample := range samples {
|
||||
wiki.ContentSample = append(wiki.ContentSample, models.WikiContentSample{
|
||||
ID: convert.UUIDToString(sample.ID),
|
||||
@@ -451,13 +448,13 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
||||
}
|
||||
keys := make([]string, len(slugs))
|
||||
for i, slug := range slugs {
|
||||
keys[i] = fmt.Sprintf("wiki:slug:%s", slug)
|
||||
keys[i] = cache.Key("wiki:slug", slug)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var wikis []*models.WikiEntity
|
||||
missingToCache := make(map[string]any)
|
||||
var missingSlugs []string
|
||||
wikis := make([]*models.WikiEntity, 0, len(slugs))
|
||||
missingToCache := make(map[string]any, len(slugs))
|
||||
missingSlugs := make([]string, 0, len(slugs))
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
@@ -465,11 +462,11 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.WikiEntity)
|
||||
dbMap := make(map[string]*models.WikiEntity, len(missingSlugs))
|
||||
if len(missingSlugs) > 0 {
|
||||
dbRows, err := r.q.GetWikisBySlugs(ctx, missingSlugs)
|
||||
if err == nil {
|
||||
var pgIds []pgtype.UUID
|
||||
pgIds := make([]pgtype.UUID, 0, len(dbRows))
|
||||
for _, row := range dbRows {
|
||||
item := models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
@@ -487,7 +484,7 @@ func (r *wikiRepository) GetBySlugs(ctx context.Context, slugs []string) ([]*mod
|
||||
if len(pgIds) > 0 {
|
||||
samples, sErr := r.q.GetWikiContentByWikiIDs(ctx, pgIds)
|
||||
if sErr == nil {
|
||||
wikiByID := make(map[string]*models.WikiEntity)
|
||||
wikiByID := make(map[string]*models.WikiEntity, len(dbMap))
|
||||
for _, item := range dbMap {
|
||||
wikiByID[item.ID] = item
|
||||
}
|
||||
@@ -554,14 +551,14 @@ func (r *wikiRepository) getContentByIDsWithFallback(ctx context.Context, ids []
|
||||
}
|
||||
keys := make([]string, len(ids))
|
||||
for i, id := range ids {
|
||||
keys[i] = fmt.Sprintf("wiki_content:id:%s", id)
|
||||
keys[i] = cache.Key("wiki_content:id", id)
|
||||
}
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
|
||||
var contents []*models.WikiContentEntity
|
||||
missingToCache := make(map[string]any)
|
||||
contents := make([]*models.WikiContentEntity, 0, len(ids))
|
||||
missingToCache := make(map[string]any, len(ids))
|
||||
|
||||
var missingPgIds []pgtype.UUID
|
||||
missingPgIds := make([]pgtype.UUID, 0, len(ids))
|
||||
for i, b := range raws {
|
||||
if len(b) == 0 {
|
||||
pgId := pgtype.UUID{}
|
||||
@@ -572,7 +569,7 @@ func (r *wikiRepository) getContentByIDsWithFallback(ctx context.Context, ids []
|
||||
}
|
||||
}
|
||||
|
||||
dbMap := make(map[string]*models.WikiContentEntity)
|
||||
dbMap := make(map[string]*models.WikiContentEntity, len(missingPgIds))
|
||||
if len(missingPgIds) > 0 {
|
||||
dbRows, err := r.q.GetWikiContentByIDs(ctx, missingPgIds)
|
||||
if err == nil {
|
||||
@@ -613,7 +610,7 @@ func (r *wikiRepository) getContentByIDsWithFallback(ctx context.Context, ids []
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetContentByID(ctx context.Context, id pgtype.UUID) (*models.WikiContentEntity, error) {
|
||||
cacheId := fmt.Sprintf("wiki_content:id:%s", convert.UUIDToString(id))
|
||||
cacheId := cache.Key("wiki_content:id", convert.UUIDToString(id))
|
||||
var content models.WikiContentEntity
|
||||
err := r.c.Get(ctx, cacheId, &content)
|
||||
if err == nil {
|
||||
@@ -651,13 +648,13 @@ func (r *wikiRepository) GetWikiIDsByEntityIDs(ctx context.Context, entityIDs []
|
||||
|
||||
keys := make([]string, len(entityIDs))
|
||||
for i, id := range entityIDs {
|
||||
keys[i] = fmt.Sprintf("entity_wikis:entity:%s", id)
|
||||
keys[i] = cache.Key("entity_wikis:entity", id)
|
||||
}
|
||||
|
||||
raws := r.c.MGet(ctx, keys...)
|
||||
result := make(map[string][]string)
|
||||
var missingEntityIDs []string
|
||||
var missingPgIDs []pgtype.UUID
|
||||
result := make(map[string][]string, len(entityIDs))
|
||||
missingEntityIDs := make([]string, 0, len(entityIDs))
|
||||
missingPgIDs := make([]pgtype.UUID, 0, len(entityIDs))
|
||||
|
||||
for i, b := range raws {
|
||||
if len(b) > 0 {
|
||||
@@ -680,7 +677,7 @@ func (r *wikiRepository) GetWikiIDsByEntityIDs(ctx context.Context, entityIDs []
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbMap := make(map[string][]string)
|
||||
dbMap := make(map[string][]string, len(missingEntityIDs))
|
||||
for _, id := range missingEntityIDs {
|
||||
dbMap[id] = []string{}
|
||||
}
|
||||
@@ -690,10 +687,10 @@ func (r *wikiRepository) GetWikiIDsByEntityIDs(ctx context.Context, entityIDs []
|
||||
dbMap[eID] = append(dbMap[eID], wID)
|
||||
}
|
||||
|
||||
missingToCache := make(map[string]any)
|
||||
missingToCache := make(map[string]any, len(dbMap))
|
||||
for eID, wIDs := range dbMap {
|
||||
result[eID] = wIDs
|
||||
missingToCache[fmt.Sprintf("entity_wikis:entity:%s", eID)] = wIDs
|
||||
missingToCache[cache.Key("entity_wikis:entity", eID)] = wIDs
|
||||
}
|
||||
if len(missingToCache) > 0 {
|
||||
_ = r.c.MSet(ctx, missingToCache, constants.NormalCacheDuration)
|
||||
|
||||
@@ -61,9 +61,16 @@ func (s *battleReplayService) GetByGeometryIDs(ctx context.Context, req *request
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get battle replays")
|
||||
}
|
||||
|
||||
result := make(map[string][]*response.BattleReplayResponse)
|
||||
counts := make(map[string]int, len(req.GeometryIDs))
|
||||
for _, replay := range replays {
|
||||
if replay != nil {
|
||||
counts[replay.GeometryID]++
|
||||
}
|
||||
}
|
||||
|
||||
result := make(map[string][]*response.BattleReplayResponse, len(req.GeometryIDs))
|
||||
for _, idStr := range req.GeometryIDs {
|
||||
result[idStr] = make([]*response.BattleReplayResponse, 0)
|
||||
result[idStr] = make([]*response.BattleReplayResponse, 0, counts[idStr])
|
||||
}
|
||||
|
||||
for _, replay := range replays {
|
||||
|
||||
@@ -70,6 +70,7 @@ func (s *chatbotService) Chat(ctx context.Context, userID string, projectID *str
|
||||
}
|
||||
|
||||
var contextBuilder strings.Builder
|
||||
contextBuilder.Grow(len(results) * 96)
|
||||
for i, res := range results {
|
||||
contextBuilder.WriteString(fmt.Sprintf("<doc id=\"%d\" score=\"%.2f\">\n%s\n</doc>\n\n", i+1, res.Similarity, res.Content))
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
@@ -12,6 +10,7 @@ import (
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
@@ -52,7 +51,7 @@ func (s *commitService) checkWritePermission(ctx context.Context, userID string,
|
||||
return fiber.NewError(fiber.StatusNotFound, "Project not found")
|
||||
}
|
||||
|
||||
lockKey := fmt.Sprintf("project:lock:%s", convert.UUIDToString(projectUUID))
|
||||
lockKey := cache.Key("project:lock", convert.UUIDToString(projectUUID))
|
||||
var lockUser string
|
||||
if err := s.c.Get(ctx, lockKey, &lockUser); err == nil && lockUser != "" && lockUser != userID {
|
||||
return fiber.NewError(fiber.StatusConflict, "Cannot commit: Project is locked by another user who is editing")
|
||||
@@ -141,7 +140,7 @@ func (s *commitService) CreateCommit(ctx context.Context, userID string, project
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("project:id:%s", projectID), fmt.Sprintf("commit:project:%s", projectID))
|
||||
_ = s.c.Del(ctx, cache.Key("project:id", projectID), cache.Key("commit:project", projectID))
|
||||
|
||||
return commit.ToResponse(), nil
|
||||
}
|
||||
@@ -178,7 +177,7 @@ func (s *commitService) RestoreCommit(ctx context.Context, userID string, projec
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to restore commit")
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("project:id:%s", projectID), fmt.Sprintf("commit:project:%s", projectID))
|
||||
_ = s.c.Del(ctx, cache.Key("project:id", projectID), cache.Key("commit:project", projectID))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,13 @@ func (s *entityService) GetEntitiesByGeometryIDs(ctx context.Context, req *reque
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entity IDs by geometry IDs")
|
||||
}
|
||||
|
||||
entityIDMap := make(map[string]struct{})
|
||||
var allEntityIDs []string
|
||||
totalEntityIDs := 0
|
||||
for _, eIDs := range mapping {
|
||||
totalEntityIDs += len(eIDs)
|
||||
}
|
||||
|
||||
entityIDMap := make(map[string]struct{}, totalEntityIDs)
|
||||
allEntityIDs := make([]string, 0, totalEntityIDs)
|
||||
for _, eIDs := range mapping {
|
||||
for _, eID := range eIDs {
|
||||
if _, ok := entityIDMap[eID]; !ok {
|
||||
@@ -125,15 +130,16 @@ func (s *entityService) GetEntitiesByGeometryIDs(ctx context.Context, req *reque
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch entities")
|
||||
}
|
||||
|
||||
entitiesByID := make(map[string]*models.EntityEntity)
|
||||
entitiesByID := make(map[string]*models.EntityEntity, len(entities))
|
||||
for _, e := range entities {
|
||||
entitiesByID[e.ID] = e
|
||||
}
|
||||
|
||||
result := make(map[string][]*response.EntityResponse)
|
||||
result := make(map[string][]*response.EntityResponse, len(req.GeometryIDs))
|
||||
for _, idStr := range req.GeometryIDs {
|
||||
result[idStr] = make([]*response.EntityResponse, 0)
|
||||
if eIDs, exists := mapping[idStr]; exists {
|
||||
eIDs, exists := mapping[idStr]
|
||||
result[idStr] = make([]*response.EntityResponse, 0, len(eIDs))
|
||||
if exists {
|
||||
for _, eID := range eIDs {
|
||||
if e, found := entitiesByID[eID]; found {
|
||||
result[idStr] = append(result[idStr], e.ToResponse())
|
||||
|
||||
@@ -57,6 +57,9 @@ func (s *geometryService) GetGeometriesByBoundWith(ctx context.Context, boundWit
|
||||
|
||||
func (s *geometryService) SearchGeometries(ctx context.Context, req *request.SearchGeometryDto) ([]*response.GeometryResponse, *fiber.Error) {
|
||||
params := sqlc.SearchGeometriesParams{}
|
||||
if req.Limit > 0 {
|
||||
params.LimitCount = int32(req.Limit)
|
||||
}
|
||||
|
||||
if req.MinLng != nil && req.MinLat != nil && req.MaxLng != nil && req.MaxLat != nil {
|
||||
if *req.MaxLng < *req.MinLng || *req.MaxLat < *req.MinLat {
|
||||
@@ -133,8 +136,8 @@ func (s *geometryService) SearchGeometriesByEntityName(
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search geometries by entity name")
|
||||
}
|
||||
|
||||
byEntity := make(map[string]*response.EntityGeometriesSearchItem)
|
||||
order := make([]string, 0)
|
||||
byEntity := make(map[string]*response.EntityGeometriesSearchItem, len(rows))
|
||||
order := make([]string, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
item := byEntity[row.EntityID]
|
||||
@@ -143,7 +146,7 @@ func (s *geometryService) SearchGeometriesByEntityName(
|
||||
EntityID: row.EntityID,
|
||||
Name: row.EntityName,
|
||||
Description: row.EntityDescription,
|
||||
Geometries: make([]*response.EntityGeometrySearchGeo, 0),
|
||||
Geometries: make([]*response.EntityGeometrySearchGeo, 0, 1),
|
||||
}
|
||||
byEntity[row.EntityID] = item
|
||||
order = append(order, row.EntityID)
|
||||
|
||||
@@ -70,7 +70,7 @@ func (s *goongService) ProxyRequest(ctx context.Context, method string, targetUR
|
||||
parsedUrl.RawQuery = q.Encode()
|
||||
finalURL := parsedUrl.String()
|
||||
|
||||
cacheKey := fmt.Sprintf("goong_proxy_v2:%s", finalURL)
|
||||
cacheKey := cache.Key("goong_proxy_v2", finalURL)
|
||||
|
||||
if method == http.MethodGet {
|
||||
var entry CacheEntry
|
||||
@@ -180,7 +180,7 @@ func (s *goongService) ProxyRequest(ctx context.Context, method string, targetUR
|
||||
Msg("Goong Map proxy request returned non-200 status code")
|
||||
}
|
||||
|
||||
respHeaders := make(map[string]string)
|
||||
respHeaders := make(map[string]string, len(resp.Header))
|
||||
for k, v := range resp.Header {
|
||||
lowerK := strings.ToLower(k)
|
||||
// Skip hop-by-hop/transport headers in response
|
||||
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
"history-api/pkg/storage"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -98,8 +98,8 @@ func (m *mediaService) BulkDeleteMedia(ctx context.Context, claims *response.JWT
|
||||
if slices.Contains(claims.Roles, constants.RoleTypeAdmin) || slices.Contains(claims.Roles, constants.RoleTypeMod) {
|
||||
shoudDelete = true
|
||||
}
|
||||
listMediaIds := make([]pgtype.UUID, 0)
|
||||
listMediaStorageEntities := make([]*models.MediaStorageEntity, 0)
|
||||
listMediaIds := make([]pgtype.UUID, 0, len(listMedia))
|
||||
listMediaStorageEntities := make([]*models.MediaStorageEntity, 0, len(listMedia))
|
||||
for _, media := range listMedia {
|
||||
if media.UserID != claims.UId && !shoudDelete {
|
||||
return fiber.NewError(fiber.StatusForbidden, "You don't have permission to delete media "+media.ID)
|
||||
@@ -171,6 +171,7 @@ func (m *mediaService) fillSearchArgs(arg *sqlc.SearchMediasParams, dto *request
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
arg.UserIds = make([]pgtype.UUID, 0, len(dto.UserIDs))
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
@@ -87,7 +86,7 @@ func (s *projectService) GetProjectByID(ctx context.Context, id string) (*respon
|
||||
}
|
||||
|
||||
res := project.ToResponse()
|
||||
lockKey := fmt.Sprintf("project:lock:%s", id)
|
||||
lockKey := cache.Key("project:lock", id)
|
||||
var lockUser string
|
||||
if err := s.c.Get(ctx, lockKey, &lockUser); err == nil && lockUser != "" {
|
||||
res.LockedBy = &lockUser
|
||||
@@ -141,6 +140,7 @@ func (s *projectService) fillSearchArgs(arg *sqlc.SearchProjectsParams, dto *req
|
||||
}
|
||||
|
||||
if len(dto.Statuses) > 0 {
|
||||
arg.Statuses = make([]int16, 0, len(dto.Statuses))
|
||||
for _, statusStr := range dto.Statuses {
|
||||
statusType := constants.ParseProjectStatusTypeText(statusStr)
|
||||
if statusType != constants.ProjectStatusTypeUnknow {
|
||||
@@ -150,6 +150,7 @@ func (s *projectService) fillSearchArgs(arg *sqlc.SearchProjectsParams, dto *req
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
arg.UserIds = make([]pgtype.UUID, 0, len(dto.UserIDs))
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
@@ -464,7 +465,7 @@ func (s *projectService) LockProject(ctx context.Context, userID string, project
|
||||
return fiber.NewError(fiber.StatusNotFound, "Project not found")
|
||||
}
|
||||
|
||||
lockKey := fmt.Sprintf("project:lock:%s", projectID)
|
||||
lockKey := cache.Key("project:lock", projectID)
|
||||
var lockUser string
|
||||
err = s.c.Get(ctx, lockKey, &lockUser)
|
||||
if err == nil && lockUser != "" {
|
||||
@@ -487,7 +488,7 @@ func (s *projectService) LockProject(ctx context.Context, userID string, project
|
||||
}
|
||||
|
||||
func (s *projectService) UnlockProject(ctx context.Context, userID string, projectID string) *fiber.Error {
|
||||
lockKey := fmt.Sprintf("project:lock:%s", projectID)
|
||||
lockKey := cache.Key("project:lock", projectID)
|
||||
var lockUser string
|
||||
err := s.c.Get(ctx, lockKey, &lockUser)
|
||||
if err != nil || lockUser == "" {
|
||||
@@ -503,7 +504,7 @@ func (s *projectService) UnlockProject(ctx context.Context, userID string, proje
|
||||
}
|
||||
|
||||
func (s *projectService) HeartbeatProject(ctx context.Context, userID string, projectID string) *fiber.Error {
|
||||
lockKey := fmt.Sprintf("project:lock:%s", projectID)
|
||||
lockKey := cache.Key("project:lock", projectID)
|
||||
var lockUser string
|
||||
err := s.c.Get(ctx, lockKey, &lockUser)
|
||||
if err != nil || lockUser == "" {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
type RasterTileService interface {
|
||||
GetMetadata(ctx context.Context) (map[string]string, *fiber.Error)
|
||||
GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error)
|
||||
GetTile(ctx context.Context, z, x, y int) (TileResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
type rasterTileService struct {
|
||||
@@ -32,26 +32,26 @@ func (t *rasterTileService) GetMetadata(ctx context.Context) (map[string]string,
|
||||
return metaData, nil
|
||||
}
|
||||
|
||||
|
||||
func (t *rasterTileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) {
|
||||
contentType := make(map[string]string)
|
||||
|
||||
func (t *rasterTileService) GetTile(ctx context.Context, z, x, y int) (TileResponse, *fiber.Error) {
|
||||
data, format, err := t.tileRepo.GetTile(ctx, z, x, y)
|
||||
if err != nil {
|
||||
return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data")
|
||||
return TileResponse{}, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data")
|
||||
}
|
||||
|
||||
res := TileResponse{
|
||||
Data: data,
|
||||
CacheControl: tileCacheControl,
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "png":
|
||||
contentType["Content-Type"] = "image/png"
|
||||
res.ContentType = "image/png"
|
||||
case "jpg", "jpeg":
|
||||
contentType["Content-Type"] = "image/jpeg"
|
||||
res.ContentType = "image/jpeg"
|
||||
case "webp":
|
||||
contentType["Content-Type"] = "image/webp"
|
||||
res.ContentType = "image/webp"
|
||||
default:
|
||||
contentType["Content-Type"] = "application/octet-stream"
|
||||
res.ContentType = "application/octet-stream"
|
||||
}
|
||||
|
||||
return data, contentType, nil
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"history-api/pkg/cache"
|
||||
"history-api/pkg/constants"
|
||||
"history-api/pkg/convert"
|
||||
json "history-api/pkg/jsonx"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -110,8 +110,8 @@ func (s *submissionService) CreateSubmission(ctx context.Context, userID string,
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to parse commit snapshot")
|
||||
}
|
||||
|
||||
var entitySlugs []string
|
||||
entitySlugToID := make(map[string]string)
|
||||
entitySlugs := make([]string, 0, len(snapshotData.Entities))
|
||||
entitySlugToID := make(map[string]string, len(snapshotData.Entities))
|
||||
for _, entity := range snapshotData.Entities {
|
||||
if entity.Source == "inline" && entity.Slug != nil {
|
||||
entitySlugs = append(entitySlugs, *entity.Slug)
|
||||
@@ -133,8 +133,8 @@ func (s *submissionService) CreateSubmission(ctx context.Context, userID string,
|
||||
}
|
||||
}
|
||||
|
||||
var wikiSlugs []string
|
||||
wikiSlugToID := make(map[string]string)
|
||||
wikiSlugs := make([]string, 0, len(snapshotData.Wikis))
|
||||
wikiSlugToID := make(map[string]string, len(snapshotData.Wikis))
|
||||
for _, wiki := range snapshotData.Wikis {
|
||||
if wiki.Source == "inline" && wiki.Slug != nil {
|
||||
wikiSlugs = append(wikiSlugs, *wiki.Slug)
|
||||
@@ -180,7 +180,7 @@ func (s *submissionService) CreateSubmission(ctx context.Context, userID string,
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create submission")
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("project:id:%s", project.ID))
|
||||
_ = s.c.Del(ctx, cache.Key("project:id", project.ID))
|
||||
|
||||
return submission.ToResponse(), nil
|
||||
}
|
||||
@@ -277,11 +277,11 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx,
|
||||
fmt.Sprintf("project:id:%s", submission.ProjectID),
|
||||
fmt.Sprintf("entity:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("geometry:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("wiki:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("battle_replay:project:%s", submission.ProjectID),
|
||||
cache.Key("project:id", submission.ProjectID),
|
||||
cache.Key("entity:project", submission.ProjectID),
|
||||
cache.Key("geometry:project", submission.ProjectID),
|
||||
cache.Key("wiki:project", submission.ProjectID),
|
||||
cache.Key("battle_replay:project", submission.ProjectID),
|
||||
)
|
||||
|
||||
return updatedSubmission.ToResponse(), nil
|
||||
@@ -300,6 +300,7 @@ func (m *submissionService) fillSearchArgs(arg *sqlc.SearchSubmissionsParams, dt
|
||||
}
|
||||
|
||||
if len(dto.Statuses) > 0 {
|
||||
arg.Statuses = make([]int16, 0, len(dto.Statuses))
|
||||
for _, id := range dto.Statuses {
|
||||
if u := constants.ParseStatusTypeText(id); u != constants.StatusTypeUnknown {
|
||||
arg.Statuses = append(arg.Statuses, u.Int16())
|
||||
@@ -308,6 +309,7 @@ func (m *submissionService) fillSearchArgs(arg *sqlc.SearchSubmissionsParams, dt
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
arg.UserIds = make([]pgtype.UUID, 0, len(dto.UserIDs))
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
@@ -503,11 +505,11 @@ func (s *submissionService) DeleteSubmission(ctx context.Context, userID string,
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx,
|
||||
fmt.Sprintf("project:id:%s", submission.ProjectID),
|
||||
fmt.Sprintf("entity:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("geometry:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("wiki:project:%s", submission.ProjectID),
|
||||
fmt.Sprintf("battle_replay:project:%s", submission.ProjectID),
|
||||
cache.Key("project:id", submission.ProjectID),
|
||||
cache.Key("entity:project", submission.ProjectID),
|
||||
cache.Key("geometry:project", submission.ProjectID),
|
||||
cache.Key("wiki:project", submission.ProjectID),
|
||||
cache.Key("battle_replay:project", submission.ProjectID),
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -521,10 +523,10 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
|
||||
projectIDStr := convert.UUIDToString(projectUUID)
|
||||
_ = s.c.Del(ctx,
|
||||
fmt.Sprintf("entity:project:%s", projectIDStr),
|
||||
fmt.Sprintf("geometry:project:%s", projectIDStr),
|
||||
fmt.Sprintf("wiki:project:%s", projectIDStr),
|
||||
fmt.Sprintf("battle_replay:project:%s", projectIDStr),
|
||||
cache.Key("entity:project", projectIDStr),
|
||||
cache.Key("geometry:project", projectIDStr),
|
||||
cache.Key("wiki:project", projectIDStr),
|
||||
cache.Key("battle_replay:project", projectIDStr),
|
||||
)
|
||||
|
||||
currentEntity, err := entityRepo.GetByProjectID(ctx, projectUUID)
|
||||
@@ -547,44 +549,44 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
return fiber.NewError(fiber.StatusNotFound, "Battle replay not found: "+err.Error())
|
||||
}
|
||||
|
||||
persistEntityIDs := make(map[string]struct{})
|
||||
persistEntityIDs := make(map[string]struct{}, len(snapshotData.Entities))
|
||||
for _, item := range snapshotData.Entities {
|
||||
persistEntityIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistGeometryIDs := make(map[string]struct{})
|
||||
persistGeometryIDs := make(map[string]struct{}, len(snapshotData.Geometries))
|
||||
for _, item := range snapshotData.Geometries {
|
||||
persistGeometryIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistWikiIDs := make(map[string]struct{})
|
||||
persistWikiIDs := make(map[string]struct{}, len(snapshotData.Wikis))
|
||||
for _, item := range snapshotData.Wikis {
|
||||
persistWikiIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistReplayIDs := make(map[string]struct{})
|
||||
persistReplayIDs := make(map[string]struct{}, len(snapshotData.Replays))
|
||||
for _, item := range snapshotData.Replays {
|
||||
persistReplayIDs[item.ID] = struct{}{}
|
||||
}
|
||||
|
||||
persistCurrentEntityIDs := make(map[string]struct{})
|
||||
persistCurrentEntityIDs := make(map[string]struct{}, len(currentEntity))
|
||||
for _, item := range currentEntity {
|
||||
persistCurrentEntityIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistCurrentGeometryIDs := make(map[string]struct{})
|
||||
persistCurrentGeometryIDs := make(map[string]struct{}, len(currentGeometry))
|
||||
for _, item := range currentGeometry {
|
||||
persistCurrentGeometryIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistCurrentWikiIDs := make(map[string]struct{})
|
||||
persistCurrentWikiIDs := make(map[string]struct{}, len(currentWiki))
|
||||
for _, item := range currentWiki {
|
||||
persistCurrentWikiIDs[item.ID] = struct{}{}
|
||||
}
|
||||
persistCurrentReplayIDs := make(map[string]struct{})
|
||||
persistCurrentReplayIDs := make(map[string]struct{}, len(currentBattleReplay))
|
||||
for _, item := range currentBattleReplay {
|
||||
persistCurrentReplayIDs[item.ID] = struct{}{}
|
||||
}
|
||||
|
||||
listDeleteEntities := make([]pgtype.UUID, 0)
|
||||
listDeleteWikis := make([]pgtype.UUID, 0)
|
||||
listDeleteGeometries := make([]pgtype.UUID, 0)
|
||||
listDeleteBattleReplays := make([]pgtype.UUID, 0)
|
||||
listDeleteEntities := make([]pgtype.UUID, 0, len(currentEntity))
|
||||
listDeleteWikis := make([]pgtype.UUID, 0, len(currentWiki))
|
||||
listDeleteGeometries := make([]pgtype.UUID, 0, len(currentGeometry))
|
||||
listDeleteBattleReplays := make([]pgtype.UUID, 0, len(currentBattleReplay))
|
||||
|
||||
for _, e := range currentEntity {
|
||||
if _, ok := persistEntityIDs[e.ID]; !ok {
|
||||
@@ -654,7 +656,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
}
|
||||
|
||||
refEntityIDs := []string{}
|
||||
refEntityIDs := make([]string, 0, len(snapshotData.Entities))
|
||||
for _, e := range snapshotData.Entities {
|
||||
if e.Source == "ref" {
|
||||
refEntityIDs = append(refEntityIDs, e.ID)
|
||||
@@ -662,7 +664,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
|
||||
refEntities, _ := entityRepo.GetByIDs(ctx, refEntityIDs)
|
||||
refEntityMap := make(map[string]bool)
|
||||
refEntityMap := make(map[string]bool, len(refEntities))
|
||||
for _, e := range refEntities {
|
||||
refEntityMap[e.ID] = true
|
||||
}
|
||||
@@ -722,19 +724,19 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
snapshotData.Entities = newEntities
|
||||
|
||||
refGeometryIDs := []string{}
|
||||
refGeometryIDs := make([]string, 0, len(snapshotData.Geometries))
|
||||
for _, g := range snapshotData.Geometries {
|
||||
if g.Source == "ref" {
|
||||
refGeometryIDs = append(refGeometryIDs, g.ID)
|
||||
}
|
||||
}
|
||||
refGeometries, _ := geometryRepo.GetByIDs(ctx, refGeometryIDs)
|
||||
refGeometryMap := make(map[string]bool)
|
||||
refGeometryMap := make(map[string]bool, len(refGeometries))
|
||||
for _, g := range refGeometries {
|
||||
refGeometryMap[g.ID] = true
|
||||
}
|
||||
|
||||
validGeometries := make(map[string]bool)
|
||||
validGeometries := make(map[string]bool, len(snapshotData.Geometries))
|
||||
for _, g := range snapshotData.Geometries {
|
||||
if g.Operation != "delete" {
|
||||
validGeometries[g.ID] = true
|
||||
@@ -851,14 +853,14 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
}
|
||||
|
||||
refWikiIDs := []string{}
|
||||
refWikiIDs := make([]string, 0, len(snapshotData.Wikis))
|
||||
for _, w := range snapshotData.Wikis {
|
||||
if w.Source == "ref" {
|
||||
refWikiIDs = append(refWikiIDs, w.ID)
|
||||
}
|
||||
}
|
||||
refWikis, _ := wikiRepo.GetByIDs(ctx, refWikiIDs)
|
||||
refWikiMap := make(map[string]bool)
|
||||
refWikiMap := make(map[string]bool, len(refWikis))
|
||||
for _, w := range refWikis {
|
||||
refWikiMap[w.ID] = true
|
||||
}
|
||||
@@ -906,7 +908,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki content: "+err.Error())
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wikiUUID.String()), fmt.Sprintf("wiki:slug:%s", *wiki.Slug))
|
||||
_ = s.c.Del(ctx, cache.Key("wiki:id", wikiUUID.String()), cache.Key("wiki:slug", *wiki.Slug))
|
||||
|
||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||
|
||||
@@ -936,7 +938,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki content: "+err.Error())
|
||||
}
|
||||
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wikiUUID.String()), fmt.Sprintf("wiki:slug:%s", *wiki.Slug))
|
||||
_ = s.c.Del(ctx, cache.Key("wiki:id", wikiUUID.String()), cache.Key("wiki:slug", *wiki.Slug))
|
||||
|
||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||
|
||||
@@ -998,17 +1000,17 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete wiki entity: "+err.Error())
|
||||
}
|
||||
|
||||
validEntities := make(map[string]bool)
|
||||
validEntities := make(map[string]bool, len(snapshotData.Entities))
|
||||
for _, e := range snapshotData.Entities {
|
||||
validEntities[e.ID] = true
|
||||
}
|
||||
validWikis := make(map[string]bool)
|
||||
validWikis := make(map[string]bool, len(snapshotData.Wikis))
|
||||
for _, w := range snapshotData.Wikis {
|
||||
validWikis[w.ID] = true
|
||||
}
|
||||
|
||||
if len(snapshotData.GeometryEntity) > 0 {
|
||||
geomLinks := make(map[string][]pgtype.UUID)
|
||||
geomLinks := make(map[string][]pgtype.UUID, len(snapshotData.GeometryEntity))
|
||||
for _, link := range snapshotData.GeometryEntity {
|
||||
if link.Operation == "delete" {
|
||||
continue
|
||||
@@ -1034,7 +1036,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
|
||||
if len(snapshotData.EntityWiki) > 0 {
|
||||
wikiLinks := make(map[string][]pgtype.UUID)
|
||||
wikiLinks := make(map[string][]pgtype.UUID, len(snapshotData.EntityWiki))
|
||||
for _, link := range snapshotData.EntityWiki {
|
||||
if link.Operation == "delete" || (link.IsDeleted != nil && *link.IsDeleted == 1) {
|
||||
continue
|
||||
@@ -1059,8 +1061,8 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
}
|
||||
}
|
||||
|
||||
wikiDeleteIDs := make([]string, 0)
|
||||
entityDeleteIDs := make([]string, 0)
|
||||
wikiDeleteIDs := make([]string, 0, len(listDeleteWikis)+len(snapshotData.Wikis))
|
||||
entityDeleteIDs := make([]string, 0, len(listDeleteEntities)+len(snapshotData.Entities))
|
||||
|
||||
for _, id := range listDeleteWikis {
|
||||
wikiDeleteIDs = append(wikiDeleteIDs, convert.UUIDToString(id))
|
||||
@@ -1084,6 +1086,8 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec
|
||||
ProjectID: convert.UUIDToString(projectUUID),
|
||||
DeleteWikiIDs: wikiDeleteIDs,
|
||||
DeleteEntityIDs: entityDeleteIDs,
|
||||
Wikis: make([]*models.RagWikiItem, 0, len(snapshotData.Wikis)),
|
||||
Entities: make([]*models.RagEntityItem, 0, len(snapshotData.Entities)),
|
||||
}
|
||||
|
||||
for _, wiki := range snapshotData.Wikis {
|
||||
@@ -1118,10 +1122,10 @@ func (s *submissionService) clearProjectItems(ctx context.Context, tx pgx.Tx, pr
|
||||
|
||||
projectIDStr := convert.UUIDToString(projectUUID)
|
||||
_ = s.c.Del(ctx,
|
||||
fmt.Sprintf("entity:project:%s", projectIDStr),
|
||||
fmt.Sprintf("geometry:project:%s", projectIDStr),
|
||||
fmt.Sprintf("wiki:project:%s", projectIDStr),
|
||||
fmt.Sprintf("battle_replay:project:%s", projectIDStr),
|
||||
cache.Key("entity:project", projectIDStr),
|
||||
cache.Key("geometry:project", projectIDStr),
|
||||
cache.Key("wiki:project", projectIDStr),
|
||||
cache.Key("battle_replay:project", projectIDStr),
|
||||
)
|
||||
|
||||
currentEntity, _ := entityRepo.GetByProjectID(ctx, projectUUID)
|
||||
@@ -1129,28 +1133,28 @@ func (s *submissionService) clearProjectItems(ctx context.Context, tx pgx.Tx, pr
|
||||
currentWiki, _ := wikiRepo.GetByProjectID(ctx, projectUUID)
|
||||
currentBattleReplay, _ := battleReplayRepo.GetByProjectID(ctx, projectUUID)
|
||||
|
||||
var entityIDs []pgtype.UUID
|
||||
entityIDs := make([]pgtype.UUID, 0, len(currentEntity))
|
||||
for _, e := range currentEntity {
|
||||
id, err := convert.StringToUUID(e.ID)
|
||||
if err == nil {
|
||||
entityIDs = append(entityIDs, id)
|
||||
}
|
||||
}
|
||||
var geometryIDs []pgtype.UUID
|
||||
geometryIDs := make([]pgtype.UUID, 0, len(currentGeometry))
|
||||
for _, g := range currentGeometry {
|
||||
id, err := convert.StringToUUID(g.ID)
|
||||
if err == nil {
|
||||
geometryIDs = append(geometryIDs, id)
|
||||
}
|
||||
}
|
||||
var wikiIDs []pgtype.UUID
|
||||
wikiIDs := make([]pgtype.UUID, 0, len(currentWiki))
|
||||
for _, w := range currentWiki {
|
||||
id, err := convert.StringToUUID(w.ID)
|
||||
if err == nil {
|
||||
wikiIDs = append(wikiIDs, id)
|
||||
}
|
||||
}
|
||||
var replayIDs []pgtype.UUID
|
||||
replayIDs := make([]pgtype.UUID, 0, len(currentBattleReplay))
|
||||
for _, br := range currentBattleReplay {
|
||||
id, err := convert.StringToUUID(br.ID)
|
||||
if err == nil {
|
||||
@@ -1161,7 +1165,7 @@ func (s *submissionService) clearProjectItems(ctx context.Context, tx pgx.Tx, pr
|
||||
if len(entityIDs) > 0 {
|
||||
_ = entityRepo.DeleteByIDs(ctx, entityIDs)
|
||||
for _, e := range currentEntity {
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("entity:slug:%s", e.Slug))
|
||||
_ = s.c.Del(ctx, cache.Key("entity:slug", e.Slug))
|
||||
}
|
||||
}
|
||||
if len(geometryIDs) > 0 {
|
||||
@@ -1170,7 +1174,7 @@ func (s *submissionService) clearProjectItems(ctx context.Context, tx pgx.Tx, pr
|
||||
if len(wikiIDs) > 0 {
|
||||
_ = wikiRepo.DeleteByIDs(ctx, wikiIDs)
|
||||
for _, w := range currentWiki {
|
||||
_ = s.c.Del(ctx, fmt.Sprintf("wiki:slug:%s", w.Slug))
|
||||
_ = s.c.Del(ctx, cache.Key("wiki:slug", w.Slug))
|
||||
}
|
||||
}
|
||||
if len(replayIDs) > 0 {
|
||||
@@ -1180,11 +1184,11 @@ func (s *submissionService) clearProjectItems(ctx context.Context, tx pgx.Tx, pr
|
||||
_ = geometryRepo.DeleteEntityGeometriesByProjectID(ctx, projectUUID)
|
||||
_ = wikiRepo.DeleteEntityWikisByProjectID(ctx, projectUUID)
|
||||
|
||||
var entityDeleteIDs []string
|
||||
entityDeleteIDs := make([]string, 0, len(currentEntity))
|
||||
for _, e := range currentEntity {
|
||||
entityDeleteIDs = append(entityDeleteIDs, e.ID)
|
||||
}
|
||||
var wikiDeleteIDs []string
|
||||
wikiDeleteIDs := make([]string, 0, len(currentWiki))
|
||||
for _, w := range currentWiki {
|
||||
wikiDeleteIDs = append(wikiDeleteIDs, w.ID)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,18 @@ import (
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
const tileCacheControl = "public, max-age=31536000, immutable"
|
||||
|
||||
type TileResponse struct {
|
||||
Data []byte
|
||||
ContentType string
|
||||
ContentEncoding string
|
||||
CacheControl string
|
||||
}
|
||||
|
||||
type TileService interface {
|
||||
GetMetadata(ctx context.Context) (map[string]string, *fiber.Error)
|
||||
GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error)
|
||||
GetTile(ctx context.Context, z, x, y int) (TileResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
type tileService struct {
|
||||
@@ -32,29 +41,30 @@ func (t *tileService) GetMetadata(ctx context.Context) (map[string]string, *fibe
|
||||
return metaData, nil
|
||||
}
|
||||
|
||||
|
||||
func (t *tileService) GetTile(ctx context.Context, z, x, y int) ([]byte, map[string]string, *fiber.Error) {
|
||||
contentType := make(map[string]string)
|
||||
|
||||
func (t *tileService) GetTile(ctx context.Context, z, x, y int) (TileResponse, *fiber.Error) {
|
||||
data, format, isPBF, err := t.tileRepo.GetTile(ctx, z, x, y)
|
||||
if err != nil {
|
||||
return nil, contentType, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data")
|
||||
return TileResponse{}, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch tile data")
|
||||
}
|
||||
|
||||
res := TileResponse{
|
||||
Data: data,
|
||||
CacheControl: tileCacheControl,
|
||||
}
|
||||
switch format {
|
||||
case "pbf":
|
||||
contentType["Content-Type"] = "application/x-protobuf"
|
||||
res.ContentType = "application/x-protobuf"
|
||||
case "png":
|
||||
contentType["Content-Type"] = "image/png"
|
||||
res.ContentType = "image/png"
|
||||
case "jpg", "jpeg":
|
||||
contentType["Content-Type"] = "image/jpeg"
|
||||
res.ContentType = "image/jpeg"
|
||||
default:
|
||||
contentType["Content-Type"] = "application/octet-stream"
|
||||
res.ContentType = "application/octet-stream"
|
||||
}
|
||||
|
||||
if isPBF {
|
||||
contentType["Content-Encoding"] = "gzip"
|
||||
res.ContentEncoding = "gzip"
|
||||
}
|
||||
|
||||
return data, contentType, nil
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
@@ -103,7 +102,7 @@ func (u *userService) CreateUser(ctx context.Context, dto *request.CreateUserDto
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create user profile")
|
||||
}
|
||||
|
||||
var roleIdList []pgtype.UUID
|
||||
roleIdList := make([]pgtype.UUID, 0, len(dto.Roles))
|
||||
for _, rId := range dto.Roles {
|
||||
rid, err := convert.StringToUUID(rId)
|
||||
if err == nil {
|
||||
@@ -339,8 +338,8 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
}
|
||||
}
|
||||
|
||||
user.Roles = make([]*models.RoleSimple, 0)
|
||||
roleIdList := make([]pgtype.UUID, 0)
|
||||
user.Roles = make([]*models.RoleSimple, 0, len(newListRole))
|
||||
roleIdList := make([]pgtype.UUID, 0, len(newListRole))
|
||||
for _, role := range newListRole {
|
||||
roleID, err := convert.StringToUUID(role.ID)
|
||||
if err != nil {
|
||||
@@ -378,8 +377,8 @@ func (u *userService) ChangeRoleUser(ctx context.Context, userId string, claims
|
||||
}
|
||||
|
||||
mapCache := map[string]any{
|
||||
fmt.Sprintf("user:email:%s", user.Email): user,
|
||||
fmt.Sprintf("user:id:%s", user.ID): user,
|
||||
cache.Key("user:email", user.Email): user,
|
||||
cache.Key("user:id", user.ID): user,
|
||||
}
|
||||
_ = u.c.MSet(ctx, mapCache, constants.NormalCacheDuration)
|
||||
|
||||
@@ -489,6 +488,7 @@ func (m *userService) fillSearchArgs(arg *sqlc.SearchUsersParams, dto *request.S
|
||||
}
|
||||
|
||||
if len(dto.RoleIDs) > 0 {
|
||||
arg.RoleIds = make([]pgtype.UUID, 0, len(dto.RoleIDs))
|
||||
for _, id := range dto.RoleIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.RoleIds = append(arg.RoleIds, u)
|
||||
|
||||
@@ -2,7 +2,6 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
@@ -92,7 +91,8 @@ func (v *verificationService) CreateVerification(ctx context.Context, userId str
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Invalid verification ID")
|
||||
}
|
||||
|
||||
mediaIdList := make([]pgtype.UUID, 0)
|
||||
mediaIdList := make([]pgtype.UUID, 0, len(mediaList))
|
||||
item.Media = make([]*models.MediaSimpleEntity, 0, len(mediaList))
|
||||
for _, it := range mediaList {
|
||||
mediaId, err := convert.StringToUUID(it.ID)
|
||||
if err != nil {
|
||||
@@ -185,6 +185,7 @@ func (m *verificationService) fillSearchArgs(arg *sqlc.SearchUserVerificationsPa
|
||||
}
|
||||
|
||||
if len(dto.Statuses) > 0 {
|
||||
arg.Statuses = make([]int16, 0, len(dto.Statuses))
|
||||
for _, id := range dto.Statuses {
|
||||
if u := constants.ParseStatusTypeText(id); u != constants.StatusTypeUnknown {
|
||||
arg.Statuses = append(arg.Statuses, u.Int16())
|
||||
@@ -193,6 +194,7 @@ func (m *verificationService) fillSearchArgs(arg *sqlc.SearchUserVerificationsPa
|
||||
}
|
||||
|
||||
if len(dto.VerifyTypes) > 0 {
|
||||
arg.VerifyTypes = make([]int16, 0, len(dto.VerifyTypes))
|
||||
for _, id := range dto.VerifyTypes {
|
||||
if u := constants.ParseVerifyTypeText(id); u != constants.VerifyTypeUnknown {
|
||||
arg.VerifyTypes = append(arg.VerifyTypes, u.Int16())
|
||||
@@ -201,6 +203,7 @@ func (m *verificationService) fillSearchArgs(arg *sqlc.SearchUserVerificationsPa
|
||||
}
|
||||
|
||||
if len(dto.UserIDs) > 0 {
|
||||
arg.UserIds = make([]pgtype.UUID, 0, len(dto.UserIDs))
|
||||
for _, id := range dto.UserIDs {
|
||||
if u, err := convert.StringToUUID(id); err == nil {
|
||||
arg.UserIds = append(arg.UserIds, u)
|
||||
@@ -355,8 +358,8 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
}
|
||||
|
||||
if statusType == constants.StatusTypeApproved {
|
||||
roleIdList := make([]pgtype.UUID, 0)
|
||||
userVerification.Roles = append(userVerification.Roles, historianRole.ToRoleSimple())
|
||||
roleIdList := make([]pgtype.UUID, 0, len(userVerification.Roles)+1)
|
||||
|
||||
roleIdList = append(roleIdList, historianRoleID)
|
||||
|
||||
@@ -395,8 +398,8 @@ func (v *verificationService) UpdateStatusVerification(ctx context.Context, user
|
||||
}
|
||||
|
||||
mapCache := map[string]any{
|
||||
fmt.Sprintf("user:email:%s", userVerification.Email): userVerification,
|
||||
fmt.Sprintf("user:id:%s", userVerification.ID): userVerification,
|
||||
cache.Key("user:email", userVerification.Email): userVerification,
|
||||
cache.Key("user:id", userVerification.ID): userVerification,
|
||||
}
|
||||
_ = v.c.MSet(ctx, mapCache, constants.NormalCacheDuration)
|
||||
} else {
|
||||
|
||||
@@ -137,8 +137,13 @@ func (s *wikiService) GetWikisByEntityIDs(ctx context.Context, req *request.GetW
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch wiki IDs by entity IDs")
|
||||
}
|
||||
|
||||
wikiIDMap := make(map[string]struct{})
|
||||
var allWikiIDs []string
|
||||
totalWikiIDs := 0
|
||||
for _, wIDs := range mapping {
|
||||
totalWikiIDs += len(wIDs)
|
||||
}
|
||||
|
||||
wikiIDMap := make(map[string]struct{}, totalWikiIDs)
|
||||
allWikiIDs := make([]string, 0, totalWikiIDs)
|
||||
for _, wIDs := range mapping {
|
||||
for _, wID := range wIDs {
|
||||
if _, ok := wikiIDMap[wID]; !ok {
|
||||
@@ -153,15 +158,16 @@ func (s *wikiService) GetWikisByEntityIDs(ctx context.Context, req *request.GetW
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch wikis")
|
||||
}
|
||||
|
||||
wikisByID := make(map[string]*models.WikiEntity)
|
||||
wikisByID := make(map[string]*models.WikiEntity, len(wikis))
|
||||
for _, w := range wikis {
|
||||
wikisByID[w.ID] = w
|
||||
}
|
||||
|
||||
result := make(map[string][]*response.WikiResponse)
|
||||
result := make(map[string][]*response.WikiResponse, len(req.EntityIDs))
|
||||
for _, idStr := range req.EntityIDs {
|
||||
result[idStr] = make([]*response.WikiResponse, 0)
|
||||
if wIDs, exists := mapping[idStr]; exists {
|
||||
wIDs, exists := mapping[idStr]
|
||||
result[idStr] = make([]*response.WikiResponse, 0, len(wIDs))
|
||||
if exists {
|
||||
for _, wID := range wIDs {
|
||||
if w, found := wikisByID[wID]; found {
|
||||
result[idStr] = append(result[idStr], w.ToResponse())
|
||||
@@ -179,7 +185,7 @@ func (s *wikiService) GetWikiContentsPreviewByIDs(ctx context.Context, req *requ
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch wiki contents")
|
||||
}
|
||||
|
||||
var results []*response.WikiContentPreviewResponse
|
||||
results := make([]*response.WikiContentPreviewResponse, 0, len(contents))
|
||||
for _, c := range contents {
|
||||
results = append(results, &response.WikiContentPreviewResponse{
|
||||
ID: c.ID,
|
||||
|
||||
+3
-2
@@ -19,6 +19,8 @@ type RagUtils struct {
|
||||
embedder *embeddings.EmbedderImpl
|
||||
}
|
||||
|
||||
var htmlTagRegex = regexp.MustCompile(`<[^>]*>`)
|
||||
|
||||
func NewRagUtils() (*RagUtils, error) {
|
||||
openRouterAPIKey, err := config.GetConfig("OPEN_ROUTER_API")
|
||||
if err != nil {
|
||||
@@ -57,8 +59,7 @@ func NewRagUtils() (*RagUtils, error) {
|
||||
}
|
||||
|
||||
func (u *RagUtils) StripHTML(text string) string {
|
||||
re := regexp.MustCompile(`<[^>]*>`)
|
||||
text = re.ReplaceAllString(text, " ")
|
||||
text = htmlTagRegex.ReplaceAllString(text, " ")
|
||||
return html.UnescapeString(text)
|
||||
}
|
||||
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
|
||||
"history-api/pkg/jsonx"
|
||||
)
|
||||
|
||||
func Key(prefix, id string) string {
|
||||
return prefix + ":" + id
|
||||
}
|
||||
|
||||
func Key2(prefix, first, second string) string {
|
||||
return prefix + ":" + first + ":" + second
|
||||
}
|
||||
|
||||
func QueryKey(prefix string, params any) string {
|
||||
data, _ := jsonx.Marshal(params)
|
||||
return prefix + ":" + strconv.FormatUint(xxhash.Sum64(data), 16)
|
||||
}
|
||||
Vendored
+30
-6
@@ -2,10 +2,11 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/pkg/config"
|
||||
"history-api/pkg/constants"
|
||||
json "history-api/pkg/jsonx"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -27,16 +28,39 @@ type RedisClient struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
func defaultRedisPoolSize() int {
|
||||
poolSize := runtime.NumCPU() * 32
|
||||
if poolSize < 64 {
|
||||
return 64
|
||||
}
|
||||
if poolSize > 256 {
|
||||
return 256
|
||||
}
|
||||
return poolSize
|
||||
}
|
||||
|
||||
func NewRedisClient() (Cache, error) {
|
||||
uri, err := config.GetConfig("REDIS_CONNECTION_URI")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
poolSize := config.GetIntConfigWithDefault("REDIS_POOL_SIZE", defaultRedisPoolSize())
|
||||
if poolSize < 1 {
|
||||
poolSize = defaultRedisPoolSize()
|
||||
}
|
||||
minIdleConns := config.GetIntConfigWithDefault("REDIS_MIN_IDLE_CONNS", poolSize/8)
|
||||
if minIdleConns < 0 {
|
||||
minIdleConns = 0
|
||||
}
|
||||
if minIdleConns > poolSize {
|
||||
minIdleConns = poolSize
|
||||
}
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: uri,
|
||||
PoolSize: 500,
|
||||
MinIdleConns: 50,
|
||||
PoolSize: poolSize,
|
||||
MinIdleConns: minIdleConns,
|
||||
DialTimeout: 5 * time.Second,
|
||||
ReadTimeout: 3 * time.Second,
|
||||
WriteTimeout: 3 * time.Second,
|
||||
@@ -83,8 +107,8 @@ func (r *RedisClient) DelByPattern(ctx context.Context, pattern string) error {
|
||||
|
||||
if len(keys) > 0 {
|
||||
if err := r.client.Unlink(ctx, keys...).Err(); err != nil {
|
||||
return fmt.Errorf("error unlinking keys during scan: %v", err)
|
||||
}
|
||||
return fmt.Errorf("error unlinking keys during scan: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
cursor = nextCursor
|
||||
@@ -155,7 +179,7 @@ func (r *RedisClient) PublishTask(ctx context.Context, streamName string, taskTy
|
||||
|
||||
func GetMultiple[T any](ctx context.Context, c Cache, keys []string) ([]T, error) {
|
||||
raws := c.MGet(ctx, keys...)
|
||||
final := make([]T, 0)
|
||||
final := make([]T, 0, len(raws))
|
||||
for _, b := range raws {
|
||||
if b == nil {
|
||||
continue
|
||||
|
||||
+33
-5
@@ -1,10 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"history-api/assets"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
@@ -13,15 +13,17 @@ import (
|
||||
func LoadEnv() error {
|
||||
envData, err := assets.GetFileContent("resources/.env")
|
||||
if err != nil {
|
||||
return errors.New("error read .env file")
|
||||
return nil
|
||||
}
|
||||
envMap, err := godotenv.Parse(strings.NewReader(envData))
|
||||
if err != nil {
|
||||
return errors.New("error parsing .env content")
|
||||
return fmt.Errorf("error parsing .env content: %w", err)
|
||||
}
|
||||
|
||||
for key, value := range envMap {
|
||||
os.Setenv(key, value)
|
||||
if os.Getenv(key) == "" {
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -41,4 +43,30 @@ func GetConfigWithDefault(config, defaultValue string) string {
|
||||
return defaultValue
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
func GetIntConfigWithDefault(config string, defaultValue int) int {
|
||||
data := strings.TrimSpace(os.Getenv(config))
|
||||
if data == "" {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(data)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func GetBoolConfigWithDefault(config string, defaultValue bool) bool {
|
||||
data := strings.TrimSpace(os.Getenv(config))
|
||||
if data == "" {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
value, err := strconv.ParseBool(data)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"history-api/pkg/cache"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func GenerateQueryKey(prefix string, params any) string {
|
||||
b, _ := json.Marshal(params)
|
||||
hash := fmt.Sprintf("%x", md5.Sum(b))
|
||||
return fmt.Sprintf("%s:query:%s", prefix, hash)
|
||||
return cache.QueryKey(prefix, params)
|
||||
}
|
||||
|
||||
func UUIDToString(v pgtype.UUID) string {
|
||||
|
||||
+29
-2
@@ -3,11 +3,23 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"history-api/pkg/config"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func defaultMaxConns() int {
|
||||
conns := runtime.NumCPU() * 8
|
||||
if conns < 16 {
|
||||
return 16
|
||||
}
|
||||
if conns > 64 {
|
||||
return 64
|
||||
}
|
||||
return conns
|
||||
}
|
||||
|
||||
func NewPostgresqlDB() (*pgxpool.Pool, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -20,8 +32,23 @@ func NewPostgresqlDB() (*pgxpool.Pool, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
poolConfig.MaxConns = 100
|
||||
poolConfig.MinConns = 10
|
||||
maxConns := config.GetIntConfigWithDefault("PGX_MAX_CONNS", defaultMaxConns())
|
||||
if maxConns < 1 {
|
||||
maxConns = defaultMaxConns()
|
||||
}
|
||||
|
||||
minConns := config.GetIntConfigWithDefault("PGX_MIN_CONNS", maxConns/4)
|
||||
if minConns < 0 {
|
||||
minConns = 0
|
||||
}
|
||||
if minConns > maxConns {
|
||||
minConns = maxConns
|
||||
}
|
||||
|
||||
poolConfig.MaxConns = int32(maxConns)
|
||||
poolConfig.MinConns = int32(minConns)
|
||||
poolConfig.MaxConnIdleTime = time.Duration(config.GetIntConfigWithDefault("PGX_MAX_CONN_IDLE_SECONDS", 300)) * time.Second
|
||||
poolConfig.HealthCheckPeriod = time.Duration(config.GetIntConfigWithDefault("PGX_HEALTH_CHECK_SECONDS", 60)) * time.Second
|
||||
|
||||
var pool *pgxpool.Pool
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package jsonx
|
||||
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
return sonic.Marshal(v)
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte, v any) error {
|
||||
return sonic.Unmarshal(data, v)
|
||||
}
|
||||
+30
-16
@@ -1,26 +1,40 @@
|
||||
package mbtiles
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"history-api/pkg/config"
|
||||
"runtime"
|
||||
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
)
|
||||
|
||||
func NewMBTilesDB(path string) (*sql.DB, error) {
|
||||
dsn := fmt.Sprintf("file:%s?mode=ro&_journal_mode=off&_synchronous=off", path)
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dsn := fmt.Sprintf("file:%s?mode=ro&_journal_mode=off&_synchronous=off", path)
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetMaxIdleConns(5)
|
||||
maxOpenConns := config.GetIntConfigWithDefault("MBTILES_MAX_OPEN_CONNS", runtime.NumCPU()*4)
|
||||
if maxOpenConns < 1 {
|
||||
maxOpenConns = 1
|
||||
}
|
||||
maxIdleConns := config.GetIntConfigWithDefault("MBTILES_MAX_IDLE_CONNS", maxOpenConns/2)
|
||||
if maxIdleConns < 1 {
|
||||
maxIdleConns = 1
|
||||
}
|
||||
if maxIdleConns > maxOpenConns {
|
||||
maxIdleConns = maxOpenConns
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
db.SetMaxOpenConns(maxOpenConns)
|
||||
db.SetMaxIdleConns(maxIdleConns)
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -212,7 +212,7 @@ func (s *s3Storage) BulkDelete(ctx context.Context, keys []string) error {
|
||||
}
|
||||
|
||||
batch := keys[i:end]
|
||||
var objects []types.ObjectIdentifier
|
||||
objects := make([]types.ObjectIdentifier, 0, len(batch))
|
||||
for _, k := range batch {
|
||||
objects = append(objects, types.ObjectIdentifier{Key: aws.String(k)})
|
||||
}
|
||||
|
||||
@@ -107,9 +107,10 @@ type ErrorResponse struct {
|
||||
|
||||
func formatValidationError(err error) []*ErrorResponse {
|
||||
var validationErrors validator.ValidationErrors
|
||||
var errorsList []*ErrorResponse
|
||||
errorsList := make([]*ErrorResponse, 0, 1)
|
||||
|
||||
if errors.As(err, &validationErrors) {
|
||||
errorsList = make([]*ErrorResponse, 0, len(validationErrors))
|
||||
for _, fieldError := range validationErrors {
|
||||
message := ""
|
||||
switch fieldError.Tag() {
|
||||
|
||||
Reference in New Issue
Block a user