Bump version to 3.4.1 and add version logging on startup
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 44s

This commit is contained in:
poignatov
2026-01-07 15:31:40 +03:00
parent b9133f60dc
commit 2f16876185
3 changed files with 136 additions and 19 deletions

View File

@@ -39,6 +39,8 @@ COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html
# Копируем собранный backend
COPY --from=backend-builder /app/backend/main /app/backend/main
COPY play-life-backend/admin.html /app/backend/admin.html
# Копируем файл версии
COPY VERSION /app/VERSION
# Копируем конфигурацию nginx
COPY nginx.conf /etc/nginx/nginx.conf

View File

@@ -1 +1 @@
3.4.0
3.4.1

View File

@@ -373,6 +373,48 @@ func calculateNextShowAtFromRepetitionDate(repetitionDate string, fromDate time.
return &nextDate
}
// calculateNextShowAtFromRepetitionPeriod calculates the next show date by adding repetition_period to fromDate
// Format: PostgreSQL INTERVAL string (e.g., "1 day", "2 weeks", "3 months")
func calculateNextShowAtFromRepetitionPeriod(repetitionPeriod string, fromDate time.Time) *time.Time {
if repetitionPeriod == "" {
return nil
}
parts := strings.Fields(strings.TrimSpace(repetitionPeriod))
if len(parts) < 2 {
return nil
}
value, err := strconv.Atoi(parts[0])
if err != nil {
return nil
}
unit := strings.ToLower(parts[1])
// Start from fromDate at midnight
nextDate := time.Date(fromDate.Year(), fromDate.Month(), fromDate.Day(), 0, 0, 0, 0, fromDate.Location())
switch unit {
case "minute", "minutes":
nextDate = nextDate.Add(time.Duration(value) * time.Minute)
case "hour", "hours":
nextDate = nextDate.Add(time.Duration(value) * time.Hour)
case "day", "days":
nextDate = nextDate.AddDate(0, 0, value)
case "week", "weeks":
nextDate = nextDate.AddDate(0, 0, value*7)
case "month", "months":
nextDate = nextDate.AddDate(0, value, 0)
case "year", "years":
nextDate = nextDate.AddDate(value, 0, 0)
default:
return nil
}
return &nextDate
}
// ============================================
// Auth types
// ============================================
@@ -3524,7 +3566,36 @@ func (a *App) startDailyReportScheduler() {
// Планировщик будет работать в фоновом режиме
}
// readVersion читает версию из файла VERSION
func readVersion() string {
// Пробуем разные пути к файлу VERSION
paths := []string{
"/app/VERSION", // В Docker контейнере
"../VERSION", // При запуске из play-life-backend/
"../../VERSION", // Альтернативный путь
"VERSION", // Текущая директория
}
for _, path := range paths {
data, err := os.ReadFile(path)
if err == nil {
version := strings.TrimSpace(string(data))
if version != "" {
return version
}
}
}
return "unknown"
}
func main() {
// Читаем версию приложения
version := readVersion()
log.Printf("========================================")
log.Printf("Play Life Backend v%s", version)
log.Printf("========================================")
// Загружаем переменные окружения из .env файла (если существует)
// Сначала пробуем загрузить из корня проекта, затем из текущей директории
// Игнорируем ошибку, если файл не найден
@@ -6805,12 +6876,28 @@ func (a *App) createTaskHandler(w http.ResponseWriter, r *http.Request) {
var insertSQL string
var insertArgs []interface{}
if repetitionPeriod.Valid {
// Вычисляем next_show_at для задачи с repetition_period
periodStr := strings.TrimSpace(repetitionPeriod.String)
isZeroPeriod := strings.HasPrefix(periodStr, "0 ") || periodStr == "0"
var nextShowAt *time.Time
if !isZeroPeriod {
nextShowAt = calculateNextShowAtFromRepetitionPeriod(repetitionPeriod.String, time.Now())
}
if nextShowAt != nil {
insertSQL = `
INSERT INTO tasks (user_id, name, reward_message, progression_base, repetition_period, repetition_date, next_show_at, completed, deleted)
VALUES ($1, $2, $3, $4, $5::INTERVAL, NULL, $6, 0, FALSE)
RETURNING id
`
insertArgs = []interface{}{userID, strings.TrimSpace(req.Name), rewardMessage, progressionBase, repetitionPeriodValue, nextShowAt}
} else {
insertSQL = `
INSERT INTO tasks (user_id, name, reward_message, progression_base, repetition_period, repetition_date, completed, deleted)
VALUES ($1, $2, $3, $4, $5::INTERVAL, NULL, 0, FALSE)
RETURNING id
`
insertArgs = []interface{}{userID, strings.TrimSpace(req.Name), rewardMessage, progressionBase, repetitionPeriodValue}
}
} else if repetitionDate.Valid {
// Вычисляем next_show_at для задачи с repetition_date
nextShowAt := calculateNextShowAtFromRepetitionDate(repetitionDate.String, time.Now())
@@ -7062,12 +7149,28 @@ func (a *App) updateTaskHandler(w http.ResponseWriter, r *http.Request) {
var updateSQL string
var updateArgs []interface{}
if repetitionPeriod.Valid {
// Вычисляем next_show_at для задачи с repetition_period
periodStr := strings.TrimSpace(repetitionPeriod.String)
isZeroPeriod := strings.HasPrefix(periodStr, "0 ") || periodStr == "0"
var nextShowAt *time.Time
if !isZeroPeriod {
nextShowAt = calculateNextShowAtFromRepetitionPeriod(repetitionPeriod.String, time.Now())
}
if nextShowAt != nil {
updateSQL = `
UPDATE tasks
SET name = $1, reward_message = $2, progression_base = $3, repetition_period = $4::INTERVAL, repetition_date = NULL, next_show_at = $5
WHERE id = $6
`
updateArgs = []interface{}{strings.TrimSpace(req.Name), rewardMessage, progressionBase, repetitionPeriod.String, nextShowAt, taskID}
} else {
updateSQL = `
UPDATE tasks
SET name = $1, reward_message = $2, progression_base = $3, repetition_period = $4::INTERVAL, repetition_date = NULL, next_show_at = NULL
WHERE id = $5
`
updateArgs = []interface{}{strings.TrimSpace(req.Name), rewardMessage, progressionBase, repetitionPeriod.String, taskID}
}
} else if repetitionDate.Valid {
// Вычисляем next_show_at для задачи с repetition_date
nextShowAt := calculateNextShowAtFromRepetitionDate(repetitionDate.String, time.Now())
@@ -7706,13 +7809,25 @@ func (a *App) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
WHERE id = $1
`, taskID)
} else {
// Обычный период: обновляем счетчик и last_completed_at, сбрасываем next_show_at
// Обычный период: обновляем счетчик и last_completed_at, вычисляем next_show_at
// next_show_at = last_completed_at + repetition_period
now := time.Now()
nextShowAt := calculateNextShowAtFromRepetitionPeriod(repetitionPeriod.String, now)
if nextShowAt != nil {
_, err = a.DB.Exec(`
UPDATE tasks
SET completed = completed + 1, last_completed_at = NOW(), next_show_at = $2
WHERE id = $1
`, taskID, nextShowAt)
} else {
// Если не удалось вычислить дату, обновляем как обычно
_, err = a.DB.Exec(`
UPDATE tasks
SET completed = completed + 1, last_completed_at = NOW(), next_show_at = NULL
WHERE id = $1
`, taskID)
}
}
} else {
_, err = a.DB.Exec(`
UPDATE tasks