优化
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAccountLocked = errors.New("account locked")
|
||||
ErrCaptchaInvalid = errors.New("captcha invalid")
|
||||
ErrTokenIssue = errors.New("token issue failed")
|
||||
ErrLoginState = errors.New("login state failed")
|
||||
)
|
||||
|
||||
type UserDisabledError struct{ UserID uint }
|
||||
|
||||
func (e *UserDisabledError) Error() string { return ErrUserDisabled.Error() }
|
||||
func (e *UserDisabledError) Unwrap() error { return ErrUserDisabled }
|
||||
|
||||
type AccountLockedError struct{ Minutes int }
|
||||
|
||||
func (e *AccountLockedError) Error() string { return ErrAccountLocked.Error() }
|
||||
func (e *AccountLockedError) Unwrap() error { return ErrAccountLocked }
|
||||
|
||||
type LoginAttempt struct {
|
||||
Username string
|
||||
Password string
|
||||
CaptchaID string
|
||||
Captcha string
|
||||
IP string
|
||||
Agent string
|
||||
}
|
||||
|
||||
type AuthenticationResult struct {
|
||||
User *User
|
||||
Token string
|
||||
ExpiresAt time.Time
|
||||
NeedChangePassword bool
|
||||
}
|
||||
|
||||
type AuthenticationUsecase struct {
|
||||
users *UserUsecase
|
||||
security *SecurityUsecase
|
||||
issuer TokenIssuer
|
||||
audit AuditRecordRepo
|
||||
}
|
||||
|
||||
func NewAuthenticationUsecase(users *UserUsecase, security *SecurityUsecase, issuer TokenIssuer, audit AuditRecordRepo) *AuthenticationUsecase {
|
||||
return &AuthenticationUsecase{users: users, security: security, issuer: issuer, audit: audit}
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) recordLogin(ctx context.Context, attempt *LoginAttempt, status bool, message string, userID uint) {
|
||||
_ = uc.audit.RecordLogin(ctx, &LoginLog{Username: attempt.Username, IP: attempt.IP, Status: status, ErrorMessage: message, Agent: attempt.Agent, UserID: userID})
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttempt) (*AuthenticationResult, error) {
|
||||
config, _ := uc.security.Current(ctx)
|
||||
if config != nil && config.LockEnable {
|
||||
if locked, _ := uc.security.LoginLocked(ctx, attempt.Username); locked {
|
||||
uc.recordLogin(ctx, attempt, false, "账号已锁定", 0)
|
||||
return nil, &AccountLockedError{Minutes: config.LockDuration}
|
||||
}
|
||||
}
|
||||
|
||||
ipTTL := time.Hour
|
||||
requireCaptcha := config == nil || config.CaptchaOpen == 0
|
||||
if config != nil {
|
||||
if config.CaptchaTimeout > 0 {
|
||||
ipTTL = time.Duration(config.CaptchaTimeout) * time.Second
|
||||
}
|
||||
failures, _ := uc.security.EnsureLoginIPCounter(ctx, attempt.IP, ipTTL)
|
||||
requireCaptcha = config.CaptchaOpen == 0 || failures > config.CaptchaOpen
|
||||
}
|
||||
if requireCaptcha && !uc.security.VerifyCaptcha(ctx, attempt.CaptchaID, attempt.Captcha, true) {
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
uc.recordLogin(ctx, attempt, false, "验证码错误", 0)
|
||||
return nil, ErrCaptchaInvalid
|
||||
}
|
||||
|
||||
user, err := uc.users.Login(ctx, attempt.Username, attempt.Password)
|
||||
if err != nil {
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
if config != nil && config.LockEnable {
|
||||
lockTTL := time.Duration(config.LockDuration) * time.Minute
|
||||
failures, _ := uc.security.IncrementLoginFailure(ctx, attempt.Username, lockTTL)
|
||||
if int(failures) >= config.LockThreshold {
|
||||
_ = uc.security.LockLogin(ctx, attempt.Username, lockTTL)
|
||||
}
|
||||
}
|
||||
uc.recordLogin(ctx, attempt, false, "用户名不存在或者密码错误", 0)
|
||||
return nil, ErrInvalidCredentials
|
||||
}
|
||||
if user.Enable != 1 {
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
uc.recordLogin(ctx, attempt, false, "用户被禁止登录", user.ID)
|
||||
return nil, &UserDisabledError{UserID: user.ID}
|
||||
}
|
||||
|
||||
uc.security.ClearLoginState(ctx, attempt.Username)
|
||||
if config != nil && config.PwdExpireEnable && user.PasswordUpdatedAt != nil && time.Since(*user.PasswordUpdatedAt) > time.Duration(config.PwdExpireDays)*24*time.Hour {
|
||||
user.MustChangePassword = true
|
||||
}
|
||||
issued, err := uc.issuer.IssueToken(user, user.AuthorityID, user.MustChangePassword, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrTokenIssue, err)
|
||||
}
|
||||
if uc.security.UseMultipoint() {
|
||||
oldToken, cacheErr := uc.security.ActiveToken(ctx, user.Username)
|
||||
if cacheErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, cacheErr)
|
||||
}
|
||||
if cacheErr = uc.security.RotateActiveToken(ctx, user.Username, oldToken, issued.Value, issued.TTL); cacheErr != nil {
|
||||
if errors.Is(cacheErr, ErrTokenRevoke) {
|
||||
return nil, cacheErr
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, cacheErr)
|
||||
}
|
||||
}
|
||||
uc.recordLogin(ctx, attempt, true, "登录成功", user.ID)
|
||||
return &AuthenticationResult{User: user, Token: issued.Value, ExpiresAt: issued.ExpiresAt, NeedChangePassword: user.MustChangePassword}, nil
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) SwitchAuthority(ctx context.Context, userID, authorityID uint) (*AuthenticationResult, error) {
|
||||
if err := uc.users.SetUserAuthority(ctx, userID, authorityID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := uc.users.User(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
issued, err := uc.issuer.IssueToken(user, authorityID, user.MustChangePassword, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uc.security.UseMultipoint() {
|
||||
oldToken, cacheErr := uc.security.ActiveToken(ctx, user.Username)
|
||||
if cacheErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, cacheErr)
|
||||
}
|
||||
if cacheErr = uc.security.RotateActiveToken(ctx, user.Username, oldToken, issued.Value, issued.TTL); cacheErr != nil {
|
||||
if errors.Is(cacheErr, ErrTokenRevoke) {
|
||||
return nil, cacheErr
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, cacheErr)
|
||||
}
|
||||
}
|
||||
return &AuthenticationResult{User: user, Token: issued.Value, ExpiresAt: issued.ExpiresAt, NeedChangePassword: user.MustChangePassword}, nil
|
||||
}
|
||||
+1
-1
@@ -3,4 +3,4 @@ package biz
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSet is biz providers.
|
||||
var ProviderSet = wire.NewSet(NewUserUsecase, NewSystemConfigUsecase, NewAuthorityUsecase, NewAPIUsecase, NewPermissionUsecase, NewAccessControlUsecase, NewMenuUsecase, NewDepartmentUsecase, NewPositionUsecase, NewDictionaryUsecase, NewParameterUsecase, NewTokenUsecase, NewSecurityUsecase, NewVersionUsecase, NewExportUsecase, NewAuditUsecase, NewAuditRecorderUsecase, NewLogViewerUsecase, NewTaskUsecase, NewMediaUsecase, NewAnnouncementUsecase, NewEmailUsecase)
|
||||
var ProviderSet = wire.NewSet(NewUserUsecase, NewAuthenticationUsecase, NewSystemConfigUsecase, NewAuthorityUsecase, NewAPIUsecase, NewPermissionUsecase, NewAccessControlUsecase, NewMenuUsecase, NewDepartmentUsecase, NewPositionUsecase, NewDictionaryUsecase, NewParameterUsecase, NewTokenUsecase, NewSecurityUsecase, NewVersionUsecase, NewExportUsecase, NewAuditUsecase, NewAuditRecorderUsecase, NewLogViewerUsecase, NewTaskUsecase, NewTaskApplicationUsecase, NewMediaUsecase, NewAnnouncementUsecase, NewEmailUsecase)
|
||||
|
||||
@@ -31,3 +31,43 @@ type FileStorage interface {
|
||||
DeletePrefix(context.Context, string) error
|
||||
List(context.Context, string, string, int) ([]*StoredFile, string, bool, error)
|
||||
}
|
||||
|
||||
type JWTSettings struct {
|
||||
SigningKey string
|
||||
Issuer string
|
||||
Expires time.Duration
|
||||
Buffer time.Duration
|
||||
}
|
||||
|
||||
type CaptchaSettings struct {
|
||||
KeyLong int
|
||||
ImageWidth int
|
||||
ImageHeight int
|
||||
StoreExpiration time.Duration
|
||||
}
|
||||
|
||||
type MediaSettings struct {
|
||||
SessionTTL int
|
||||
MaxFileSize int64
|
||||
}
|
||||
|
||||
// RuntimeSettings exposes only the active values needed by the application.
|
||||
// The data implementation resolves every call from conf.Runtime so hot reloads
|
||||
// take effect without rebuilding services.
|
||||
type RuntimeSettings interface {
|
||||
RouterPrefix() string
|
||||
JWTSettings() JWTSettings
|
||||
CaptchaSettings() CaptchaSettings
|
||||
MediaSettings() MediaSettings
|
||||
UseMultipoint() bool
|
||||
}
|
||||
|
||||
type IssuedToken struct {
|
||||
Value string
|
||||
ExpiresAt time.Time
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
type TokenIssuer interface {
|
||||
IssueToken(*User, uint, bool, time.Duration) (*IssuedToken, error)
|
||||
}
|
||||
|
||||
+158
-4
@@ -3,6 +3,10 @@ package biz
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SecurityConfig struct {
|
||||
@@ -33,14 +37,54 @@ type SecurityRepo interface {
|
||||
SaveSecurityConfig(context.Context, *SecurityConfig) error
|
||||
}
|
||||
|
||||
type SecurityUsecase struct{ SecurityRepo }
|
||||
type SecurityUsecase struct {
|
||||
repo SecurityRepo
|
||||
cache Cache
|
||||
settings RuntimeSettings
|
||||
tokens *TokenUsecase
|
||||
mu sync.RWMutex
|
||||
cachedConfig *SecurityConfig
|
||||
}
|
||||
|
||||
func NewSecurityUsecase(repo SecurityRepo) *SecurityUsecase {
|
||||
return &SecurityUsecase{SecurityRepo: repo}
|
||||
var ErrTokenRevoke = errors.New("token revoke failed")
|
||||
|
||||
type TokenRevokeError struct{ Err error }
|
||||
|
||||
func (e *TokenRevokeError) Error() string { return e.Err.Error() }
|
||||
func (e *TokenRevokeError) Is(target error) bool { return target == ErrTokenRevoke }
|
||||
|
||||
func NewSecurityUsecase(repo SecurityRepo, cache Cache, settings RuntimeSettings, tokens *TokenUsecase) *SecurityUsecase {
|
||||
return &SecurityUsecase{repo: repo, cache: cache, settings: settings, tokens: tokens}
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) UpdateSecurity(ctx context.Context, value *SecurityConfig) error {
|
||||
return uc.SaveSecurityConfig(ctx, value)
|
||||
if err := uc.repo.SaveSecurityConfig(ctx, value); err != nil {
|
||||
return err
|
||||
}
|
||||
uc.mu.Lock()
|
||||
copy := *value
|
||||
uc.cachedConfig = ©
|
||||
uc.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) Current(ctx context.Context) (*SecurityConfig, error) {
|
||||
uc.mu.RLock()
|
||||
if uc.cachedConfig != nil {
|
||||
copy := *uc.cachedConfig
|
||||
uc.mu.RUnlock()
|
||||
return ©, nil
|
||||
}
|
||||
uc.mu.RUnlock()
|
||||
value, err := uc.repo.SecurityConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uc.mu.Lock()
|
||||
copy := *value
|
||||
uc.cachedConfig = ©
|
||||
uc.mu.Unlock()
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) ValidatePassword(value *SecurityConfig, password string) error {
|
||||
@@ -74,3 +118,113 @@ func (uc *SecurityUsecase) ValidatePassword(value *SecurityConfig, password stri
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) ValidateCurrentPassword(ctx context.Context, password string) error {
|
||||
config, err := uc.Current(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return uc.ValidatePassword(config, password)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) LoginLocked(ctx context.Context, username string) (bool, error) {
|
||||
_, locked, err := uc.cache.Get(ctx, "login:lock:"+username)
|
||||
return locked, err
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) IncrementLoginFailure(ctx context.Context, username string, expiration time.Duration) (int64, error) {
|
||||
return uc.cache.Increment(ctx, "login:fail:"+username, expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) LockLogin(ctx context.Context, username string, expiration time.Duration) error {
|
||||
return uc.cache.Set(ctx, "login:lock:"+username, "1", expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) ClearLoginState(ctx context.Context, username string) {
|
||||
_ = uc.cache.Delete(ctx, "login:fail:"+username)
|
||||
_ = uc.cache.Delete(ctx, "login:lock:"+username)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) EnsureLoginIPCounter(ctx context.Context, ip string, expiration time.Duration) (int, error) {
|
||||
value, exists, err := uc.cache.Get(ctx, ip)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if exists {
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
if expiration <= 0 {
|
||||
expiration = time.Hour
|
||||
}
|
||||
if err = uc.cache.Set(ctx, ip, "1", expiration); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) IncrementLoginIP(ctx context.Context, ip string, expiration time.Duration) (int64, error) {
|
||||
return uc.cache.Increment(ctx, ip, expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) IncrementRateLimit(ctx context.Context, key string, expiration time.Duration) (int64, error) {
|
||||
return uc.cache.Increment(ctx, key, expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) SetCaptcha(ctx context.Context, id, value string, expiration time.Duration) error {
|
||||
return uc.cache.Set(ctx, "captcha:"+id, value, expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) GetCaptcha(ctx context.Context, id string) (string, bool, error) {
|
||||
return uc.cache.Get(ctx, "captcha:"+id)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) DeleteCaptcha(ctx context.Context, id string) error {
|
||||
return uc.cache.Delete(ctx, "captcha:"+id)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) VerifyCaptcha(ctx context.Context, id, answer string, clear bool) bool {
|
||||
if id == "" || answer == "" {
|
||||
return false
|
||||
}
|
||||
value, ok, err := uc.GetCaptcha(ctx, id)
|
||||
if err != nil || !ok {
|
||||
return false
|
||||
}
|
||||
if clear {
|
||||
_ = uc.DeleteCaptcha(ctx, id)
|
||||
}
|
||||
return strings.EqualFold(value, answer)
|
||||
}
|
||||
|
||||
func activeTokenKey(username string) string { return "jwt:active:" + username }
|
||||
|
||||
func (uc *SecurityUsecase) ActiveToken(ctx context.Context, username string) (string, error) {
|
||||
value, _, err := uc.cache.Get(ctx, activeTokenKey(username))
|
||||
return value, err
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) ActiveTokenMatches(ctx context.Context, username, token string) (bool, error) {
|
||||
if !uc.settings.UseMultipoint() {
|
||||
return true, nil
|
||||
}
|
||||
active, ok, err := uc.cache.Get(ctx, activeTokenKey(username))
|
||||
return ok && active == token, err
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) RotateActiveToken(ctx context.Context, username, oldToken, newToken string, expiration time.Duration) error {
|
||||
if !uc.settings.UseMultipoint() {
|
||||
return nil
|
||||
}
|
||||
if oldToken != "" && oldToken != newToken {
|
||||
if err := uc.tokens.BlacklistToken(ctx, oldToken); err != nil {
|
||||
return &TokenRevokeError{Err: err}
|
||||
}
|
||||
}
|
||||
return uc.cache.Set(ctx, activeTokenKey(username), newToken, expiration)
|
||||
}
|
||||
|
||||
func (uc *SecurityUsecase) UseMultipoint() bool { return uc.settings.UseMultipoint() }
|
||||
|
||||
func (uc *SecurityUsecase) CaptchaRuntimeSettings() CaptchaSettings {
|
||||
return uc.settings.CaptchaSettings()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
package biz
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var ErrTaskRuntimeReload = errors.New("task runtime reload failed")
|
||||
|
||||
type TaskRuntimeReloadError struct{ Err error }
|
||||
|
||||
func (e *TaskRuntimeReloadError) Error() string { return e.Err.Error() }
|
||||
func (e *TaskRuntimeReloadError) Is(target error) bool { return target == ErrTaskRuntimeReload }
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Driver, Host, Port, User, Password, Name, Path, Config, Template, AdminPassword string
|
||||
@@ -16,10 +26,13 @@ type InitializationRepo interface {
|
||||
ReloadConfig(context.Context) error
|
||||
}
|
||||
|
||||
type SystemConfigUsecase struct{ repo InitializationRepo }
|
||||
type SystemConfigUsecase struct {
|
||||
repo InitializationRepo
|
||||
tasks TaskRuntime
|
||||
}
|
||||
|
||||
func NewSystemConfigUsecase(repo InitializationRepo) *SystemConfigUsecase {
|
||||
return &SystemConfigUsecase{repo: repo}
|
||||
func NewSystemConfigUsecase(repo InitializationRepo, tasks TaskRuntime) *SystemConfigUsecase {
|
||||
return &SystemConfigUsecase{repo: repo, tasks: tasks}
|
||||
}
|
||||
|
||||
func (uc *SystemConfigUsecase) IsInitialized(ctx context.Context) (bool, error) {
|
||||
@@ -29,7 +42,13 @@ func (uc *SystemConfigUsecase) Initialize(ctx context.Context, config *DatabaseC
|
||||
if config == nil || len(config.AdminPassword) < 6 {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
return uc.repo.Initialize(ctx, config)
|
||||
if err := uc.repo.Initialize(ctx, config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.tasks.Reload(ctx); err != nil {
|
||||
return &TaskRuntimeReloadError{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (uc *SystemConfigUsecase) PersistConfig(ctx context.Context) error {
|
||||
return uc.repo.PersistConfig(ctx)
|
||||
@@ -41,5 +60,11 @@ func (uc *SystemConfigUsecase) PersistRuntimeConfig(ctx context.Context, data, a
|
||||
return uc.repo.PersistRuntimeConfig(ctx, data, admin)
|
||||
}
|
||||
func (uc *SystemConfigUsecase) ReloadConfig(ctx context.Context) error {
|
||||
return uc.repo.ReloadConfig(ctx)
|
||||
if err := uc.repo.ReloadConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.tasks.Reload(ctx); err != nil {
|
||||
return &TaskRuntimeReloadError{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@ const (
|
||||
TaskMethodUploads = "CleanStaleUploads"
|
||||
)
|
||||
|
||||
var ErrTaskSchedule = errors.New("task schedule failed")
|
||||
|
||||
type TaskScheduleError struct{ Err error }
|
||||
|
||||
func (e *TaskScheduleError) Error() string { return e.Err.Error() }
|
||||
func (e *TaskScheduleError) Is(target error) bool { return target == ErrTaskSchedule }
|
||||
|
||||
type TaskMethod struct {
|
||||
Name string
|
||||
Description string
|
||||
@@ -77,6 +84,17 @@ type TaskRepo interface {
|
||||
CleanupLogs(context.Context) error
|
||||
TaskNameExists(context.Context, string, uint) (bool, error)
|
||||
}
|
||||
|
||||
type TaskRuntime interface {
|
||||
ScheduleID(context.Context, uint) error
|
||||
Remove(uint)
|
||||
TriggerID(context.Context, uint) error
|
||||
NextRuns() map[uint]time.Time
|
||||
Reload(context.Context) error
|
||||
Subscribe() chan []byte
|
||||
Unsubscribe(chan []byte)
|
||||
}
|
||||
|
||||
type TaskUsecase struct{ TaskRepo }
|
||||
|
||||
func NewTaskUsecase(repo TaskRepo) *TaskUsecase { return &TaskUsecase{TaskRepo: repo} }
|
||||
@@ -149,3 +167,69 @@ func (uc *TaskUsecase) Update(ctx context.Context, value *TimedTask) error {
|
||||
}
|
||||
return uc.UpdateTask(ctx, value)
|
||||
}
|
||||
|
||||
type TaskApplicationUsecase struct {
|
||||
tasks *TaskUsecase
|
||||
runtime TaskRuntime
|
||||
}
|
||||
|
||||
func NewTaskApplicationUsecase(tasks *TaskUsecase, runtime TaskRuntime) *TaskApplicationUsecase {
|
||||
return &TaskApplicationUsecase{tasks: tasks, runtime: runtime}
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Create(ctx context.Context, value *TimedTask) error {
|
||||
if err := uc.tasks.Create(ctx, value); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.runtime.ScheduleID(ctx, value.ID); err != nil {
|
||||
return &TaskScheduleError{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Update(ctx context.Context, value *TimedTask) error {
|
||||
if err := uc.tasks.Update(ctx, value); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.runtime.ScheduleID(ctx, value.ID); err != nil {
|
||||
return &TaskScheduleError{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Delete(ctx context.Context, id uint) error {
|
||||
uc.runtime.Remove(id)
|
||||
return uc.tasks.DeleteTask(ctx, id)
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Toggle(ctx context.Context, id uint, enabled bool) error {
|
||||
if err := uc.tasks.ToggleTask(ctx, id, enabled); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uc.runtime.ScheduleID(ctx, id); err != nil {
|
||||
return &TaskScheduleError{Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Trigger(ctx context.Context, id uint) error {
|
||||
return uc.runtime.TriggerID(ctx, id)
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) List(ctx context.Context, page, size int, filter *TimedTask) ([]*TimedTask, int64, map[uint]time.Time, error) {
|
||||
items, total, err := uc.tasks.ListTasks(ctx, page, size, filter)
|
||||
return items, total, uc.runtime.NextRuns(), err
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Logs(ctx context.Context, page, size int, taskID uint, status string) ([]*TimedTaskLog, int64, error) {
|
||||
return uc.tasks.ListTaskLogs(ctx, page, size, taskID, status)
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Reload(ctx context.Context) error {
|
||||
return uc.runtime.Reload(ctx)
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Subscribe() chan []byte { return uc.runtime.Subscribe() }
|
||||
func (uc *TaskApplicationUsecase) Unsubscribe(events chan []byte) {
|
||||
uc.runtime.Unsubscribe(events)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user