Commit: Bulk unfinished work

This commit is contained in:
2026-01-22 17:39:04 +01:00
parent 6c46b4efcc
commit 3a9acc42a2
68 changed files with 5047 additions and 1064 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
.env

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Environment-dependent path to Maven home directory
/mavenHomeManager.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/golang.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/golang.iml" filepath="$PROJECT_DIR$/.idea/golang.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

3
.vscode/launch.json vendored
View File

@@ -4,12 +4,13 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"program": "${workspaceFolder}/cmd/api",
"envFile": "/home/kocoder/src/js/golang/.env"
}
]

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM golang:1.25.4
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
RUN CGO_ENABLED=1 GOOS=linux go build -o /backend ./cmd/api/main.go
EXPOSE 8080
CMD ["/backend"]

25
buf.gen.yaml Normal file
View File

@@ -0,0 +1,25 @@
version: v2
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
- local: protoc-gen-connect-go
out: gen
opt:
- paths=source_relative
- simple
- local: protoc-gen-es
out: ../query/src/gen
include_imports: true
opt: target=ts
- local: protoc-gen-connect-query
out: "../query/src/gen"
opt: target=ts
managed:
enabled: true
override:
- file_option: go_package_prefix
value: git.kocoder.xyz/kocoded/vt/gen
disable:
- file_option: go_package
module: buf.build/bufbuild/protovalidate

8
buf.yaml Normal file
View File

@@ -0,0 +1,8 @@
# For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml
version: v2
lint:
use:
- STANDARD
breaking:
use:
- FILE

View File

@@ -1,63 +1,94 @@
package main
import (
"context"
"log"
"log/slog"
"os"
"time"
"fmt"
"net/http"
"git.kocoder.xyz/kocoded/vt/routers"
kfx "git.kocoder.xyz/kocoded/vt/fx"
"git.kocoder.xyz/kocoded/vt/fx/interfaces"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
"git.kocoder.xyz/kocoded/vt/integration"
"git.kocoder.xyz/kocoded/vt/integration/mailing"
"git.kocoder.xyz/kocoded/vt/interceptors"
"git.kocoder.xyz/kocoded/vt/repositories"
mandantv1 "git.kocoder.xyz/kocoded/vt/routers/mandant/v1"
messagebusv1 "git.kocoder.xyz/kocoded/vt/routers/messagebus/v1"
projectv1 "git.kocoder.xyz/kocoded/vt/routers/project/v1"
todov1 "git.kocoder.xyz/kocoded/vt/routers/todo/v1"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
"github.com/joho/godotenv"
"go.uber.org/fx"
)
func main() {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
err := godotenv.Load()
if err != nil {
logger.Error("Error Loading Environment variables! ", "error", err)
}
db := utils.SetupDatabase(os.Getenv("DB_DSN"), logger)
cache, ok := utils.SetupCache(os.Getenv("VALKEY_HOST"), os.Getenv("VALKEY_PORT"), os.Getenv("VALKEY_USER"), os.Getenv("VALKEY_PASS"))
if !ok {
logger.Error("Configuring and connecting to Valkey failed!")
}
appCtx := utils.Application{Logger: logger, DB: db, Client: cache}
app := fiber.New()
utils.RegisterMiddlewares(app)
utils.CreateOIDCClient(context.Background(), app, appCtx)
routers.RegisterMandantRouter(app.Group("/v1/mandant"), appCtx)
routers.RegisterAnsprechpartnerRouter(app.Group("/v1/ansprechpartner"), appCtx)
routers.RegisterFirmaRouter(app.Group("/v1/firma"), appCtx)
routers.RegisterProjectRouter(app.Group("/v1/projects"), appCtx)
routers.RegisterUserRouter(app.Group("/v1/users"), appCtx)
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
go func() {
time.Sleep(time.Second)
}()
utils.MessageBus.AddConn(1, c)
}))
log.Fatalln(app.Listen(":3000"))
func AsIntegration(f any) any {
return fx.Annotate(
f,
fx.As(new(interfaces.BasicIntegration)),
fx.ResultTags(`group:"integration"`),
)
}
func AsRoute(f any) any {
return fx.Annotate(
f,
fx.As(new(kfx.Handler)),
fx.ResultTags(`group:"connectRoute"`),
)
}
func main() {
err := godotenv.Load()
if err != nil {
fmt.Println("Error loading env vars... %v", err)
}
fx.New(
fx.Provide(
kfx.NewHTTPServer,
utils.NewReflector,
utils.NewHealthchecker,
utils.NewDB,
utils.NewCache,
utils.NewOIDCProvider,
utils.NewOIDCVerifier,
fx.Annotate(repositories.NewSessionRepository, fx.As(new(stores.SessionStore))),
fx.Annotate(repositories.NewInMemeoryMessageRepository, fx.As(new(stores.MessageStore))),
interceptors.NewOIDCInterceptor,
AsIntegration(mailing.NewImapIntegration),
fx.Annotate(
integration.NewIntegrationHandler,
fx.ParamTags(`group:"integration"`),
),
kfx.NewOtelLoggerProvider,
kfx.NewOtelMeterProvider,
kfx.NewOtelTracerProvider,
kfx.NewLogger,
AsRoute(utils.NewHealthCheckV1),
AsRoute(utils.NewReflectorV1),
AsRoute(utils.NewReflectorV1Alpha1),
AsRoute(kfx.NewEchoHandler),
AsRoute(messagebusv1.NewMessagebusRoute),
AsRoute(projectv1.NewProjectRoute),
AsRoute(todov1.NewTodoRoute),
AsRoute(mandantv1.NewMandantRoute),
fx.Annotate(
kfx.NewServeMux,
fx.ParamTags(`group:"connectRoute"`),
),
),
fx.Invoke(func(*integration.IntegrationHandler) {}, kfx.SetupOTelSDK, func(*http.Server) {}),
).Run()
}

38
cmd/generate/main.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"log/slog"
"os"
"git.kocoder.xyz/kocoded/vt/model"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/gen"
"gorm.io/gorm"
)
func main() {
err := godotenv.Load()
if err != nil {
slog.Error("Error Loading Environment variables! ", "error", err)
}
db, err := gorm.Open(postgres.Open(os.Getenv("DB_DSN")), &gorm.Config{})
if err != nil {
slog.Error("Error connecting to the Database", "error", err)
}
g := gen.NewGenerator(gen.Config{
OutPath: "query",
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
})
// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
g.UseDB(db) // reuse your gorm db
// Generate basic type-safe DAO API for struct `model.User` following conventions
g.ApplyBasic(model.Mandant{}, model.User{}, model.Ansprechpartner{}, model.Dokument{}, model.Firma{}, model.Kalender{}, model.Kalendereintrag{}, model.Kostenstelle{}, model.Lager{}, model.Lagerplatz{}, model.Material{}, model.Nachricht{}, model.Projekt{}, model.Rechnung{}, model.Rechnungsposition{}, model.Scanobject{}, model.User{}, model.Zahlung{}, model.FirmaAnsprechpartner{}, model.Task{})
// Generate the code
g.Execute()
}

22
docker-compose.yaml Normal file
View File

@@ -0,0 +1,22 @@
services:
backend:
image: "git.kocoder.xyz/kocoded/vt-be"
build: .
environment:
- CLIENT_ID="golang-vt-backend"
- CLIENT_SECRET="awumIoacqNmwKTxRilQSM9cDmA7xA0j0"
- BACKEND_URI="http://10.8.0.3:3000"
- FRONTEND_URI="http://10.8.0.3:3001"
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://otel.kocoder.xyz/v1/traces"
- OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="https://otel.kocoder.xyz/v1/metrics"
- OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="https://otel.kocoder.xyz/v1/logs"
- DB_DSN="host=10.1.0.2 user=vt password=20a1c7809cd065bc5afe7c36fde26abf625316c8a83cc841b435c9acf3619b1f dbname=vt port=5432 sslmode=prefer TimeZone=Europe/Vienna"
- VALKEY_HOST="10.8.0.1"
- VALKEY_PORT="6379"
- VALKEY_USER="default"
- VALKEY_PASS="Konsti2007!"
ports:
- 3003:3002

32
fx/echo.go Normal file
View File

@@ -0,0 +1,32 @@
package fx
import (
"fmt"
"io"
"net/http"
"os"
)
// EchoHandler is an http.Handler that copies its request body
// back to the response.
type EchoHandler struct{}
// NewEchoHandler builds a new EchoHandler.
func NewEchoHandler() *EchoHandler {
return &EchoHandler{}
}
// ServeHTTP handles an HTTP request to the /echo endpoint.
func (*EchoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if _, err := io.Copy(w, r.Body); err != nil {
fmt.Fprintln(os.Stderr, "Failed to handle request:", err)
}
}
func (e *EchoHandler) Handler() http.Handler {
return e
}
func (*EchoHandler) Path() string {
return "/echo"
}

View File

@@ -0,0 +1,9 @@
package interfaces
import "context"
type BasicIntegration interface {
OnStart(context.Context) error
OnStop(context.Context) error
Invoke()
}

View File

@@ -0,0 +1 @@
package stores

View File

@@ -0,0 +1,17 @@
package stores
import "context"
type Message struct {
Id int
From string
Subject string
Body string
}
type MessageStore interface {
AddMessage(ctx context.Context, m *Message) error
GetMessage(ctx context.Context, id int) (*Message, error)
ListMessages(ctx context.Context) ([]*Message, error)
RemoveMessage(ctx context.Context, id int) error
}

View File

@@ -0,0 +1 @@
package stores

View File

@@ -0,0 +1,44 @@
package stores
import (
"errors"
"strconv"
)
type Session struct {
Token string
UserID uint
MandantId uint
}
func (s *Session) Deserialize(t string, m map[string]string) (*Session, error) {
userid, err := strconv.Atoi(m["userid"])
if err != nil {
return nil, errors.New("Userid from cache not an int")
}
mandantid, err := strconv.Atoi(m["mandantid"])
if err != nil {
return nil, errors.New("Mandantid from cache not an int")
}
s.Token = t
s.UserID = uint(userid)
s.MandantId = uint(mandantid)
return s, nil
}
func (s *Session) Serialize() map[string]string {
m := make(map[string]string)
m["userid"] = strconv.Itoa(int(s.UserID))
m["mandantid"] = strconv.Itoa(int(s.MandantId))
return m
}
type SessionStore interface {
AddSession(s *Session)
GetSessionFromToken(token string) (*Session, error)
RemoveSession(token string)
SetMandantInSession(token string, mandantId uint) error
}

View File

@@ -0,0 +1 @@
package stores

137
fx/logger.go Normal file
View File

@@ -0,0 +1,137 @@
package fx
import (
"context"
"log/slog"
"time"
"go.uber.org/fx"
"go.opentelemetry.io/contrib/bridges/otelslog"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/propagation"
olog "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/trace"
)
func NewOtelTracerProvider(lc fx.Lifecycle) *trace.TracerProvider {
tp, err := newTracerProvider()
if err != nil {
panic(err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return tp.Shutdown(ctx)
},
})
return tp
}
func NewOtelMeterProvider(lc fx.Lifecycle) *metric.MeterProvider {
mp, err := newMeterProvider()
if err != nil {
panic(err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return mp.Shutdown(ctx)
},
})
return mp
}
func NewOtelLoggerProvider(lc fx.Lifecycle) *olog.LoggerProvider {
lp, err := newLoggerProvider()
if err != nil {
panic(err)
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return lp.Shutdown(ctx)
},
})
return lp
}
func NewLogger(loggerProvider *olog.LoggerProvider) *slog.Logger {
logger := otelslog.NewLogger("Application", otelslog.WithLoggerProvider(loggerProvider))
return logger
}
// setupOTelSDK bootstraps the OpenTelemetry pipeline.
// If it does not return an error, make sure to call shutdown for proper cleanup.
func SetupOTelSDK(tracerProvider *trace.TracerProvider, meterProvider *metric.MeterProvider, loggerProvider *olog.LoggerProvider) {
// Set up propagator.
prop := newPropagator()
otel.SetTextMapPropagator(prop)
// Set up trace provider.
otel.SetTracerProvider(tracerProvider)
otel.SetMeterProvider(meterProvider)
// Set up logger provider.
global.SetLoggerProvider(loggerProvider)
}
func newPropagator() propagation.TextMapPropagator {
return propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
)
}
func newTracerProvider() (*trace.TracerProvider, error) {
traceExporter, err := otlptracehttp.New(context.Background())
if err != nil {
return nil, err
}
tracerProvider := trace.NewTracerProvider(
trace.WithBatcher(traceExporter,
// Default is 5s. Set to 1s for demonstrative purposes.
trace.WithBatchTimeout(time.Second)),
)
return tracerProvider, nil
}
func newMeterProvider() (*metric.MeterProvider, error) {
metricExporter, err := otlpmetrichttp.New(context.Background())
if err != nil {
return nil, err
}
meterProvider := metric.NewMeterProvider(
metric.WithReader(metric.NewPeriodicReader(metricExporter,
// Default is 1m. Set to 3s for demonstrative purposes.
metric.WithInterval(3*time.Second))),
)
return meterProvider, nil
}
func newLoggerProvider() (*olog.LoggerProvider, error) {
logExporter, err := otlploghttp.New(context.Background())
if err != nil {
return nil, err
}
loggerProvider := olog.NewLoggerProvider(
olog.WithProcessor(olog.NewBatchProcessor(logExporter)),
)
logger := otelslog.NewLogger("sloger", otelslog.WithLoggerProvider(loggerProvider))
logger.Info("Hello")
return loggerProvider, nil
}

56
fx/routes.go Normal file
View File

@@ -0,0 +1,56 @@
package fx
import (
"log/slog"
"net/http"
"os"
connectcors "connectrpc.com/cors"
"github.com/rs/cors"
)
type Handler interface {
Handler() http.Handler
Path() string
}
type route struct {
path string
handler http.Handler
}
func NewRoute(path string, handler http.Handler) Handler {
return &route{
path: path,
handler: handler,
}
}
func (r *route) Handler() http.Handler {
return r.handler
}
func (r *route) Path() string {
return r.path
}
// NewServeMux builds a ServeMux that will route requests
// to the given EchoHandler.
func NewServeMux(handlers []Handler, logger *slog.Logger) http.Handler {
mux := http.NewServeMux()
for _, h := range handlers {
logger.Debug("Registering route", "path", h.Path())
mux.Handle(h.Path(), h.Handler())
}
handler := cors.New(cors.Options{
AllowedOrigins: []string{"http://10.8.0.3:3001", "http://10.8.0.3:3001/", os.Getenv("FRONTEND_URI")}, // replace with your domain
AllowedMethods: connectcors.AllowedMethods(),
AllowedHeaders: append(connectcors.AllowedHeaders(), "authentication", "Authentication"),
ExposedHeaders: connectcors.ExposedHeaders(),
AllowCredentials: true,
}).Handler(mux)
return handler
}

40
fx/server.go Normal file
View File

@@ -0,0 +1,40 @@
package fx
import (
"context"
"fmt"
"net/http"
"go.uber.org/fx"
)
func NewHTTPServer(lc fx.Lifecycle, handler http.Handler) *http.Server {
p := new(http.Protocols)
p.SetHTTP1(true)
// Use h2c so we can serve HTTP/2 without TLS.
p.SetUnencryptedHTTP2(true)
srv := &http.Server{
Addr: ":3002",
Protocols: p,
Handler: handler,
}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go func() {
fmt.Println("Listening on :3002")
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}

View File

@@ -0,0 +1,532 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: mandant/v1/mandant.proto
package mandantv1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetCurrentTenantRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetCurrentTenantRequest) Reset() {
*x = GetCurrentTenantRequest{}
mi := &file_mandant_v1_mandant_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetCurrentTenantRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetCurrentTenantRequest) ProtoMessage() {}
func (x *GetCurrentTenantRequest) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetCurrentTenantRequest.ProtoReflect.Descriptor instead.
func (*GetCurrentTenantRequest) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{0}
}
type GetTenantRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTenantRequest) Reset() {
*x = GetTenantRequest{}
mi := &file_mandant_v1_mandant_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetTenantRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetTenantRequest) ProtoMessage() {}
func (x *GetTenantRequest) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetTenantRequest.ProtoReflect.Descriptor instead.
func (*GetTenantRequest) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{1}
}
func (x *GetTenantRequest) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type GetTenantResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Plan string `protobuf:"bytes,3,opt,name=plan,proto3" json:"plan,omitempty"`
Logo string `protobuf:"bytes,4,opt,name=logo,proto3" json:"logo,omitempty"`
Color string `protobuf:"bytes,5,opt,name=color,proto3" json:"color,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTenantResponse) Reset() {
*x = GetTenantResponse{}
mi := &file_mandant_v1_mandant_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetTenantResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetTenantResponse) ProtoMessage() {}
func (x *GetTenantResponse) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetTenantResponse.ProtoReflect.Descriptor instead.
func (*GetTenantResponse) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{2}
}
func (x *GetTenantResponse) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *GetTenantResponse) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetTenantResponse) GetPlan() string {
if x != nil {
return x.Plan
}
return ""
}
func (x *GetTenantResponse) GetLogo() string {
if x != nil {
return x.Logo
}
return ""
}
func (x *GetTenantResponse) GetColor() string {
if x != nil {
return x.Color
}
return ""
}
type ListTenantRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
PerPage int32 `protobuf:"varint,2,opt,name=per_page,json=perPage,proto3" json:"per_page,omitempty"`
OrberBy string `protobuf:"bytes,3,opt,name=orber_by,json=orberBy,proto3" json:"orber_by,omitempty"`
Asc bool `protobuf:"varint,4,opt,name=asc,proto3" json:"asc,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListTenantRequest) Reset() {
*x = ListTenantRequest{}
mi := &file_mandant_v1_mandant_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListTenantRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListTenantRequest) ProtoMessage() {}
func (x *ListTenantRequest) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListTenantRequest.ProtoReflect.Descriptor instead.
func (*ListTenantRequest) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{3}
}
func (x *ListTenantRequest) GetPage() int32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ListTenantRequest) GetPerPage() int32 {
if x != nil {
return x.PerPage
}
return 0
}
func (x *ListTenantRequest) GetOrberBy() string {
if x != nil {
return x.OrberBy
}
return ""
}
func (x *ListTenantRequest) GetAsc() bool {
if x != nil {
return x.Asc
}
return false
}
type Metadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
TotalCount int32 `protobuf:"varint,1,opt,name=totalCount,proto3" json:"totalCount,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Metadata) Reset() {
*x = Metadata{}
mi := &file_mandant_v1_mandant_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{4}
}
func (x *Metadata) GetTotalCount() int32 {
if x != nil {
return x.TotalCount
}
return 0
}
type ListProjectsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Data []*GetTenantResponse `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
Meta *Metadata `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListProjectsResponse) Reset() {
*x = ListProjectsResponse{}
mi := &file_mandant_v1_mandant_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListProjectsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListProjectsResponse) ProtoMessage() {}
func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead.
func (*ListProjectsResponse) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{5}
}
func (x *ListProjectsResponse) GetData() []*GetTenantResponse {
if x != nil {
return x.Data
}
return nil
}
func (x *ListProjectsResponse) GetMeta() *Metadata {
if x != nil {
return x.Meta
}
return nil
}
type SetCurrentTenantRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
TenantId int64 `protobuf:"varint,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SetCurrentTenantRequest) Reset() {
*x = SetCurrentTenantRequest{}
mi := &file_mandant_v1_mandant_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SetCurrentTenantRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetCurrentTenantRequest) ProtoMessage() {}
func (x *SetCurrentTenantRequest) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetCurrentTenantRequest.ProtoReflect.Descriptor instead.
func (*SetCurrentTenantRequest) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{6}
}
func (x *SetCurrentTenantRequest) GetTenantId() int64 {
if x != nil {
return x.TenantId
}
return 0
}
type SetCurrentTenantResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SetCurrentTenantResponse) Reset() {
*x = SetCurrentTenantResponse{}
mi := &file_mandant_v1_mandant_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SetCurrentTenantResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SetCurrentTenantResponse) ProtoMessage() {}
func (x *SetCurrentTenantResponse) ProtoReflect() protoreflect.Message {
mi := &file_mandant_v1_mandant_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SetCurrentTenantResponse.ProtoReflect.Descriptor instead.
func (*SetCurrentTenantResponse) Descriptor() ([]byte, []int) {
return file_mandant_v1_mandant_proto_rawDescGZIP(), []int{7}
}
func (x *SetCurrentTenantResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_mandant_v1_mandant_proto protoreflect.FileDescriptor
const file_mandant_v1_mandant_proto_rawDesc = "" +
"\n" +
"\x18mandant/v1/mandant.proto\x12\n" +
"mandant.v1\"\x19\n" +
"\x17GetCurrentTenantRequest\"\"\n" +
"\x10GetTenantRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\"u\n" +
"\x11GetTenantResponse\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" +
"\x04plan\x18\x03 \x01(\tR\x04plan\x12\x12\n" +
"\x04logo\x18\x04 \x01(\tR\x04logo\x12\x14\n" +
"\x05color\x18\x05 \x01(\tR\x05color\"o\n" +
"\x11ListTenantRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x19\n" +
"\bper_page\x18\x02 \x01(\x05R\aperPage\x12\x19\n" +
"\borber_by\x18\x03 \x01(\tR\aorberBy\x12\x10\n" +
"\x03asc\x18\x04 \x01(\bR\x03asc\"*\n" +
"\bMetadata\x12\x1e\n" +
"\n" +
"totalCount\x18\x01 \x01(\x05R\n" +
"totalCount\"s\n" +
"\x14ListProjectsResponse\x121\n" +
"\x04data\x18\x01 \x03(\v2\x1d.mandant.v1.GetTenantResponseR\x04data\x12(\n" +
"\x04meta\x18\x02 \x01(\v2\x14.mandant.v1.MetadataR\x04meta\"6\n" +
"\x17SetCurrentTenantRequest\x12\x1b\n" +
"\ttenant_id\x18\x01 \x01(\x03R\btenantId\"4\n" +
"\x18SetCurrentTenantResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess2\x99\x02\n" +
"\x0eMandantService\x12V\n" +
"\x10GetCurrentTenant\x12#.mandant.v1.GetCurrentTenantRequest\x1a\x1d.mandant.v1.GetTenantResponse\x12P\n" +
"\rGetAllTenants\x12\x1d.mandant.v1.ListTenantRequest\x1a .mandant.v1.ListProjectsResponse\x12]\n" +
"\x10SetCurrentTenant\x12#.mandant.v1.SetCurrentTenantRequest\x1a$.mandant.v1.SetCurrentTenantResponseB\x9c\x01\n" +
"\x0ecom.mandant.v1B\fMandantProtoP\x01Z3git.kocoder.xyz/kocoded/vt/gen/mandant/v1;mandantv1\xa2\x02\x03MXX\xaa\x02\n" +
"Mandant.V1\xca\x02\n" +
"Mandant\\V1\xe2\x02\x16Mandant\\V1\\GPBMetadata\xea\x02\vMandant::V1b\x06proto3"
var (
file_mandant_v1_mandant_proto_rawDescOnce sync.Once
file_mandant_v1_mandant_proto_rawDescData []byte
)
func file_mandant_v1_mandant_proto_rawDescGZIP() []byte {
file_mandant_v1_mandant_proto_rawDescOnce.Do(func() {
file_mandant_v1_mandant_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_mandant_v1_mandant_proto_rawDesc), len(file_mandant_v1_mandant_proto_rawDesc)))
})
return file_mandant_v1_mandant_proto_rawDescData
}
var file_mandant_v1_mandant_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_mandant_v1_mandant_proto_goTypes = []any{
(*GetCurrentTenantRequest)(nil), // 0: mandant.v1.GetCurrentTenantRequest
(*GetTenantRequest)(nil), // 1: mandant.v1.GetTenantRequest
(*GetTenantResponse)(nil), // 2: mandant.v1.GetTenantResponse
(*ListTenantRequest)(nil), // 3: mandant.v1.ListTenantRequest
(*Metadata)(nil), // 4: mandant.v1.Metadata
(*ListProjectsResponse)(nil), // 5: mandant.v1.ListProjectsResponse
(*SetCurrentTenantRequest)(nil), // 6: mandant.v1.SetCurrentTenantRequest
(*SetCurrentTenantResponse)(nil), // 7: mandant.v1.SetCurrentTenantResponse
}
var file_mandant_v1_mandant_proto_depIdxs = []int32{
2, // 0: mandant.v1.ListProjectsResponse.data:type_name -> mandant.v1.GetTenantResponse
4, // 1: mandant.v1.ListProjectsResponse.meta:type_name -> mandant.v1.Metadata
0, // 2: mandant.v1.MandantService.GetCurrentTenant:input_type -> mandant.v1.GetCurrentTenantRequest
3, // 3: mandant.v1.MandantService.GetAllTenants:input_type -> mandant.v1.ListTenantRequest
6, // 4: mandant.v1.MandantService.SetCurrentTenant:input_type -> mandant.v1.SetCurrentTenantRequest
2, // 5: mandant.v1.MandantService.GetCurrentTenant:output_type -> mandant.v1.GetTenantResponse
5, // 6: mandant.v1.MandantService.GetAllTenants:output_type -> mandant.v1.ListProjectsResponse
7, // 7: mandant.v1.MandantService.SetCurrentTenant:output_type -> mandant.v1.SetCurrentTenantResponse
5, // [5:8] is the sub-list for method output_type
2, // [2:5] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_mandant_v1_mandant_proto_init() }
func file_mandant_v1_mandant_proto_init() {
if File_mandant_v1_mandant_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_mandant_v1_mandant_proto_rawDesc), len(file_mandant_v1_mandant_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_mandant_v1_mandant_proto_goTypes,
DependencyIndexes: file_mandant_v1_mandant_proto_depIdxs,
MessageInfos: file_mandant_v1_mandant_proto_msgTypes,
}.Build()
File_mandant_v1_mandant_proto = out.File
file_mandant_v1_mandant_proto_goTypes = nil
file_mandant_v1_mandant_proto_depIdxs = nil
}

View File

@@ -0,0 +1,179 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: mandant/v1/mandant.proto
package mandantv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "git.kocoder.xyz/kocoded/vt/gen/mandant/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// MandantServiceName is the fully-qualified name of the MandantService service.
MandantServiceName = "mandant.v1.MandantService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// MandantServiceGetCurrentTenantProcedure is the fully-qualified name of the MandantService's
// GetCurrentTenant RPC.
MandantServiceGetCurrentTenantProcedure = "/mandant.v1.MandantService/GetCurrentTenant"
// MandantServiceGetAllTenantsProcedure is the fully-qualified name of the MandantService's
// GetAllTenants RPC.
MandantServiceGetAllTenantsProcedure = "/mandant.v1.MandantService/GetAllTenants"
// MandantServiceSetCurrentTenantProcedure is the fully-qualified name of the MandantService's
// SetCurrentTenant RPC.
MandantServiceSetCurrentTenantProcedure = "/mandant.v1.MandantService/SetCurrentTenant"
)
// MandantServiceClient is a client for the mandant.v1.MandantService service.
type MandantServiceClient interface {
GetCurrentTenant(context.Context, *v1.GetCurrentTenantRequest) (*v1.GetTenantResponse, error)
GetAllTenants(context.Context, *v1.ListTenantRequest) (*v1.ListProjectsResponse, error)
SetCurrentTenant(context.Context, *v1.SetCurrentTenantRequest) (*v1.SetCurrentTenantResponse, error)
}
// NewMandantServiceClient constructs a client for the mandant.v1.MandantService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewMandantServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MandantServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
mandantServiceMethods := v1.File_mandant_v1_mandant_proto.Services().ByName("MandantService").Methods()
return &mandantServiceClient{
getCurrentTenant: connect.NewClient[v1.GetCurrentTenantRequest, v1.GetTenantResponse](
httpClient,
baseURL+MandantServiceGetCurrentTenantProcedure,
connect.WithSchema(mandantServiceMethods.ByName("GetCurrentTenant")),
connect.WithClientOptions(opts...),
),
getAllTenants: connect.NewClient[v1.ListTenantRequest, v1.ListProjectsResponse](
httpClient,
baseURL+MandantServiceGetAllTenantsProcedure,
connect.WithSchema(mandantServiceMethods.ByName("GetAllTenants")),
connect.WithClientOptions(opts...),
),
setCurrentTenant: connect.NewClient[v1.SetCurrentTenantRequest, v1.SetCurrentTenantResponse](
httpClient,
baseURL+MandantServiceSetCurrentTenantProcedure,
connect.WithSchema(mandantServiceMethods.ByName("SetCurrentTenant")),
connect.WithClientOptions(opts...),
),
}
}
// mandantServiceClient implements MandantServiceClient.
type mandantServiceClient struct {
getCurrentTenant *connect.Client[v1.GetCurrentTenantRequest, v1.GetTenantResponse]
getAllTenants *connect.Client[v1.ListTenantRequest, v1.ListProjectsResponse]
setCurrentTenant *connect.Client[v1.SetCurrentTenantRequest, v1.SetCurrentTenantResponse]
}
// GetCurrentTenant calls mandant.v1.MandantService.GetCurrentTenant.
func (c *mandantServiceClient) GetCurrentTenant(ctx context.Context, req *v1.GetCurrentTenantRequest) (*v1.GetTenantResponse, error) {
response, err := c.getCurrentTenant.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// GetAllTenants calls mandant.v1.MandantService.GetAllTenants.
func (c *mandantServiceClient) GetAllTenants(ctx context.Context, req *v1.ListTenantRequest) (*v1.ListProjectsResponse, error) {
response, err := c.getAllTenants.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// SetCurrentTenant calls mandant.v1.MandantService.SetCurrentTenant.
func (c *mandantServiceClient) SetCurrentTenant(ctx context.Context, req *v1.SetCurrentTenantRequest) (*v1.SetCurrentTenantResponse, error) {
response, err := c.setCurrentTenant.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// MandantServiceHandler is an implementation of the mandant.v1.MandantService service.
type MandantServiceHandler interface {
GetCurrentTenant(context.Context, *v1.GetCurrentTenantRequest) (*v1.GetTenantResponse, error)
GetAllTenants(context.Context, *v1.ListTenantRequest) (*v1.ListProjectsResponse, error)
SetCurrentTenant(context.Context, *v1.SetCurrentTenantRequest) (*v1.SetCurrentTenantResponse, error)
}
// NewMandantServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewMandantServiceHandler(svc MandantServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
mandantServiceMethods := v1.File_mandant_v1_mandant_proto.Services().ByName("MandantService").Methods()
mandantServiceGetCurrentTenantHandler := connect.NewUnaryHandlerSimple(
MandantServiceGetCurrentTenantProcedure,
svc.GetCurrentTenant,
connect.WithSchema(mandantServiceMethods.ByName("GetCurrentTenant")),
connect.WithHandlerOptions(opts...),
)
mandantServiceGetAllTenantsHandler := connect.NewUnaryHandlerSimple(
MandantServiceGetAllTenantsProcedure,
svc.GetAllTenants,
connect.WithSchema(mandantServiceMethods.ByName("GetAllTenants")),
connect.WithHandlerOptions(opts...),
)
mandantServiceSetCurrentTenantHandler := connect.NewUnaryHandlerSimple(
MandantServiceSetCurrentTenantProcedure,
svc.SetCurrentTenant,
connect.WithSchema(mandantServiceMethods.ByName("SetCurrentTenant")),
connect.WithHandlerOptions(opts...),
)
return "/mandant.v1.MandantService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case MandantServiceGetCurrentTenantProcedure:
mandantServiceGetCurrentTenantHandler.ServeHTTP(w, r)
case MandantServiceGetAllTenantsProcedure:
mandantServiceGetAllTenantsHandler.ServeHTTP(w, r)
case MandantServiceSetCurrentTenantProcedure:
mandantServiceSetCurrentTenantHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedMandantServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedMandantServiceHandler struct{}
func (UnimplementedMandantServiceHandler) GetCurrentTenant(context.Context, *v1.GetCurrentTenantRequest) (*v1.GetTenantResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mandant.v1.MandantService.GetCurrentTenant is not implemented"))
}
func (UnimplementedMandantServiceHandler) GetAllTenants(context.Context, *v1.ListTenantRequest) (*v1.ListProjectsResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mandant.v1.MandantService.GetAllTenants is not implemented"))
}
func (UnimplementedMandantServiceHandler) SetCurrentTenant(context.Context, *v1.SetCurrentTenantRequest) (*v1.SetCurrentTenantResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mandant.v1.MandantService.SetCurrentTenant is not implemented"))
}

View File

@@ -0,0 +1,217 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: messagebus/v1/messagebus.proto
package messagebusv1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type MessageBusEntityType int32
const (
MessageBusEntityType_OTHER MessageBusEntityType = 0
MessageBusEntityType_INVALIDATION_REQUEST MessageBusEntityType = 1
)
// Enum value maps for MessageBusEntityType.
var (
MessageBusEntityType_name = map[int32]string{
0: "OTHER",
1: "INVALIDATION_REQUEST",
}
MessageBusEntityType_value = map[string]int32{
"OTHER": 0,
"INVALIDATION_REQUEST": 1,
}
)
func (x MessageBusEntityType) Enum() *MessageBusEntityType {
p := new(MessageBusEntityType)
*p = x
return p
}
func (x MessageBusEntityType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (MessageBusEntityType) Descriptor() protoreflect.EnumDescriptor {
return file_messagebus_v1_messagebus_proto_enumTypes[0].Descriptor()
}
func (MessageBusEntityType) Type() protoreflect.EnumType {
return &file_messagebus_v1_messagebus_proto_enumTypes[0]
}
func (x MessageBusEntityType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use MessageBusEntityType.Descriptor instead.
func (MessageBusEntityType) EnumDescriptor() ([]byte, []int) {
return file_messagebus_v1_messagebus_proto_rawDescGZIP(), []int{0}
}
type MessageBusEntity struct {
state protoimpl.MessageState `protogen:"open.v1"`
QueryKey string `protobuf:"bytes,1,opt,name=queryKey,proto3" json:"queryKey,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MessageBusEntity) Reset() {
*x = MessageBusEntity{}
mi := &file_messagebus_v1_messagebus_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MessageBusEntity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MessageBusEntity) ProtoMessage() {}
func (x *MessageBusEntity) ProtoReflect() protoreflect.Message {
mi := &file_messagebus_v1_messagebus_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MessageBusEntity.ProtoReflect.Descriptor instead.
func (*MessageBusEntity) Descriptor() ([]byte, []int) {
return file_messagebus_v1_messagebus_proto_rawDescGZIP(), []int{0}
}
func (x *MessageBusEntity) GetQueryKey() string {
if x != nil {
return x.QueryKey
}
return ""
}
type SubscribeToConnectInvalidationRequestsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SubscribeToConnectInvalidationRequestsRequest) Reset() {
*x = SubscribeToConnectInvalidationRequestsRequest{}
mi := &file_messagebus_v1_messagebus_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SubscribeToConnectInvalidationRequestsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubscribeToConnectInvalidationRequestsRequest) ProtoMessage() {}
func (x *SubscribeToConnectInvalidationRequestsRequest) ProtoReflect() protoreflect.Message {
mi := &file_messagebus_v1_messagebus_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeToConnectInvalidationRequestsRequest.ProtoReflect.Descriptor instead.
func (*SubscribeToConnectInvalidationRequestsRequest) Descriptor() ([]byte, []int) {
return file_messagebus_v1_messagebus_proto_rawDescGZIP(), []int{1}
}
var File_messagebus_v1_messagebus_proto protoreflect.FileDescriptor
const file_messagebus_v1_messagebus_proto_rawDesc = "" +
"\n" +
"\x1emessagebus/v1/messagebus.proto\x12\rmessagebus.v1\".\n" +
"\x10MessageBusEntity\x12\x1a\n" +
"\bqueryKey\x18\x01 \x01(\tR\bqueryKey\"/\n" +
"-SubscribeToConnectInvalidationRequestsRequest*;\n" +
"\x14MessageBusEntityType\x12\t\n" +
"\x05OTHER\x10\x00\x12\x18\n" +
"\x14INVALIDATION_REQUEST\x10\x012\x9f\x01\n" +
"\x11MessageBusService\x12\x89\x01\n" +
"&SubscribeToConnectInvalidationRequests\x12<.messagebus.v1.SubscribeToConnectInvalidationRequestsRequest\x1a\x1f.messagebus.v1.MessageBusEntity0\x01B\xb4\x01\n" +
"\x11com.messagebus.v1B\x0fMessagebusProtoP\x01Z9git.kocoder.xyz/kocoded/vt/gen/messagebus/v1;messagebusv1\xa2\x02\x03MXX\xaa\x02\rMessagebus.V1\xca\x02\rMessagebus\\V1\xe2\x02\x19Messagebus\\V1\\GPBMetadata\xea\x02\x0eMessagebus::V1b\x06proto3"
var (
file_messagebus_v1_messagebus_proto_rawDescOnce sync.Once
file_messagebus_v1_messagebus_proto_rawDescData []byte
)
func file_messagebus_v1_messagebus_proto_rawDescGZIP() []byte {
file_messagebus_v1_messagebus_proto_rawDescOnce.Do(func() {
file_messagebus_v1_messagebus_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_messagebus_v1_messagebus_proto_rawDesc), len(file_messagebus_v1_messagebus_proto_rawDesc)))
})
return file_messagebus_v1_messagebus_proto_rawDescData
}
var file_messagebus_v1_messagebus_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_messagebus_v1_messagebus_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_messagebus_v1_messagebus_proto_goTypes = []any{
(MessageBusEntityType)(0), // 0: messagebus.v1.MessageBusEntityType
(*MessageBusEntity)(nil), // 1: messagebus.v1.MessageBusEntity
(*SubscribeToConnectInvalidationRequestsRequest)(nil), // 2: messagebus.v1.SubscribeToConnectInvalidationRequestsRequest
}
var file_messagebus_v1_messagebus_proto_depIdxs = []int32{
2, // 0: messagebus.v1.MessageBusService.SubscribeToConnectInvalidationRequests:input_type -> messagebus.v1.SubscribeToConnectInvalidationRequestsRequest
1, // 1: messagebus.v1.MessageBusService.SubscribeToConnectInvalidationRequests:output_type -> messagebus.v1.MessageBusEntity
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_messagebus_v1_messagebus_proto_init() }
func file_messagebus_v1_messagebus_proto_init() {
if File_messagebus_v1_messagebus_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_messagebus_v1_messagebus_proto_rawDesc), len(file_messagebus_v1_messagebus_proto_rawDesc)),
NumEnums: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_messagebus_v1_messagebus_proto_goTypes,
DependencyIndexes: file_messagebus_v1_messagebus_proto_depIdxs,
EnumInfos: file_messagebus_v1_messagebus_proto_enumTypes,
MessageInfos: file_messagebus_v1_messagebus_proto_msgTypes,
}.Build()
File_messagebus_v1_messagebus_proto = out.File
file_messagebus_v1_messagebus_proto_goTypes = nil
file_messagebus_v1_messagebus_proto_depIdxs = nil
}

View File

@@ -0,0 +1,110 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: messagebus/v1/messagebus.proto
package messagebusv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "git.kocoder.xyz/kocoded/vt/gen/messagebus/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// MessageBusServiceName is the fully-qualified name of the MessageBusService service.
MessageBusServiceName = "messagebus.v1.MessageBusService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// MessageBusServiceSubscribeToConnectInvalidationRequestsProcedure is the fully-qualified name of
// the MessageBusService's SubscribeToConnectInvalidationRequests RPC.
MessageBusServiceSubscribeToConnectInvalidationRequestsProcedure = "/messagebus.v1.MessageBusService/SubscribeToConnectInvalidationRequests"
)
// MessageBusServiceClient is a client for the messagebus.v1.MessageBusService service.
type MessageBusServiceClient interface {
SubscribeToConnectInvalidationRequests(context.Context, *v1.SubscribeToConnectInvalidationRequestsRequest) (*connect.ServerStreamForClient[v1.MessageBusEntity], error)
}
// NewMessageBusServiceClient constructs a client for the messagebus.v1.MessageBusService service.
// By default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped
// responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewMessageBusServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MessageBusServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
messageBusServiceMethods := v1.File_messagebus_v1_messagebus_proto.Services().ByName("MessageBusService").Methods()
return &messageBusServiceClient{
subscribeToConnectInvalidationRequests: connect.NewClient[v1.SubscribeToConnectInvalidationRequestsRequest, v1.MessageBusEntity](
httpClient,
baseURL+MessageBusServiceSubscribeToConnectInvalidationRequestsProcedure,
connect.WithSchema(messageBusServiceMethods.ByName("SubscribeToConnectInvalidationRequests")),
connect.WithClientOptions(opts...),
),
}
}
// messageBusServiceClient implements MessageBusServiceClient.
type messageBusServiceClient struct {
subscribeToConnectInvalidationRequests *connect.Client[v1.SubscribeToConnectInvalidationRequestsRequest, v1.MessageBusEntity]
}
// SubscribeToConnectInvalidationRequests calls
// messagebus.v1.MessageBusService.SubscribeToConnectInvalidationRequests.
func (c *messageBusServiceClient) SubscribeToConnectInvalidationRequests(ctx context.Context, req *v1.SubscribeToConnectInvalidationRequestsRequest) (*connect.ServerStreamForClient[v1.MessageBusEntity], error) {
return c.subscribeToConnectInvalidationRequests.CallServerStream(ctx, connect.NewRequest(req))
}
// MessageBusServiceHandler is an implementation of the messagebus.v1.MessageBusService service.
type MessageBusServiceHandler interface {
SubscribeToConnectInvalidationRequests(context.Context, *v1.SubscribeToConnectInvalidationRequestsRequest, *connect.ServerStream[v1.MessageBusEntity]) error
}
// NewMessageBusServiceHandler builds an HTTP handler from the service implementation. It returns
// the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewMessageBusServiceHandler(svc MessageBusServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
messageBusServiceMethods := v1.File_messagebus_v1_messagebus_proto.Services().ByName("MessageBusService").Methods()
messageBusServiceSubscribeToConnectInvalidationRequestsHandler := connect.NewServerStreamHandlerSimple(
MessageBusServiceSubscribeToConnectInvalidationRequestsProcedure,
svc.SubscribeToConnectInvalidationRequests,
connect.WithSchema(messageBusServiceMethods.ByName("SubscribeToConnectInvalidationRequests")),
connect.WithHandlerOptions(opts...),
)
return "/messagebus.v1.MessageBusService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case MessageBusServiceSubscribeToConnectInvalidationRequestsProcedure:
messageBusServiceSubscribeToConnectInvalidationRequestsHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedMessageBusServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedMessageBusServiceHandler struct{}
func (UnimplementedMessageBusServiceHandler) SubscribeToConnectInvalidationRequests(context.Context, *v1.SubscribeToConnectInvalidationRequestsRequest, *connect.ServerStream[v1.MessageBusEntity]) error {
return connect.NewError(connect.CodeUnimplemented, errors.New("messagebus.v1.MessageBusService.SubscribeToConnectInvalidationRequests is not implemented"))
}

View File

@@ -0,0 +1,433 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: project/v1/project.proto
package projectv1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetProjectRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetProjectRequest) Reset() {
*x = GetProjectRequest{}
mi := &file_project_v1_project_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetProjectRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetProjectRequest) ProtoMessage() {}
func (x *GetProjectRequest) ProtoReflect() protoreflect.Message {
mi := &file_project_v1_project_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetProjectRequest.ProtoReflect.Descriptor instead.
func (*GetProjectRequest) Descriptor() ([]byte, []int) {
return file_project_v1_project_proto_rawDescGZIP(), []int{0}
}
func (x *GetProjectRequest) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
type GetProjectResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
IsMaterialized *bool `protobuf:"varint,5,opt,name=is_materialized,json=isMaterialized,proto3,oneof" json:"is_materialized,omitempty"`
IsPersonalized *bool `protobuf:"varint,6,opt,name=is_personalized,json=isPersonalized,proto3,oneof" json:"is_personalized,omitempty"`
IsConfirmed *bool `protobuf:"varint,7,opt,name=is_confirmed,json=isConfirmed,proto3,oneof" json:"is_confirmed,omitempty"`
IsPaid *bool `protobuf:"varint,8,opt,name=is_paid,json=isPaid,proto3,oneof" json:"is_paid,omitempty"`
IsDone *bool `protobuf:"varint,9,opt,name=is_done,json=isDone,proto3,oneof" json:"is_done,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetProjectResponse) Reset() {
*x = GetProjectResponse{}
mi := &file_project_v1_project_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetProjectResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetProjectResponse) ProtoMessage() {}
func (x *GetProjectResponse) ProtoReflect() protoreflect.Message {
mi := &file_project_v1_project_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetProjectResponse.ProtoReflect.Descriptor instead.
func (*GetProjectResponse) Descriptor() ([]byte, []int) {
return file_project_v1_project_proto_rawDescGZIP(), []int{1}
}
func (x *GetProjectResponse) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *GetProjectResponse) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetProjectResponse) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *GetProjectResponse) GetIsMaterialized() bool {
if x != nil && x.IsMaterialized != nil {
return *x.IsMaterialized
}
return false
}
func (x *GetProjectResponse) GetIsPersonalized() bool {
if x != nil && x.IsPersonalized != nil {
return *x.IsPersonalized
}
return false
}
func (x *GetProjectResponse) GetIsConfirmed() bool {
if x != nil && x.IsConfirmed != nil {
return *x.IsConfirmed
}
return false
}
func (x *GetProjectResponse) GetIsPaid() bool {
if x != nil && x.IsPaid != nil {
return *x.IsPaid
}
return false
}
func (x *GetProjectResponse) GetIsDone() bool {
if x != nil && x.IsDone != nil {
return *x.IsDone
}
return false
}
type ListProjectsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
PerPage int32 `protobuf:"varint,2,opt,name=per_page,json=perPage,proto3" json:"per_page,omitempty"`
OrberBy string `protobuf:"bytes,3,opt,name=orber_by,json=orberBy,proto3" json:"orber_by,omitempty"`
Asc bool `protobuf:"varint,4,opt,name=asc,proto3" json:"asc,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListProjectsRequest) Reset() {
*x = ListProjectsRequest{}
mi := &file_project_v1_project_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListProjectsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListProjectsRequest) ProtoMessage() {}
func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message {
mi := &file_project_v1_project_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListProjectsRequest.ProtoReflect.Descriptor instead.
func (*ListProjectsRequest) Descriptor() ([]byte, []int) {
return file_project_v1_project_proto_rawDescGZIP(), []int{2}
}
func (x *ListProjectsRequest) GetPage() int32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ListProjectsRequest) GetPerPage() int32 {
if x != nil {
return x.PerPage
}
return 0
}
func (x *ListProjectsRequest) GetOrberBy() string {
if x != nil {
return x.OrberBy
}
return ""
}
func (x *ListProjectsRequest) GetAsc() bool {
if x != nil {
return x.Asc
}
return false
}
type Metadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
TotalCount int32 `protobuf:"varint,1,opt,name=totalCount,proto3" json:"totalCount,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Metadata) Reset() {
*x = Metadata{}
mi := &file_project_v1_project_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
mi := &file_project_v1_project_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
return file_project_v1_project_proto_rawDescGZIP(), []int{3}
}
func (x *Metadata) GetTotalCount() int32 {
if x != nil {
return x.TotalCount
}
return 0
}
type ListProjectsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Data []*GetProjectResponse `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
Meta *Metadata `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListProjectsResponse) Reset() {
*x = ListProjectsResponse{}
mi := &file_project_v1_project_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListProjectsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListProjectsResponse) ProtoMessage() {}
func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
mi := &file_project_v1_project_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead.
func (*ListProjectsResponse) Descriptor() ([]byte, []int) {
return file_project_v1_project_proto_rawDescGZIP(), []int{4}
}
func (x *ListProjectsResponse) GetData() []*GetProjectResponse {
if x != nil {
return x.Data
}
return nil
}
func (x *ListProjectsResponse) GetMeta() *Metadata {
if x != nil {
return x.Meta
}
return nil
}
var File_project_v1_project_proto protoreflect.FileDescriptor
const file_project_v1_project_proto_rawDesc = "" +
"\n" +
"\x18project/v1/project.proto\x12\n" +
"project.v1\"#\n" +
"\x11GetProjectRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\"\xeb\x02\n" +
"\x12GetProjectResponse\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12 \n" +
"\vdescription\x18\x03 \x01(\tR\vdescription\x12,\n" +
"\x0fis_materialized\x18\x05 \x01(\bH\x00R\x0eisMaterialized\x88\x01\x01\x12,\n" +
"\x0fis_personalized\x18\x06 \x01(\bH\x01R\x0eisPersonalized\x88\x01\x01\x12&\n" +
"\fis_confirmed\x18\a \x01(\bH\x02R\visConfirmed\x88\x01\x01\x12\x1c\n" +
"\ais_paid\x18\b \x01(\bH\x03R\x06isPaid\x88\x01\x01\x12\x1c\n" +
"\ais_done\x18\t \x01(\bH\x04R\x06isDone\x88\x01\x01B\x12\n" +
"\x10_is_materializedB\x12\n" +
"\x10_is_personalizedB\x0f\n" +
"\r_is_confirmedB\n" +
"\n" +
"\b_is_paidB\n" +
"\n" +
"\b_is_done\"q\n" +
"\x13ListProjectsRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x19\n" +
"\bper_page\x18\x02 \x01(\x05R\aperPage\x12\x19\n" +
"\borber_by\x18\x03 \x01(\tR\aorberBy\x12\x10\n" +
"\x03asc\x18\x04 \x01(\bR\x03asc\"*\n" +
"\bMetadata\x12\x1e\n" +
"\n" +
"totalCount\x18\x01 \x01(\x05R\n" +
"totalCount\"t\n" +
"\x14ListProjectsResponse\x122\n" +
"\x04data\x18\x01 \x03(\v2\x1e.project.v1.GetProjectResponseR\x04data\x12(\n" +
"\x04meta\x18\x02 \x01(\v2\x14.project.v1.MetadataR\x04meta2\xb0\x01\n" +
"\x0eProjectService\x12K\n" +
"\n" +
"GetProject\x12\x1d.project.v1.GetProjectRequest\x1a\x1e.project.v1.GetProjectResponse\x12Q\n" +
"\fListProjects\x12\x1f.project.v1.ListProjectsRequest\x1a .project.v1.ListProjectsResponseB\x9c\x01\n" +
"\x0ecom.project.v1B\fProjectProtoP\x01Z3git.kocoder.xyz/kocoded/vt/gen/project/v1;projectv1\xa2\x02\x03PXX\xaa\x02\n" +
"Project.V1\xca\x02\n" +
"Project\\V1\xe2\x02\x16Project\\V1\\GPBMetadata\xea\x02\vProject::V1b\x06proto3"
var (
file_project_v1_project_proto_rawDescOnce sync.Once
file_project_v1_project_proto_rawDescData []byte
)
func file_project_v1_project_proto_rawDescGZIP() []byte {
file_project_v1_project_proto_rawDescOnce.Do(func() {
file_project_v1_project_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_project_v1_project_proto_rawDesc), len(file_project_v1_project_proto_rawDesc)))
})
return file_project_v1_project_proto_rawDescData
}
var file_project_v1_project_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_project_v1_project_proto_goTypes = []any{
(*GetProjectRequest)(nil), // 0: project.v1.GetProjectRequest
(*GetProjectResponse)(nil), // 1: project.v1.GetProjectResponse
(*ListProjectsRequest)(nil), // 2: project.v1.ListProjectsRequest
(*Metadata)(nil), // 3: project.v1.Metadata
(*ListProjectsResponse)(nil), // 4: project.v1.ListProjectsResponse
}
var file_project_v1_project_proto_depIdxs = []int32{
1, // 0: project.v1.ListProjectsResponse.data:type_name -> project.v1.GetProjectResponse
3, // 1: project.v1.ListProjectsResponse.meta:type_name -> project.v1.Metadata
0, // 2: project.v1.ProjectService.GetProject:input_type -> project.v1.GetProjectRequest
2, // 3: project.v1.ProjectService.ListProjects:input_type -> project.v1.ListProjectsRequest
1, // 4: project.v1.ProjectService.GetProject:output_type -> project.v1.GetProjectResponse
4, // 5: project.v1.ProjectService.ListProjects:output_type -> project.v1.ListProjectsResponse
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_project_v1_project_proto_init() }
func file_project_v1_project_proto_init() {
if File_project_v1_project_proto != nil {
return
}
file_project_v1_project_proto_msgTypes[1].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_project_v1_project_proto_rawDesc), len(file_project_v1_project_proto_rawDesc)),
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_project_v1_project_proto_goTypes,
DependencyIndexes: file_project_v1_project_proto_depIdxs,
MessageInfos: file_project_v1_project_proto_msgTypes,
}.Build()
File_project_v1_project_proto = out.File
file_project_v1_project_proto_goTypes = nil
file_project_v1_project_proto_depIdxs = nil
}

View File

@@ -0,0 +1,146 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: project/v1/project.proto
package projectv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "git.kocoder.xyz/kocoded/vt/gen/project/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ProjectServiceName is the fully-qualified name of the ProjectService service.
ProjectServiceName = "project.v1.ProjectService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ProjectServiceGetProjectProcedure is the fully-qualified name of the ProjectService's GetProject
// RPC.
ProjectServiceGetProjectProcedure = "/project.v1.ProjectService/GetProject"
// ProjectServiceListProjectsProcedure is the fully-qualified name of the ProjectService's
// ListProjects RPC.
ProjectServiceListProjectsProcedure = "/project.v1.ProjectService/ListProjects"
)
// ProjectServiceClient is a client for the project.v1.ProjectService service.
type ProjectServiceClient interface {
GetProject(context.Context, *v1.GetProjectRequest) (*v1.GetProjectResponse, error)
ListProjects(context.Context, *v1.ListProjectsRequest) (*v1.ListProjectsResponse, error)
}
// NewProjectServiceClient constructs a client for the project.v1.ProjectService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewProjectServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ProjectServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
projectServiceMethods := v1.File_project_v1_project_proto.Services().ByName("ProjectService").Methods()
return &projectServiceClient{
getProject: connect.NewClient[v1.GetProjectRequest, v1.GetProjectResponse](
httpClient,
baseURL+ProjectServiceGetProjectProcedure,
connect.WithSchema(projectServiceMethods.ByName("GetProject")),
connect.WithClientOptions(opts...),
),
listProjects: connect.NewClient[v1.ListProjectsRequest, v1.ListProjectsResponse](
httpClient,
baseURL+ProjectServiceListProjectsProcedure,
connect.WithSchema(projectServiceMethods.ByName("ListProjects")),
connect.WithClientOptions(opts...),
),
}
}
// projectServiceClient implements ProjectServiceClient.
type projectServiceClient struct {
getProject *connect.Client[v1.GetProjectRequest, v1.GetProjectResponse]
listProjects *connect.Client[v1.ListProjectsRequest, v1.ListProjectsResponse]
}
// GetProject calls project.v1.ProjectService.GetProject.
func (c *projectServiceClient) GetProject(ctx context.Context, req *v1.GetProjectRequest) (*v1.GetProjectResponse, error) {
response, err := c.getProject.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// ListProjects calls project.v1.ProjectService.ListProjects.
func (c *projectServiceClient) ListProjects(ctx context.Context, req *v1.ListProjectsRequest) (*v1.ListProjectsResponse, error) {
response, err := c.listProjects.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// ProjectServiceHandler is an implementation of the project.v1.ProjectService service.
type ProjectServiceHandler interface {
GetProject(context.Context, *v1.GetProjectRequest) (*v1.GetProjectResponse, error)
ListProjects(context.Context, *v1.ListProjectsRequest) (*v1.ListProjectsResponse, error)
}
// NewProjectServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewProjectServiceHandler(svc ProjectServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
projectServiceMethods := v1.File_project_v1_project_proto.Services().ByName("ProjectService").Methods()
projectServiceGetProjectHandler := connect.NewUnaryHandlerSimple(
ProjectServiceGetProjectProcedure,
svc.GetProject,
connect.WithSchema(projectServiceMethods.ByName("GetProject")),
connect.WithHandlerOptions(opts...),
)
projectServiceListProjectsHandler := connect.NewUnaryHandlerSimple(
ProjectServiceListProjectsProcedure,
svc.ListProjects,
connect.WithSchema(projectServiceMethods.ByName("ListProjects")),
connect.WithHandlerOptions(opts...),
)
return "/project.v1.ProjectService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ProjectServiceGetProjectProcedure:
projectServiceGetProjectHandler.ServeHTTP(w, r)
case ProjectServiceListProjectsProcedure:
projectServiceListProjectsHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedProjectServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedProjectServiceHandler struct{}
func (UnimplementedProjectServiceHandler) GetProject(context.Context, *v1.GetProjectRequest) (*v1.GetProjectResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("project.v1.ProjectService.GetProject is not implemented"))
}
func (UnimplementedProjectServiceHandler) ListProjects(context.Context, *v1.ListProjectsRequest) (*v1.ListProjectsResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("project.v1.ProjectService.ListProjects is not implemented"))
}

645
gen/todo/v1/todo.pb.go Normal file
View File

@@ -0,0 +1,645 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: todo/v1/todo.proto
package todov1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Status int32
const (
Status_Todo Status = 0
Status_NeedsMoreInfo Status = 1
Status_Doing Status = 2
Status_Done Status = 3
)
// Enum value maps for Status.
var (
Status_name = map[int32]string{
0: "Todo",
1: "NeedsMoreInfo",
2: "Doing",
3: "Done",
}
Status_value = map[string]int32{
"Todo": 0,
"NeedsMoreInfo": 1,
"Doing": 2,
"Done": 3,
}
)
func (x Status) Enum() *Status {
p := new(Status)
*p = x
return p
}
func (x Status) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Status) Descriptor() protoreflect.EnumDescriptor {
return file_todo_v1_todo_proto_enumTypes[0].Descriptor()
}
func (Status) Type() protoreflect.EnumType {
return &file_todo_v1_todo_proto_enumTypes[0]
}
func (x Status) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Status.Descriptor instead.
func (Status) EnumDescriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{0}
}
type Field int32
const (
Field_FieldId Field = 0
Field_FieldTitle Field = 1
Field_FieldDescription Field = 2
Field_FieldStatus Field = 3
)
// Enum value maps for Field.
var (
Field_name = map[int32]string{
0: "FieldId",
1: "FieldTitle",
2: "FieldDescription",
3: "FieldStatus",
}
Field_value = map[string]int32{
"FieldId": 0,
"FieldTitle": 1,
"FieldDescription": 2,
"FieldStatus": 3,
}
)
func (x Field) Enum() *Field {
p := new(Field)
*p = x
return p
}
func (x Field) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Field) Descriptor() protoreflect.EnumDescriptor {
return file_todo_v1_todo_proto_enumTypes[1].Descriptor()
}
func (Field) Type() protoreflect.EnumType {
return &file_todo_v1_todo_proto_enumTypes[1]
}
func (x Field) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Field.Descriptor instead.
func (Field) EnumDescriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{1}
}
type Operation int32
const (
Operation_Equals Operation = 0
Operation_NotEquals Operation = 1
Operation_GreaterThan Operation = 2
Operation_LessThan Operation = 3
Operation_Like Operation = 4
)
// Enum value maps for Operation.
var (
Operation_name = map[int32]string{
0: "Equals",
1: "NotEquals",
2: "GreaterThan",
3: "LessThan",
4: "Like",
}
Operation_value = map[string]int32{
"Equals": 0,
"NotEquals": 1,
"GreaterThan": 2,
"LessThan": 3,
"Like": 4,
}
)
func (x Operation) Enum() *Operation {
p := new(Operation)
*p = x
return p
}
func (x Operation) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Operation) Descriptor() protoreflect.EnumDescriptor {
return file_todo_v1_todo_proto_enumTypes[2].Descriptor()
}
func (Operation) Type() protoreflect.EnumType {
return &file_todo_v1_todo_proto_enumTypes[2]
}
func (x Operation) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Operation.Descriptor instead.
func (Operation) EnumDescriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{2}
}
type GetTodosRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTodosRequest) Reset() {
*x = GetTodosRequest{}
mi := &file_todo_v1_todo_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetTodosRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetTodosRequest) ProtoMessage() {}
func (x *GetTodosRequest) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetTodosRequest.ProtoReflect.Descriptor instead.
func (*GetTodosRequest) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{0}
}
func (x *GetTodosRequest) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
type GetTodosResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=todo.v1.Status" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetTodosResponse) Reset() {
*x = GetTodosResponse{}
mi := &file_todo_v1_todo_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetTodosResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetTodosResponse) ProtoMessage() {}
func (x *GetTodosResponse) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetTodosResponse.ProtoReflect.Descriptor instead.
func (*GetTodosResponse) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{1}
}
func (x *GetTodosResponse) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *GetTodosResponse) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *GetTodosResponse) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *GetTodosResponse) GetStatus() Status {
if x != nil {
return x.Status
}
return Status_Todo
}
type ListTodosRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
PerPage int32 `protobuf:"varint,2,opt,name=per_page,json=perPage,proto3" json:"per_page,omitempty"`
OrberBy string `protobuf:"bytes,3,opt,name=orber_by,json=orberBy,proto3" json:"orber_by,omitempty"`
Asc bool `protobuf:"varint,4,opt,name=asc,proto3" json:"asc,omitempty"`
Filters []*Filter `protobuf:"bytes,5,rep,name=filters,proto3" json:"filters,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListTodosRequest) Reset() {
*x = ListTodosRequest{}
mi := &file_todo_v1_todo_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListTodosRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListTodosRequest) ProtoMessage() {}
func (x *ListTodosRequest) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListTodosRequest.ProtoReflect.Descriptor instead.
func (*ListTodosRequest) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{2}
}
func (x *ListTodosRequest) GetPage() int32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ListTodosRequest) GetPerPage() int32 {
if x != nil {
return x.PerPage
}
return 0
}
func (x *ListTodosRequest) GetOrberBy() string {
if x != nil {
return x.OrberBy
}
return ""
}
func (x *ListTodosRequest) GetAsc() bool {
if x != nil {
return x.Asc
}
return false
}
func (x *ListTodosRequest) GetFilters() []*Filter {
if x != nil {
return x.Filters
}
return nil
}
type Filter struct {
state protoimpl.MessageState `protogen:"open.v1"`
Field Field `protobuf:"varint,1,opt,name=field,proto3,enum=todo.v1.Field" json:"field,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Operation Operation `protobuf:"varint,3,opt,name=operation,proto3,enum=todo.v1.Operation" json:"operation,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Filter) Reset() {
*x = Filter{}
mi := &file_todo_v1_todo_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Filter) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Filter) ProtoMessage() {}
func (x *Filter) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Filter.ProtoReflect.Descriptor instead.
func (*Filter) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{3}
}
func (x *Filter) GetField() Field {
if x != nil {
return x.Field
}
return Field_FieldId
}
func (x *Filter) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *Filter) GetOperation() Operation {
if x != nil {
return x.Operation
}
return Operation_Equals
}
type Metadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
TotalCount int32 `protobuf:"varint,1,opt,name=totalCount,proto3" json:"totalCount,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Metadata) Reset() {
*x = Metadata{}
mi := &file_todo_v1_todo_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Metadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{4}
}
func (x *Metadata) GetTotalCount() int32 {
if x != nil {
return x.TotalCount
}
return 0
}
type ListTodosResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Data []*GetTodosResponse `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
Meta *Metadata `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListTodosResponse) Reset() {
*x = ListTodosResponse{}
mi := &file_todo_v1_todo_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListTodosResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListTodosResponse) ProtoMessage() {}
func (x *ListTodosResponse) ProtoReflect() protoreflect.Message {
mi := &file_todo_v1_todo_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListTodosResponse.ProtoReflect.Descriptor instead.
func (*ListTodosResponse) Descriptor() ([]byte, []int) {
return file_todo_v1_todo_proto_rawDescGZIP(), []int{5}
}
func (x *ListTodosResponse) GetData() []*GetTodosResponse {
if x != nil {
return x.Data
}
return nil
}
func (x *ListTodosResponse) GetMeta() *Metadata {
if x != nil {
return x.Meta
}
return nil
}
var File_todo_v1_todo_proto protoreflect.FileDescriptor
const file_todo_v1_todo_proto_rawDesc = "" +
"\n" +
"\x12todo/v1/todo.proto\x12\atodo.v1\"!\n" +
"\x0fGetTodosRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x05R\x02id\"\x83\x01\n" +
"\x10GetTodosResponse\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x03 \x01(\tR\vdescription\x12'\n" +
"\x06status\x18\x04 \x01(\x0e2\x0f.todo.v1.StatusR\x06status\"\x99\x01\n" +
"\x10ListTodosRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x19\n" +
"\bper_page\x18\x02 \x01(\x05R\aperPage\x12\x19\n" +
"\borber_by\x18\x03 \x01(\tR\aorberBy\x12\x10\n" +
"\x03asc\x18\x04 \x01(\bR\x03asc\x12)\n" +
"\afilters\x18\x05 \x03(\v2\x0f.todo.v1.FilterR\afilters\"v\n" +
"\x06Filter\x12$\n" +
"\x05field\x18\x01 \x01(\x0e2\x0e.todo.v1.FieldR\x05field\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value\x120\n" +
"\toperation\x18\x03 \x01(\x0e2\x12.todo.v1.OperationR\toperation\"*\n" +
"\bMetadata\x12\x1e\n" +
"\n" +
"totalCount\x18\x01 \x01(\x05R\n" +
"totalCount\"i\n" +
"\x11ListTodosResponse\x12-\n" +
"\x04data\x18\x01 \x03(\v2\x19.todo.v1.GetTodosResponseR\x04data\x12%\n" +
"\x04meta\x18\x02 \x01(\v2\x11.todo.v1.MetadataR\x04meta*:\n" +
"\x06Status\x12\b\n" +
"\x04Todo\x10\x00\x12\x11\n" +
"\rNeedsMoreInfo\x10\x01\x12\t\n" +
"\x05Doing\x10\x02\x12\b\n" +
"\x04Done\x10\x03*K\n" +
"\x05Field\x12\v\n" +
"\aFieldId\x10\x00\x12\x0e\n" +
"\n" +
"FieldTitle\x10\x01\x12\x14\n" +
"\x10FieldDescription\x10\x02\x12\x0f\n" +
"\vFieldStatus\x10\x03*O\n" +
"\tOperation\x12\n" +
"\n" +
"\x06Equals\x10\x00\x12\r\n" +
"\tNotEquals\x10\x01\x12\x0f\n" +
"\vGreaterThan\x10\x02\x12\f\n" +
"\bLessThan\x10\x03\x12\b\n" +
"\x04Like\x10\x042\x91\x01\n" +
"\vTodoService\x12>\n" +
"\aGetTodo\x12\x18.todo.v1.GetTodosRequest\x1a\x19.todo.v1.GetTodosResponse\x12B\n" +
"\tListTodos\x12\x19.todo.v1.ListTodosRequest\x1a\x1a.todo.v1.ListTodosResponseB\x84\x01\n" +
"\vcom.todo.v1B\tTodoProtoP\x01Z-git.kocoder.xyz/kocoded/vt/gen/todo/v1;todov1\xa2\x02\x03TXX\xaa\x02\aTodo.V1\xca\x02\aTodo\\V1\xe2\x02\x13Todo\\V1\\GPBMetadata\xea\x02\bTodo::V1b\x06proto3"
var (
file_todo_v1_todo_proto_rawDescOnce sync.Once
file_todo_v1_todo_proto_rawDescData []byte
)
func file_todo_v1_todo_proto_rawDescGZIP() []byte {
file_todo_v1_todo_proto_rawDescOnce.Do(func() {
file_todo_v1_todo_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_todo_v1_todo_proto_rawDesc), len(file_todo_v1_todo_proto_rawDesc)))
})
return file_todo_v1_todo_proto_rawDescData
}
var file_todo_v1_todo_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_todo_v1_todo_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_todo_v1_todo_proto_goTypes = []any{
(Status)(0), // 0: todo.v1.Status
(Field)(0), // 1: todo.v1.Field
(Operation)(0), // 2: todo.v1.Operation
(*GetTodosRequest)(nil), // 3: todo.v1.GetTodosRequest
(*GetTodosResponse)(nil), // 4: todo.v1.GetTodosResponse
(*ListTodosRequest)(nil), // 5: todo.v1.ListTodosRequest
(*Filter)(nil), // 6: todo.v1.Filter
(*Metadata)(nil), // 7: todo.v1.Metadata
(*ListTodosResponse)(nil), // 8: todo.v1.ListTodosResponse
}
var file_todo_v1_todo_proto_depIdxs = []int32{
0, // 0: todo.v1.GetTodosResponse.status:type_name -> todo.v1.Status
6, // 1: todo.v1.ListTodosRequest.filters:type_name -> todo.v1.Filter
1, // 2: todo.v1.Filter.field:type_name -> todo.v1.Field
2, // 3: todo.v1.Filter.operation:type_name -> todo.v1.Operation
4, // 4: todo.v1.ListTodosResponse.data:type_name -> todo.v1.GetTodosResponse
7, // 5: todo.v1.ListTodosResponse.meta:type_name -> todo.v1.Metadata
3, // 6: todo.v1.TodoService.GetTodo:input_type -> todo.v1.GetTodosRequest
5, // 7: todo.v1.TodoService.ListTodos:input_type -> todo.v1.ListTodosRequest
4, // 8: todo.v1.TodoService.GetTodo:output_type -> todo.v1.GetTodosResponse
8, // 9: todo.v1.TodoService.ListTodos:output_type -> todo.v1.ListTodosResponse
8, // [8:10] is the sub-list for method output_type
6, // [6:8] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_todo_v1_todo_proto_init() }
func file_todo_v1_todo_proto_init() {
if File_todo_v1_todo_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_todo_v1_todo_proto_rawDesc), len(file_todo_v1_todo_proto_rawDesc)),
NumEnums: 3,
NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_todo_v1_todo_proto_goTypes,
DependencyIndexes: file_todo_v1_todo_proto_depIdxs,
EnumInfos: file_todo_v1_todo_proto_enumTypes,
MessageInfos: file_todo_v1_todo_proto_msgTypes,
}.Build()
File_todo_v1_todo_proto = out.File
file_todo_v1_todo_proto_goTypes = nil
file_todo_v1_todo_proto_depIdxs = nil
}

View File

@@ -0,0 +1,144 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: todo/v1/todo.proto
package todov1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "git.kocoder.xyz/kocoded/vt/gen/todo/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// TodoServiceName is the fully-qualified name of the TodoService service.
TodoServiceName = "todo.v1.TodoService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// TodoServiceGetTodoProcedure is the fully-qualified name of the TodoService's GetTodo RPC.
TodoServiceGetTodoProcedure = "/todo.v1.TodoService/GetTodo"
// TodoServiceListTodosProcedure is the fully-qualified name of the TodoService's ListTodos RPC.
TodoServiceListTodosProcedure = "/todo.v1.TodoService/ListTodos"
)
// TodoServiceClient is a client for the todo.v1.TodoService service.
type TodoServiceClient interface {
GetTodo(context.Context, *v1.GetTodosRequest) (*v1.GetTodosResponse, error)
ListTodos(context.Context, *v1.ListTodosRequest) (*v1.ListTodosResponse, error)
}
// NewTodoServiceClient constructs a client for the todo.v1.TodoService service. By default, it uses
// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewTodoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) TodoServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
todoServiceMethods := v1.File_todo_v1_todo_proto.Services().ByName("TodoService").Methods()
return &todoServiceClient{
getTodo: connect.NewClient[v1.GetTodosRequest, v1.GetTodosResponse](
httpClient,
baseURL+TodoServiceGetTodoProcedure,
connect.WithSchema(todoServiceMethods.ByName("GetTodo")),
connect.WithClientOptions(opts...),
),
listTodos: connect.NewClient[v1.ListTodosRequest, v1.ListTodosResponse](
httpClient,
baseURL+TodoServiceListTodosProcedure,
connect.WithSchema(todoServiceMethods.ByName("ListTodos")),
connect.WithClientOptions(opts...),
),
}
}
// todoServiceClient implements TodoServiceClient.
type todoServiceClient struct {
getTodo *connect.Client[v1.GetTodosRequest, v1.GetTodosResponse]
listTodos *connect.Client[v1.ListTodosRequest, v1.ListTodosResponse]
}
// GetTodo calls todo.v1.TodoService.GetTodo.
func (c *todoServiceClient) GetTodo(ctx context.Context, req *v1.GetTodosRequest) (*v1.GetTodosResponse, error) {
response, err := c.getTodo.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// ListTodos calls todo.v1.TodoService.ListTodos.
func (c *todoServiceClient) ListTodos(ctx context.Context, req *v1.ListTodosRequest) (*v1.ListTodosResponse, error) {
response, err := c.listTodos.CallUnary(ctx, connect.NewRequest(req))
if response != nil {
return response.Msg, err
}
return nil, err
}
// TodoServiceHandler is an implementation of the todo.v1.TodoService service.
type TodoServiceHandler interface {
GetTodo(context.Context, *v1.GetTodosRequest) (*v1.GetTodosResponse, error)
ListTodos(context.Context, *v1.ListTodosRequest) (*v1.ListTodosResponse, error)
}
// NewTodoServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewTodoServiceHandler(svc TodoServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
todoServiceMethods := v1.File_todo_v1_todo_proto.Services().ByName("TodoService").Methods()
todoServiceGetTodoHandler := connect.NewUnaryHandlerSimple(
TodoServiceGetTodoProcedure,
svc.GetTodo,
connect.WithSchema(todoServiceMethods.ByName("GetTodo")),
connect.WithHandlerOptions(opts...),
)
todoServiceListTodosHandler := connect.NewUnaryHandlerSimple(
TodoServiceListTodosProcedure,
svc.ListTodos,
connect.WithSchema(todoServiceMethods.ByName("ListTodos")),
connect.WithHandlerOptions(opts...),
)
return "/todo.v1.TodoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case TodoServiceGetTodoProcedure:
todoServiceGetTodoHandler.ServeHTTP(w, r)
case TodoServiceListTodosProcedure:
todoServiceListTodosHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedTodoServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedTodoServiceHandler struct{}
func (UnimplementedTodoServiceHandler) GetTodo(context.Context, *v1.GetTodosRequest) (*v1.GetTodosResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("todo.v1.TodoService.GetTodo is not implemented"))
}
func (UnimplementedTodoServiceHandler) ListTodos(context.Context, *v1.ListTodosRequest) (*v1.ListTodosResponse, error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("todo.v1.TodoService.ListTodos is not implemented"))
}

86
go.mod
View File

@@ -1,12 +1,18 @@
module git.kocoder.xyz/kocoded/vt
go 1.24.4
go 1.25
require (
github.com/emersion/go-imap/v2 v2.0.0-beta.6
github.com/emersion/go-message v0.18.1
github.com/gofiber/fiber/v2 v2.52.9
golang.org/x/oauth2 v0.28.0
github.com/valkey-io/valkey-glide/go/v2 v2.1.1
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/log v0.15.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/sdk/log v0.14.0
go.opentelemetry.io/otel/sdk/metric v1.38.0
google.golang.org/protobuf v1.36.9
gorm.io/driver/postgres v1.6.0
gorm.io/gen v0.3.27
gorm.io/gorm v1.30.1
@@ -14,26 +20,69 @@ require (
)
require (
github.com/ClickHouse/ch-go v0.61.5 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.30.0 // indirect
github.com/StirlingMarketingGroup/go-retry v0.0.0-20190512160921-94a8eb23e893 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/fasthttp/websocket v1.5.8 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
github.com/jhillyerd/enmime v1.3.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
github.com/valkey-io/valkey-glide/go/v2 v2.1.1 // indirect
golang.org/x/net v0.43.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sqs/go-xoauth2 v0.0.0-20120917012134-0911dad68e56 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/dig v1.19.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/clickhouse v0.7.0 // indirect
)
require (
connectrpc.com/connect v1.19.1
connectrpc.com/cors v0.1.0
connectrpc.com/grpchealth v1.4.0
connectrpc.com/grpcreflect v1.3.0
connectrpc.com/otelconnect v0.8.0
filippo.io/edwards25519 v1.1.0 // indirect
github.com/BrianLeishman/go-imap v0.1.20
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/coreos/go-oidc/v3 v3.15.0
github.com/fatih/structs v1.1.0
github.com/coreos/go-oidc/v3 v3.17.0
github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/gofiber/contrib/websocket v1.3.4
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.5 // indirect
github.com/jackc/pgx/v5 v5.8.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@@ -44,16 +93,23 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.11.1
github.com/tinylib/msgp v1.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.64.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.36.0 // indirect
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0
go.uber.org/fx v1.24.0
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.39.0 // indirect
gorm.io/datatypes v1.2.6 // indirect
gorm.io/driver/mysql v1.6.0 // indirect
gorm.io/hints v1.1.2 // indirect
gorm.io/plugin/opentelemetry v0.1.16
)

225
go.sum
View File

@@ -1,12 +1,38 @@
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
connectrpc.com/cors v0.1.0 h1:f3gTXJyDZPrDIZCQ567jxfD9PAIpopHiRDnJRt3QuOQ=
connectrpc.com/cors v0.1.0/go.mod h1:v8SJZCPfHtGH1zsm+Ttajpozd4cYIUryl4dFB6QEpfg=
connectrpc.com/grpchealth v1.4.0 h1:MJC96JLelARPgZTiRF9KRfY/2N9OcoQvF2EWX07v2IE=
connectrpc.com/grpchealth v1.4.0/go.mod h1:WhW6m1EzTmq3Ky1FE8EfkIpSDc6TfUx2M2KqZO3ts/Q=
connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc=
connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs=
connectrpc.com/otelconnect v0.8.0 h1:a4qrN4H8aEE2jAoCxheZYYfEjXMgVPyL9OzPQLBEFXU=
connectrpc.com/otelconnect v0.8.0/go.mod h1:AEkVLjCPXra+ObGFCOClcJkNjS7zPaQSqvO0lCyjfZc=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BrianLeishman/go-imap v0.1.20 h1:jvkir/v5A7Ml0zAAZyTSq60+WDN54RzRhhPwC9Bw9Bg=
github.com/BrianLeishman/go-imap v0.1.20/go.mod h1:MiXltWbhjxLKrYHGivAieizDwQ+oOULASiV9kVMyBLc=
github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo=
github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo=
github.com/StirlingMarketingGroup/go-retry v0.0.0-20190512160921-94a8eb23e893 h1:y1OlgL2twHNQGJ4OTHhvVLebgDCwP4pttmZc2w4UAz8=
github.com/StirlingMarketingGroup/go-retry v0.0.0-20190512160921-94a8eb23e893/go.mod h1:RHK0VFlYDZQeNFg4C2dp7cPE6urfbpgyEZIGxa9f5zw=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
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/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/emersion/go-imap/v2 v2.0.0-beta.6 h1:3w7QGUcDEoZXr+okRZR75VBjX0yvvJqkfy3gibbH2yY=
github.com/emersion/go-imap/v2 v2.0.0-beta.6/go.mod h1:BZTFHsS1hmgBkFlHqbxGLXk2hnRqTItUgwjSSCsYNAk=
github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E=
@@ -15,44 +41,81 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpz
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8=
github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/gofiber/contrib/websocket v1.3.4 h1:tWeBdbJ8q0WFQXariLN4dBIbGH9KBU75s0s7YXplOSg=
github.com/gofiber/contrib/websocket v1.3.4/go.mod h1:kTFBPC6YENCnKfKx0BoOFjgXxdz7E85/STdkmZPEmPs=
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
@@ -60,20 +123,46 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
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/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sqs/go-xoauth2 v0.0.0-20120917012134-0911dad68e56 h1:KCgKdj+ha4CgnVHIiJYGKzgZk3HfCc6XssESfOa6atM=
github.com/sqs/go-xoauth2 v0.0.0-20120917012134-0911dad68e56/go.mod h1:ghDEBrT4oFcM4rv18bzcZaAWXbHPGpDa4e2hh9oXL8A=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
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/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/valkey-io/valkey-glide/go/v2 v2.1.1 h1:78eoWXIYLbse0ZpspKRMwbREj0+Tkoc/qkSR8H9iRsc=
@@ -82,32 +171,111 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3og=
github.com/valyala/fasthttp v1.64.0/go.mod h1:dGmFxwkWXSK0NbOSJuF7AMVzU+lkHz0wQVvVITv2UQA=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0 h1:eypSOd+0txRKCXPNyqLPsbSfA0jULgJcGmSAdFAnrCM=
go.opentelemetry.io/contrib/bridges/otelslog v0.14.0/go.mod h1:CRGvIBL/aAxpQU34ZxyQVFlovVcp67s4cAmQu8Jh9mc=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM=
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno=
go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY=
go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg=
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg=
go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -115,31 +283,58 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.6 h1:KafLdXvFUhzNeL2ncm03Gl3eTLONQfNKZ+wJ+9Y4Nck=
gorm.io/datatypes v1.2.6/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
gorm.io/driver/clickhouse v0.7.0 h1:BCrqvgONayvZRgtuA6hdya+eAW5P2QVagV3OlEp1vtA=
gorm.io/driver/clickhouse v0.7.0/go.mod h1:TmNo0wcVTsD4BBObiRnCahUgHJHjBIwuRejHwYt3JRs=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
@@ -158,3 +353,5 @@ gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
gorm.io/plugin/dbresolver v1.6.2 h1:F4b85TenghUeITqe3+epPSUtHH7RIk3fXr5l83DF8Pc=
gorm.io/plugin/dbresolver v1.6.2/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM=
gorm.io/plugin/opentelemetry v0.1.16 h1:Kypj2YYAliJqkIczDZDde6P6sFMhKSlG5IpngMFQGpc=
gorm.io/plugin/opentelemetry v0.1.16/go.mod h1:P3RmTeZXT+9n0F1ccUqR5uuTvEXDxF8k2UpO7mTIB2Y=

View File

@@ -0,0 +1,17 @@
package integration
import (
"git.kocoder.xyz/kocoded/vt/fx/interfaces"
"go.uber.org/fx"
)
type IntegrationHandler struct {
}
func NewIntegrationHandler(integrations []interfaces.BasicIntegration, lc fx.Lifecycle) *IntegrationHandler {
for _, i := range integrations {
lc.Append(fx.StartStopHook(i.OnStart, i.OnStop))
}
return &IntegrationHandler{}
}

View File

@@ -0,0 +1,84 @@
package mailing
import (
"context"
"fmt"
"time"
"git.kocoder.xyz/kocoded/vt/fx/interfaces"
imap "github.com/BrianLeishman/go-imap"
)
type imapIntegration struct {
id *imap.Dialer
}
// Invoke implements interfaces.BasicIntegration.
func (i *imapIntegration) Invoke() {
fmt.Println("Invoked!")
}
// OnStart implements interfaces.BasicIntegration.
func (i *imapIntegration) OnStart(ctx context.Context) error {
fmt.Println("Integration IMAP started")
return nil
}
// OnStop implements interfaces.BasicIntegration.
func (i *imapIntegration) OnStop(ctx context.Context) error {
fmt.Println("Integration IMAP stopped")
return nil
}
func NewImapIntegration() interfaces.BasicIntegration {
fmt.Println("New Imap Integration")
imap.Verbose = true
imap.RetryCount = 1
imap.DialTimeout = 10 * time.Second
imap.CommandTimeout = 30 * time.Second
m, err := imap.New("me@kocoder.xyz", "&,25,upMeddeEnTYPTifaccEptIonaAlErGiE", "mx.kocoder.xyz", 993)
if err != nil {
panic(err)
}
if err := m.SelectFolder("INBOX"); err != nil {
panic("Failed to select inbox")
}
uids, err := m.GetUIDs("UNSEEN")
if err != nil {
panic("Search")
}
emails, err := m.GetEmails(uids[:5]...)
if err != nil {
panic("Getting EMails")
}
for uid, email := range emails {
fmt.Printf("\n--- Email UID %d ---\n", uid)
fmt.Printf("From: %s\n", email.From)
fmt.Printf("Subject: %s\n", email.Subject)
fmt.Printf("Date: %s\n", email.Sent.Format("Jan 2, 2006 3:04 PM"))
fmt.Printf("Size: %.1f KB\n", float64(email.Size)/1024)
if len(email.Text) > 100 {
fmt.Printf("Preview: %.100s...\n", email.Text)
} else if len(email.Text) > 0 {
fmt.Printf("Preview: %s\n", email.Text)
}
if len(email.Attachments) > 0 {
fmt.Printf("Attachments: %d\n", len(email.Attachments))
for _, att := range email.Attachments {
fmt.Printf(" - %s (%.1f KB)\n", att.Name, float64(len(att.Content))/1024)
}
}
}
return &imapIntegration{id: m}
}

116
interceptors/auth.go Normal file
View File

@@ -0,0 +1,116 @@
package interceptors
import (
"context"
"errors"
"log/slog"
"strings"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoded/vt/cmd/mailingest/query"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
"git.kocoder.xyz/kocoded/vt/model"
"github.com/coreos/go-oidc/v3/oidc"
"gorm.io/gen/field"
"gorm.io/gorm"
)
const tokenHeader = "authentication"
const alternativeTokenHeader = "Authorization"
type AuthenticationInterceptor struct {
verifier *oidc.IDTokenVerifier
sr stores.SessionStore
logger *slog.Logger
db *gorm.DB
}
// WrapStreamingClient implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapStreamingClient(connect.StreamingClientFunc) connect.StreamingClientFunc {
panic("unimplemented")
}
// WrapStreamingHandler implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapStreamingHandler(connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
panic("unimplemented")
}
// WrapUnary implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(
ctx context.Context,
req connect.AnyRequest,
) (connect.AnyResponse, error) {
if req.Spec().IsClient {
panic(errors.New(""))
} else {
authToken := req.Header().Get(tokenHeader)
if authToken == "" {
authToken = req.Header().Get(alternativeTokenHeader)
}
if ok := strings.HasPrefix(authToken, "Bearer "); !ok {
a.logger.Info("No Authtoken with Bearer Prefix provided")
return nil, connect.NewError(
connect.CodeUnauthenticated,
errors.New("No valid token provided"),
)
}
authToken = strings.TrimPrefix(authToken, "Bearer ")
IDToken, err := a.verifier.Verify(context.TODO(), authToken)
if err != nil {
a.logger.Info("No valid/verifyable Authtoken provided", "err", err)
return nil, err
}
session, err := a.sr.GetSessionFromToken(IDToken.Subject)
if err != nil {
q := query.Use(a.db)
u := q.User
claims := model.User{}
if err := IDToken.Claims(&claims); err != nil {
a.logger.Error("Failed to parse claims from IDToken", "err", err)
panic(err)
}
a.logger.Info("Parsed Claims", "claims", claims)
us, err := u.Where(u.Sub.Eq(IDToken.Subject)).Assign(field.Attrs(&claims)).FirstOrCreate()
if err != nil {
a.logger.Error("Failed to get or create user from DB", "err", err, "sub", IDToken.Subject)
return nil, connect.NewError(connect.CodeInternal, errors.New("Failed to create user in DB"))
}
session = &stores.Session{Token: IDToken.Subject, UserID: us.ID, MandantId: 1}
a.sr.AddSession(session)
}
ctx = NewSessionContext(ctx, session)
}
return next(ctx, req)
}
}
func NewOIDCInterceptor(verifier *oidc.IDTokenVerifier, sr stores.SessionStore, logger *slog.Logger, db *gorm.DB) *AuthenticationInterceptor {
return &AuthenticationInterceptor{verifier: verifier, sr: sr, logger: logger, db: db}
}
type key int
var sessionKey key
func NewSessionContext(ctx context.Context, u *stores.Session) context.Context {
return context.WithValue(ctx, sessionKey, u)
}
// FromContext returns the User value stored in ctx, if any.
func SessionFromContext(ctx context.Context) (*stores.Session, bool) {
u, ok := ctx.Value(sessionKey).(*stores.Session)
return u, ok
}

29
interceptors/db.go Normal file
View File

@@ -0,0 +1,29 @@
package interceptors
import (
"context"
"net/http"
"gorm.io/gorm"
)
var dbKey key
func NewDBContext(ctx context.Context, db *gorm.DB) context.Context {
return context.WithValue(ctx, dbKey, db)
}
// FromContext returns the User value stored in ctx, if any.
func DBFromContex(ctx context.Context) (*gorm.DB, bool) {
u, ok := ctx.Value(dbKey).(*gorm.DB)
return u, ok
}
func AddDBToContext(next http.Handler, db *gorm.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = NewDBContext(ctx, db)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}

View File

@@ -1,6 +1,8 @@
package model
import "gorm.io/gorm"
import (
"gorm.io/gorm"
)
type Projekt struct {
gorm.Model

27
model/task.go Normal file
View File

@@ -0,0 +1,27 @@
package model
import "gorm.io/gorm"
type Status int
const (
Todo Status = iota
NeedsMoreInfo
Started
Done
)
func (s Status) IsDone() bool {
return s == Done
}
type Task struct {
gorm.Model
ProjektID uint
Projekt Projekt
MandantID uint
Mandant Mandant
Titel string `json:"titel"`
Description string `json:"description"`
Status Status `json:"status"`
}

View File

@@ -5,6 +5,9 @@
},
{
"path": "."
},
{
"path": "../eventory-mobile"
}
],
"settings": {}

View File

@@ -33,6 +33,7 @@ var (
Rechnung *rechnung
Rechnungsposition *rechnungsposition
Scanobject *scanobject
Task *task
User *user
Zahlung *zahlung
)
@@ -55,6 +56,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Rechnung = &Q.Rechnung
Rechnungsposition = &Q.Rechnungsposition
Scanobject = &Q.Scanobject
Task = &Q.Task
User = &Q.User
Zahlung = &Q.Zahlung
}
@@ -78,6 +80,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Rechnung: newRechnung(db, opts...),
Rechnungsposition: newRechnungsposition(db, opts...),
Scanobject: newScanobject(db, opts...),
Task: newTask(db, opts...),
User: newUser(db, opts...),
Zahlung: newZahlung(db, opts...),
}
@@ -102,6 +105,7 @@ type Query struct {
Rechnung rechnung
Rechnungsposition rechnungsposition
Scanobject scanobject
Task task
User user
Zahlung zahlung
}
@@ -127,6 +131,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
Rechnung: q.Rechnung.clone(db),
Rechnungsposition: q.Rechnungsposition.clone(db),
Scanobject: q.Scanobject.clone(db),
Task: q.Task.clone(db),
User: q.User.clone(db),
Zahlung: q.Zahlung.clone(db),
}
@@ -159,6 +164,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Rechnung: q.Rechnung.replaceDB(db),
Rechnungsposition: q.Rechnungsposition.replaceDB(db),
Scanobject: q.Scanobject.replaceDB(db),
Task: q.Task.replaceDB(db),
User: q.User.replaceDB(db),
Zahlung: q.Zahlung.replaceDB(db),
}
@@ -181,6 +187,7 @@ type queryCtx struct {
Rechnung IRechnungDo
Rechnungsposition IRechnungspositionDo
Scanobject IScanobjectDo
Task ITaskDo
User IUserDo
Zahlung IZahlungDo
}
@@ -203,6 +210,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Rechnung: q.Rechnung.WithContext(ctx),
Rechnungsposition: q.Rechnungsposition.WithContext(ctx),
Scanobject: q.Scanobject.WithContext(ctx),
Task: q.Task.WithContext(ctx),
User: q.User.WithContext(ctx),
Zahlung: q.Zahlung.WithContext(ctx),
}

607
query/tasks.gen.go Normal file
View File

@@ -0,0 +1,607 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"database/sql"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"git.kocoder.xyz/kocoded/vt/model"
)
func newTask(db *gorm.DB, opts ...gen.DOOption) task {
_task := task{}
_task.taskDo.UseDB(db, opts...)
_task.taskDo.UseModel(&model.Task{})
tableName := _task.taskDo.TableName()
_task.ALL = field.NewAsterisk(tableName)
_task.ID = field.NewUint(tableName, "id")
_task.CreatedAt = field.NewTime(tableName, "created_at")
_task.UpdatedAt = field.NewTime(tableName, "updated_at")
_task.DeletedAt = field.NewField(tableName, "deleted_at")
_task.ProjektID = field.NewUint(tableName, "projekt_id")
_task.MandantID = field.NewUint(tableName, "mandant_id")
_task.Titel = field.NewString(tableName, "titel")
_task.Description = field.NewString(tableName, "description")
_task.Status = field.NewInt(tableName, "status")
_task.Projekt = taskBelongsToProjekt{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Projekt", "model.Projekt"),
Mandant: struct {
field.RelationField
}{
RelationField: field.NewRelation("Projekt.Mandant", "model.Mandant"),
},
}
_task.Mandant = taskBelongsToMandant{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Mandant", "model.Mandant"),
}
_task.fillFieldMap()
return _task
}
type task struct {
taskDo
ALL field.Asterisk
ID field.Uint
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
ProjektID field.Uint
MandantID field.Uint
Titel field.String
Description field.String
Status field.Int
Projekt taskBelongsToProjekt
Mandant taskBelongsToMandant
fieldMap map[string]field.Expr
}
func (t task) Table(newTableName string) *task {
t.taskDo.UseTable(newTableName)
return t.updateTableName(newTableName)
}
func (t task) As(alias string) *task {
t.taskDo.DO = *(t.taskDo.As(alias).(*gen.DO))
return t.updateTableName(alias)
}
func (t *task) updateTableName(table string) *task {
t.ALL = field.NewAsterisk(table)
t.ID = field.NewUint(table, "id")
t.CreatedAt = field.NewTime(table, "created_at")
t.UpdatedAt = field.NewTime(table, "updated_at")
t.DeletedAt = field.NewField(table, "deleted_at")
t.ProjektID = field.NewUint(table, "projekt_id")
t.MandantID = field.NewUint(table, "mandant_id")
t.Titel = field.NewString(table, "titel")
t.Description = field.NewString(table, "description")
t.Status = field.NewInt(table, "status")
t.fillFieldMap()
return t
}
func (t *task) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := t.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (t *task) fillFieldMap() {
t.fieldMap = make(map[string]field.Expr, 11)
t.fieldMap["id"] = t.ID
t.fieldMap["created_at"] = t.CreatedAt
t.fieldMap["updated_at"] = t.UpdatedAt
t.fieldMap["deleted_at"] = t.DeletedAt
t.fieldMap["projekt_id"] = t.ProjektID
t.fieldMap["mandant_id"] = t.MandantID
t.fieldMap["titel"] = t.Titel
t.fieldMap["description"] = t.Description
t.fieldMap["status"] = t.Status
}
func (t task) clone(db *gorm.DB) task {
t.taskDo.ReplaceConnPool(db.Statement.ConnPool)
t.Projekt.db = db.Session(&gorm.Session{Initialized: true})
t.Projekt.db.Statement.ConnPool = db.Statement.ConnPool
t.Mandant.db = db.Session(&gorm.Session{Initialized: true})
t.Mandant.db.Statement.ConnPool = db.Statement.ConnPool
return t
}
func (t task) replaceDB(db *gorm.DB) task {
t.taskDo.ReplaceDB(db)
t.Projekt.db = db.Session(&gorm.Session{})
t.Mandant.db = db.Session(&gorm.Session{})
return t
}
type taskBelongsToProjekt struct {
db *gorm.DB
field.RelationField
Mandant struct {
field.RelationField
}
}
func (a taskBelongsToProjekt) Where(conds ...field.Expr) *taskBelongsToProjekt {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a taskBelongsToProjekt) WithContext(ctx context.Context) *taskBelongsToProjekt {
a.db = a.db.WithContext(ctx)
return &a
}
func (a taskBelongsToProjekt) Session(session *gorm.Session) *taskBelongsToProjekt {
a.db = a.db.Session(session)
return &a
}
func (a taskBelongsToProjekt) Model(m *model.Task) *taskBelongsToProjektTx {
return &taskBelongsToProjektTx{a.db.Model(m).Association(a.Name())}
}
func (a taskBelongsToProjekt) Unscoped() *taskBelongsToProjekt {
a.db = a.db.Unscoped()
return &a
}
type taskBelongsToProjektTx struct{ tx *gorm.Association }
func (a taskBelongsToProjektTx) Find() (result *model.Projekt, err error) {
return result, a.tx.Find(&result)
}
func (a taskBelongsToProjektTx) Append(values ...*model.Projekt) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a taskBelongsToProjektTx) Replace(values ...*model.Projekt) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a taskBelongsToProjektTx) Delete(values ...*model.Projekt) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a taskBelongsToProjektTx) Clear() error {
return a.tx.Clear()
}
func (a taskBelongsToProjektTx) Count() int64 {
return a.tx.Count()
}
func (a taskBelongsToProjektTx) Unscoped() *taskBelongsToProjektTx {
a.tx = a.tx.Unscoped()
return &a
}
type taskBelongsToMandant struct {
db *gorm.DB
field.RelationField
}
func (a taskBelongsToMandant) Where(conds ...field.Expr) *taskBelongsToMandant {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a taskBelongsToMandant) WithContext(ctx context.Context) *taskBelongsToMandant {
a.db = a.db.WithContext(ctx)
return &a
}
func (a taskBelongsToMandant) Session(session *gorm.Session) *taskBelongsToMandant {
a.db = a.db.Session(session)
return &a
}
func (a taskBelongsToMandant) Model(m *model.Task) *taskBelongsToMandantTx {
return &taskBelongsToMandantTx{a.db.Model(m).Association(a.Name())}
}
func (a taskBelongsToMandant) Unscoped() *taskBelongsToMandant {
a.db = a.db.Unscoped()
return &a
}
type taskBelongsToMandantTx struct{ tx *gorm.Association }
func (a taskBelongsToMandantTx) Find() (result *model.Mandant, err error) {
return result, a.tx.Find(&result)
}
func (a taskBelongsToMandantTx) Append(values ...*model.Mandant) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a taskBelongsToMandantTx) Replace(values ...*model.Mandant) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a taskBelongsToMandantTx) Delete(values ...*model.Mandant) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a taskBelongsToMandantTx) Clear() error {
return a.tx.Clear()
}
func (a taskBelongsToMandantTx) Count() int64 {
return a.tx.Count()
}
func (a taskBelongsToMandantTx) Unscoped() *taskBelongsToMandantTx {
a.tx = a.tx.Unscoped()
return &a
}
type taskDo struct{ gen.DO }
type ITaskDo interface {
gen.SubQuery
Debug() ITaskDo
WithContext(ctx context.Context) ITaskDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() ITaskDo
WriteDB() ITaskDo
As(alias string) gen.Dao
Session(config *gorm.Session) ITaskDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) ITaskDo
Not(conds ...gen.Condition) ITaskDo
Or(conds ...gen.Condition) ITaskDo
Select(conds ...field.Expr) ITaskDo
Where(conds ...gen.Condition) ITaskDo
Order(conds ...field.Expr) ITaskDo
Distinct(cols ...field.Expr) ITaskDo
Omit(cols ...field.Expr) ITaskDo
Join(table schema.Tabler, on ...field.Expr) ITaskDo
LeftJoin(table schema.Tabler, on ...field.Expr) ITaskDo
RightJoin(table schema.Tabler, on ...field.Expr) ITaskDo
Group(cols ...field.Expr) ITaskDo
Having(conds ...gen.Condition) ITaskDo
Limit(limit int) ITaskDo
Offset(offset int) ITaskDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) ITaskDo
Unscoped() ITaskDo
Create(values ...*model.Task) error
CreateInBatches(values []*model.Task, batchSize int) error
Save(values ...*model.Task) error
First() (*model.Task, error)
Take() (*model.Task, error)
Last() (*model.Task, error)
Find() ([]*model.Task, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Task, err error)
FindInBatches(result *[]*model.Task, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.Task) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) ITaskDo
Assign(attrs ...field.AssignExpr) ITaskDo
Joins(fields ...field.RelationField) ITaskDo
Preload(fields ...field.RelationField) ITaskDo
FirstOrInit() (*model.Task, error)
FirstOrCreate() (*model.Task, error)
FindByPage(offset int, limit int) (result []*model.Task, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Rows() (*sql.Rows, error)
Row() *sql.Row
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) ITaskDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (t taskDo) Debug() ITaskDo {
return t.withDO(t.DO.Debug())
}
func (t taskDo) WithContext(ctx context.Context) ITaskDo {
return t.withDO(t.DO.WithContext(ctx))
}
func (t taskDo) ReadDB() ITaskDo {
return t.Clauses(dbresolver.Read)
}
func (t taskDo) WriteDB() ITaskDo {
return t.Clauses(dbresolver.Write)
}
func (t taskDo) Session(config *gorm.Session) ITaskDo {
return t.withDO(t.DO.Session(config))
}
func (t taskDo) Clauses(conds ...clause.Expression) ITaskDo {
return t.withDO(t.DO.Clauses(conds...))
}
func (t taskDo) Returning(value interface{}, columns ...string) ITaskDo {
return t.withDO(t.DO.Returning(value, columns...))
}
func (t taskDo) Not(conds ...gen.Condition) ITaskDo {
return t.withDO(t.DO.Not(conds...))
}
func (t taskDo) Or(conds ...gen.Condition) ITaskDo {
return t.withDO(t.DO.Or(conds...))
}
func (t taskDo) Select(conds ...field.Expr) ITaskDo {
return t.withDO(t.DO.Select(conds...))
}
func (t taskDo) Where(conds ...gen.Condition) ITaskDo {
return t.withDO(t.DO.Where(conds...))
}
func (t taskDo) Order(conds ...field.Expr) ITaskDo {
return t.withDO(t.DO.Order(conds...))
}
func (t taskDo) Distinct(cols ...field.Expr) ITaskDo {
return t.withDO(t.DO.Distinct(cols...))
}
func (t taskDo) Omit(cols ...field.Expr) ITaskDo {
return t.withDO(t.DO.Omit(cols...))
}
func (t taskDo) Join(table schema.Tabler, on ...field.Expr) ITaskDo {
return t.withDO(t.DO.Join(table, on...))
}
func (t taskDo) LeftJoin(table schema.Tabler, on ...field.Expr) ITaskDo {
return t.withDO(t.DO.LeftJoin(table, on...))
}
func (t taskDo) RightJoin(table schema.Tabler, on ...field.Expr) ITaskDo {
return t.withDO(t.DO.RightJoin(table, on...))
}
func (t taskDo) Group(cols ...field.Expr) ITaskDo {
return t.withDO(t.DO.Group(cols...))
}
func (t taskDo) Having(conds ...gen.Condition) ITaskDo {
return t.withDO(t.DO.Having(conds...))
}
func (t taskDo) Limit(limit int) ITaskDo {
return t.withDO(t.DO.Limit(limit))
}
func (t taskDo) Offset(offset int) ITaskDo {
return t.withDO(t.DO.Offset(offset))
}
func (t taskDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ITaskDo {
return t.withDO(t.DO.Scopes(funcs...))
}
func (t taskDo) Unscoped() ITaskDo {
return t.withDO(t.DO.Unscoped())
}
func (t taskDo) Create(values ...*model.Task) error {
if len(values) == 0 {
return nil
}
return t.DO.Create(values)
}
func (t taskDo) CreateInBatches(values []*model.Task, batchSize int) error {
return t.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (t taskDo) Save(values ...*model.Task) error {
if len(values) == 0 {
return nil
}
return t.DO.Save(values)
}
func (t taskDo) First() (*model.Task, error) {
if result, err := t.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.Task), nil
}
}
func (t taskDo) Take() (*model.Task, error) {
if result, err := t.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.Task), nil
}
}
func (t taskDo) Last() (*model.Task, error) {
if result, err := t.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.Task), nil
}
}
func (t taskDo) Find() ([]*model.Task, error) {
result, err := t.DO.Find()
return result.([]*model.Task), err
}
func (t taskDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Task, err error) {
buf := make([]*model.Task, 0, batchSize)
err = t.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (t taskDo) FindInBatches(result *[]*model.Task, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return t.DO.FindInBatches(result, batchSize, fc)
}
func (t taskDo) Attrs(attrs ...field.AssignExpr) ITaskDo {
return t.withDO(t.DO.Attrs(attrs...))
}
func (t taskDo) Assign(attrs ...field.AssignExpr) ITaskDo {
return t.withDO(t.DO.Assign(attrs...))
}
func (t taskDo) Joins(fields ...field.RelationField) ITaskDo {
for _, _f := range fields {
t = *t.withDO(t.DO.Joins(_f))
}
return &t
}
func (t taskDo) Preload(fields ...field.RelationField) ITaskDo {
for _, _f := range fields {
t = *t.withDO(t.DO.Preload(_f))
}
return &t
}
func (t taskDo) FirstOrInit() (*model.Task, error) {
if result, err := t.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.Task), nil
}
}
func (t taskDo) FirstOrCreate() (*model.Task, error) {
if result, err := t.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.Task), nil
}
}
func (t taskDo) FindByPage(offset int, limit int) (result []*model.Task, count int64, err error) {
result, err = t.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = t.Offset(-1).Limit(-1).Count()
return
}
func (t taskDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = t.Count()
if err != nil {
return
}
err = t.Offset(offset).Limit(limit).Scan(result)
return
}
func (t taskDo) Scan(result interface{}) (err error) {
return t.DO.Scan(result)
}
func (t taskDo) Delete(models ...*model.Task) (result gen.ResultInfo, err error) {
return t.DO.Delete(models)
}
func (t *taskDo) withDO(do gen.Dao) *taskDo {
t.DO = *do.(*gen.DO)
return t
}

48
repositories/message.go Normal file
View File

@@ -0,0 +1,48 @@
package repositories
import (
"context"
"errors"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
)
type InMemoryMessageRepository struct {
m map[int]*stores.Message
}
func (i *InMemoryMessageRepository) AddMessage(_ context.Context, m *stores.Message) error {
i.m[m.Id] = m
return nil
}
func (i *InMemoryMessageRepository) GetMessage(ctx context.Context, id int) (*stores.Message, error) {
m, ok := i.m[id]
if !ok {
return nil, errors.New("Message not found")
}
return m, nil
}
func (i *InMemoryMessageRepository) ListMessages(ctx context.Context) ([]*stores.Message, error) {
messages := make([]*stores.Message, 0, len(i.m))
for _, v := range i.m {
messages = append(messages, v)
}
return messages, nil
}
func (i *InMemoryMessageRepository) RemoveMessage(ctx context.Context, id int) error {
delete(i.m, id)
return nil
}
func NewInMemeoryMessageRepository() stores.MessageStore {
return &InMemoryMessageRepository{}
}

78
repositories/session.go Normal file
View File

@@ -0,0 +1,78 @@
package repositories
import (
"context"
"fmt"
"log/slog"
"strconv"
"time"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
glide "github.com/valkey-io/valkey-glide/go/v2"
)
type SessionRepository struct {
store *glide.Client
logger *slog.Logger
}
func NewSessionRepository(store *glide.Client, logger *slog.Logger) stores.SessionStore {
return &SessionRepository{store: store, logger: logger}
}
func (sr *SessionRepository) AddSession(s *stores.Session) {
// options.HSetExOptions{Expiry: options.NewExpiryIn(time.Hour * 2)}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
_, err := sr.store.HSet(ctx, s.Token, s.Serialize())
if err != nil {
sr.logger.Error("Error adding session to store", "error", err)
panic(err)
}
_, err = sr.store.Expire(context.Background(), s.Token, time.Hour*2)
if err != nil {
sr.logger.Error("Error setting session expiration", "error", err)
panic(err)
}
}
func (sr *SessionRepository) GetSessionFromToken(token string) (*stores.Session, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
s, err := sr.store.HGetAll(ctx, token)
if err != nil {
sr.logger.Error("Error getting session from store", "error", err)
fmt.Println("PANIC - HGETALL")
panic(err)
}
_, err = sr.store.Expire(ctx, token, time.Hour*2)
if err != nil {
sr.logger.Error("Error setting session expiration", "error", err)
panic(err)
}
return (&stores.Session{}).Deserialize(token, s)
}
func (sr *SessionRepository) SetMandantInSession(token string, mandantId uint) error {
_, err := sr.store.HSet(context.Background(), token, map[string]string{"mandantid": strconv.Itoa(int(mandantId))})
if err != nil {
sr.logger.Error("Error setting mandant in session", "error", err)
return err
}
return nil
}
func (sr *SessionRepository) RemoveSession(token string) {
_, err := sr.store.HDel(context.Background(), token, []string{"userid", "mandantid"})
if err != nil {
sr.logger.Error("Error removing session from store", "error", err)
panic(err)
}
}

View File

@@ -1,128 +0,0 @@
package routers
import (
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/fiber/v2"
)
type ansprechpartnerRouter struct {
utils.Application
}
func RegisterAnsprechpartnerRouter(group fiber.Router, appCtx utils.Application) {
router := &ansprechpartnerRouter{Application: appCtx}
r := group.Use(utils.IsAuthenticated(appCtx))
r.Post("/new", router.createAnsprechpartner)
r.Get("/all", router.getAllAnsprechpartners)
r.Get("/:id<int>", router.getAnsprechpartner)
r.Get("/:id<int>/firmen", router.getAnsprechpartnerFirmen)
r.Put("/:id<int>", router.updateAnsprechpartner)
r.Delete("/:id<int>", router.deleteAnsprechpartner)
}
func (r *ansprechpartnerRouter) createAnsprechpartner(c *fiber.Ctx) error {
ansprechpartner := new(model.Ansprechpartner)
if err := c.BodyParser(ansprechpartner); err != nil {
return err
}
ap := query.Ansprechpartner
if err := ap.Omit(ap.UpdatedAt, ap.DeletedAt).Create(ansprechpartner); err != nil {
return err
}
return c.SendString("Hello")
}
func (r *ansprechpartnerRouter) getAllAnsprechpartners(c *fiber.Ctx) error {
aps, err := query.Ansprechpartner.Find()
if err != nil {
return err
}
err = c.JSON(aps)
if err != nil {
return err
}
return nil
}
func (r *ansprechpartnerRouter) getAnsprechpartner(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil {
return err
}
ap, err := query.Ansprechpartner.Where(query.Ansprechpartner.ID.Eq(uint(id))).First()
if err != nil {
return err
}
return c.JSON(ap)
}
func (r *ansprechpartnerRouter) getAnsprechpartnerFirmen(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil {
return err
}
ap, err := query.Ansprechpartner.Where(query.Ansprechpartner.ID.Eq(uint(id))).First()
if err != nil {
return err
}
firmen, err := query.Ansprechpartner.Firmen.Model(ap).Find()
if err != nil {
return err
}
ap.Firmen = firmen
return c.JSON(ap)
}
func (r *ansprechpartnerRouter) updateAnsprechpartner(c *fiber.Ctx) error {
ansprechpartner := new(model.Ansprechpartner)
id, err := c.ParamsInt("id")
if err != nil {
return err
}
if err = c.BodyParser(ansprechpartner); err != nil {
return err
}
ap := query.Ansprechpartner
res, err := ap.Where(ap.ID.Eq(uint(id))).Omit(ap.ID, ap.CreatedAt, ap.UpdatedAt, ap.DeletedAt).Updates(ansprechpartner)
if err != nil {
return err
}
return c.JSON(res)
}
func (r *ansprechpartnerRouter) deleteAnsprechpartner(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil {
return err
}
res, err := query.Ansprechpartner.Where(query.Ansprechpartner.ID.Value(uint(id))).Delete()
if err != nil {
return err
}
return c.JSON(res)
}

View File

@@ -1,105 +0,0 @@
package routers
import (
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/fiber/v2"
)
type firmaRouter struct {
utils.Application
}
func RegisterFirmaRouter(group fiber.Router, appCtx utils.Application) {
router := &firmaRouter{Application: appCtx}
r := group.Use(utils.IsAuthenticated(appCtx))
r.Post("/new", router.createFirma)
r.Get("/all", router.getAllFirmen)
r.Get("/:id<int>", router.getFirma)
r.Put("/:id<int>", router.updateFirma)
r.Delete("/:id<int>", router.deleteFirma)
}
func (r *firmaRouter) createFirma(c *fiber.Ctx) error {
firma := new(model.Firma)
if err := c.BodyParser(firma); err != nil {
return err
}
ap := query.Firma
if err := ap.Omit(ap.UpdatedAt, ap.DeletedAt).Create(firma); err != nil {
return err
}
return c.SendString("Hello")
}
func (r *firmaRouter) getAllFirmen(c *fiber.Ctx) error {
aps, err := query.Firma.Find()
if err != nil {
return err
}
err = c.JSON(aps)
if err != nil {
return err
}
return nil
}
func (r *firmaRouter) getFirma(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil {
return err
}
ap, err := query.Firma.Where(query.Firma.ID.Eq(uint(id))).First()
if err != nil {
return err
}
return c.JSON(ap)
}
func (r *firmaRouter) updateFirma(c *fiber.Ctx) error {
firma := new(model.Firma)
id, err := c.ParamsInt("id")
if err != nil {
return err
}
if err = c.BodyParser(firma); err != nil {
return err
}
ap := query.Firma
res, err := ap.Where(ap.ID.Eq(uint(id))).Omit(ap.ID, ap.CreatedAt, ap.UpdatedAt, ap.DeletedAt).Updates(firma)
if err != nil {
return err
}
return c.JSON(res)
}
func (r *firmaRouter) deleteFirma(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil {
return err
}
res, err := query.Firma.Where(query.Firma.ID.Value(uint(id))).Delete()
if err != nil {
return err
}
return c.JSON(res)
}

View File

@@ -1,65 +0,0 @@
package routers
import (
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/fiber/v2"
)
type mandantRouter struct {
utils.Application
currentMandant uint
}
func RegisterMandantRouter(group fiber.Router, appCtx utils.Application) {
router := &mandantRouter{currentMandant: 1, Application: appCtx}
r := group.Use(utils.IsAuthenticated(appCtx))
r.Get("/current", router.getCurrentMandant)
r.Put("/current", router.setCurrentMandant)
r.Get("/all", router.getAllMandant)
}
func (r *mandantRouter) getCurrentMandant(c *fiber.Ctx) error {
m := query.Mandant
currentMandant, err := m.Where(m.ID.Eq(r.currentMandant)).First()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(currentMandant)
}
func (r *mandantRouter) getAllMandant(c *fiber.Ctx) error {
m := query.Mandant
mandanten, err := m.Find()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(mandanten)
}
func (r *mandantRouter) setCurrentMandant(c *fiber.Ctx) error {
m := query.Mandant
mandant := &model.Mandant{}
if err := c.BodyParser(mandant); err != nil {
return err
}
r.currentMandant = mandant.ID
currentMandant, err := m.Where(m.ID.Eq(r.currentMandant)).First()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(currentMandant)
}

View File

@@ -0,0 +1,96 @@
package mandantv1
import (
"context"
"errors"
"log/slog"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoded/vt/fx"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
mandantv1 "git.kocoder.xyz/kocoded/vt/gen/mandant/v1"
"git.kocoder.xyz/kocoded/vt/gen/mandant/v1/mandantv1connect"
"git.kocoder.xyz/kocoded/vt/interceptors"
"git.kocoder.xyz/kocoded/vt/query"
"gorm.io/gorm"
)
func NewMandantRoute(logger *slog.Logger, db *gorm.DB, sr stores.SessionStore, oidcInterceptor *interceptors.AuthenticationInterceptor) fx.Handler {
path, handler := mandantv1connect.NewMandantServiceHandler(&mandantService{logger: logger, db: db, sr: sr}, connect.WithInterceptors(oidcInterceptor))
return fx.NewRoute(path, handler)
}
type mandantService struct {
logger *slog.Logger
db *gorm.DB
sr stores.SessionStore
}
func (m *mandantService) GetAllTenants(ctx context.Context, req *mandantv1.ListTenantRequest) (*mandantv1.ListProjectsResponse, error) {
ma := query.Use(m.db).Mandant
tenants, err := ma.Find()
if err != nil {
m.logger.Error("Error fetching tenants", "error", err)
return nil, connect.NewError(connect.CodeUnknown, err)
}
tenantResponses := make([]*mandantv1.GetTenantResponse, len(tenants))
for i, t := range tenants {
tenantResponses[i] = &mandantv1.GetTenantResponse{
Id: int64(t.ID),
Name: t.Name,
Plan: t.Plan,
Logo: t.Logo,
Color: t.Color,
}
}
return &mandantv1.ListProjectsResponse{
Data: tenantResponses,
Meta: &mandantv1.Metadata{
TotalCount: int32(len(tenants)),
},
}, nil
}
func (m *mandantService) GetCurrentTenant(ctx context.Context, req *mandantv1.GetCurrentTenantRequest) (*mandantv1.GetTenantResponse, error) {
s, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set."))
}
ma := query.Use(m.db).Mandant
res, err := ma.Where(ma.ID.Eq(s.MandantId)).First()
if err != nil {
m.logger.Error("Error fetching current tenant", "error", err)
return nil, connect.NewError(connect.CodeUnknown, err)
}
return &mandantv1.GetTenantResponse{
Id: int64(res.ID),
Name: res.Name,
Plan: res.Plan,
Logo: res.Logo,
Color: res.Color,
}, nil
}
func (m *mandantService) SetCurrentTenant(ctx context.Context, req *mandantv1.SetCurrentTenantRequest) (*mandantv1.SetCurrentTenantResponse, error) {
s, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set."))
}
err := m.sr.SetMandantInSession(s.Token, uint(req.TenantId))
if err != nil {
m.logger.Error("Error setting mandant in session", "error", err)
return nil, connect.NewError(connect.CodeUnknown, err)
}
return &mandantv1.SetCurrentTenantResponse{
Success: true,
}, nil
}

View File

@@ -0,0 +1,48 @@
syntax = "proto3";
package mandant.v1;
message GetCurrentTenantRequest {
}
message GetTenantRequest {
int64 id = 1;
}
message GetTenantResponse {
int64 id = 1;
string name = 2;
string plan = 3;
string logo = 4;
string color = 5;
}
message ListTenantRequest {
int32 page = 1;
int32 per_page = 2;
string orber_by = 3;
bool asc = 4;
}
message Metadata {
int32 totalCount = 1;
}
message ListProjectsResponse {
repeated GetTenantResponse data = 1;
Metadata meta = 2;
}
message SetCurrentTenantRequest {
int64 tenant_id = 1;
}
message SetCurrentTenantResponse {
bool success = 1;
}
service MandantService {
rpc GetCurrentTenant(GetCurrentTenantRequest) returns (GetTenantResponse);
rpc GetAllTenants(ListTenantRequest) returns (ListProjectsResponse);
rpc SetCurrentTenant(SetCurrentTenantRequest) returns (SetCurrentTenantResponse);
}

View File

@@ -0,0 +1,48 @@
syntax = "proto3";
package mandant.v1;
message GetCurrentTenantRequest {
}
message GetTenantRequest {
int64 id = 1;
}
message GetTenantResponse {
int64 id = 1;
string name = 2;
string plan = 3;
string logo = 4;
string color = 5;
}
message ListTenantRequest {
int32 page = 1;
int32 per_page = 2;
string orber_by = 3;
bool asc = 4;
}
message Metadata {
int32 totalCount = 1;
}
message ListProjectsResponse {
repeated GetTenantResponse data = 1;
Metadata meta = 2;
}
message SetCurrentTenantRequest {
int64 tenant_id = 1;
}
message SetCurrentTenantResponse {
bool success = 1;
}
service MandantService {
rpc GetCurrentTenant(GetCurrentTenantRequest) returns (GetTenantResponse);
rpc GetAllTenants(ListTenantRequest) returns (ListProjectsResponse);
rpc SetCurrentTenant(SetCurrentTenantRequest) returns (SetCurrentTenantResponse);
}

View File

@@ -0,0 +1,30 @@
package messagebusv1
import (
"context"
"time"
"connectrpc.com/connect"
kfx "git.kocoder.xyz/kocoded/vt/fx"
messagebusv1 "git.kocoder.xyz/kocoded/vt/gen/messagebus/v1"
"git.kocoder.xyz/kocoded/vt/gen/messagebus/v1/messagebusv1connect"
)
func NewMessagebusRoute() kfx.Handler {
path, handler := messagebusv1connect.NewMessageBusServiceHandler(&messagebusService{})
return kfx.NewRoute(path, handler)
}
type messagebusService struct {
}
func (mbs *messagebusService) SubscribeToConnectInvalidationRequests(ctx context.Context, req *messagebusv1.SubscribeToConnectInvalidationRequestsRequest, res *connect.ServerStream[messagebusv1.MessageBusEntity]) error {
for {
err := res.Send(&messagebusv1.MessageBusEntity{QueryKey: "Hello World!"})
if err != nil {
panic(err)
}
time.Sleep(time.Second * 2)
}
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
package messagebus.v1;
enum MessageBusEntityType {
OTHER = 0;
INVALIDATION_REQUEST = 1;
}
message MessageBusEntity {
string queryKey = 1;
}
message SubscribeToConnectInvalidationRequestsRequest {}
service MessageBusService {
rpc SubscribeToConnectInvalidationRequests(SubscribeToConnectInvalidationRequestsRequest) returns (stream MessageBusEntity);
}

View File

@@ -1,171 +0,0 @@
package routers
import (
"strconv"
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/fiber/v2"
)
type projectRouter struct {
utils.Application
currentMandant uint
}
func RegisterProjectRouter(group fiber.Router, appCtx utils.Application) {
router := &projectRouter{currentMandant: 1, Application: appCtx}
r := group.Use(utils.IsAuthenticated(appCtx))
r.Get("/all", router.getAllProjects)
r.Post("/new", router.createNewProject)
r.Get("/:id<int>", router.getProject)
r.Post("/:id<int>/edit", router.editProject)
r.Delete("/:id<int>/delete", router.deleteProject)
}
func (r *projectRouter) getAllProjects(c *fiber.Ctx) error {
p := query.Projekt
pph := c.Get("X-PER-PAGE")
ofh := c.Get("X-OFFSET")
params := struct {
Id string `params:"id"`
Desc bool `params:"desc"`
}{}
if err := c.QueryParser(&params); err != nil {
r.Logger.Warn("Param Parser Error: ", "err", err)
}
var pp, of int
pp, err := strconv.Atoi(pph)
if err != nil {
r.Logger.Warn("Per Page header not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
of, err = strconv.Atoi(ofh)
if err != nil {
r.Logger.Warn("Offset header not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
query := p.Where().Limit(pp).Offset(of)
if params.Id != "" {
f, ok := p.GetFieldByName(params.Id)
if ok {
if params.Desc {
query = query.Order(f.Desc())
} else {
query = query.Order(f.Asc())
}
}
} else {
query = query.Order(p.ID.Asc())
}
projects, err := query.Order(p.Name.Asc()).Find()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
cnt, err := p.Count()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
res := &PaginatedProjects{Data: projects, Meta: struct {
TotalProjectsCount int64 `json:"totalProjectsCount"`
}{TotalProjectsCount: cnt}}
return c.JSON(res)
}
type PaginatedProjects struct {
Data []*model.Projekt `json:"data"`
Meta struct {
TotalProjectsCount int64 `json:"totalProjectsCount"`
} `json:"meta"`
}
func (r *projectRouter) getProject(c *fiber.Ctx) error {
ids := c.Params("id")
id, err := strconv.ParseUint(ids, 10, 32)
if err != nil {
r.Logger.Warn("Id is not an int.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
p := query.Projekt
currentProject, err := p.Where(p.ID.Eq(uint(id))).First()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(currentProject)
}
func (r *projectRouter) createNewProject(c *fiber.Ctx) error {
p := query.Projekt
project := &model.Projekt{}
if err := c.BodyParser(project); err != nil {
return err
}
err := p.Create(project)
if err != nil {
r.Logger.Warn("Couldn't create Projejct.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
utils.MessageBus.SendMBObject(int(project.MandantID), utils.MessageBusObject{Entity: []string{"projects", "list"}})
utils.MessageBus.SendMBObject(int(project.MandantID), utils.MessageBusObject{Entity: []string{"projects", "get"}, Id: int(project.ID)})
return c.JSON(project)
}
func (r *projectRouter) editProject(c *fiber.Ctx) error {
p := query.Projekt
project := &model.Projekt{}
if err := c.BodyParser(project); err != nil {
return err
}
res, err := p.Where(p.ID.Eq(project.ID)).Updates(project)
if err != nil {
r.Logger.Warn("Couldn't create Projejct.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
utils.MessageBus.SendMBObject(int(project.MandantID), utils.MessageBusObject{Entity: []string{"projects", "list"}})
utils.MessageBus.SendMBObject(int(project.MandantID), utils.MessageBusObject{Entity: []string{"projects", "get"}, Id: int(project.ID)})
return c.JSON(res)
}
func (r *projectRouter) deleteProject(c *fiber.Ctx) error {
ids := c.Params("id")
id, err := strconv.ParseUint(ids, 10, 32)
if err != nil {
r.Logger.Warn("Id is not an int.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
p := query.Projekt
res, err := p.Where(p.ID.Eq(uint(id))).Delete()
if err != nil {
r.Logger.Warn("Couldn't create Projejct.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(res)
}

View File

@@ -0,0 +1,125 @@
package projectv1
import (
"context"
"errors"
"slices"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoded/vt/fx"
projectv1 "git.kocoder.xyz/kocoded/vt/gen/project/v1"
"git.kocoder.xyz/kocoded/vt/gen/project/v1/projectv1connect"
"git.kocoder.xyz/kocoded/vt/interceptors"
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"gorm.io/gorm"
)
func NewProjectRoute(db *gorm.DB, oidcInterceptor *interceptors.AuthenticationInterceptor) fx.Handler {
path, handler := projectv1connect.NewProjectServiceHandler(&projectService{db: db}, connect.WithInterceptors(oidcInterceptor))
return fx.NewRoute(path, handler)
}
type projectService struct{ db *gorm.DB }
// ListProjects implements projectv1connect.ProjectServiceHandler.
func (s *projectService) ListProjects(ctx context.Context, req *projectv1.ListProjectsRequest) (*projectv1.ListProjectsResponse, error) {
session, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set in interceptor"))
}
p := query.Use(s.db).Projekt
query := p.Where(p.MandantID.Eq(session.MandantId)).Limit(int(req.PerPage)).Offset(int(req.Page))
if req.OrberBy != "" {
f, ok := p.GetFieldByName(req.OrberBy)
if ok {
if !req.Asc {
query = query.Order(f.Desc())
} else {
query = query.Order(f.Asc())
}
}
} else {
query = query.Order(p.ID.Asc())
}
projects, err := query.Find()
if err != nil {
return nil, connect.NewError(connect.CodeUnknown, err)
}
cnt, err := p.Where(p.MandantID.Eq(session.MandantId)).Count()
if err != nil {
return nil, connect.NewError(connect.CodeUnimplemented, err)
}
data := make([]*projectv1.GetProjectResponse, len(projects))
for i, p := range projects {
data[i] = &projectv1.GetProjectResponse{
Id: int64(p.ID),
Name: p.Name,
Description: p.Description,
IsMaterialized: newPointerBoolean(true),
IsPersonalized: newPointerBoolean(true),
IsConfirmed: newPointerBoolean(true),
IsPaid: nil,
IsDone: s.IsDone(p),
}
}
return &projectv1.ListProjectsResponse{
Data: data,
Meta: &projectv1.Metadata{TotalCount: int32(cnt)},
}, nil
}
func newPointerBoolean(val bool) *bool {
b := val
return &b
}
func (s *projectService) IsDone(p *model.Projekt) *bool {
t := query.Use(s.db).Task
tasks, err := t.Where(t.ProjektID.Eq(p.ID)).Find()
if err != nil {
panic(err)
}
if len(tasks) == 0 {
return nil
}
if i := slices.IndexFunc(tasks, func(t *model.Task) bool {
return !t.Status.IsDone()
}); i != -1 {
return newPointerBoolean(false)
}
return newPointerBoolean(true)
}
func (s *projectService) GetProject(ctx context.Context, req *projectv1.GetProjectRequest) (*projectv1.GetProjectResponse, error) {
session, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set in interceptor"))
}
p := query.Use(s.db).Projekt
query, err := p.Where(p.ID.Eq(uint(req.Id))).Where(p.MandantID.Eq(session.MandantId)).First()
if err != nil {
return nil, err
}
return &projectv1.GetProjectResponse{
Id: int64(query.ID),
Name: query.Name,
Description: query.Description,
IsMaterialized: newPointerBoolean(true),
IsPersonalized: newPointerBoolean(true),
IsConfirmed: newPointerBoolean(true),
IsPaid: newPointerBoolean(true),
IsDone: newPointerBoolean(true),
}, nil
}

View File

@@ -0,0 +1,39 @@
syntax = "proto3";
package project.v1;
message GetProjectRequest {
int32 id = 1;
}
message GetProjectResponse {
int64 id = 1;
string name = 2;
string description = 3;
optional bool is_materialized = 5;
optional bool is_personalized = 6;
optional bool is_confirmed = 7;
optional bool is_paid = 8;
optional bool is_done = 9;
}
message ListProjectsRequest {
int32 page = 1;
int32 per_page = 2;
string orber_by = 3;
bool asc = 4;
}
message Metadata {
int32 totalCount = 1;
}
message ListProjectsResponse {
repeated GetProjectResponse data = 1;
Metadata meta = 2;
}
service ProjectService {
rpc GetProject(GetProjectRequest) returns (GetProjectResponse);
rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse);
}

182
routers/todo/v1/todo.go Normal file
View File

@@ -0,0 +1,182 @@
package todov1
import (
"context"
"errors"
"log/slog"
"strconv"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoded/vt/fx"
todov1 "git.kocoder.xyz/kocoded/vt/gen/todo/v1"
"git.kocoder.xyz/kocoded/vt/gen/todo/v1/todov1connect"
"git.kocoder.xyz/kocoded/vt/interceptors"
"git.kocoder.xyz/kocoded/vt/query"
"gorm.io/gen/field"
"gorm.io/gorm"
)
func NewTodoRoute(db *gorm.DB, logger *slog.Logger, oidcInterceptor *interceptors.AuthenticationInterceptor) fx.Handler {
path, handler := todov1connect.NewTodoServiceHandler(&todoService{logger: logger, db: db}, connect.WithInterceptors(oidcInterceptor))
return fx.NewRoute(path, handler)
}
type todoService struct {
logger *slog.Logger
db *gorm.DB
}
func (ts *todoService) GetTodo(ctx context.Context, req *todov1.GetTodosRequest) (*todov1.GetTodosResponse, error) {
s, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set."))
}
t := query.Use(ts.db).Task
res, err := t.Where(t.ID.Eq(uint(req.Id))).Where(t.MandantID.Eq(s.MandantId)).First()
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
tres := &todov1.GetTodosResponse{
Id: int64(res.ID),
Title: res.Titel,
Description: res.Description,
Status: todov1.Status(res.Status),
}
return tres, nil
// return &todov1.GetTodosResponse{
// Id: 1,
// Title: "Sample Todo",
// Description: "This is a sample todo item.",
// Status: todov1.Status_Doing,
// }, nil
}
func (ts *todoService) ContributeToQuery(f *todov1.Filter, q query.ITaskDo) query.ITaskDo {
t := query.Use(ts.db).Task
switch f.Field {
case todov1.Field_FieldDescription, todov1.Field_FieldTitle:
var field field.String
if f.Field == todov1.Field_FieldDescription {
field = t.Description
} else {
field = t.Titel
}
switch f.Operation {
case todov1.Operation_Equals:
q = q.Where(field.Eq(f.Value))
case todov1.Operation_NotEquals:
q = q.Where(field.Neq(f.Value))
case todov1.Operation_GreaterThan:
q = q.Where(field.Gt(f.Value))
case todov1.Operation_LessThan:
q = q.Where(field.Lt(f.Value))
case todov1.Operation_Like:
q = q.Where(field.Like(f.Value))
}
case todov1.Field_FieldId:
i, err := strconv.Atoi(f.Value)
if err != nil {
slog.Warn("Value not an int", "err", err)
return q
}
switch f.Operation {
case todov1.Operation_Equals:
q = q.Where(t.ID.Eq(uint(i)))
case todov1.Operation_NotEquals:
q = q.Where(t.ID.Neq(uint(i)))
case todov1.Operation_GreaterThan:
q = q.Where(t.ID.Gt(uint(i)))
case todov1.Operation_LessThan:
q = q.Where(t.ID.Lt(uint(i)))
}
case todov1.Field_FieldStatus:
i, err := strconv.Atoi(f.Value)
if err != nil {
slog.Warn("Value not an int", "err", err)
return q
}
switch f.Operation {
case todov1.Operation_Equals:
q = q.Where(t.Status.Eq(i))
case todov1.Operation_NotEquals:
q = q.Where(t.Status.Neq(i))
case todov1.Operation_GreaterThan:
q = q.Where(t.Status.Gt(i))
case todov1.Operation_LessThan:
q = q.Where(t.Status.Lt(i))
}
default:
return q
}
return q
}
func (ts *todoService) ListTodos(ctx context.Context, req *todov1.ListTodosRequest) (*todov1.ListTodosResponse, error) {
ts.logger.Info("ListTodos called", "request", req)
s, ok := interceptors.SessionFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("No session set."))
}
t := query.Use(ts.db).Task
taskQuery := t.Where(t.MandantID.Eq(s.MandantId))
for _, v := range req.Filters {
taskQuery = ts.ContributeToQuery(v, taskQuery)
}
taskQuery = taskQuery.Limit(int(req.PerPage)).Offset(int(req.Page))
if req.OrberBy != "" {
f, ok := t.GetFieldByName(req.OrberBy)
if ok {
if !req.Asc {
taskQuery = taskQuery.Order(f.Desc())
} else {
taskQuery = taskQuery.Order(f.Asc())
}
}
} else {
taskQuery = taskQuery.Order(t.ID.Asc())
}
tasks, err := taskQuery.Find()
if err != nil {
return nil, err
}
taskQuery = t.Where(t.MandantID.Eq(s.MandantId))
for _, v := range req.Filters {
taskQuery = ts.ContributeToQuery(v, taskQuery)
}
cnt, err := taskQuery.Count()
if err != nil {
ts.logger.Info("Counting Todos failed", "request", req, "err", err)
panic(err)
}
data := make([]*todov1.GetTodosResponse, len(tasks))
for i, p := range tasks {
data[i] = &todov1.GetTodosResponse{
Id: int64(p.ID),
Title: p.Titel,
Description: p.Description,
Status: todov1.Status(p.Status),
}
}
return &todov1.ListTodosResponse{
Data: data,
Meta: &todov1.Metadata{
TotalCount: int32(cnt),
},
}, nil
}

View File

@@ -0,0 +1,64 @@
syntax = "proto3";
package todo.v1;
enum Status {
Todo = 0;
NeedsMoreInfo = 1;
Doing = 2;
Done = 3;
}
enum Field {
FieldId = 0;
FieldTitle = 1;
FieldDescription = 2;
FieldStatus = 3;
}
enum Operation {
Equals = 0;
NotEquals = 1;
GreaterThan = 2;
LessThan = 3;
Like = 4;
}
message GetTodosRequest {
int32 id = 1;
}
message GetTodosResponse {
int64 id = 1;
string title = 2;
string description = 3;
Status status = 4;
}
message ListTodosRequest {
int32 page = 1;
int32 per_page = 2;
string orber_by = 3;
bool asc = 4;
repeated Filter filters = 5;
}
message Filter {
Field field = 1;
string value = 2;
Operation operation = 3;
}
message Metadata {
int32 totalCount = 1;
}
message ListTodosResponse {
repeated GetTodosResponse data = 1;
Metadata meta = 2;
}
service TodoService {
rpc GetTodo(GetTodosRequest) returns (GetTodosResponse);
rpc ListTodos(ListTodosRequest) returns (ListTodosResponse);
}

View File

@@ -1,32 +0,0 @@
package routers
import (
"git.kocoder.xyz/kocoded/vt/query"
"git.kocoder.xyz/kocoded/vt/utils"
"github.com/gofiber/fiber/v2"
)
type userRouter struct {
utils.Application
}
func RegisterUserRouter(group fiber.Router, appCtx utils.Application) {
router := &userRouter{Application: appCtx}
r := group.Use(utils.IsAuthenticated(appCtx))
r.Get("/current", router.getCurrentUserInfo)
}
func (r *userRouter) getCurrentUserInfo(c *fiber.Ctx) error {
u := query.User
session := c.Locals("USER_KEY").(*utils.Session)
currentUser, err := u.Where(u.ID.Eq(session.UserID)).First()
if err != nil {
r.Logger.Warn("Current mandant not found.", "error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(currentUser)
}

View File

@@ -1,84 +0,0 @@
package utils
import (
"context"
"errors"
"log/slog"
"strconv"
"time"
glide "github.com/valkey-io/valkey-glide/go/v2"
"gorm.io/gorm"
)
type Application struct {
Logger *slog.Logger
DB *gorm.DB
Client *glide.Client
}
type Session struct {
Token string
UserID uint
MandantId uint
}
func (s *Session) Deserialize(t string, m map[string]string) (*Session, error) {
userid, err := strconv.Atoi(m["userid"])
if err != nil {
return nil, errors.New("Userid from cache not an int")
}
mandantid, err := strconv.Atoi(m["mandantid"])
if err != nil {
return nil, errors.New("Mandantid from cache not an int")
}
s.Token = t
s.UserID = uint(userid)
s.MandantId = uint(mandantid)
return s, nil
}
func (s *Session) Serialize() map[string]string {
m := make(map[string]string)
m["userid"] = strconv.Itoa(int(s.UserID))
m["mandantid"] = strconv.Itoa(int(s.MandantId))
return m
}
func (a *Application) AddSession(s *Session) {
// options.HSetExOptions{Expiry: options.NewExpiryIn(time.Hour * 2)}
_, err := a.Client.HSet(context.Background(), s.Token, s.Serialize())
if err != nil {
panic(err)
}
_, err = a.Client.Expire(context.Background(), s.Token, time.Hour*2)
if err != nil {
panic(err)
}
}
func (a *Application) GetSessionFromToken(token string) (*Session, error) {
s, err := a.Client.HGetAll(context.Background(), token)
if err != nil {
panic(err)
}
_, err = a.Client.Expire(context.Background(), token, time.Hour*2)
if err != nil {
panic(err)
}
return (&Session{}).Deserialize(token, s)
}
func (a *Application) RemoveSession(token string) {
_, err := a.Client.HDel(context.Background(), token, []string{"userid", "mandantid"})
if err != nil {
panic(err)
}
}

View File

@@ -1,173 +0,0 @@
package utils
import (
"context"
"fmt"
"net/http"
"os"
"time"
"git.kocoder.xyz/kocoded/vt/model"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"golang.org/x/oauth2"
)
func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) {
setCallbackCookieExp(w, r, name, value, int(time.Hour.Seconds()))
}
func setCallbackCookieExp(w http.ResponseWriter, r *http.Request, name, value string, maxAge int) {
c := &http.Cookie{
Name: name,
Value: value,
Path: "/",
MaxAge: maxAge,
Secure: r.TLS != nil,
HttpOnly: true,
}
http.SetCookie(w, c)
}
type Info struct {
}
func CreateOIDCClient(ctx context.Context, app *fiber.App, appCtx Application) {
provider, err := oidc.NewProvider(ctx, "https://keycloak.kocoder.xyz/realms/che")
if err != nil {
appCtx.Logger.Error("Error generating OIDC Provider. ", "error", err)
}
oauthConfig := oauth2.Config{
ClientID: os.Getenv("CLIENT_ID"),
ClientSecret: os.Getenv("CLIENT_SECRET"),
RedirectURL: os.Getenv("BACKEND_URI") + "/api/auth/callback",
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile", "email"},
}
app.Get("/api/auth", adaptor.HTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
state, err := RandString(16)
if err != nil {
appCtx.Logger.Warn("Unable to create a state", "error", err)
http.Error(w, "Unable to create a state", http.StatusInternalServerError)
}
setCallbackCookie(w, r, "state", state)
http.Redirect(w, r, oauthConfig.AuthCodeURL(state), http.StatusFound)
}))
app.Get("/api/auth/callback", adaptor.HTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
state, err := r.Cookie("state")
if err != nil {
appCtx.Logger.Warn("State cookie not found", "error", err)
http.Error(w, "state not found", http.StatusBadRequest)
return
}
if r.URL.Query().Get("state") != state.Value {
appCtx.Logger.Warn("State cookie and header not matching", "error", err)
http.Error(w, "states not matching", http.StatusBadRequest)
return
}
oauth2Token, err := oauthConfig.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
appCtx.Logger.Warn("Failed to exchange token", "error", err)
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
userInfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
appCtx.Logger.Warn("failed to get userinfo", "error", err)
http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
return
}
resp := struct {
Token *oauth2.Token
UserInfo *oidc.UserInfo
}{oauth2Token, userInfo}
claims := &model.User{}
err = resp.UserInfo.Claims(claims)
if err != nil {
panic(err)
}
fmt.Println(claims)
user := &model.User{}
if appCtx.DB.Where(model.User{Email: resp.UserInfo.Email}).Assign(claims).FirstOrCreate(user).Error != nil {
appCtx.Logger.Warn("Failed to create user in DB")
http.Error(w, "failed to create user", http.StatusInternalServerError)
return
}
setCallbackCookieExp(w, r, "state", "", -1)
cookie, err := RandString(24)
if err != nil {
appCtx.Logger.Warn("Couldn't generate session-cookie.")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
setCallbackCookieExp(w, r, "auth-cookie", cookie, int(time.Hour.Seconds()))
appCtx.AddSession(&Session{Token: cookie, UserID: user.ID, MandantId: 1})
http.Redirect(w, r, os.Getenv("FRONTEND_URI")+"/dashboard", http.StatusFound)
}))
app.Get("/api/auth/currentSession", func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
session, err := appCtx.GetSessionFromToken(authToken)
if err != nil {
return err
}
return c.JSON(session)
})
app.Get("/api/auth/logout", func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
appCtx.RemoveSession(authToken)
cookie := new(fiber.Cookie)
cookie.Name = "auth-cookie"
cookie.Expires = time.Now().Add(-1 * time.Minute)
c.Cookie(cookie)
return c.Redirect(os.Getenv("FRONTEND_URI"))
})
}
type keyType struct{}
var UserKey keyType
func IsAuthenticated(appCtx Application) fiber.Handler {
fmt.Println("Gettings Session")
return func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
fmt.Println("Gettings Session", "sessiontoken", authToken)
session, err := appCtx.GetSessionFromToken(authToken)
if err != nil {
appCtx.Logger.Warn("Unauthorized GET Attempt", "reason", err)
return c.SendStatus(fiber.StatusUnauthorized)
}
fmt.Println("Saving Session", "session", session)
c.Locals("USER_KEY", session)
return c.Next()
}
}

View File

@@ -1,35 +1,43 @@
package utils
// Keep (used in cmd/api/main.go)
// Cache utilities for Valkey Glide
import (
"context"
"fmt"
"log/slog"
"os"
"strconv"
glide "github.com/valkey-io/valkey-glide/go/v2"
"github.com/valkey-io/valkey-glide/go/v2/config"
)
func SetupCache(host, port, user, pass string) (*glide.Client, bool) {
func NewCache(logger *slog.Logger) *glide.Client {
user := os.Getenv("VALKEY_USER")
pass := os.Getenv("VALKEY_PASS")
host := os.Getenv("VALKEY_HOST")
port := os.Getenv("VALKEY_PORT")
p, err := strconv.Atoi(port)
if err != nil {
fmt.Println("VALKEY_PORT is not a number")
return nil, false
logger.Error("VALKEY_PORT is not a number", "error", err, "port", port)
panic(err)
}
config := config.NewClientConfiguration().WithAddress(&config.NodeAddress{Host: host, Port: p}).WithCredentials(config.NewServerCredentials(user, pass))
client, err := glide.NewClient(config)
if err != nil {
fmt.Println("There was an error: ", err)
return nil, false
logger.Error("There was an error in glide.NewClient!", "err", err)
panic(err)
}
res, err := client.Ping(context.Background())
_, err = client.Ping(context.Background())
if err != nil {
fmt.Println("There was an error: ", err)
return nil, false
logger.Error("There was an error pinging the client initially!", "err", err)
panic(err)
}
fmt.Println(res) // PONG
return client, true
return client
}

View File

@@ -1,21 +1,30 @@
package utils
// Keep (used in cmd/api/main.go)
// Database utilities for GORM
// using PostgreSQL driver and OpenTelemetry tracing
import (
"log/slog"
"os"
"git.kocoder.xyz/kocoded/vt/model"
"git.kocoder.xyz/kocoded/vt/query"
"gorm.io/driver/postgres"
"gorm.io/gen"
"gorm.io/gorm"
"gorm.io/plugin/opentelemetry/tracing"
)
func SetupDatabase(dsn string, logger *slog.Logger) *gorm.DB {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
func NewDB(logger *slog.Logger) *gorm.DB {
db, err := gorm.Open(postgres.Open(os.Getenv("DB_DSN")), &gorm.Config{})
if err != nil {
logger.Error("Error connecting to the Database", "error", err)
}
if err := db.Use(tracing.NewPlugin()); err != nil {
panic(err)
}
err = db.SetupJoinTable(model.Ansprechpartner{}, "Firmen", model.FirmaAnsprechpartner{})
if err != nil {
logger.Error("Error setting up Join Tables", "error", err)
@@ -24,26 +33,10 @@ func SetupDatabase(dsn string, logger *slog.Logger) *gorm.DB {
if err != nil {
logger.Error("Error setting up Join Tables", "error", err)
}
err = db.AutoMigrate(&model.Mandant{}, &model.User{}, &model.Ansprechpartner{}, &model.FirmaAnsprechpartner{}, &model.Firma{}, &model.Projekt{})
err = db.AutoMigrate(&model.Mandant{}, &model.User{}, &model.Ansprechpartner{}, &model.FirmaAnsprechpartner{}, &model.Firma{}, &model.Projekt{}, &model.Task{})
if err != nil {
logger.Error("Error setting up Join Tables", "error", err)
}
g := gen.NewGenerator(gen.Config{
OutPath: "query",
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
})
// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
g.UseDB(db) // reuse your gorm db
// Generate basic type-safe DAO API for struct `model.User` following conventions
g.ApplyBasic(model.Mandant{}, model.User{}, model.Ansprechpartner{}, model.Dokument{}, model.Firma{}, model.Kalender{}, model.Kalendereintrag{}, model.Kostenstelle{}, model.Lager{}, model.Lagerplatz{}, model.Material{}, model.Nachricht{}, model.Projekt{}, model.Rechnung{}, model.Rechnungsposition{}, model.Scanobject{}, model.User{}, model.Zahlung{}, model.FirmaAnsprechpartner{})
// Generate the code
g.Execute()
query.SetDefault(db)
return db
}

26
utils/healthcheck.go Normal file
View File

@@ -0,0 +1,26 @@
package utils
// Keep (used in cmd/api/main.go)
// Healthcheck utilities for gRPC services
// using connect-go and grpchealth
import (
"connectrpc.com/grpchealth"
"git.kocoder.xyz/kocoded/vt/fx"
"git.kocoder.xyz/kocoded/vt/gen/project/v1/projectv1connect"
"git.kocoder.xyz/kocoded/vt/gen/todo/v1/todov1connect"
)
func NewHealthchecker() grpchealth.Checker {
checker := grpchealth.NewStaticChecker()
checker.SetStatus(projectv1connect.ProjectServiceName, grpchealth.StatusServing)
checker.SetStatus(todov1connect.TodoServiceName, grpchealth.StatusServing)
return checker
}
func NewHealthCheckV1(healthChecker grpchealth.Checker) fx.Handler {
path, handler := grpchealth.NewHandler(healthChecker)
return fx.NewRoute(path, handler)
}

View File

@@ -1,68 +0,0 @@
package utils
import (
"encoding/json"
"io"
"log/slog"
"github.com/gofiber/contrib/websocket"
)
var MessageBus = &messagebus{mandanten: make(map[int][]*websocket.Conn)}
type messagebus struct {
mandanten map[int][]*websocket.Conn
}
func (mb *messagebus) AddConn(mid int, c *websocket.Conn) {
mb.mandanten[mid] = append(mb.mandanten[mid], c)
ReadLoop(c)
}
func ReadLoop(c *websocket.Conn) {
for {
var t int
var r io.Reader
var err error
if t, r, err = c.NextReader(); err != nil {
c.Close()
break
}
bytes, err := io.ReadAll(r)
slog.Info("READLOOP: ", "Type", t, "value", bytes)
}
}
func (mb *messagebus) SendMBObject(mid int, mbo MessageBusObject) {
bytes, err := json.Marshal(mbo)
if err != nil {
slog.Info("marshal", "error", err)
}
i := 0
for _, c := range mb.mandanten[mid] {
if err := c.WriteMessage(websocket.TextMessage, bytes); err != nil {
slog.Info("conn to remove", "conns", mb.mandanten)
mb.RemoveConn(mid, i)
slog.Info("write", "error", err, "index", i)
} else {
i++
}
}
}
func (mb *messagebus) RemoveConn(mid int, i int) {
s := mb.mandanten[mid]
s[i] = s[len(s)-1]
mb.mandanten[mid] = s[:len(s)-1]
slog.Info("conn removed", "conns", mb.mandanten)
}
type MessageBusObject struct {
Entity []string `json:"entity"`
Id any `json:"id"`
}

View File

@@ -1,38 +0,0 @@
package utils
import (
"os"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/idempotency"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/monitor"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/gofiber/fiber/v2/middleware/skip"
)
func RegisterMiddlewares(app *fiber.App) {
app.Use(requestid.New())
app.Use(compress.New())
app.Use(helmet.New())
app.Use(cors.New(cors.Config{
AllowOrigins: os.Getenv("FRONTEND_URI"),
AllowCredentials: true,
}))
// app.Use(csrf.New())
// app.Use(healthcheck.New(healthcheck.Config{}))
app.Use(idempotency.New())
// app.Use(limiter.New())
app.Use(logger.New())
app.Use("/dbg/monitor", monitor.New())
app.Use(pprof.New())
app.Use(recover.New())
app.Use(skip.New(AddPaginationParams, func(c *fiber.Ctx) bool {
return c.Method() != fiber.MethodGet
}))
}

26
utils/oidc.go Normal file
View File

@@ -0,0 +1,26 @@
package utils
import (
"context"
"log/slog"
"os"
"github.com/coreos/go-oidc/v3/oidc"
)
func NewOIDCProvider(logger *slog.Logger) *oidc.Provider {
issuerUrl := os.Getenv("OIDC_ISSUER_URL")
provider, err := oidc.NewProvider(context.Background(), issuerUrl)
if err != nil {
logger.Error("Error generating OIDC Provider. ", "error", err, "url", issuerUrl)
panic(err)
}
return provider
}
func NewOIDCVerifier(provider *oidc.Provider) *oidc.IDTokenVerifier {
return provider.Verifier(&oidc.Config{ClientID: "account"})
}

View File

@@ -1,68 +0,0 @@
package utils
import (
"errors"
"strconv"
"github.com/gofiber/fiber/v2"
)
type OffsetPaginationError struct {
Page int
Pages int
NextPage int
LastPage int
}
func (p *OffsetPaginationError) Error() string {
return "Not an error, just for offsetbased pagination."
}
func NewOffsetPaginationError(page int, pages int) error {
var nextPage int
if page+1 <= pages {
nextPage = page + 1
} else {
nextPage = -1
}
return &OffsetPaginationError{Page: page, Pages: pages, NextPage: nextPage, LastPage: page - 1}
}
type KeysetPaginationError struct {
Key int
NextKey int
PreviousKey int
}
func (p *KeysetPaginationError) Error() string {
return "Not an error, just for Keysetbased pagination."
}
func NewKeysetPaginationError(key int, next int, previous int) error {
return &KeysetPaginationError{Key: key, NextKey: next, PreviousKey: previous}
}
func AddPaginationParams(c *fiber.Ctx) error {
err := c.Next()
if err != nil {
var offset *OffsetPaginationError
if errors.As(err, &offset) {
c.Append("X-Page", strconv.Itoa(offset.Page))
c.Append("X-Pages", strconv.Itoa(offset.Pages))
c.Append("X-Next-Page", strconv.Itoa(offset.NextPage))
c.Append("X-Last-Page", strconv.Itoa(offset.LastPage))
return nil
}
var keyset *KeysetPaginationError
if errors.As(err, &keyset) {
c.Append("X-Key", strconv.Itoa(keyset.Key))
c.Append("X-Previous-Key", strconv.Itoa(keyset.PreviousKey))
c.Append("X-Next-Key", strconv.Itoa(keyset.NextKey))
return nil
}
}
return err
}

View File

@@ -1,15 +0,0 @@
package utils
import (
"crypto/rand"
"encoding/base64"
"io"
)
func RandString(nByte int) (string, error) {
b := make([]byte, nByte)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(b), nil
}

37
utils/reflection.go Normal file
View File

@@ -0,0 +1,37 @@
package utils
// Keep (used in cmd/api/main.go)
// Reflection utilities for gRPC services
// using connect-go and grpcreflect
import (
"connectrpc.com/grpcreflect"
"git.kocoder.xyz/kocoded/vt/fx"
"git.kocoder.xyz/kocoded/vt/gen/mandant/v1/mandantv1connect"
"git.kocoder.xyz/kocoded/vt/gen/messagebus/v1/messagebusv1connect"
"git.kocoder.xyz/kocoded/vt/gen/project/v1/projectv1connect"
"git.kocoder.xyz/kocoded/vt/gen/todo/v1/todov1connect"
)
func NewReflector() *grpcreflect.Reflector {
strings := []string{
todov1connect.TodoServiceName,
projectv1connect.ProjectServiceName,
mandantv1connect.MandantServiceName,
messagebusv1connect.MessageBusServiceName,
}
return grpcreflect.NewReflector(grpcreflect.NamerFunc(func() []string {
return strings
}))
}
func NewReflectorV1(reflector *grpcreflect.Reflector) fx.Handler {
path, handler := grpcreflect.NewHandlerV1(reflector)
return fx.NewRoute(path, handler)
}
func NewReflectorV1Alpha1(reflector *grpcreflect.Reflector) fx.Handler {
path, handler := grpcreflect.NewHandlerV1Alpha(reflector)
return fx.NewRoute(path, handler)
}