initial commit. Cloned timetracker repository

This commit is contained in:
Daniel Goc
2026-03-10 09:02:57 +01:00
commit f2952bcef0
189 changed files with 21334 additions and 0 deletions

20
app/view/emails.go Normal file
View File

@@ -0,0 +1,20 @@
package view
type EmailLayout[T any] struct {
LangID uint
Data T
}
type EmailVerificationData struct {
VerificationURL string
}
type EmailAdminNotificationData struct {
UserName string
UserEmail string
BaseURL string
}
type EmailPasswordResetData struct {
ResetURL string
}

166
app/view/errors.go Normal file
View File

@@ -0,0 +1,166 @@
package view
import (
"errors"
"git.ma-al.com/goc_marek/timetracker/app/utils/i18n"
"github.com/gofiber/fiber/v3"
)
var (
// Typed errors for request validation and authentication
ErrInvalidBody = errors.New("invalid request body")
ErrNotAuthenticated = errors.New("not authenticated")
ErrUserNotFound = errors.New("user not found")
ErrUserInactive = errors.New("user account is inactive")
ErrInvalidToken = errors.New("invalid token")
ErrTokenExpired = errors.New("token has expired")
ErrTokenRequired = errors.New("token is required")
// Typed errors for logging in and registering
ErrInvalidCredentials = errors.New("invalid email or password")
ErrEmailNotVerified = errors.New("email not verified")
ErrEmailExists = errors.New("email already exists")
ErrFirstLastNameRequired = errors.New("first and last name is required")
ErrEmailRequired = errors.New("email is required")
ErrEmailPasswordRequired = errors.New("email and password are required")
ErrRefreshTokenRequired = errors.New("refresh token is required")
// Typed errors for password reset
ErrInvalidResetToken = errors.New("invalid reset token")
ErrResetTokenExpired = errors.New("reset token has expired")
ErrPasswordsDoNotMatch = errors.New("passwords do not match")
ErrInvalidPassword = errors.New("password must be at least 8 characters long and contain at least one lowercase letter, one uppercase letter, and one digit")
ErrTokenPasswordRequired = errors.New("token and password are required")
// Typed errors for verification
ErrInvalidVerificationToken = errors.New("invalid verification token")
ErrVerificationTokenExpired = errors.New("verification token has expired")
// Typed errors for data extraction
ErrBadRepoIDAttribute = errors.New("invalid repo id attribute")
ErrBadYearAttribute = errors.New("invalid year attribute")
ErrBadQuarterAttribute = errors.New("invalid quarter attribute")
ErrBadPaging = errors.New("invalid paging")
ErrInvalidRepoID = errors.New("repo not accessible")
)
// Error represents an error with HTTP status code
type Error struct {
Err error
Status int
}
func (e *Error) Error() string {
return e.Err.Error()
}
func (e *Error) Unwrap() error {
return e.Err
}
// NewError creates a new typed error
func NewError(err error, status int) *Error {
return &Error{Err: err, Status: status}
}
// GetErrorCode returns the error code string for HTTP response mapping
func GetErrorCode(c fiber.Ctx, err error) string {
switch {
case errors.Is(err, ErrInvalidBody):
return i18n.T_(c, "error.err_invalid_body")
case errors.Is(err, ErrInvalidCredentials):
return i18n.T_(c, "error.err_invalid_credentials")
case errors.Is(err, ErrNotAuthenticated):
return i18n.T_(c, "error.err_not_authenticated")
case errors.Is(err, ErrUserNotFound):
return i18n.T_(c, "error.err_user_not_found")
case errors.Is(err, ErrUserInactive):
return i18n.T_(c, "error.err_user_inactive")
case errors.Is(err, ErrEmailNotVerified):
return i18n.T_(c, "error.err_email_not_verified")
case errors.Is(err, ErrEmailExists):
return i18n.T_(c, "error.err_email_exists")
case errors.Is(err, ErrInvalidToken):
return i18n.T_(c, "error.err_invalid_token")
case errors.Is(err, ErrTokenExpired):
return i18n.T_(c, "error.err_token_expired")
case errors.Is(err, ErrFirstLastNameRequired):
return i18n.T_(c, "error.err_first_last_name_required")
case errors.Is(err, ErrEmailRequired):
return i18n.T_(c, "error.err_email_required")
case errors.Is(err, ErrEmailPasswordRequired):
return i18n.T_(c, "error.err_email_password_required")
case errors.Is(err, ErrTokenRequired):
return i18n.T_(c, "error.err_token_required")
case errors.Is(err, ErrRefreshTokenRequired):
return i18n.T_(c, "error.err_refresh_token_required")
case errors.Is(err, ErrInvalidResetToken):
return i18n.T_(c, "error.err_invalid_reset_token")
case errors.Is(err, ErrResetTokenExpired):
return i18n.T_(c, "error.err_reset_token_expired")
case errors.Is(err, ErrPasswordsDoNotMatch):
return i18n.T_(c, "error.err_passwords_do_not_match")
case errors.Is(err, ErrInvalidPassword):
return i18n.T_(c, "error.err_invalid_password")
case errors.Is(err, ErrTokenPasswordRequired):
return i18n.T_(c, "error.err_token_password_required")
case errors.Is(err, ErrInvalidVerificationToken):
return i18n.T_(c, "error.err_invalid_verification_token")
case errors.Is(err, ErrVerificationTokenExpired):
return i18n.T_(c, "error.err_verification_token_expired")
case errors.Is(err, ErrBadRepoIDAttribute):
return i18n.T_(c, "error.err_bad_repo_id_attribute")
case errors.Is(err, ErrBadYearAttribute):
return i18n.T_(c, "error.err_bad_year_attribute")
case errors.Is(err, ErrBadQuarterAttribute):
return i18n.T_(c, "error.err_bad_quarter_attribute")
case errors.Is(err, ErrBadPaging):
return i18n.T_(c, "error.err_bad_paging")
case errors.Is(err, ErrInvalidRepoID):
return i18n.T_(c, "error.err_invalid_repo_id")
default:
return i18n.T_(c, "error.err_internal_server_error")
}
}
// GetErrorStatus returns the HTTP status code for the given error
func GetErrorStatus(err error) int {
switch {
case errors.Is(err, ErrInvalidCredentials),
errors.Is(err, ErrNotAuthenticated),
errors.Is(err, ErrInvalidToken),
errors.Is(err, ErrTokenExpired):
return fiber.StatusUnauthorized
case errors.Is(err, ErrInvalidBody),
errors.Is(err, ErrUserNotFound),
errors.Is(err, ErrUserInactive),
errors.Is(err, ErrEmailNotVerified),
errors.Is(err, ErrFirstLastNameRequired),
errors.Is(err, ErrEmailRequired),
errors.Is(err, ErrEmailPasswordRequired),
errors.Is(err, ErrTokenRequired),
errors.Is(err, ErrRefreshTokenRequired),
errors.Is(err, ErrPasswordsDoNotMatch),
errors.Is(err, ErrTokenPasswordRequired),
errors.Is(err, ErrInvalidResetToken),
errors.Is(err, ErrResetTokenExpired),
errors.Is(err, ErrInvalidVerificationToken),
errors.Is(err, ErrVerificationTokenExpired),
errors.Is(err, ErrInvalidPassword),
errors.Is(err, ErrBadRepoIDAttribute),
errors.Is(err, ErrBadYearAttribute),
errors.Is(err, ErrBadQuarterAttribute),
errors.Is(err, ErrBadPaging),
errors.Is(err, ErrInvalidRepoID):
return fiber.StatusBadRequest
case errors.Is(err, ErrEmailExists):
return fiber.StatusConflict
default:
return fiber.StatusInternalServerError
}
}

21
app/view/i18n.go Normal file
View File

@@ -0,0 +1,21 @@
package view
import (
"time"
)
type Language struct {
ID uint64 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
Name string `json:"name"`
ISOCode string `json:"iso_code"`
LangCode string `json:"lang_code"`
DateFormat string `json:"date_format"`
DateFormatShort string `json:"date_format_short"`
RTL bool `json:"rtl"`
IsDefault bool `json:"is_default"`
Active bool `json:"active"`
Flag string `json:"flag"`
}

36
app/view/repo.go Normal file
View File

@@ -0,0 +1,36 @@
package view
import (
"time"
"git.ma-al.com/goc_marek/timetracker/app/model"
"git.ma-al.com/goc_marek/timetracker/app/utils/pagination"
)
type RepositoryChartData struct {
Years []uint
Quarters []model.QuarterData
QuartersJSON string
Year uint
}
type TimeTrackedData struct {
RepoId uint
Year uint
Quarter uint
Step string
TotalTime float64
DailyData []model.DayData
DailyDataJSON string
Years []uint
IssueSummaries *pagination.Found[IssueTimeSummary]
}
type IssueTimeSummary struct {
IssueID uint `gorm:"column:issue_id"`
IssueName string `gorm:"column:issue_name"`
UserID uint `gorm:"column:user_id"`
Initials string `gorm:"column:initials"`
CreatedDate time.Time `gorm:"column:created_date"`
TotalHoursSpent float64 `gorm:"column:total_hours_spent"`
}