This commit is contained in:
r8zavetr8v 2024-11-23 03:52:06 -05:00
parent 24f225f68b
commit b9d8aa6525
30 changed files with 2004 additions and 117 deletions

12
.mockery.yaml Normal file
View File

@ -0,0 +1,12 @@
filename: "mock_{{.InterfaceName}}.go"
dir: "mocks/{{.PackagePath}}"
outpkg: "{{.PackageName}}"
with-expecter: true
packages:
git.optclblast.xyz/draincloud/draincloud-core/internal/storage:
interfaces:
Database:
AuthAuditLogStorage:
AuthStorage:
BlobStorage:
MetaStorage:

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// 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": "${workspaceFolder}/cmd/main.go"
}
]
}

View File

@ -2,20 +2,29 @@ package main
import ( import (
"context" "context"
"os"
"os/signal"
"git.optclblast.xyz/draincloud/draincloud-core/internal/app" "git.optclblast.xyz/draincloud/draincloud-core/internal/app"
cleanupsessions "git.optclblast.xyz/draincloud/draincloud-core/internal/cron/cleanup_sessions"
"git.optclblast.xyz/draincloud/draincloud-core/internal/plugin" "git.optclblast.xyz/draincloud/draincloud-core/internal/plugin"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/postgres" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/postgres"
) )
func main() { func main() {
ctx := context.Background() ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer cancel()
plugin.MustNewPluginLoader(ctx, 8081, plugin.NewPluginStore()). plugin.MustNewPluginLoader(ctx, 8081, plugin.NewPluginStore()).
Run(ctx) Run(ctx)
pg := postgres.New(ctx, "postgres://draincloud:draincloud@localhost:5432/draincloud?sslmode=disable") pg := postgres.New(ctx, "postgres://draincloud:draincloud@localhost:5432/draincloud?sslmode=disable")
app.New(pg). cleanupSessionsCron := cleanupsessions.New(pg)
cleanupSessionsCron.Run(ctx)
go app.New(pg).
Run(ctx) Run(ctx)
<-ctx.Done()
} }

4
go.mod
View File

@ -3,12 +3,14 @@ module git.optclblast.xyz/draincloud/draincloud-core
go 1.23.0 go 1.23.0
require ( require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/fatih/color v1.17.0 github.com/fatih/color v1.17.0
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/jackc/pgx/v5 v5.7.1 github.com/jackc/pgx/v5 v5.7.1
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.28.0
) )
@ -36,6 +38,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@ -43,6 +46,7 @@ require (
github.com/spf13/afero v1.11.0 // indirect github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect

1
go.sum
View File

@ -105,6 +105,7 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

View File

@ -2,31 +2,31 @@ package app
import ( import (
"context" "context"
"crypto/rand" "errors"
"encoding/base64"
"fmt"
"net/http" "net/http"
"time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain" "git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
filesengine "git.optclblast.xyz/draincloud/draincloud-core/internal/files_engine"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger" "git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
) )
type DrainCloud struct { type DrainCloud struct {
mux *gin.Engine mux *gin.Engine
datadase storage.Database database storage.Database
filesEngine *filesengine.FilesEngine
} }
func New( func New(
datadase storage.Database, database storage.Database,
filesEngine *filesengine.FilesEngine,
) *DrainCloud { ) *DrainCloud {
mux := gin.Default() mux := gin.Default()
d := &DrainCloud{ d := &DrainCloud{
datadase: datadase, database: database,
filesEngine: filesEngine,
} }
// Built-in auth component of DrainCloud-Core // Built-in auth component of DrainCloud-Core
@ -34,7 +34,17 @@ func New(
{ {
authGroup.POST("/register", d.Register) authGroup.POST("/register", d.Register)
authGroup.POST("/login", d.Login) authGroup.POST("/login", d.Login)
authGroup.POST("/logout", d.Logout) }
filesGroup := mux.Group("/files")
{
filesGroup.POST("/upload", func(ctx *gin.Context) {
err := d.uploadFile(ctx)
if err != nil {
logger.Error(ctx.Request.Context(), "", logger.Err(err))
ctx.String(400, err.Error())
}
})
} }
d.mux = mux d.mux = mux
@ -46,95 +56,22 @@ func (d *DrainCloud) Run(ctx context.Context) error {
return d.mux.Run() return d.mux.Run()
} }
func (d *DrainCloud) Register(ctx *gin.Context) { func writeError(ctx *gin.Context, err error) {
logger.Debug(ctx, "[register] new request") switch {
case errors.Is(err, ErrorAccessDenied):
req := new(domain.RegisterRequest) ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
err := ctx.BindJSON(req) Code: http.StatusForbidden,
if err != nil { Message: err.Error(),
logger.Error(ctx, "[register] failed to bind request", logger.Err(err))
ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "bad request",
}) })
return case errors.Is(err, ErrorSessionExpired):
} ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
_, err = d.register(ctx, req) Message: err.Error(),
if err != nil {
logger.Error(ctx, "[register] failed to register user", logger.Err(err))
ctx.JSON(http.StatusBadRequest, map[string]string{
"error": err.Error(),
}) })
return default:
} ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusInternalServerError,
ctx.JSON(http.StatusOK, map[string]bool{ Message: "Internal Error",
"ok": true,
}) })
} }
type registerResult struct {
// cookies []http.Cookie
}
func (d *DrainCloud) register(ctx *gin.Context, req *domain.RegisterRequest) (*registerResult, error) {
if err := validateLoginAndPassword(req.Login, req.Password); err != nil {
return nil, fmt.Errorf("invalid creds: %w", err)
}
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
if err != nil {
logger.Error(ctx, "[register] failed to generate password", logger.Err(err))
return nil, fmt.Errorf("failed to generate password: %w", err)
}
_, err = d.datadase.AddUser(ctx, req.Login, req.Login, passwordHash)
if err != nil {
return nil, fmt.Errorf("failed to add new user: %w", err)
}
sessionToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a session token: %w", err)
}
ctx.SetCookie("__Session_token", sessionToken, int((time.Hour * 24).Seconds()), "_path", "_domain", true, true)
csrfToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a csrf token: %w", err)
}
ctx.SetCookie("__Csrf_token", csrfToken, int((time.Hour * 24).Seconds()), "_path", "_domain", true, false)
// TODO save session into database
return &registerResult{}, nil
}
func validateLoginAndPassword(login, password string) error {
if len(login) < 4 {
return fmt.Errorf("login must be longer than 8 chars")
}
if len(password) < 6 {
return fmt.Errorf("password must be longer than 8 chars")
}
return nil
}
func generateSessionToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("failed to generate token: %w", err)
}
return base64.URLEncoding.EncodeToString(bytes), nil
}
func (d *DrainCloud) Login(ctx *gin.Context) {
}
func (d *DrainCloud) Logout(ctx *gin.Context) {
} }

63
internal/app/auth.go Normal file
View File

@ -0,0 +1,63 @@
package app
import (
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"log/slog"
"net/http"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/gin-gonic/gin"
)
const (
csrfTokenCookie = "__Csrf_token"
sessionTokenCookie = "__Session_token"
)
var (
ErrorUnauthorized = errors.New("unauthorized")
)
func (d *DrainCloud) authorize(ctx *gin.Context) (*models.Session, error) {
session, err := d.getSession(ctx)
if err != nil && !errors.Is(err, http.ErrNoCookie) {
return nil, ErrorUnauthorized
}
if session == nil {
return nil, ErrorUnauthorized
}
if err := validateSession(ctx, session); err != nil {
// TODO add audit log entry
return nil, ErrorUnauthorized
}
logger.Debug(ctx, "[authorize] user authorized", slog.Int64("session_id", session.ID))
return session, nil
}
func validateLoginAndPassword(login, password string) error {
if len(login) < 4 {
return fmt.Errorf("login must be longer than 8 chars")
}
if len(password) < 6 {
return fmt.Errorf("password must be longer than 8 chars")
}
return nil
}
func generateSessionToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("failed to generate token: %w", err)
}
return base64.URLEncoding.EncodeToString(bytes), nil
}

156
internal/app/login.go Normal file
View File

@ -0,0 +1,156 @@
package app
import (
"bytes"
"errors"
"fmt"
"log/slog"
"net/http"
"time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
)
var (
ErrorAccessDenied = errors.New("access denied")
ErrorSessionExpired = errors.New("session expired")
)
func (d *DrainCloud) Login(ctx *gin.Context) {
logger.Debug(ctx, "[Login] new request")
req := new(domain.LoginRequest)
err := ctx.BindJSON(req)
if err != nil {
logger.Error(ctx, "[Login] failed to bind request", logger.Err(err))
ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "bad request",
})
return
}
resp, err := d.login(ctx, req)
if err != nil {
logger.Error(ctx, "[Login] failed to login user", logger.Err(err))
ctx.JSON(http.StatusInternalServerError, map[string]string{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, resp)
}
func (d *DrainCloud) login(ctx *gin.Context, req *domain.LoginRequest) (*domain.LoginResponse, error) {
session, err := d.getSession(ctx)
if err != nil && !errors.Is(err, http.ErrNoCookie) {
return nil, err
}
if session != nil {
if err := validateSession(ctx, session); err != nil {
// TODO add audit log entry
return nil, err
}
logger.Debug(ctx, "[login] user is already logged in", slog.Int64("session_id", session.ID))
return &domain.LoginResponse{
Ok: true,
}, nil
}
logger.Debug(ctx, "[login] session not found. trying to authorize")
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
if err != nil {
logger.Error(ctx, "[login] failed to generate password hash", logger.Err(err))
return nil, fmt.Errorf("failed to generate password hash: %w", err)
}
user, err := d.database.GetUserByLogin(ctx, req.Login)
if err != nil {
return nil, fmt.Errorf("failed to fetch user by login: %w", err)
}
if bytes.Equal(passwordHash, user.PasswordHash) {
logger.Warn(ctx, "[login] failed to login user. passwords hashes not equal")
return nil, ErrorAccessDenied
}
sessionCreatedAt := time.Now()
sessionExpiredAt := sessionCreatedAt.Add(time.Hour * 24 * 7)
sessionToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a session token: %w", err)
}
ctx.SetCookie(sessionTokenCookie, sessionToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, true)
csrfToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a csrf token: %w", err)
}
ctx.SetCookie(csrfTokenCookie, csrfToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, false)
if _, err = d.database.AddSession(ctx, &models.Session{
SessionToken: sessionToken,
CsrfToken: csrfToken,
UserID: user.ID,
CreatedAt: sessionCreatedAt,
ExpiredAt: sessionExpiredAt,
}); err != nil {
return nil, fmt.Errorf("failed to save session: %w", err)
}
// TODO add audit log entry
return &domain.LoginResponse{
Ok: true,
}, nil
}
func (d *DrainCloud) getSession(ctx *gin.Context) (*models.Session, error) {
token, err := ctx.Cookie(sessionTokenCookie)
if err != nil {
return nil, fmt.Errorf("failed to fetch session cookie from request: %w", err)
}
csrfToken, err := ctx.Cookie(csrfTokenCookie)
if err != nil {
return nil, fmt.Errorf("failed to fetch csrf cookie from request: %w", err)
}
if len(csrfToken) == 0 || len(token) == 0 {
return nil, fmt.Errorf("session token or csrf token is empty")
}
session, err := d.database.GetSession(ctx, token)
if err != nil {
return nil, fmt.Errorf("failed to fetch session from repo: %w", err)
}
return session, nil
}
func validateSession(ctx *gin.Context, session *models.Session) error {
if session == nil {
return ErrorAccessDenied
}
csrfToken, err := ctx.Cookie(csrfTokenCookie)
if err != nil {
return fmt.Errorf("failed to fetch csrf cookie from request: %w", err)
}
if session.CsrfToken != csrfToken {
return ErrorAccessDenied
}
if session.ExpiredAt.Before(time.Now()) {
return ErrorSessionExpired
}
return nil
}

View File

@ -0,0 +1,28 @@
package app
import (
"git.optclblast.xyz/draincloud/draincloud-core/internal/reqcontext"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/gin-gonic/gin"
)
type authorizer interface {
authorize(ctx *gin.Context) (*models.Session, error)
}
func WithAuth(handler gin.HandlerFunc, auth authorizer) gin.HandlerFunc {
return func(ctx *gin.Context) {
sess, err := auth.authorize(ctx)
if err != nil {
writeError(ctx, err)
ctx.Abort()
return
}
authCtx := reqcontext.WithSession(ctx.Request.Context(), sess)
authCtx = reqcontext.WithUserID(authCtx, sess.UserID)
ctx.Request = ctx.Request.WithContext(authCtx)
handler(ctx)
}
}

93
internal/app/register.go Normal file
View File

@ -0,0 +1,93 @@
package app
import (
"fmt"
"net/http"
"time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
)
func (d *DrainCloud) Register(ctx *gin.Context) {
logger.Debug(ctx, "[register] new request")
// TODO check if registration is enabled
req := new(domain.RegisterRequest)
err := ctx.BindJSON(req)
if err != nil {
logger.Error(ctx, "[register] failed to bind request", logger.Err(err))
ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "bad request",
})
return
}
resp, err := d.register(ctx, req)
if err != nil {
logger.Error(ctx, "[register] failed to register user", logger.Err(err))
ctx.JSON(http.StatusInternalServerError, map[string]string{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, resp)
}
func (d *DrainCloud) register(ctx *gin.Context, req *domain.RegisterRequest) (*domain.RegisterResponse, error) {
if err := validateLoginAndPassword(req.Login, req.Password); err != nil {
return nil, fmt.Errorf("invalid creds: %w", err)
}
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
if err != nil {
logger.Error(ctx, "[register] failed to generate password hash", logger.Err(err))
return nil, fmt.Errorf("failed to generate password hash: %w", err)
}
user := &models.User{
Username: req.Login,
Login: req.Login,
PasswordHash: passwordHash,
}
userID, err := d.database.AddUser(ctx, user.Login, user.Username, user.PasswordHash)
if err != nil {
return nil, fmt.Errorf("failed to add new user: %w", err)
}
user.ID = userID
sessionCreatedAt := time.Now()
sessionExpiredAt := sessionCreatedAt.Add(time.Hour * 24 * 7)
sessionToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a session token: %w", err)
}
ctx.SetCookie(sessionTokenCookie, sessionToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, true)
csrfToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a csrf token: %w", err)
}
ctx.SetCookie(csrfTokenCookie, csrfToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, false)
if _, err = d.database.AddSession(ctx, &models.Session{
SessionToken: sessionToken,
CsrfToken: csrfToken,
UserID: user.ID,
CreatedAt: sessionCreatedAt,
ExpiredAt: sessionExpiredAt,
}); err != nil {
return nil, fmt.Errorf("failed to save session: %w", err)
}
return &domain.RegisterResponse{
Ok: true,
}, nil
}

View File

@ -0,0 +1,52 @@
package app
import (
"io"
"log/slog"
filesengine "git.optclblast.xyz/draincloud/draincloud-core/internal/files_engine"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"github.com/davecgh/go-spew/spew"
"github.com/gin-gonic/gin"
)
const (
maxFileSize = 10 << 30
)
func (d *DrainCloud) UploadFile(ctx *gin.Context) {
if err := ctx.Request.ParseMultipartForm(maxFileSize); err != nil {
logger.Error(ctx, "uploadFile handler error", logger.Err(err))
writeError(ctx, err)
return
}
if err := d.uploadFile(ctx); err != nil {
logger.Error(ctx, "uploadFile handle", logger.Err(err))
writeError(ctx, err)
return
}
}
func (d *DrainCloud) uploadFile(ctx *gin.Context) error {
title := ctx.PostForm("file")
logger.Info(ctx, "uploadFile", slog.Any("postForm data", spew.Sdump(title)))
file, header, err := ctx.Request.FormFile("file")
if err != nil {
return err
}
logger.Info(ctx, "uploadFile", slog.Any("header", spew.Sdump(header)))
data, err := io.ReadAll(file)
if err != nil {
return err
}
d.filesEngine.SaveFile(ctx, filesengine.File{
Name: header.Filename,
// UserID: ,
})
return nil
}

View File

@ -0,0 +1,46 @@
package cleanupsessions
import (
"context"
"time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
)
// TODO set with config
const cronInterval = time.Minute * 10
type ExpiredSessionsRemover interface {
RemoveExpiredSessions(ctx context.Context) error
}
type CleanupSessionCron struct {
db ExpiredSessionsRemover
}
func New(db ExpiredSessionsRemover) *CleanupSessionCron {
return &CleanupSessionCron{
db: db,
}
}
func (c *CleanupSessionCron) Run(ctx context.Context) {
logger.Info(ctx, "[CleanupSessionCron] running cron")
go func() {
t := time.NewTicker(cronInterval)
defer t.Stop()
for {
select {
case <-ctx.Done():
logger.Warn(ctx, "[CleanupSessionCron] context cancelled")
return
case <-t.C:
logger.Notice(ctx, "[CleanupSessionCron] cleanup started")
t.Reset(cronInterval)
if err := c.db.RemoveExpiredSessions(ctx); err != nil {
logger.Error(ctx, "[CleanupSessionCron] failed to remove expired sessions", logger.Err(err))
}
}
}
}()
}

7
internal/cron/cron.go Normal file
View File

@ -0,0 +1,7 @@
package cron
import "context"
type Cron interface {
Run(ctx context.Context)
}

1
internal/domain/files.go Normal file
View File

@ -0,0 +1 @@
package domain

View File

@ -15,5 +15,15 @@ type LoginRequest struct {
Password string `json:"password"` Password string `json:"password"`
} }
type LoginResponse struct {
Ok bool `json:"ok"`
Message string `json:"message"`
}
type LogoutRequest struct { type LogoutRequest struct {
} }
type ErrorJson struct {
Code int `json:"code"`
Message string `json:"message"`
}

View File

@ -0,0 +1,39 @@
package filesengine
import (
"context"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
)
type FilesEngine struct {
blobStorage storage.BlobStorage
metaStorage storage.MetaStorage
}
func NewFilesEngine(
blobStorage storage.BlobStorage,
metaStorage storage.MetaStorage,
) *FilesEngine {
return &FilesEngine{
blobStorage: blobStorage,
metaStorage: metaStorage,
}
}
type File struct {
Name string
UserID int64
Ext string
Type string
Size int64
Data []byte
}
func (e *FilesEngine) SaveFile(
ctx context.Context,
file File,
) (int64, error) {
return -1, nil
}

View File

@ -7,7 +7,11 @@ import (
) )
//nolint:gochecknoglobals // ... //nolint:gochecknoglobals // ...
var globalLogger *slog.Logger = newLogger(LevelInfo, os.Stdout) var globalLogger *slog.Logger = newLogger(LevelDebug, os.Stdout)
func SetLevel(l slog.Level) {
globalLogger = newLogger(l, os.Stdout)
}
const ( const (
LevelEmergency = slog.Level(10000) LevelEmergency = slog.Level(10000)
@ -73,5 +77,5 @@ func Info(ctx context.Context, message string, attrs ...any) {
func Debug(ctx context.Context, message string, attrs ...any) { func Debug(ctx context.Context, message string, attrs ...any) {
l := loggerFromCtx(ctx) l := loggerFromCtx(ctx)
l.Log(ctx, LevelCritial, message, attrs...) l.DebugContext(ctx, message, attrs...)
} }

View File

@ -0,0 +1,37 @@
package reqcontext
import (
"context"
"fmt"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
)
type CtxKey string
const (
UserIDCtxKey CtxKey = "_ctx_user_id"
SessionCtxKey CtxKey = "_ctx_session"
)
func WithUserID(parent context.Context, userID int64) context.Context {
return context.WithValue(parent, UserIDCtxKey, userID)
}
func GetUserID(ctx context.Context) (int64, error) {
if id, ok := ctx.Value(UserIDCtxKey).(int64); ok {
return id, nil
}
return -1, fmt.Errorf("userID not passed with context")
}
func WithSession(parent context.Context, session *models.Session) context.Context {
return context.WithValue(parent, SessionCtxKey, session)
}
func GetSession(ctx context.Context) (*models.Session, error) {
if ses, ok := ctx.Value(UserIDCtxKey).(*models.Session); ok {
return ses, nil
}
return nil, fmt.Errorf("session not passed with context")
}

View File

@ -0,0 +1,19 @@
package audit
import (
"context"
"github.com/jackc/pgx/v5"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models/audit"
)
type Repository struct {
db *pgx.Conn
}
func (r *Repository) AddEntry(ctx context.Context, entry audit.AuditLogEntry) error {
logger.Warn(ctx, "[Repository][AddEntry] not implemented yet!")
return nil
}

View File

@ -0,0 +1,86 @@
package fs
import (
"context"
"fmt"
"os"
"sync"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
)
type Storage struct {
lm *sync.Map
dir string
}
func NewFSStorage(dir string) *Storage {
return &Storage{
lm: &sync.Map{},
dir: dir,
}
}
func (s *Storage) GetFile(ctx context.Context, id int64) (*os.File, error) {
tx := lockFile(s.lm, id)
defer unlockFile(s.lm, id, tx)
file, err := os.Open(getFilePath(s.dir, id))
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer func() {
if err = file.Close(); err != nil {
logger.Error(ctx, "[getFile] close error", logger.Err(err))
}
}()
return file, nil
}
func (s *Storage) SaveBlob(ctx context.Context, id int64, data []byte) error {
tx := lockFile(s.lm, id)
defer unlockFile(s.lm, id, tx)
file, err := os.Open(getFilePath(s.dir, id))
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer func() {
if err = file.Close(); err != nil {
logger.Error(ctx, "[saveFile] close error", logger.Err(err))
}
}()
if _, err = file.Write(data); err != nil {
return fmt.Errorf("failed to write data to file: %w", err)
}
return nil
}
func (s *Storage) DeleteFile(ctx context.Context, id int64) error {
tx := lockFile(s.lm, id)
defer unlockFile(s.lm, id, tx)
return nil
}
func getFilePath(dir string, id int64) string {
return fmt.Sprintf("%s/%v", dir, id)
}
func lockFile(lm *sync.Map, id int64) sync.Locker {
_m := &sync.Mutex{}
many, _ := lm.LoadOrStore(id, _m)
_m, _ = many.(*sync.Mutex)
_m.Lock()
return _m
}
func unlockFile(lm *sync.Map, id int64, tx sync.Locker) {
tx.Unlock()
lm.Delete(id)
}

View File

@ -2,8 +2,10 @@ package storage
import ( import (
"context" "context"
"os"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
auditmodels "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models/audit"
) )
type Database interface { type Database interface {
@ -11,7 +13,25 @@ type Database interface {
} }
type AuthStorage interface { type AuthStorage interface {
AddUser(ctx context.Context, login string, username string, passwordHash []byte) (uint64, error) AddUser(ctx context.Context, login string, username string, passwordHash []byte) (int64, error)
GetUserByLogin(ctx context.Context, login string) (*models.User, error) GetUserByLogin(ctx context.Context, login string) (*models.User, error)
GetUserByID(ctx context.Context, id uint64) (*models.User, error) GetUserByID(ctx context.Context, id uint64) (*models.User, error)
AddSession(ctx context.Context, ses *models.Session) (int64, error)
GetSession(ctx context.Context, sessionToken string) (*models.Session, error)
RemoveSession(ctx context.Context, id int64) error
}
type AuthAuditLogStorage interface {
AddEntry(ctx context.Context, entry auditmodels.AuditLogEntry) error
}
type MetaStorage interface {
SaveMetadata(ctx context.Context, fileType string, size int64, ext string) (int64, error)
}
type BlobStorage interface {
GetFile(ctx context.Context, id int64) (*os.File, error)
SaveBlob(ctx context.Context, id int64, data []byte) error
DeleteFile(ctx context.Context, id int64) error
} }

View File

@ -0,0 +1,49 @@
package audit
import "time"
type EventType int
const (
EventUnspecified EventType = iota
EventSuccessfullLogin
EventFailedLogin
EventSuccessfullRegister
EventFailedRegister
EventSuccessfullAuth
EventFailedAuth
EventUserUpdated
)
type Severity int
const (
SeverityAlert = 0
SeverityWarning = 10
SeverityInfo = 100
SeverityNotice = 200
)
type Actor struct {
ActorSysName string
RemoteIP string
ID int64
}
const (
ActorDrainCloudCore = "_actor_draincloud_core"
ActorUser = "user"
)
type AuditLogEntry struct {
EventType EventType
// Who caused changes
Actor Actor
Severity Severity
SessionID int64
CreatedAt time.Time
// What changed
Object string
// How it was changed
Action string
}

View File

@ -3,16 +3,16 @@ package models
import "time" import "time"
type Session struct { type Session struct {
ID string ID int64
SessionToken string SessionToken string
CsrfToken string CsrfToken string
User *User UserID int64
CreatedAt time.Time CreatedAt time.Time
ExpiredAt time.Time ExpiredAt time.Time
} }
type User struct { type User struct {
ID uint64 ID int64
Username string Username string
Login string Login string
PasswordHash []byte PasswordHash []byte

View File

@ -2,7 +2,9 @@ package postgres
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"log/slog"
"time" "time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/closer" "git.optclblast.xyz/draincloud/draincloud-core/internal/closer"
@ -37,7 +39,7 @@ type dbtx interface {
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
} }
func (d *Database) AddUser(ctx context.Context, login string, username string, passwordHash []byte) (uint64, error) { func (d *Database) AddUser(ctx context.Context, login string, username string, passwordHash []byte) (int64, error) {
return addUser(ctx, d.db, login, username, passwordHash) return addUser(ctx, d.db, login, username, passwordHash)
} }
@ -49,18 +51,59 @@ func (d *Database) GetUserByLogin(ctx context.Context, login string) (*models.Us
return getUserByLogin(ctx, d.db, login) return getUserByLogin(ctx, d.db, login)
} }
func (d *Database) AddSession(ctx context.Context, ses *models.Session) (uint64, error) { func (d *Database) AddSession(ctx context.Context, ses *models.Session) (int64, error) {
return addSession(ctx, d.db, ses) return addSession(ctx, d.db, ses)
} }
func addUser(ctx context.Context, conn dbtx, login string, username string, passwordHash []byte) (uint64, error) { func (d *Database) GetSession(ctx context.Context, sessionToken string) (*models.Session, error) {
const stmt = `SELECT
s.id, s.session_token, s.csrf_token, s.user_id, s.created_at, s.expired_at
FROM sessions as s
WHERE s.session_token = $1;`
row := d.db.QueryRow(ctx, stmt, sessionToken)
var (
id int64
sesToken, csrfToken string
userID int64
createdAt sql.NullTime
expiredAt sql.NullTime
)
if err := row.Scan(&id, &sesToken, &csrfToken, &userID, &createdAt, &expiredAt); err != nil {
return nil, err
}
return &models.Session{
ID: id,
SessionToken: sesToken,
CsrfToken: csrfToken,
UserID: userID,
CreatedAt: createdAt.Time,
ExpiredAt: expiredAt.Time,
}, nil
}
func (d *Database) RemoveSession(ctx context.Context, id int64) error {
const stmt = `DELETE FROM sessions WHERE id = $1;`
_, err := d.db.Exec(ctx, stmt, id)
return err
}
func (d *Database) RemoveExpiredSessions(ctx context.Context) error {
const stmt = `DELETE FROM sessions WHERE expired_at < $1;`
res, err := d.db.Exec(ctx, stmt, time.Now())
logger.Notice(ctx, "[Database][RemoveExpiredSessions] sessions cleanup", slog.Int64("removed", res.RowsAffected()))
return err
}
func addUser(ctx context.Context, conn dbtx, login string, username string, passwordHash []byte) (int64, error) {
const stmt = `INSERT INTO users (login,username,password) const stmt = `INSERT INTO users (login,username,password)
VALUES ($1,$2,$3) RETURNING id` VALUES ($1,$2,$3) RETURNING id`
row := conn.QueryRow(ctx, stmt, login, username, passwordHash) row := conn.QueryRow(ctx, stmt, login, username, passwordHash)
var id int64
var id uint64
if err := row.Scan(&id); err != nil { if err := row.Scan(&id); err != nil {
return 0, fmt.Errorf("failed to insert user data into users table: %w", err) return 0, fmt.Errorf("failed to insert user data into users table: %w", err)
} }
@ -71,7 +114,6 @@ func addUser(ctx context.Context, conn dbtx, login string, username string, pass
func getUserByID(ctx context.Context, conn dbtx, id uint64) (*models.User, error) { func getUserByID(ctx context.Context, conn dbtx, id uint64) (*models.User, error) {
const stmt = `SELECT * FROM users WHERE id = $1 LIMIT 1` const stmt = `SELECT * FROM users WHERE id = $1 LIMIT 1`
u := new(models.User) u := new(models.User)
row := conn.QueryRow(ctx, stmt, id) row := conn.QueryRow(ctx, stmt, id)
if err := row.Scan(&u.ID, &u.Login, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt); err != nil { if err := row.Scan(&u.ID, &u.Login, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt); err != nil {
return nil, fmt.Errorf("failed to fetch user by id: %w", err) return nil, fmt.Errorf("failed to fetch user by id: %w", err)
@ -83,7 +125,6 @@ func getUserByID(ctx context.Context, conn dbtx, id uint64) (*models.User, error
func getUserByLogin(ctx context.Context, conn dbtx, login string) (*models.User, error) { func getUserByLogin(ctx context.Context, conn dbtx, login string) (*models.User, error) {
const stmt = `SELECT * FROM users WHERE login = $1 LIMIT 1` const stmt = `SELECT * FROM users WHERE login = $1 LIMIT 1`
u := new(models.User) u := new(models.User)
row := conn.QueryRow(ctx, stmt, login) row := conn.QueryRow(ctx, stmt, login)
if err := row.Scan(&u.ID, &u.Login, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt); err != nil { if err := row.Scan(&u.ID, &u.Login, &u.Username, &u.PasswordHash, &u.CreatedAt, &u.UpdatedAt); err != nil {
return nil, fmt.Errorf("failed to fetch user by login: %w", err) return nil, fmt.Errorf("failed to fetch user by login: %w", err)
@ -92,11 +133,11 @@ func getUserByLogin(ctx context.Context, conn dbtx, login string) (*models.User,
return u, nil return u, nil
} }
func addSession(ctx context.Context, conn dbtx, session *models.Session) (uint64, error) { func addSession(ctx context.Context, conn dbtx, session *models.Session) (int64, error) {
const stmt = `INSERT INTO sessions (session_token, csrf_token, user_id, const stmt = `INSERT INTO sessions (session_token, csrf_token, user_id,
created_at, expired_at) VALUES ($1, $2, $3, $4, $5) RETURNING id;` created_at, expired_at) VALUES ($1, $2, $3, $4, $5) RETURNING id;`
var id uint64 var id int64
row := conn.QueryRow(ctx, stmt, session.SessionToken, session.CsrfToken, session.User.ID, session.CreatedAt, session.ExpiredAt) row := conn.QueryRow(ctx, stmt, session.SessionToken, session.CsrfToken, session.UserID, session.CreatedAt, session.ExpiredAt)
if err := row.Scan(&id); err != nil { if err := row.Scan(&id); err != nil {
return 0, fmt.Errorf("failed to insert new session: %w", err) return 0, fmt.Errorf("failed to insert new session: %w", err)
} }

View File

@ -0,0 +1,85 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import (
context "context"
audit "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models/audit"
mock "github.com/stretchr/testify/mock"
)
// MockAuthAuditLogStorage is an autogenerated mock type for the AuthAuditLogStorage type
type MockAuthAuditLogStorage struct {
mock.Mock
}
type MockAuthAuditLogStorage_Expecter struct {
mock *mock.Mock
}
func (_m *MockAuthAuditLogStorage) EXPECT() *MockAuthAuditLogStorage_Expecter {
return &MockAuthAuditLogStorage_Expecter{mock: &_m.Mock}
}
// AddEntry provides a mock function with given fields: ctx, entry
func (_m *MockAuthAuditLogStorage) AddEntry(ctx context.Context, entry audit.AuditLogEntry) error {
ret := _m.Called(ctx, entry)
if len(ret) == 0 {
panic("no return value specified for AddEntry")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, audit.AuditLogEntry) error); ok {
r0 = rf(ctx, entry)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockAuthAuditLogStorage_AddEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddEntry'
type MockAuthAuditLogStorage_AddEntry_Call struct {
*mock.Call
}
// AddEntry is a helper method to define mock.On call
// - ctx context.Context
// - entry audit.AuditLogEntry
func (_e *MockAuthAuditLogStorage_Expecter) AddEntry(ctx interface{}, entry interface{}) *MockAuthAuditLogStorage_AddEntry_Call {
return &MockAuthAuditLogStorage_AddEntry_Call{Call: _e.mock.On("AddEntry", ctx, entry)}
}
func (_c *MockAuthAuditLogStorage_AddEntry_Call) Run(run func(ctx context.Context, entry audit.AuditLogEntry)) *MockAuthAuditLogStorage_AddEntry_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(audit.AuditLogEntry))
})
return _c
}
func (_c *MockAuthAuditLogStorage_AddEntry_Call) Return(_a0 error) *MockAuthAuditLogStorage_AddEntry_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockAuthAuditLogStorage_AddEntry_Call) RunAndReturn(run func(context.Context, audit.AuditLogEntry) error) *MockAuthAuditLogStorage_AddEntry_Call {
_c.Call.Return(run)
return _c
}
// NewMockAuthAuditLogStorage creates a new instance of MockAuthAuditLogStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockAuthAuditLogStorage(t interface {
mock.TestingT
Cleanup(func())
}) *MockAuthAuditLogStorage {
mock := &MockAuthAuditLogStorage{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,377 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import (
context "context"
models "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
mock "github.com/stretchr/testify/mock"
)
// MockAuthStorage is an autogenerated mock type for the AuthStorage type
type MockAuthStorage struct {
mock.Mock
}
type MockAuthStorage_Expecter struct {
mock *mock.Mock
}
func (_m *MockAuthStorage) EXPECT() *MockAuthStorage_Expecter {
return &MockAuthStorage_Expecter{mock: &_m.Mock}
}
// AddSession provides a mock function with given fields: ctx, ses
func (_m *MockAuthStorage) AddSession(ctx context.Context, ses *models.Session) (int64, error) {
ret := _m.Called(ctx, ses)
if len(ret) == 0 {
panic("no return value specified for AddSession")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Session) (int64, error)); ok {
return rf(ctx, ses)
}
if rf, ok := ret.Get(0).(func(context.Context, *models.Session) int64); ok {
r0 = rf(ctx, ses)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, *models.Session) error); ok {
r1 = rf(ctx, ses)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAuthStorage_AddSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddSession'
type MockAuthStorage_AddSession_Call struct {
*mock.Call
}
// AddSession is a helper method to define mock.On call
// - ctx context.Context
// - ses *models.Session
func (_e *MockAuthStorage_Expecter) AddSession(ctx interface{}, ses interface{}) *MockAuthStorage_AddSession_Call {
return &MockAuthStorage_AddSession_Call{Call: _e.mock.On("AddSession", ctx, ses)}
}
func (_c *MockAuthStorage_AddSession_Call) Run(run func(ctx context.Context, ses *models.Session)) *MockAuthStorage_AddSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*models.Session))
})
return _c
}
func (_c *MockAuthStorage_AddSession_Call) Return(_a0 int64, _a1 error) *MockAuthStorage_AddSession_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockAuthStorage_AddSession_Call) RunAndReturn(run func(context.Context, *models.Session) (int64, error)) *MockAuthStorage_AddSession_Call {
_c.Call.Return(run)
return _c
}
// AddUser provides a mock function with given fields: ctx, login, username, passwordHash
func (_m *MockAuthStorage) AddUser(ctx context.Context, login string, username string, passwordHash []byte) (int64, error) {
ret := _m.Called(ctx, login, username, passwordHash)
if len(ret) == 0 {
panic("no return value specified for AddUser")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte) (int64, error)); ok {
return rf(ctx, login, username, passwordHash)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte) int64); ok {
r0 = rf(ctx, login, username, passwordHash)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, []byte) error); ok {
r1 = rf(ctx, login, username, passwordHash)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAuthStorage_AddUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUser'
type MockAuthStorage_AddUser_Call struct {
*mock.Call
}
// AddUser is a helper method to define mock.On call
// - ctx context.Context
// - login string
// - username string
// - passwordHash []byte
func (_e *MockAuthStorage_Expecter) AddUser(ctx interface{}, login interface{}, username interface{}, passwordHash interface{}) *MockAuthStorage_AddUser_Call {
return &MockAuthStorage_AddUser_Call{Call: _e.mock.On("AddUser", ctx, login, username, passwordHash)}
}
func (_c *MockAuthStorage_AddUser_Call) Run(run func(ctx context.Context, login string, username string, passwordHash []byte)) *MockAuthStorage_AddUser_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].([]byte))
})
return _c
}
func (_c *MockAuthStorage_AddUser_Call) Return(_a0 int64, _a1 error) *MockAuthStorage_AddUser_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockAuthStorage_AddUser_Call) RunAndReturn(run func(context.Context, string, string, []byte) (int64, error)) *MockAuthStorage_AddUser_Call {
_c.Call.Return(run)
return _c
}
// GetSession provides a mock function with given fields: ctx, sessionToken
func (_m *MockAuthStorage) GetSession(ctx context.Context, sessionToken string) (*models.Session, error) {
ret := _m.Called(ctx, sessionToken)
if len(ret) == 0 {
panic("no return value specified for GetSession")
}
var r0 *models.Session
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.Session, error)); ok {
return rf(ctx, sessionToken)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.Session); ok {
r0 = rf(ctx, sessionToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Session)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, sessionToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAuthStorage_GetSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSession'
type MockAuthStorage_GetSession_Call struct {
*mock.Call
}
// GetSession is a helper method to define mock.On call
// - ctx context.Context
// - sessionToken string
func (_e *MockAuthStorage_Expecter) GetSession(ctx interface{}, sessionToken interface{}) *MockAuthStorage_GetSession_Call {
return &MockAuthStorage_GetSession_Call{Call: _e.mock.On("GetSession", ctx, sessionToken)}
}
func (_c *MockAuthStorage_GetSession_Call) Run(run func(ctx context.Context, sessionToken string)) *MockAuthStorage_GetSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *MockAuthStorage_GetSession_Call) Return(_a0 *models.Session, _a1 error) *MockAuthStorage_GetSession_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockAuthStorage_GetSession_Call) RunAndReturn(run func(context.Context, string) (*models.Session, error)) *MockAuthStorage_GetSession_Call {
_c.Call.Return(run)
return _c
}
// GetUserByID provides a mock function with given fields: ctx, id
func (_m *MockAuthStorage) GetUserByID(ctx context.Context, id uint64) (*models.User, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for GetUserByID")
}
var r0 *models.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, uint64) (*models.User, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, uint64) *models.User); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAuthStorage_GetUserByID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByID'
type MockAuthStorage_GetUserByID_Call struct {
*mock.Call
}
// GetUserByID is a helper method to define mock.On call
// - ctx context.Context
// - id uint64
func (_e *MockAuthStorage_Expecter) GetUserByID(ctx interface{}, id interface{}) *MockAuthStorage_GetUserByID_Call {
return &MockAuthStorage_GetUserByID_Call{Call: _e.mock.On("GetUserByID", ctx, id)}
}
func (_c *MockAuthStorage_GetUserByID_Call) Run(run func(ctx context.Context, id uint64)) *MockAuthStorage_GetUserByID_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(uint64))
})
return _c
}
func (_c *MockAuthStorage_GetUserByID_Call) Return(_a0 *models.User, _a1 error) *MockAuthStorage_GetUserByID_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockAuthStorage_GetUserByID_Call) RunAndReturn(run func(context.Context, uint64) (*models.User, error)) *MockAuthStorage_GetUserByID_Call {
_c.Call.Return(run)
return _c
}
// GetUserByLogin provides a mock function with given fields: ctx, login
func (_m *MockAuthStorage) GetUserByLogin(ctx context.Context, login string) (*models.User, error) {
ret := _m.Called(ctx, login)
if len(ret) == 0 {
panic("no return value specified for GetUserByLogin")
}
var r0 *models.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.User, error)); ok {
return rf(ctx, login)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.User); ok {
r0 = rf(ctx, login)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, login)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAuthStorage_GetUserByLogin_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByLogin'
type MockAuthStorage_GetUserByLogin_Call struct {
*mock.Call
}
// GetUserByLogin is a helper method to define mock.On call
// - ctx context.Context
// - login string
func (_e *MockAuthStorage_Expecter) GetUserByLogin(ctx interface{}, login interface{}) *MockAuthStorage_GetUserByLogin_Call {
return &MockAuthStorage_GetUserByLogin_Call{Call: _e.mock.On("GetUserByLogin", ctx, login)}
}
func (_c *MockAuthStorage_GetUserByLogin_Call) Run(run func(ctx context.Context, login string)) *MockAuthStorage_GetUserByLogin_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *MockAuthStorage_GetUserByLogin_Call) Return(_a0 *models.User, _a1 error) *MockAuthStorage_GetUserByLogin_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockAuthStorage_GetUserByLogin_Call) RunAndReturn(run func(context.Context, string) (*models.User, error)) *MockAuthStorage_GetUserByLogin_Call {
_c.Call.Return(run)
return _c
}
// RemoveSession provides a mock function with given fields: ctx, id
func (_m *MockAuthStorage) RemoveSession(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for RemoveSession")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockAuthStorage_RemoveSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveSession'
type MockAuthStorage_RemoveSession_Call struct {
*mock.Call
}
// RemoveSession is a helper method to define mock.On call
// - ctx context.Context
// - id int64
func (_e *MockAuthStorage_Expecter) RemoveSession(ctx interface{}, id interface{}) *MockAuthStorage_RemoveSession_Call {
return &MockAuthStorage_RemoveSession_Call{Call: _e.mock.On("RemoveSession", ctx, id)}
}
func (_c *MockAuthStorage_RemoveSession_Call) Run(run func(ctx context.Context, id int64)) *MockAuthStorage_RemoveSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int64))
})
return _c
}
func (_c *MockAuthStorage_RemoveSession_Call) Return(_a0 error) *MockAuthStorage_RemoveSession_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockAuthStorage_RemoveSession_Call) RunAndReturn(run func(context.Context, int64) error) *MockAuthStorage_RemoveSession_Call {
_c.Call.Return(run)
return _c
}
// NewMockAuthStorage creates a new instance of MockAuthStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockAuthStorage(t interface {
mock.TestingT
Cleanup(func())
}) *MockAuthStorage {
mock := &MockAuthStorage{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,191 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import (
context "context"
os "os"
mock "github.com/stretchr/testify/mock"
)
// MockBlobStorage is an autogenerated mock type for the BlobStorage type
type MockBlobStorage struct {
mock.Mock
}
type MockBlobStorage_Expecter struct {
mock *mock.Mock
}
func (_m *MockBlobStorage) EXPECT() *MockBlobStorage_Expecter {
return &MockBlobStorage_Expecter{mock: &_m.Mock}
}
// DeleteFile provides a mock function with given fields: ctx, id
func (_m *MockBlobStorage) DeleteFile(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for DeleteFile")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBlobStorage_DeleteFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteFile'
type MockBlobStorage_DeleteFile_Call struct {
*mock.Call
}
// DeleteFile is a helper method to define mock.On call
// - ctx context.Context
// - id int64
func (_e *MockBlobStorage_Expecter) DeleteFile(ctx interface{}, id interface{}) *MockBlobStorage_DeleteFile_Call {
return &MockBlobStorage_DeleteFile_Call{Call: _e.mock.On("DeleteFile", ctx, id)}
}
func (_c *MockBlobStorage_DeleteFile_Call) Run(run func(ctx context.Context, id int64)) *MockBlobStorage_DeleteFile_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int64))
})
return _c
}
func (_c *MockBlobStorage_DeleteFile_Call) Return(_a0 error) *MockBlobStorage_DeleteFile_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBlobStorage_DeleteFile_Call) RunAndReturn(run func(context.Context, int64) error) *MockBlobStorage_DeleteFile_Call {
_c.Call.Return(run)
return _c
}
// GetFile provides a mock function with given fields: ctx, id
func (_m *MockBlobStorage) GetFile(ctx context.Context, id int64) (*os.File, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for GetFile")
}
var r0 *os.File
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int64) (*os.File, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, int64) *os.File); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*os.File)
}
}
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockBlobStorage_GetFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFile'
type MockBlobStorage_GetFile_Call struct {
*mock.Call
}
// GetFile is a helper method to define mock.On call
// - ctx context.Context
// - id int64
func (_e *MockBlobStorage_Expecter) GetFile(ctx interface{}, id interface{}) *MockBlobStorage_GetFile_Call {
return &MockBlobStorage_GetFile_Call{Call: _e.mock.On("GetFile", ctx, id)}
}
func (_c *MockBlobStorage_GetFile_Call) Run(run func(ctx context.Context, id int64)) *MockBlobStorage_GetFile_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int64))
})
return _c
}
func (_c *MockBlobStorage_GetFile_Call) Return(_a0 *os.File, _a1 error) *MockBlobStorage_GetFile_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockBlobStorage_GetFile_Call) RunAndReturn(run func(context.Context, int64) (*os.File, error)) *MockBlobStorage_GetFile_Call {
_c.Call.Return(run)
return _c
}
// SaveBlob provides a mock function with given fields: ctx, id, data
func (_m *MockBlobStorage) SaveBlob(ctx context.Context, id int64, data []byte) error {
ret := _m.Called(ctx, id, data)
if len(ret) == 0 {
panic("no return value specified for SaveBlob")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, []byte) error); ok {
r0 = rf(ctx, id, data)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBlobStorage_SaveBlob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveBlob'
type MockBlobStorage_SaveBlob_Call struct {
*mock.Call
}
// SaveBlob is a helper method to define mock.On call
// - ctx context.Context
// - id int64
// - data []byte
func (_e *MockBlobStorage_Expecter) SaveBlob(ctx interface{}, id interface{}, data interface{}) *MockBlobStorage_SaveBlob_Call {
return &MockBlobStorage_SaveBlob_Call{Call: _e.mock.On("SaveBlob", ctx, id, data)}
}
func (_c *MockBlobStorage_SaveBlob_Call) Run(run func(ctx context.Context, id int64, data []byte)) *MockBlobStorage_SaveBlob_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int64), args[2].([]byte))
})
return _c
}
func (_c *MockBlobStorage_SaveBlob_Call) Return(_a0 error) *MockBlobStorage_SaveBlob_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBlobStorage_SaveBlob_Call) RunAndReturn(run func(context.Context, int64, []byte) error) *MockBlobStorage_SaveBlob_Call {
_c.Call.Return(run)
return _c
}
// NewMockBlobStorage creates a new instance of MockBlobStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockBlobStorage(t interface {
mock.TestingT
Cleanup(func())
}) *MockBlobStorage {
mock := &MockBlobStorage{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,377 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import (
context "context"
models "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
mock "github.com/stretchr/testify/mock"
)
// MockDatabase is an autogenerated mock type for the Database type
type MockDatabase struct {
mock.Mock
}
type MockDatabase_Expecter struct {
mock *mock.Mock
}
func (_m *MockDatabase) EXPECT() *MockDatabase_Expecter {
return &MockDatabase_Expecter{mock: &_m.Mock}
}
// AddSession provides a mock function with given fields: ctx, ses
func (_m *MockDatabase) AddSession(ctx context.Context, ses *models.Session) (int64, error) {
ret := _m.Called(ctx, ses)
if len(ret) == 0 {
panic("no return value specified for AddSession")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *models.Session) (int64, error)); ok {
return rf(ctx, ses)
}
if rf, ok := ret.Get(0).(func(context.Context, *models.Session) int64); ok {
r0 = rf(ctx, ses)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, *models.Session) error); ok {
r1 = rf(ctx, ses)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockDatabase_AddSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddSession'
type MockDatabase_AddSession_Call struct {
*mock.Call
}
// AddSession is a helper method to define mock.On call
// - ctx context.Context
// - ses *models.Session
func (_e *MockDatabase_Expecter) AddSession(ctx interface{}, ses interface{}) *MockDatabase_AddSession_Call {
return &MockDatabase_AddSession_Call{Call: _e.mock.On("AddSession", ctx, ses)}
}
func (_c *MockDatabase_AddSession_Call) Run(run func(ctx context.Context, ses *models.Session)) *MockDatabase_AddSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*models.Session))
})
return _c
}
func (_c *MockDatabase_AddSession_Call) Return(_a0 int64, _a1 error) *MockDatabase_AddSession_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDatabase_AddSession_Call) RunAndReturn(run func(context.Context, *models.Session) (int64, error)) *MockDatabase_AddSession_Call {
_c.Call.Return(run)
return _c
}
// AddUser provides a mock function with given fields: ctx, login, username, passwordHash
func (_m *MockDatabase) AddUser(ctx context.Context, login string, username string, passwordHash []byte) (int64, error) {
ret := _m.Called(ctx, login, username, passwordHash)
if len(ret) == 0 {
panic("no return value specified for AddUser")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte) (int64, error)); ok {
return rf(ctx, login, username, passwordHash)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string, []byte) int64); ok {
r0 = rf(ctx, login, username, passwordHash)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string, []byte) error); ok {
r1 = rf(ctx, login, username, passwordHash)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockDatabase_AddUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddUser'
type MockDatabase_AddUser_Call struct {
*mock.Call
}
// AddUser is a helper method to define mock.On call
// - ctx context.Context
// - login string
// - username string
// - passwordHash []byte
func (_e *MockDatabase_Expecter) AddUser(ctx interface{}, login interface{}, username interface{}, passwordHash interface{}) *MockDatabase_AddUser_Call {
return &MockDatabase_AddUser_Call{Call: _e.mock.On("AddUser", ctx, login, username, passwordHash)}
}
func (_c *MockDatabase_AddUser_Call) Run(run func(ctx context.Context, login string, username string, passwordHash []byte)) *MockDatabase_AddUser_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].([]byte))
})
return _c
}
func (_c *MockDatabase_AddUser_Call) Return(_a0 int64, _a1 error) *MockDatabase_AddUser_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDatabase_AddUser_Call) RunAndReturn(run func(context.Context, string, string, []byte) (int64, error)) *MockDatabase_AddUser_Call {
_c.Call.Return(run)
return _c
}
// GetSession provides a mock function with given fields: ctx, sessionToken
func (_m *MockDatabase) GetSession(ctx context.Context, sessionToken string) (*models.Session, error) {
ret := _m.Called(ctx, sessionToken)
if len(ret) == 0 {
panic("no return value specified for GetSession")
}
var r0 *models.Session
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.Session, error)); ok {
return rf(ctx, sessionToken)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.Session); ok {
r0 = rf(ctx, sessionToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Session)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, sessionToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockDatabase_GetSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSession'
type MockDatabase_GetSession_Call struct {
*mock.Call
}
// GetSession is a helper method to define mock.On call
// - ctx context.Context
// - sessionToken string
func (_e *MockDatabase_Expecter) GetSession(ctx interface{}, sessionToken interface{}) *MockDatabase_GetSession_Call {
return &MockDatabase_GetSession_Call{Call: _e.mock.On("GetSession", ctx, sessionToken)}
}
func (_c *MockDatabase_GetSession_Call) Run(run func(ctx context.Context, sessionToken string)) *MockDatabase_GetSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *MockDatabase_GetSession_Call) Return(_a0 *models.Session, _a1 error) *MockDatabase_GetSession_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDatabase_GetSession_Call) RunAndReturn(run func(context.Context, string) (*models.Session, error)) *MockDatabase_GetSession_Call {
_c.Call.Return(run)
return _c
}
// GetUserByID provides a mock function with given fields: ctx, id
func (_m *MockDatabase) GetUserByID(ctx context.Context, id uint64) (*models.User, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for GetUserByID")
}
var r0 *models.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, uint64) (*models.User, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, uint64) *models.User); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockDatabase_GetUserByID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByID'
type MockDatabase_GetUserByID_Call struct {
*mock.Call
}
// GetUserByID is a helper method to define mock.On call
// - ctx context.Context
// - id uint64
func (_e *MockDatabase_Expecter) GetUserByID(ctx interface{}, id interface{}) *MockDatabase_GetUserByID_Call {
return &MockDatabase_GetUserByID_Call{Call: _e.mock.On("GetUserByID", ctx, id)}
}
func (_c *MockDatabase_GetUserByID_Call) Run(run func(ctx context.Context, id uint64)) *MockDatabase_GetUserByID_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(uint64))
})
return _c
}
func (_c *MockDatabase_GetUserByID_Call) Return(_a0 *models.User, _a1 error) *MockDatabase_GetUserByID_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDatabase_GetUserByID_Call) RunAndReturn(run func(context.Context, uint64) (*models.User, error)) *MockDatabase_GetUserByID_Call {
_c.Call.Return(run)
return _c
}
// GetUserByLogin provides a mock function with given fields: ctx, login
func (_m *MockDatabase) GetUserByLogin(ctx context.Context, login string) (*models.User, error) {
ret := _m.Called(ctx, login)
if len(ret) == 0 {
panic("no return value specified for GetUserByLogin")
}
var r0 *models.User
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.User, error)); ok {
return rf(ctx, login)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.User); ok {
r0 = rf(ctx, login)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.User)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, login)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockDatabase_GetUserByLogin_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByLogin'
type MockDatabase_GetUserByLogin_Call struct {
*mock.Call
}
// GetUserByLogin is a helper method to define mock.On call
// - ctx context.Context
// - login string
func (_e *MockDatabase_Expecter) GetUserByLogin(ctx interface{}, login interface{}) *MockDatabase_GetUserByLogin_Call {
return &MockDatabase_GetUserByLogin_Call{Call: _e.mock.On("GetUserByLogin", ctx, login)}
}
func (_c *MockDatabase_GetUserByLogin_Call) Run(run func(ctx context.Context, login string)) *MockDatabase_GetUserByLogin_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *MockDatabase_GetUserByLogin_Call) Return(_a0 *models.User, _a1 error) *MockDatabase_GetUserByLogin_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockDatabase_GetUserByLogin_Call) RunAndReturn(run func(context.Context, string) (*models.User, error)) *MockDatabase_GetUserByLogin_Call {
_c.Call.Return(run)
return _c
}
// RemoveSession provides a mock function with given fields: ctx, id
func (_m *MockDatabase) RemoveSession(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for RemoveSession")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockDatabase_RemoveSession_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveSession'
type MockDatabase_RemoveSession_Call struct {
*mock.Call
}
// RemoveSession is a helper method to define mock.On call
// - ctx context.Context
// - id int64
func (_e *MockDatabase_Expecter) RemoveSession(ctx interface{}, id interface{}) *MockDatabase_RemoveSession_Call {
return &MockDatabase_RemoveSession_Call{Call: _e.mock.On("RemoveSession", ctx, id)}
}
func (_c *MockDatabase_RemoveSession_Call) Run(run func(ctx context.Context, id int64)) *MockDatabase_RemoveSession_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int64))
})
return _c
}
func (_c *MockDatabase_RemoveSession_Call) Return(_a0 error) *MockDatabase_RemoveSession_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockDatabase_RemoveSession_Call) RunAndReturn(run func(context.Context, int64) error) *MockDatabase_RemoveSession_Call {
_c.Call.Return(run)
return _c
}
// NewMockDatabase creates a new instance of MockDatabase. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockDatabase(t interface {
mock.TestingT
Cleanup(func())
}) *MockDatabase {
mock := &MockDatabase{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,32 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import mock "github.com/stretchr/testify/mock"
// MockFileStorage is an autogenerated mock type for the FileStorage type
type MockFileStorage struct {
mock.Mock
}
type MockFileStorage_Expecter struct {
mock *mock.Mock
}
func (_m *MockFileStorage) EXPECT() *MockFileStorage_Expecter {
return &MockFileStorage_Expecter{mock: &_m.Mock}
}
// NewMockFileStorage creates a new instance of MockFileStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockFileStorage(t interface {
mock.TestingT
Cleanup(func())
}) *MockFileStorage {
mock := &MockFileStorage{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,95 @@
// Code generated by mockery v2.48.0. DO NOT EDIT.
package storage
import (
context "context"
mock "github.com/stretchr/testify/mock"
)
// MockMetaStorage is an autogenerated mock type for the MetaStorage type
type MockMetaStorage struct {
mock.Mock
}
type MockMetaStorage_Expecter struct {
mock *mock.Mock
}
func (_m *MockMetaStorage) EXPECT() *MockMetaStorage_Expecter {
return &MockMetaStorage_Expecter{mock: &_m.Mock}
}
// SaveMetadata provides a mock function with given fields: ctx, fileType, size, ext
func (_m *MockMetaStorage) SaveMetadata(ctx context.Context, fileType string, size int64, ext string) (int64, error) {
ret := _m.Called(ctx, fileType, size, ext)
if len(ret) == 0 {
panic("no return value specified for SaveMetadata")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, int64, string) (int64, error)); ok {
return rf(ctx, fileType, size, ext)
}
if rf, ok := ret.Get(0).(func(context.Context, string, int64, string) int64); ok {
r0 = rf(ctx, fileType, size, ext)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, string, int64, string) error); ok {
r1 = rf(ctx, fileType, size, ext)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockMetaStorage_SaveMetadata_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveMetadata'
type MockMetaStorage_SaveMetadata_Call struct {
*mock.Call
}
// SaveMetadata is a helper method to define mock.On call
// - ctx context.Context
// - fileType string
// - size int64
// - ext string
func (_e *MockMetaStorage_Expecter) SaveMetadata(ctx interface{}, fileType interface{}, size interface{}, ext interface{}) *MockMetaStorage_SaveMetadata_Call {
return &MockMetaStorage_SaveMetadata_Call{Call: _e.mock.On("SaveMetadata", ctx, fileType, size, ext)}
}
func (_c *MockMetaStorage_SaveMetadata_Call) Run(run func(ctx context.Context, fileType string, size int64, ext string)) *MockMetaStorage_SaveMetadata_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(int64), args[3].(string))
})
return _c
}
func (_c *MockMetaStorage_SaveMetadata_Call) Return(_a0 int64, _a1 error) *MockMetaStorage_SaveMetadata_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockMetaStorage_SaveMetadata_Call) RunAndReturn(run func(context.Context, string, int64, string) (int64, error)) *MockMetaStorage_SaveMetadata_Call {
_c.Call.Return(run)
return _c
}
// NewMockMetaStorage creates a new instance of MockMetaStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockMetaStorage(t interface {
mock.TestingT
Cleanup(func())
}) *MockMetaStorage {
mock := &MockMetaStorage{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}