Commit: Bulk unfinished work
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
35
utils/db.go
35
utils/db.go
@@ -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
26
utils/healthcheck.go
Normal 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)
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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
26
utils/oidc.go
Normal 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"})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
37
utils/reflection.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user