Compare commits

...

7 Commits

Author SHA1 Message Date
Rostislav Dugin
7859951653 Merge branch 'main' of https://github.com/RostislavDugin/postgresus 2025-07-21 14:59:12 +03:00
Rostislav Dugin
7472aa1e1f FIX (backups): Do not double close backup file 2025-07-21 14:58:34 +03:00
Rostislav Dugin
9283713eab Merge pull request #6 from RostislavDugin/feature/update_readme
FEATURE (readme): Move badges under the description [skip-release]
2025-07-21 14:47:39 +03:00
Rostislav Dugin
9a9c170ffc FEATURE (readme): Move badges under the description [skip-release] 2025-07-21 14:43:48 +03:00
Rostislav Dugin
d05efc3151 FIX (deployments): Remove Docker Hub description update 2025-07-21 14:13:50 +03:00
Rostislav Dugin
1ee41fb673 FEATURE (auth): Add rate limiting for sign in edpoint to not allow brute force 2025-07-21 14:00:31 +03:00
Rostislav Dugin
529f080ca5 FEATURE (readme): Add pretty labels to GitHub 2025-07-21 13:47:47 +03:00
12 changed files with 43 additions and 58 deletions

View File

@@ -336,16 +336,6 @@ jobs:
rostislavdugin/postgresus:v${{ needs.determine-version.outputs.new_version }}
rostislavdugin/postgresus:${{ github.sha }}
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@v4
continue-on-error: true
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: rostislavdugin/postgresus
short-description: "Free PostgreSQL monitoring & backup solution with multi-storage support"
readme-filepath: ./README.md
release:
runs-on: ubuntu-latest
needs: [determine-version, build-and-push]

View File

@@ -1,10 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
<!-- This file is automatically updated by the release workflow -->

View File

@@ -1,9 +1,19 @@
<div align="center">
<img src="assets/logo.svg" alt="Postgresus Logo" width="250"/>
<img src="assets/logo.svg" style="margin-bottom: 20px;" alt="Postgresus Logo" width="250"/>
<h3>PostgreSQL monitoring and backup</h3>
<p>Free, open source and self-hosted solution for automated PostgreSQL monitoring and backups. With multiple storage options and notifications</p>
<!-- Badges -->
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Docker Pulls](https://img.shields.io/docker/pulls/rostislavdugin/postgresus?color=brightgreen)](https://hub.docker.com/r/rostislavdugin/postgresus)
[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey)](https://github.com/RostislavDugin/postgresus)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-13%20%7C%2014%20%7C%2015%20%7C%2016%20%7C%2017-336791?logo=postgresql&logoColor=white)](https://www.postgresql.org/)
[![Self Hosted](https://img.shields.io/badge/self--hosted-yes-brightgreen)](https://github.com/RostislavDugin/postgresus)
[![Open Source](https://img.shields.io/badge/open%20source-❤️-red)](https://github.com/RostislavDugin/postgresus)
<p>
<a href="#-features">Features</a> •
<a href="#-installation">Installation</a> •

View File

@@ -1,15 +0,0 @@
repos:
- repo: local
hooks:
- id: golangci-lint-fmt
name: Format Go Code using golangci-lint fmt
entry: golangci-lint fmt ./...
language: system
types: [go]
- id: golangci-lint-run
name: Run golangci-lint for static analysis
entry: golangci-lint run
language: system
types: [go]
pass_filenames: false

View File

@@ -19,6 +19,7 @@ require (
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
golang.org/x/crypto v0.39.0
golang.org/x/time v0.12.0
gorm.io/driver/postgres v1.5.11
gorm.io/gorm v1.26.1
)

View File

@@ -252,6 +252,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@@ -222,11 +222,8 @@ func (uc *RestorePostgresqlBackupUsecase) downloadBackupToTempFile(
return "", nil, fmt.Errorf("failed to write backup to temporary file: %w", err)
}
// Close the temp file to ensure all data is written
if err := tempFile.Close(); err != nil {
cleanupFunc()
return "", nil, fmt.Errorf("failed to close temporary backup file: %w", err)
}
// Close the temp file to ensure all data is written - this is handled by defer
// Removing explicit close to avoid double-close error
uc.logger.Info("Backup file written to temporary location", "tempFile", tempBackupFile)
return tempBackupFile, cleanupFunc, nil

View File

@@ -62,12 +62,6 @@ func (l *LocalStorage) SaveFile(logger *slog.Logger, fileID uuid.UUID, file io.R
return fmt.Errorf("failed to sync temp file: %w", err)
}
err = tempFile.Close()
if err != nil {
logger.Error("Failed to close temp file", "fileId", fileID.String(), "error", err)
return fmt.Errorf("failed to close temp file: %w", err)
}
finalPath := filepath.Join(config.GetEnv().DataFolder, fileID.String())
logger.Debug(
"Moving file from temp to final location",

View File

@@ -222,10 +222,14 @@ func verifyDataIntegrity(t *testing.T, originalDB *sqlx.DB, restoredDB *sqlx.DB)
assert.NoError(t, err)
assert.Equal(t, len(originalData), len(restoredData), "Should have same number of rows")
for i := range originalData {
assert.Equal(t, originalData[i].ID, restoredData[i].ID, "ID should match")
assert.Equal(t, originalData[i].Name, restoredData[i].Name, "Name should match")
assert.Equal(t, originalData[i].Value, restoredData[i].Value, "Value should match")
// Only compare data if both slices have elements (to avoid panic)
if len(originalData) > 0 && len(restoredData) > 0 {
for i := range originalData {
assert.Equal(t, originalData[i].ID, restoredData[i].ID, "ID should match")
assert.Equal(t, originalData[i].Name, restoredData[i].Name, "Name should match")
assert.Equal(t, originalData[i].Value, restoredData[i].Value, "Value should match")
}
}
}

View File

@@ -4,10 +4,12 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
type UserController struct {
userService *UserService
userService *UserService
signinLimiter *rate.Limiter
}
func (c *UserController) RegisterRoutes(router *gin.RouterGroup) {
@@ -51,8 +53,18 @@ func (c *UserController) SignUp(ctx *gin.Context) {
// @Param request body SignInRequest true "User signin data"
// @Success 200 {object} SignInResponse
// @Failure 400
// @Failure 429 {object} map[string]string "Rate limit exceeded"
// @Router /users/signin [post]
func (c *UserController) SignIn(ctx *gin.Context) {
// We use rate limiter to prevent brute force attacks
if !c.signinLimiter.Allow() {
ctx.JSON(
http.StatusTooManyRequests,
gin.H{"error": "Rate limit exceeded. Please try again later."},
)
return
}
var request SignInRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})

View File

@@ -2,6 +2,8 @@ package users
import (
user_repositories "postgresus-backend/internal/features/users/repositories"
"golang.org/x/time/rate"
)
var secretKeyRepository = &user_repositories.SecretKeyRepository{}
@@ -12,6 +14,7 @@ var userService = &UserService{
}
var userController = &UserController{
userService,
rate.NewLimiter(rate.Limit(3), 3), // 3 RPS with burst of 3
}
func GetUserService() *UserService {

View File

@@ -86,17 +86,14 @@ Notifications flow:
Extra:
- add tests running on each PR (in progress by Rostislav Dugin)
- add prettier labels to GitHub README
- create pretty website like rybbit.io with demo
- add HTTPS for Postgresus
- add simple SQL queries via UI
- add brute force protection on auth (via local RPS limiter)
- add support of Kubernetes Helm
- create pretty website like rybbit.io with demo
Monitoring flow:
- add system metrics (CPU, RAM, disk, IO)
- add system metrics (CPU, RAM, disk, IO) (in progress by Rostislav Dugin)
- add queries stats (slowest, most frequent, etc. via pg_stat_statements)
- add alerting for slow queries (listen for slow query and if they reach >100ms - send message)
- add alerting for high resource usage (listen for high resource usage and if they reach >90% - send message)