feat(observabillity): #115 integrate otel for metrics and traces
This commit was merged in pull request #152.
This commit is contained in:
@@ -52,7 +52,7 @@ func (db AuthSqlite) InsertUser(user *types.User) error {
|
||||
return ErrAlreadyExists
|
||||
}
|
||||
|
||||
log.Error("SQL error InsertUser: %v", err)
|
||||
log.L.Error("SQL error InsertUser", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (db AuthSqlite) UpdateUser(user *types.User) error {
|
||||
user.EmailVerified, user.EmailVerifiedAt, user.Password, user.Id)
|
||||
|
||||
if err != nil {
|
||||
log.Error("SQL error UpdateUser: %v", err)
|
||||
log.L.Error("SQL error UpdateUser", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func (db AuthSqlite) GetUserByEmail(email string) (*types.User, error) {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
} else {
|
||||
log.Error("SQL error GetUser: %v", err)
|
||||
log.L.Error("SQL error GetUser", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func (db AuthSqlite) GetUser(userId uuid.UUID) (*types.User, error) {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
} else {
|
||||
log.Error("SQL error GetUser %v", err)
|
||||
log.L.Error("SQL error GetUser", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
}
|
||||
@@ -131,55 +131,55 @@ func (db AuthSqlite) GetUser(userId uuid.UUID) (*types.User, error) {
|
||||
func (db AuthSqlite) DeleteUser(userId uuid.UUID) error {
|
||||
tx, err := db.db.Begin()
|
||||
if err != nil {
|
||||
log.Error("Could not start transaction: %v", err)
|
||||
log.L.Error("Could not start transaction", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM account WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete accounts: %v", err)
|
||||
log.L.Error("Could not delete accounts", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM token WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete user tokens: %v", err)
|
||||
log.L.Error("Could not delete user tokens", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM session WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete sessions: %v", err)
|
||||
log.L.Error("Could not delete sessions", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM user WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete user: %v", err)
|
||||
log.L.Error("Could not delete user", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM treasure_chest WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete user: %v", err)
|
||||
log.L.Error("Could not delete user", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
_, err = tx.Exec("DELETE FROM \"transaction\" WHERE user_id = ?", userId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Error("Could not delete user: %v", err)
|
||||
log.L.Error("Could not delete user", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Error("Could not commit transaction: %v", err)
|
||||
log.L.Error("Could not commit transaction", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ func (db AuthSqlite) InsertToken(token *types.Token) error {
|
||||
VALUES (?, ?, ?, ?, ?, ?)`, token.UserId, token.SessionId, token.Type, token.Token, token.CreatedAt, token.ExpiresAt)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Could not insert token: %v", err)
|
||||
log.L.Error("Could not insert token", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -217,23 +217,23 @@ func (db AuthSqlite) GetToken(token string) (*types.Token, error) {
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
log.Info("Token '%v' not found", token)
|
||||
log.L.Info("Token not found", "token", token)
|
||||
return nil, ErrNotFound
|
||||
} else {
|
||||
log.Error("Could not get token: %v", err)
|
||||
log.L.Error("Could not get token", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
}
|
||||
|
||||
createdAt, err = time.Parse(time.RFC3339, createdAtStr)
|
||||
if err != nil {
|
||||
log.Error("Could not parse token.created_at: %v", err)
|
||||
log.L.Error("Could not parse token.created_at", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
expiresAt, err = time.Parse(time.RFC3339, expiresAtStr)
|
||||
if err != nil {
|
||||
log.Error("Could not parse token.expires_at: %v", err)
|
||||
log.L.Error("Could not parse token.expires_at", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ func (db AuthSqlite) GetTokensByUserIdAndType(userId uuid.UUID, tokenType types.
|
||||
AND type = ?`, userId, tokenType)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Could not get token: %v", err)
|
||||
log.L.Error("Could not get token", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func (db AuthSqlite) GetTokensBySessionIdAndType(sessionId string, tokenType typ
|
||||
AND type = ?`, sessionId, tokenType)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Could not get token: %v", err)
|
||||
log.L.Error("Could not get token", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -287,19 +287,19 @@ func getTokensFromQuery(query *sql.Rows, userId uuid.UUID, sessionId string, tok
|
||||
|
||||
err := query.Scan(&token, &createdAtStr, &expiresAtStr)
|
||||
if err != nil {
|
||||
log.Error("Could not scan token: %v", err)
|
||||
log.L.Error("Could not scan token", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
createdAt, err = time.Parse(time.RFC3339, createdAtStr)
|
||||
if err != nil {
|
||||
log.Error("Could not parse token.created_at: %v", err)
|
||||
log.L.Error("Could not parse token.created_at", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
expiresAt, err = time.Parse(time.RFC3339, expiresAtStr)
|
||||
if err != nil {
|
||||
log.Error("Could not parse token.expires_at: %v", err)
|
||||
log.L.Error("Could not parse token.expires_at", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func getTokensFromQuery(query *sql.Rows, userId uuid.UUID, sessionId string, tok
|
||||
func (db AuthSqlite) DeleteToken(token string) error {
|
||||
_, err := db.db.Exec("DELETE FROM token WHERE token = ?", token)
|
||||
if err != nil {
|
||||
log.Error("Could not delete token: %v", err)
|
||||
log.L.Error("Could not delete token", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
return nil
|
||||
@@ -328,7 +328,7 @@ func (db AuthSqlite) InsertSession(session *types.Session) error {
|
||||
VALUES (?, ?, ?, ?)`, session.Id, session.UserId, session.CreatedAt, session.ExpiresAt)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Could not insert new session %v", err)
|
||||
log.L.Error("Could not insert new session", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func (db AuthSqlite) GetSession(sessionId string) (*types.Session, error) {
|
||||
WHERE session_id = ?`, sessionId).Scan(&userId, &createdAt, &expiresAt)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Session \"%s\" not found: %v", sessionId, err)
|
||||
log.L.Warn("Session not found", "session-id", sessionId, "err", err)
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ func (db AuthSqlite) GetSessions(userId uuid.UUID) ([]*types.Session, error) {
|
||||
FROM session
|
||||
WHERE user_id = ?`, userId)
|
||||
if err != nil {
|
||||
log.Error("Could not get sessions: %v", err)
|
||||
log.L.Error("Could not get sessions", "err", err)
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ func (db AuthSqlite) DeleteOldSessions(userId uuid.UUID) error {
|
||||
WHERE expires_at < datetime('now')
|
||||
AND user_id = ?`, userId)
|
||||
if err != nil {
|
||||
log.Error("Could not delete old sessions: %v", err)
|
||||
log.L.Error("Could not delete old sessions", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
return nil
|
||||
@@ -385,7 +385,7 @@ func (db AuthSqlite) DeleteSession(sessionId string) error {
|
||||
if sessionId != "" {
|
||||
_, err := db.db.Exec("DELETE FROM session WHERE session_id = ?", sessionId)
|
||||
if err != nil {
|
||||
log.Error("Could not delete session: %v", err)
|
||||
log.L.Error("Could not delete session", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,19 @@ func TransformAndLogDbError(module string, r sql.Result, err error) error {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return ErrNotFound
|
||||
}
|
||||
log.Error("%v: %v", module, err)
|
||||
log.L.Error("database sql", "module", module, "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
rows, err := r.RowsAffected()
|
||||
if err != nil {
|
||||
log.Error("%v: %v", module, err)
|
||||
log.L.Error("database rows affected", "module", module, "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
log.Info("%v: not found", module)
|
||||
log.L.Info("row not found", "module", module)
|
||||
return ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
|
||||
type migrationLogger struct{}
|
||||
|
||||
func (l migrationLogger) Printf(format string, v ...interface{}) {
|
||||
log.Info(format, v...)
|
||||
func (l migrationLogger) Printf(format string, v ...any) {
|
||||
log.L.Info(format, v...)
|
||||
}
|
||||
func (l migrationLogger) Verbose() bool {
|
||||
return false
|
||||
@@ -24,7 +24,7 @@ func (l migrationLogger) Verbose() bool {
|
||||
func RunMigrations(db *sqlx.DB, pathPrefix string) error {
|
||||
driver, err := sqlite3.WithInstance(db.DB, &sqlite3.Config{})
|
||||
if err != nil {
|
||||
log.Error("Could not create Migration instance: %v", err)
|
||||
log.L.Error("Could not create Migration instance", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -33,14 +33,14 @@ func RunMigrations(db *sqlx.DB, pathPrefix string) error {
|
||||
"",
|
||||
driver)
|
||||
if err != nil {
|
||||
log.Error("Could not create migrations instance: %v", err)
|
||||
log.L.Error("Could not create migrations instance", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
m.Log = migrationLogger{}
|
||||
|
||||
if err = m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||
log.Error("Could not run migrations: %v", err)
|
||||
log.L.Error("Could not run migrations", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
|
||||
@@ -19,56 +19,67 @@ import (
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
)
|
||||
|
||||
func Run(ctx context.Context, database *sqlx.DB, migrationsPrefix string, env func(string) string) error {
|
||||
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
log.Info("Starting server...")
|
||||
log.L.Info("Starting server...")
|
||||
|
||||
// init server settings
|
||||
serverSettings := types.NewSettingsFromEnv(env)
|
||||
serverSettings, err := types.NewSettingsFromEnv(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// init db
|
||||
err := db.RunMigrations(database, migrationsPrefix)
|
||||
err = db.RunMigrations(database, migrationsPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not run migrations: %w", err)
|
||||
}
|
||||
|
||||
// init servers
|
||||
var prometheusServer *http.Server
|
||||
if serverSettings.PrometheusEnabled {
|
||||
prometheusServer := &http.Server{
|
||||
Addr: ":8081",
|
||||
Handler: promhttp.Handler(),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
if serverSettings.OtelEnabled {
|
||||
// use context.Background(), otherwise the shutdown can't be called, as the context is already cancelled
|
||||
otelShutdown, err := setupOTelSDK(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not setup OpenTelemetry SDK: %w", err)
|
||||
}
|
||||
go startServer(prometheusServer)
|
||||
defer func() {
|
||||
// User context.Background(), as the main context is already cancelled
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
err = otelShutdown(ctx)
|
||||
if err != nil {
|
||||
log.L.Error("error shutting down OpenTelemetry SDK", "err", err)
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
|
||||
log.InitOtelLogger()
|
||||
}
|
||||
|
||||
// init server
|
||||
httpServer := &http.Server{
|
||||
Addr: ":" + serverSettings.Port,
|
||||
Handler: createHandler(database, serverSettings),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
}
|
||||
go startServer(httpServer)
|
||||
|
||||
// graceful shutdown
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
wg.Add(1)
|
||||
go shutdownServer(httpServer, ctx, &wg)
|
||||
go shutdownServer(prometheusServer, ctx, &wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startServer(s *http.Server) {
|
||||
log.Info("Starting server on %q", s.Addr)
|
||||
log.L.Info("Starting server", "addr", s.Addr)
|
||||
if err := s.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error("error listening and serving: %v", err)
|
||||
log.L.Error("error listening and serving", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,9 +94,9 @@ func shutdownServer(s *http.Server, ctx context.Context, wg *sync.WaitGroup) {
|
||||
shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second)
|
||||
defer cancel()
|
||||
if err := s.Shutdown(shutdownCtx); err != nil {
|
||||
log.Error("error shutting down http server: %v", err)
|
||||
log.L.Error("error shutting down http server", "err", err)
|
||||
} else {
|
||||
log.Info("Gracefully stopped http server on %v", s.Addr)
|
||||
log.L.Info("Gracefully stopped http server", "addr", s.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +133,7 @@ func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler {
|
||||
// Serve static files (CSS, JS and images)
|
||||
router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
|
||||
|
||||
return middleware.Wrapper(
|
||||
wrapper := middleware.Wrapper(
|
||||
router,
|
||||
middleware.GenerateRecurringTransactions(transactionRecurringService),
|
||||
middleware.SecurityHeaders(serverSettings),
|
||||
@@ -132,4 +143,8 @@ func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler {
|
||||
middleware.Gzip,
|
||||
middleware.Log,
|
||||
)
|
||||
|
||||
wrapper = otelhttp.NewHandler(wrapper, "http.request")
|
||||
|
||||
return wrapper
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ func (h AccountImpl) Handle(r *http.ServeMux) {
|
||||
|
||||
func (h AccountImpl) handleAccountPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -55,6 +57,8 @@ func (h AccountImpl) handleAccountPage() http.HandlerFunc {
|
||||
|
||||
func (h AccountImpl) handleAccountItemComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -86,6 +90,8 @@ func (h AccountImpl) handleAccountItemComp() http.HandlerFunc {
|
||||
|
||||
func (h AccountImpl) handleUpdateAccount() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -119,6 +125,8 @@ func (h AccountImpl) handleUpdateAccount() http.HandlerFunc {
|
||||
|
||||
func (h AccountImpl) handleDeleteAccount() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
|
||||
@@ -59,6 +59,8 @@ var (
|
||||
|
||||
func (handler AuthImpl) handleSignInPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user != nil {
|
||||
if !user.EmailVerified {
|
||||
@@ -77,6 +79,8 @@ func (handler AuthImpl) handleSignInPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleSignIn() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user, err := utils.WaitMinimumTime(securityWaitDuration, func() (*types.User, error) {
|
||||
session := middleware.GetSession(r)
|
||||
email := r.FormValue("email")
|
||||
@@ -112,6 +116,8 @@ func (handler AuthImpl) handleSignIn() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleSignUpPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
|
||||
if user != nil {
|
||||
@@ -130,6 +136,8 @@ func (handler AuthImpl) handleSignUpPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleSignUpVerifyPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -148,6 +156,8 @@ func (handler AuthImpl) handleSignUpVerifyPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleVerifyResendComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -158,13 +168,15 @@ func (handler AuthImpl) handleVerifyResendComp() http.HandlerFunc {
|
||||
|
||||
_, err := w.Write([]byte("<p class=\"mt-8\">Verification email sent</p>"))
|
||||
if err != nil {
|
||||
log.Error("Could not write response: %v", err)
|
||||
log.L.Error("Could not write response", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (handler AuthImpl) handleSignUpVerifyResponsePage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
token := r.URL.Query().Get("token")
|
||||
|
||||
err := handler.service.VerifyUserEmail(token)
|
||||
@@ -185,17 +197,19 @@ func (handler AuthImpl) handleSignUpVerifyResponsePage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleSignUp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
var email = r.FormValue("email")
|
||||
var password = r.FormValue("password")
|
||||
|
||||
_, err := utils.WaitMinimumTime(securityWaitDuration, func() (interface{}, error) {
|
||||
log.Info("Signing up %v", email)
|
||||
_, err := utils.WaitMinimumTime(securityWaitDuration, func() (any, error) {
|
||||
log.L.Info("signing up", "email", email)
|
||||
user, err := handler.service.SignUp(email, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("Sending verification email to %v", user.Email)
|
||||
log.L.Info("Sending verification email", "to", user.Email)
|
||||
go handler.service.SendVerificationMail(user.Id, user.Email)
|
||||
return nil, nil
|
||||
})
|
||||
@@ -221,6 +235,8 @@ func (handler AuthImpl) handleSignUp() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleSignOut() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
session := middleware.GetSession(r)
|
||||
|
||||
if session != nil {
|
||||
@@ -248,6 +264,8 @@ func (handler AuthImpl) handleSignOut() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleDeleteAccountPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -261,6 +279,8 @@ func (handler AuthImpl) handleDeleteAccountPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleDeleteAccountComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -285,6 +305,8 @@ func (handler AuthImpl) handleDeleteAccountComp() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleChangePasswordPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
isPasswordReset := r.URL.Query().Has("token")
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
@@ -301,6 +323,8 @@ func (handler AuthImpl) handleChangePasswordPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleChangePasswordComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
session := middleware.GetSession(r)
|
||||
user := middleware.GetUser(r)
|
||||
if session == nil || user == nil {
|
||||
@@ -323,6 +347,8 @@ func (handler AuthImpl) handleChangePasswordComp() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleForgotPasswordPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user != nil {
|
||||
utils.DoRedirect(w, r, "/")
|
||||
@@ -336,13 +362,15 @@ func (handler AuthImpl) handleForgotPasswordPage() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleForgotPasswordComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
email := r.FormValue("email")
|
||||
if email == "" {
|
||||
utils.TriggerToastWithStatus(w, r, "error", "Please enter an email", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := utils.WaitMinimumTime(securityWaitDuration, func() (interface{}, error) {
|
||||
_, err := utils.WaitMinimumTime(securityWaitDuration, func() (any, error) {
|
||||
err := handler.service.SendForgotPasswordMail(email)
|
||||
return nil, err
|
||||
})
|
||||
@@ -357,9 +385,11 @@ func (handler AuthImpl) handleForgotPasswordComp() http.HandlerFunc {
|
||||
|
||||
func (handler AuthImpl) handleForgotPasswordResponseComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
pageUrl, err := url.Parse(r.Header.Get("Hx-Current-Url"))
|
||||
if err != nil {
|
||||
log.Error("Could not get current URL: %v", err)
|
||||
log.L.Error("Could not get current URL", "err", err)
|
||||
utils.TriggerToastWithStatus(w, r, "error", "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import (
|
||||
"spend-sparrow/internal/service"
|
||||
"spend-sparrow/internal/utils"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func handleError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
@@ -33,3 +36,11 @@ func extractErrorMessage(err error) string {
|
||||
|
||||
return strings.SplitN(errMsg, ":", 2)[0]
|
||||
}
|
||||
|
||||
func updateSpan(r *http.Request) {
|
||||
currentSpan := trace.SpanFromContext(r.Context())
|
||||
if currentSpan != nil {
|
||||
currentSpan.SetAttributes(attribute.String("http.pattern", r.Pattern))
|
||||
currentSpan.SetAttributes(attribute.String("http.pattern.id", r.PathValue("id")))
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,15 @@ package middleware
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
func CacheControl(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
counter, _ := otel.Meter("").Int64Counter("spend.sparrow.test")
|
||||
counter.Add(r.Context(), 1)
|
||||
|
||||
shouldCache := strings.HasPrefix(r.URL.Path, "/static")
|
||||
|
||||
if !shouldCache {
|
||||
|
||||
@@ -46,7 +46,7 @@ func CrossSiteRequestForgery(auth service.Auth) func(http.Handler) http.Handler
|
||||
csrfToken := r.Header.Get("Csrf-Token")
|
||||
|
||||
if session == nil || csrfToken == "" || !auth.IsCsrfTokenValid(csrfToken, session.Id) {
|
||||
log.Info("CSRF-Token \"%s\" not correct", csrfToken)
|
||||
log.L.Info("CSRF-Token not correct", "token", csrfToken)
|
||||
if r.Header.Get("Hx-Request") == "true" {
|
||||
utils.TriggerToastWithStatus(w, r, "error", "CSRF-Token not correct", http.StatusBadRequest)
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ func Gzip(next http.Handler) http.Handler {
|
||||
|
||||
err := gz.Close()
|
||||
if err != nil && !errors.Is(err, http.ErrBodyNotAllowed) {
|
||||
log.Error("Gzip: could not close Writer: %v", err)
|
||||
log.L.Error("Gzip: could not close Writer", "err", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,23 +2,8 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"spend-sparrow/internal/log"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
metrics = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "mefit_request_total",
|
||||
Help: "The total number of requests processed",
|
||||
},
|
||||
[]string{"path", "method", "status"},
|
||||
)
|
||||
"time"
|
||||
)
|
||||
|
||||
type WrappedWriter struct {
|
||||
@@ -35,13 +20,19 @@ func Log(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
log.L.Info("request pattern", "pattern", r.Pattern)
|
||||
|
||||
wrapped := &WrappedWriter{
|
||||
ResponseWriter: w,
|
||||
StatusCode: http.StatusOK,
|
||||
}
|
||||
next.ServeHTTP(wrapped, r)
|
||||
|
||||
log.Info(r.RemoteAddr + " " + strconv.Itoa(wrapped.StatusCode) + " " + r.Method + " " + r.URL.Path + " " + time.Since(start).String())
|
||||
metrics.WithLabelValues(r.URL.Path, r.Method, http.StatusText(wrapped.StatusCode)).Inc()
|
||||
log.L.Info("request",
|
||||
"remoteAddr", r.RemoteAddr,
|
||||
"status", wrapped.StatusCode,
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"duration", time.Since(start).String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"spend-sparrow/internal/log"
|
||||
"spend-sparrow/internal/template"
|
||||
"spend-sparrow/internal/template/auth"
|
||||
"spend-sparrow/internal/types"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
@@ -23,7 +22,7 @@ func (render *Render) RenderWithStatus(r *http.Request, w http.ResponseWriter, c
|
||||
w.WriteHeader(status)
|
||||
err := comp.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
log.Error("Failed to render layout: %v", err)
|
||||
log.L.Error("Failed to render layout", "err", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ func (handler IndexImpl) Handle(router *http.ServeMux) {
|
||||
|
||||
func (handler IndexImpl) handleRootAnd404() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
|
||||
var comp templ.Component
|
||||
@@ -52,6 +54,8 @@ func (handler IndexImpl) handleRootAnd404() http.HandlerFunc {
|
||||
|
||||
func (handler IndexImpl) handleEmpty() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
// Return nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/google/uuid"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type Transaction interface {
|
||||
@@ -45,12 +47,17 @@ func (h TransactionImpl) Handle(r *http.ServeMux) {
|
||||
|
||||
func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
return
|
||||
}
|
||||
|
||||
currentSpan := trace.SpanFromContext(r.Context())
|
||||
currentSpan.SetAttributes(attribute.String("", "test"))
|
||||
|
||||
filter := types.TransactionItemsFilter{
|
||||
AccountId: r.URL.Query().Get("account-id"),
|
||||
TreasureChestId: r.URL.Query().Get("treasure-chest-id"),
|
||||
@@ -89,12 +96,16 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
|
||||
|
||||
func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
return
|
||||
}
|
||||
|
||||
// log.L.Info("request", "pattern", r.Pattern, "path", r.URL.Path, "method", r.Method, "path", r.URL.Path)
|
||||
|
||||
accounts, err := h.account.GetAll(user)
|
||||
if err != nil {
|
||||
handleError(w, r, err)
|
||||
@@ -133,6 +144,8 @@ func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
|
||||
|
||||
func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -233,6 +246,8 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
|
||||
|
||||
func (h TransactionImpl) handleRecalculate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -251,6 +266,8 @@ func (h TransactionImpl) handleRecalculate() http.HandlerFunc {
|
||||
|
||||
func (h TransactionImpl) handleDeleteTransaction() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
|
||||
@@ -33,6 +33,8 @@ func (h TransactionRecurringImpl) Handle(r *http.ServeMux) {
|
||||
|
||||
func (h TransactionRecurringImpl) handleTransactionRecurringItemComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -48,6 +50,8 @@ func (h TransactionRecurringImpl) handleTransactionRecurringItemComp() http.Hand
|
||||
|
||||
func (h TransactionRecurringImpl) handleUpdateTransactionRecurring() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -85,6 +89,8 @@ func (h TransactionRecurringImpl) handleUpdateTransactionRecurring() http.Handle
|
||||
|
||||
func (h TransactionRecurringImpl) handleDeleteTransactionRecurring() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
|
||||
@@ -40,6 +40,8 @@ func (h TreasureChestImpl) Handle(r *http.ServeMux) {
|
||||
|
||||
func (h TreasureChestImpl) handleTreasureChestPage() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -67,6 +69,8 @@ func (h TreasureChestImpl) handleTreasureChestPage() http.HandlerFunc {
|
||||
|
||||
func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -112,6 +116,8 @@ func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
|
||||
|
||||
func (h TreasureChestImpl) handleUpdateTreasureChest() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
@@ -155,6 +161,8 @@ func (h TreasureChestImpl) handleUpdateTreasureChest() http.HandlerFunc {
|
||||
|
||||
func (h TreasureChestImpl) handleDeleteTreasureChest() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
updateSpan(r)
|
||||
|
||||
user := middleware.GetUser(r)
|
||||
if user == nil {
|
||||
utils.DoRedirect(w, r, "/auth/signin")
|
||||
|
||||
@@ -1,56 +1,14 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
// "go.opentelemetry.io/contrib/bridges/otelslog".
|
||||
)
|
||||
|
||||
var (
|
||||
errorMetric = promauto.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "spendsparrow_error_total",
|
||||
Help: "The total number of errors during processing",
|
||||
},
|
||||
)
|
||||
L = slog.New(slog.Default().Handler())
|
||||
)
|
||||
|
||||
func Fatal(message string, args ...interface{}) {
|
||||
errorMetric.Inc()
|
||||
|
||||
s := format(message, args)
|
||||
log.Fatal(s)
|
||||
}
|
||||
|
||||
func Error(message string, args ...interface{}) {
|
||||
errorMetric.Inc()
|
||||
|
||||
s := format(message, args)
|
||||
slog.Error(s)
|
||||
}
|
||||
|
||||
func Warn(message string, args ...interface{}) {
|
||||
s := format(message, args)
|
||||
slog.Warn(s)
|
||||
}
|
||||
|
||||
func Info(message string, args ...interface{}) {
|
||||
s := format(message, args)
|
||||
slog.Info(s)
|
||||
}
|
||||
|
||||
func format(message string, args []interface{}) string {
|
||||
var w strings.Builder
|
||||
|
||||
if len(args) > 0 {
|
||||
fmt.Fprintf(&w, message, args...)
|
||||
} else {
|
||||
w.WriteString(message)
|
||||
}
|
||||
|
||||
return w.String()
|
||||
func InitOtelLogger() {
|
||||
// L = otelslog.NewLogger("spend-sparrow")
|
||||
}
|
||||
|
||||
123
internal/otel.go
Normal file
123
internal/otel.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
// "go.opentelemetry.io/otel/exporters/stdout/stdoutlog".
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
// "go.opentelemetry.io/otel/log/global".
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
// "go.opentelemetry.io/otel/sdk/log".
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
otelEndpoint = "192.168.188.155:4317"
|
||||
)
|
||||
|
||||
// setupOTelSDK bootstraps the OpenTelemetry pipeline.
|
||||
// If it does not return an error, make sure to call shutdown for proper cleanup.
|
||||
func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) {
|
||||
var shutdownFuncs []func(context.Context) error
|
||||
|
||||
// shutdown calls cleanup functions registered via shutdownFuncs.
|
||||
// The errors from the calls are joined.
|
||||
// Each registered cleanup will be invoked once.
|
||||
shutdown := func(ctxInternal context.Context) error {
|
||||
var err error
|
||||
for _, fn := range shutdownFuncs {
|
||||
err = errors.Join(err, fn(ctxInternal))
|
||||
}
|
||||
shutdownFuncs = nil
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
// handleErr calls shutdown for cleanup and makes sure that all errors are returned.
|
||||
handleErr := func(ctxInternal context.Context, inErr error) {
|
||||
err = errors.Join(inErr, shutdown(ctxInternal))
|
||||
}
|
||||
|
||||
// Set up propagator.
|
||||
prop := newPropagator()
|
||||
otel.SetTextMapPropagator(prop)
|
||||
|
||||
// Set up trace provider.
|
||||
tracerProvider, err := newTracerProvider(ctx)
|
||||
if err != nil {
|
||||
handleErr(ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
|
||||
// Set up meter provider.
|
||||
meterProvider, err := newMeterProvider(ctx)
|
||||
if err != nil {
|
||||
handleErr(ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
|
||||
otel.SetMeterProvider(meterProvider)
|
||||
|
||||
// Set up logger provider.
|
||||
// loggerProvider, err := newLoggerProvider()
|
||||
// if err != nil {
|
||||
// handleErr(err)
|
||||
// return
|
||||
// }
|
||||
// shutdownFuncs = append(shutdownFuncs, loggerProvider.Shutdown)
|
||||
// global.SetLoggerProvider(loggerProvider)
|
||||
|
||||
return shutdown, nil
|
||||
}
|
||||
|
||||
func newPropagator() propagation.TextMapPropagator {
|
||||
return propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
}
|
||||
|
||||
func newTracerProvider(ctx context.Context) (*trace.TracerProvider, error) {
|
||||
exp, err := otlptracegrpc.New(ctx,
|
||||
otlptracegrpc.WithEndpoint(otelEndpoint),
|
||||
otlptracegrpc.WithInsecure(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return trace.NewTracerProvider(trace.WithBatcher(exp)), nil
|
||||
}
|
||||
|
||||
func newMeterProvider(ctx context.Context) (*metric.MeterProvider, error) {
|
||||
exp, err := otlpmetricgrpc.New(ctx,
|
||||
otlpmetricgrpc.WithInsecure(),
|
||||
otlpmetricgrpc.WithEndpoint(otelEndpoint))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metric.NewMeterProvider(
|
||||
metric.WithReader(
|
||||
metric.NewPeriodicReader(
|
||||
exp, metric.WithInterval(15*time.Second)))), nil
|
||||
}
|
||||
|
||||
// func newLoggerProvider() (*log.LoggerProvider, error) {
|
||||
// logExporter, err := stdoutlog.New()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// loggerProvider := log.NewLoggerProvider(
|
||||
// log.WithProcessor(log.NewBatchProcessor(logExporter)),
|
||||
// )
|
||||
// return loggerProvider, nil
|
||||
// }
|
||||
@@ -9,18 +9,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
accountMetric = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "spendsparrow_account_total",
|
||||
Help: "The total of account operations",
|
||||
},
|
||||
[]string{"operation"},
|
||||
)
|
||||
)
|
||||
|
||||
type Account interface {
|
||||
@@ -46,8 +34,6 @@ func NewAccount(db *sqlx.DB, random Random, clock Clock) Account {
|
||||
}
|
||||
|
||||
func (s AccountImpl) Add(user *types.User, name string) (*types.Account, error) {
|
||||
accountMetric.WithLabelValues("add").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -90,7 +76,6 @@ func (s AccountImpl) Add(user *types.User, name string) (*types.Account, error)
|
||||
}
|
||||
|
||||
func (s AccountImpl) UpdateName(user *types.User, id string, name string) (*types.Account, error) {
|
||||
accountMetric.WithLabelValues("update").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -100,7 +85,7 @@ func (s AccountImpl) UpdateName(user *types.User, id string, name string) (*type
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("account update: %v", err)
|
||||
log.L.Error("account update", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -151,14 +136,12 @@ func (s AccountImpl) UpdateName(user *types.User, id string, name string) (*type
|
||||
}
|
||||
|
||||
func (s AccountImpl) Get(user *types.User, id string) (*types.Account, error) {
|
||||
accountMetric.WithLabelValues("get").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("account get: %v", err)
|
||||
log.L.Error("account get", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -167,7 +150,7 @@ func (s AccountImpl) Get(user *types.User, id string) (*types.Account, error) {
|
||||
SELECT * FROM account WHERE user_id = ? AND id = ?`, user.Id, uuid)
|
||||
err = db.TransformAndLogDbError("account Get", nil, err)
|
||||
if err != nil {
|
||||
log.Error("account get: %v", err)
|
||||
log.L.Error("account get", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -175,7 +158,6 @@ func (s AccountImpl) Get(user *types.User, id string) (*types.Account, error) {
|
||||
}
|
||||
|
||||
func (s AccountImpl) GetAll(user *types.User) ([]*types.Account, error) {
|
||||
accountMetric.WithLabelValues("get_all").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -192,13 +174,12 @@ func (s AccountImpl) GetAll(user *types.User) ([]*types.Account, error) {
|
||||
}
|
||||
|
||||
func (s AccountImpl) Delete(user *types.User, id string) error {
|
||||
accountMetric.WithLabelValues("delete").Inc()
|
||||
if user == nil {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("account delete: %v", err)
|
||||
log.L.Error("account delete", "err", err)
|
||||
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ func (service AuthImpl) SignInAnonymous() (*types.Session, error) {
|
||||
return nil, types.ErrInternal
|
||||
}
|
||||
|
||||
log.Info("Anonymous session created: %v", session.Id)
|
||||
log.L.Info("anonymous session created", "session-id", session.Id)
|
||||
|
||||
return session, nil
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func (service AuthImpl) SendVerificationMail(userId uuid.UUID, email string) {
|
||||
var w strings.Builder
|
||||
err = mailTemplate.Register(service.serverSettings.BaseUrl, token.Token).Render(context.Background(), &w)
|
||||
if err != nil {
|
||||
log.Error("Could not render welcome email: %v", err)
|
||||
log.L.Error("Could not render welcome email", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ func (service AuthImpl) SendForgotPasswordMail(email string) error {
|
||||
var mail strings.Builder
|
||||
err = mailTemplate.ResetPassword(service.serverSettings.BaseUrl, token.Token).Render(context.Background(), &mail)
|
||||
if err != nil {
|
||||
log.Error("Could not render reset password email: %v", err)
|
||||
log.L.Error("Could not render reset password email", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
service.mail.SendMail(email, "Reset Password", mail.String())
|
||||
@@ -370,7 +370,7 @@ func (service AuthImpl) ForgotPassword(tokenStr string, newPass string) error {
|
||||
|
||||
user, err := service.db.GetUser(token.UserId)
|
||||
if err != nil {
|
||||
log.Error("Could not get user from token: %v", err)
|
||||
log.L.Error("Could not get user from token", "err", err)
|
||||
return types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ func (service AuthImpl) GetCsrfToken(session *types.Session) (string, error) {
|
||||
return "", types.ErrInternal
|
||||
}
|
||||
|
||||
log.Info("CSRF-Token created: %v", tokenStr)
|
||||
log.L.Info("CSRF-Token created", "token", tokenStr)
|
||||
|
||||
return tokenStr, nil
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ func (m MailImpl) internalSendMail(to string, subject string, message string) {
|
||||
subject,
|
||||
message)
|
||||
|
||||
log.Info("Sending mail to %v", to)
|
||||
log.L.Info("sending mail", "to", to)
|
||||
err := smtp.SendMail(s.Host+":"+s.Port, auth, s.FromMail, []string{to}, []byte(msg))
|
||||
if err != nil {
|
||||
log.Error("Error sending mail: %v", err)
|
||||
log.L.Error("Error sending mail", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (r *RandomImpl) Bytes(size int) ([]byte, error) {
|
||||
b := make([]byte, 32)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
log.Error("Error generating random bytes: %v", err)
|
||||
log.L.Error("Error generating random bytes", "err", err)
|
||||
return []byte{}, types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (r *RandomImpl) Bytes(size int) ([]byte, error) {
|
||||
func (r *RandomImpl) String(size int) (string, error) {
|
||||
bytes, err := r.Bytes(size)
|
||||
if err != nil {
|
||||
log.Error("Error generating random string: %v", err)
|
||||
log.L.Error("Error generating random string", "err", err)
|
||||
return "", types.ErrInternal
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func (r *RandomImpl) String(size int) (string, error) {
|
||||
func (r *RandomImpl) UUID() (uuid.UUID, error) {
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
log.Error("Error generating random UUID: %v", err)
|
||||
log.L.Error("Error generating random UUID", "err", err)
|
||||
return uuid.Nil, types.ErrInternal
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
transactionMetric = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "spendsparrow_transaction_total",
|
||||
Help: "The total of transaction operations",
|
||||
},
|
||||
[]string{"operation"},
|
||||
)
|
||||
)
|
||||
|
||||
type Transaction interface {
|
||||
@@ -49,8 +37,6 @@ func NewTransaction(db *sqlx.DB, random Random, clock Clock) Transaction {
|
||||
}
|
||||
|
||||
func (s TransactionImpl) Add(tx *sqlx.Tx, user *types.User, transactionInput types.Transaction) (*types.Transaction, error) {
|
||||
transactionMetric.WithLabelValues("add").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -118,7 +104,6 @@ func (s TransactionImpl) Add(tx *sqlx.Tx, user *types.User, transactionInput typ
|
||||
}
|
||||
|
||||
func (s TransactionImpl) Update(user *types.User, input types.Transaction) (*types.Transaction, error) {
|
||||
transactionMetric.WithLabelValues("update").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -218,14 +203,12 @@ func (s TransactionImpl) Update(user *types.User, input types.Transaction) (*typ
|
||||
}
|
||||
|
||||
func (s TransactionImpl) Get(user *types.User, id string) (*types.Transaction, error) {
|
||||
transactionMetric.WithLabelValues("get").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("transaction get: %v", err)
|
||||
log.L.Error("transaction get", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -243,7 +226,6 @@ func (s TransactionImpl) Get(user *types.User, id string) (*types.Transaction, e
|
||||
}
|
||||
|
||||
func (s TransactionImpl) GetAll(user *types.User, filter types.TransactionItemsFilter) ([]*types.Transaction, error) {
|
||||
transactionMetric.WithLabelValues("get_all").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -273,13 +255,12 @@ func (s TransactionImpl) GetAll(user *types.User, filter types.TransactionItemsF
|
||||
}
|
||||
|
||||
func (s TransactionImpl) Delete(user *types.User, id string) error {
|
||||
transactionMetric.WithLabelValues("delete").Inc()
|
||||
if user == nil {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("transaction delete: %v", err)
|
||||
log.L.Error("transaction delete", "err", err)
|
||||
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -339,7 +320,6 @@ func (s TransactionImpl) Delete(user *types.User, id string) error {
|
||||
}
|
||||
|
||||
func (s TransactionImpl) RecalculateBalances(user *types.User) error {
|
||||
transactionMetric.WithLabelValues("recalculate").Inc()
|
||||
if user == nil {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
@@ -382,7 +362,7 @@ func (s TransactionImpl) RecalculateBalances(user *types.User) error {
|
||||
defer func() {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
log.Error("transaction RecalculateBalances: %v", err)
|
||||
log.L.Error("transaction RecalculateBalances", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -475,7 +455,7 @@ func (s TransactionImpl) validateAndEnrichTransaction(tx *sqlx.Tx, oldTransactio
|
||||
return nil, err
|
||||
}
|
||||
if rowCount == 0 {
|
||||
log.Error("transaction validate: %v", err)
|
||||
log.L.Error("transaction validate", "err", err)
|
||||
return nil, fmt.Errorf("account not found: %w", ErrBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,18 +11,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
transactionRecurringMetric = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "spendsparrow_transaction_recurring_total",
|
||||
Help: "The total of transactionRecurring operations",
|
||||
},
|
||||
[]string{"operation"},
|
||||
)
|
||||
)
|
||||
|
||||
type TransactionRecurring interface {
|
||||
@@ -54,9 +42,8 @@ func NewTransactionRecurring(db *sqlx.DB, random Random, clock Clock, transactio
|
||||
|
||||
func (s TransactionRecurringImpl) Add(
|
||||
user *types.User,
|
||||
transactionRecurringInput types.TransactionRecurringInput) (*types.TransactionRecurring, error) {
|
||||
transactionRecurringMetric.WithLabelValues("add").Inc()
|
||||
|
||||
transactionRecurringInput types.TransactionRecurringInput,
|
||||
) (*types.TransactionRecurring, error) {
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -97,14 +84,14 @@ func (s TransactionRecurringImpl) Add(
|
||||
|
||||
func (s TransactionRecurringImpl) Update(
|
||||
user *types.User,
|
||||
input types.TransactionRecurringInput) (*types.TransactionRecurring, error) {
|
||||
transactionRecurringMetric.WithLabelValues("update").Inc()
|
||||
input types.TransactionRecurringInput,
|
||||
) (*types.TransactionRecurring, error) {
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(input.Id)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring update: %v", err)
|
||||
log.L.Error("transactionRecurring update", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -161,7 +148,6 @@ func (s TransactionRecurringImpl) Update(
|
||||
}
|
||||
|
||||
func (s TransactionRecurringImpl) GetAll(user *types.User) ([]*types.TransactionRecurring, error) {
|
||||
transactionRecurringMetric.WithLabelValues("get_all_by_account").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -182,14 +168,13 @@ func (s TransactionRecurringImpl) GetAll(user *types.User) ([]*types.Transaction
|
||||
}
|
||||
|
||||
func (s TransactionRecurringImpl) GetAllByAccount(user *types.User, accountId string) ([]*types.TransactionRecurring, error) {
|
||||
transactionRecurringMetric.WithLabelValues("get_all_by_account").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
|
||||
accountUuid, err := uuid.Parse(accountId)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring GetAllByAccount: %v", err)
|
||||
log.L.Error("transactionRecurring GetAllByAccount", "err", err)
|
||||
return nil, fmt.Errorf("could not parse accountId: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -234,15 +219,17 @@ func (s TransactionRecurringImpl) GetAllByAccount(user *types.User, accountId st
|
||||
return transactionRecurrings, nil
|
||||
}
|
||||
|
||||
func (s TransactionRecurringImpl) GetAllByTreasureChest(user *types.User, treasureChestId string) ([]*types.TransactionRecurring, error) {
|
||||
transactionRecurringMetric.WithLabelValues("get_all_by_treasurechest").Inc()
|
||||
func (s TransactionRecurringImpl) GetAllByTreasureChest(
|
||||
user *types.User,
|
||||
treasureChestId string,
|
||||
) ([]*types.TransactionRecurring, error) {
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
|
||||
treasureChestUuid, err := uuid.Parse(treasureChestId)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring GetAllByTreasureChest: %v", err)
|
||||
log.L.Error("transactionRecurring GetAllByTreasureChest", "err", err)
|
||||
return nil, fmt.Errorf("could not parse treasureChestId: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -288,13 +275,12 @@ func (s TransactionRecurringImpl) GetAllByTreasureChest(user *types.User, treasu
|
||||
}
|
||||
|
||||
func (s TransactionRecurringImpl) Delete(user *types.User, id string) error {
|
||||
transactionRecurringMetric.WithLabelValues("delete").Inc()
|
||||
if user == nil {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring delete: %v", err)
|
||||
log.L.Error("transactionRecurring delete", "err", err)
|
||||
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -389,7 +375,8 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
tx *sqlx.Tx,
|
||||
oldTransactionRecurring *types.TransactionRecurring,
|
||||
userId uuid.UUID,
|
||||
input types.TransactionRecurringInput) (*types.TransactionRecurring, error) {
|
||||
input types.TransactionRecurringInput,
|
||||
) (*types.TransactionRecurring, error) {
|
||||
var (
|
||||
id uuid.UUID
|
||||
accountUuid *uuid.UUID
|
||||
@@ -425,7 +412,7 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
if input.AccountId != "" {
|
||||
temp, err := uuid.Parse(input.AccountId)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("could not parse accountId: %w", ErrBadRequest)
|
||||
}
|
||||
accountUuid = &temp
|
||||
@@ -435,7 +422,7 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
return nil, err
|
||||
}
|
||||
if rowCount == 0 {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("account not found: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -445,7 +432,7 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
if input.TreasureChestId != "" {
|
||||
temp, err := uuid.Parse(input.TreasureChestId)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("could not parse treasureChestId: %w", ErrBadRequest)
|
||||
}
|
||||
treasureChestUuid = &temp
|
||||
@@ -465,17 +452,17 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
}
|
||||
|
||||
if !hasAccount && !hasTreasureChest {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("either account or treasure chest is required: %w", ErrBadRequest)
|
||||
}
|
||||
if hasAccount && hasTreasureChest {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("either account or treasure chest is required, not both: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
valueFloat, err := strconv.ParseFloat(input.Value, 64)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("could not parse value: %w", ErrBadRequest)
|
||||
}
|
||||
valueInt := int64(valueFloat * DECIMALS_MULTIPLIER)
|
||||
@@ -494,18 +481,18 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
|
||||
}
|
||||
intervalMonths, err = strconv.ParseInt(input.IntervalMonths, 10, 0)
|
||||
if err != nil {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("could not parse intervalMonths: %w", ErrBadRequest)
|
||||
}
|
||||
if intervalMonths < 1 {
|
||||
log.Error("transactionRecurring validate: %v", err)
|
||||
log.L.Error("transactionRecurring validate", "err", err)
|
||||
return nil, fmt.Errorf("intervalMonths needs to be greater than 0: %w", ErrBadRequest)
|
||||
}
|
||||
var nextExecution *time.Time = nil
|
||||
if input.NextExecution != "" {
|
||||
t, err := time.Parse("2006-01-02", input.NextExecution)
|
||||
if err != nil {
|
||||
log.Error("transaction validate: %v", err)
|
||||
log.L.Error("transaction validate", "err", err)
|
||||
return nil, fmt.Errorf("could not parse timestamp: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
treasureChestMetric = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "spendsparrow_treasurechest_total",
|
||||
Help: "The total of treasurechest operations",
|
||||
},
|
||||
[]string{"operation"},
|
||||
)
|
||||
)
|
||||
|
||||
type TreasureChest interface {
|
||||
@@ -47,8 +35,6 @@ func NewTreasureChest(db *sqlx.DB, random Random, clock Clock) TreasureChest {
|
||||
}
|
||||
|
||||
func (s TreasureChestImpl) Add(user *types.User, parentId, name string) (*types.TreasureChest, error) {
|
||||
treasureChestMetric.WithLabelValues("add").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -102,7 +88,6 @@ func (s TreasureChestImpl) Add(user *types.User, parentId, name string) (*types.
|
||||
}
|
||||
|
||||
func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string) (*types.TreasureChest, error) {
|
||||
treasureChestMetric.WithLabelValues("update").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -112,7 +97,7 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string
|
||||
}
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
log.Error("treasureChest update: %v", err)
|
||||
log.L.Error("treasureChest update", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -185,14 +170,12 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string
|
||||
}
|
||||
|
||||
func (s TreasureChestImpl) Get(user *types.User, id string) (*types.TreasureChest, error) {
|
||||
treasureChestMetric.WithLabelValues("get").Inc()
|
||||
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
log.Error("treasureChest get: %v", err)
|
||||
log.L.Error("treasureChest get", "err", err)
|
||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
@@ -210,7 +193,6 @@ func (s TreasureChestImpl) Get(user *types.User, id string) (*types.TreasureChes
|
||||
}
|
||||
|
||||
func (s TreasureChestImpl) GetAll(user *types.User) ([]*types.TreasureChest, error) {
|
||||
treasureChestMetric.WithLabelValues("get_all").Inc()
|
||||
if user == nil {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
@@ -226,13 +208,12 @@ func (s TreasureChestImpl) GetAll(user *types.User) ([]*types.TreasureChest, err
|
||||
}
|
||||
|
||||
func (s TreasureChestImpl) Delete(user *types.User, idStr string) error {
|
||||
treasureChestMetric.WithLabelValues("delete").Inc()
|
||||
if user == nil {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
log.Error("treasureChest delete: %v", err)
|
||||
log.L.Error("treasureChest delete", "err", err)
|
||||
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"spend-sparrow/internal/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingConfig = errors.New("missing config")
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Port string
|
||||
PrometheusEnabled bool
|
||||
Port string
|
||||
OtelEnabled bool
|
||||
|
||||
BaseUrl string
|
||||
Environment string
|
||||
@@ -22,37 +27,46 @@ type SmtpSettings struct {
|
||||
FromName string
|
||||
}
|
||||
|
||||
func NewSettingsFromEnv(env func(string) string) *Settings {
|
||||
var smtp *SmtpSettings
|
||||
func NewSettingsFromEnv(env func(string) string) (*Settings, error) {
|
||||
var (
|
||||
smtp *SmtpSettings
|
||||
err error
|
||||
)
|
||||
if env("SMTP_ENABLED") == "true" {
|
||||
smtp = getSmtpSettings(env)
|
||||
smtp, err = getSmtpSettings(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
settings := &Settings{
|
||||
Port: env("PORT"),
|
||||
PrometheusEnabled: env("PROMETHEUS_ENABLED") == "true",
|
||||
BaseUrl: env("BASE_URL"),
|
||||
Environment: env("ENVIRONMENT"),
|
||||
Smtp: smtp,
|
||||
Port: env("PORT"),
|
||||
OtelEnabled: env("OTEL_ENABLED") == "true",
|
||||
BaseUrl: env("BASE_URL"),
|
||||
Environment: env("ENVIRONMENT"),
|
||||
Smtp: smtp,
|
||||
}
|
||||
|
||||
if settings.BaseUrl == "" {
|
||||
log.Fatal("BASE_URL must be set")
|
||||
log.L.Error("BASE_URL must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if settings.Port == "" {
|
||||
log.Fatal("PORT must be set")
|
||||
log.L.Error("PORT must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if settings.Environment == "" {
|
||||
log.Fatal("ENVIRONMENT must be set")
|
||||
log.L.Error("ENVIRONMENT must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
|
||||
log.Info("BASE_URL is %q", settings.BaseUrl)
|
||||
log.Info("ENVIRONMENT is %q", settings.Environment)
|
||||
log.L.Info("settings read", "BASE_URL", settings.BaseUrl)
|
||||
log.L.Info("settings read", "ENVIRONMENT", settings.Environment)
|
||||
|
||||
return settings
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func getSmtpSettings(env func(string) string) *SmtpSettings {
|
||||
func getSmtpSettings(env func(string) string) (*SmtpSettings, error) {
|
||||
smtp := SmtpSettings{
|
||||
Host: env("SMTP_HOST"),
|
||||
Port: env("SMTP_PORT"),
|
||||
@@ -63,23 +77,29 @@ func getSmtpSettings(env func(string) string) *SmtpSettings {
|
||||
}
|
||||
|
||||
if smtp.Host == "" {
|
||||
log.Fatal("SMTP_HOST must be set")
|
||||
log.L.Error("SMTP_HOST must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if smtp.Port == "" {
|
||||
log.Fatal("SMTP_PORT must be set")
|
||||
log.L.Error("SMTP_PORT must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if smtp.User == "" {
|
||||
log.Fatal("SMTP_USER must be set")
|
||||
log.L.Error("SMTP_USER must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if smtp.Pass == "" {
|
||||
log.Fatal("SMTP_PASS must be set")
|
||||
log.L.Error("SMTP_PASS must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if smtp.FromMail == "" {
|
||||
log.Fatal("SMTP_FROM_MAIL must be set")
|
||||
log.L.Error("SMTP_FROM_MAIL must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if smtp.FromName == "" {
|
||||
log.Fatal("SMTP_FROM_NAME must be set")
|
||||
log.L.Error("SMTP_FROM_NAME must be set")
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
|
||||
return &smtp
|
||||
return &smtp, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func TriggerToast(w http.ResponseWriter, r *http.Request, class string, message
|
||||
if IsHtmx(r) {
|
||||
w.Header().Set("Hx-Trigger", fmt.Sprintf(`{"toast": "%v|%v"}`, class, strings.ReplaceAll(message, `"`, `\"`)))
|
||||
} else {
|
||||
log.Error("Trying to trigger toast in non-HTMX request")
|
||||
log.L.Error("Trying to trigger toast in non-HTMX request")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user