5.0.8: часовой пояс, разблокировка по удалению задачи
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m21s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m21s
This commit is contained in:
@@ -3886,8 +3886,14 @@ func (a *App) syncFitbitDataForAllUsers() error {
|
||||
|
||||
log.Printf("Syncing Fitbit data for %d users", len(userIDs))
|
||||
|
||||
// Синхронизируем данные за сегодня для каждого пользователя
|
||||
today := time.Now()
|
||||
// Синхронизируем данные за сегодня для каждого пользователя (в настроенном часовом поясе)
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, err := time.LoadLocation(timezoneStr)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Invalid timezone '%s': %v. Using UTC for Fitbit sync.", timezoneStr, err)
|
||||
loc = time.UTC
|
||||
}
|
||||
today := time.Now().In(loc)
|
||||
for _, userID := range userIDs {
|
||||
if err := a.syncFitbitData(userID, today); err != nil {
|
||||
log.Printf("Failed to sync Fitbit data for user_id=%d: %v", userID, err)
|
||||
@@ -4062,8 +4068,9 @@ func main() {
|
||||
// Логируем параметры подключения к БД (без пароля)
|
||||
log.Printf("Database connection parameters: host=%s port=%s user=%s dbname=%s", dbHost, dbPort, dbUser, dbName)
|
||||
|
||||
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
|
||||
dbHost, dbPort, dbUser, dbPassword, dbName)
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable options='-c timezone=%s'",
|
||||
dbHost, dbPort, dbUser, dbPassword, dbName, timezoneStr)
|
||||
|
||||
var db *sql.DB
|
||||
var err error
|
||||
@@ -4832,8 +4839,14 @@ func (a *App) processTelegramMessage(fullText string, entities []TelegramEntity,
|
||||
}
|
||||
processedText := strings.Join(cleanedLines, "\n")
|
||||
|
||||
// Используем текущее время в формате ISO 8601 (UTC)
|
||||
createdDate := time.Now().UTC().Format(time.RFC3339)
|
||||
// Используем текущее время в формате ISO 8601 в настроенном часовом поясе
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, err := time.LoadLocation(timezoneStr)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Invalid timezone '%s': %v. Using UTC instead.", timezoneStr, err)
|
||||
loc = time.UTC
|
||||
}
|
||||
createdDate := time.Now().In(loc).Format(time.RFC3339)
|
||||
|
||||
// Вставляем данные в БД только если есть nodes
|
||||
if len(scoreNodes) > 0 {
|
||||
@@ -4929,8 +4942,14 @@ func (a *App) processMessageInternal(rawText string, sendToTelegram bool, userID
|
||||
// Формируем Markdown (Legacy) контент: заменяем ** на *
|
||||
markdownText := strings.ReplaceAll(rawText, "**", "*")
|
||||
|
||||
// Используем текущее время
|
||||
createdDate := time.Now().UTC().Format(time.RFC3339)
|
||||
// Используем текущее время в настроенном часовом поясе
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, err := time.LoadLocation(timezoneStr)
|
||||
if err != nil {
|
||||
log.Printf("Warning: Invalid timezone '%s': %v. Using UTC instead.", timezoneStr, err)
|
||||
loc = time.UTC
|
||||
}
|
||||
createdDate := time.Now().In(loc).Format(time.RFC3339)
|
||||
|
||||
// Вставляем данные в БД только если есть nodes
|
||||
if len(nodes) > 0 {
|
||||
@@ -6696,8 +6715,13 @@ func (a *App) getFullStatisticsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Получаем ISO год и неделю для текущей даты
|
||||
now := time.Now()
|
||||
// Получаем ISO год и неделю для текущей даты (в настроенном часовом поясе)
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
now := time.Now().In(loc)
|
||||
_, currentWeekInt := now.ISOWeek()
|
||||
currentYearInt := now.Year()
|
||||
|
||||
@@ -6849,10 +6873,10 @@ func (a *App) getFullStatisticsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
normalizedScore = math.Min(totalScore, maxGoalScore)
|
||||
}
|
||||
|
||||
_, weekISO := time.Now().ISOWeek()
|
||||
_, weekISO := now.ISOWeek()
|
||||
item := FullStatisticsItem{
|
||||
ProjectName: projectName,
|
||||
ReportYear: time.Now().Year(),
|
||||
ReportYear: now.Year(),
|
||||
ReportWeek: weekISO,
|
||||
TotalScore: totalScore,
|
||||
NormalizedTotalScore: normalizedScore,
|
||||
@@ -6892,7 +6916,7 @@ func (a *App) getTodayEntriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
projectFilter = &projectName
|
||||
}
|
||||
|
||||
// Получаем дату из query string (формат: YYYY-MM-DD), если не указана - используем сегодня
|
||||
// Получаем дату из query string (формат: YYYY-MM-DD), если не указана - используем сегодня в настроенном часовом поясе
|
||||
dateParam := r.URL.Query().Get("date")
|
||||
var targetDate time.Time
|
||||
if dateParam != "" {
|
||||
@@ -6904,7 +6928,13 @@ func (a *App) getTodayEntriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
targetDate = parsedDate
|
||||
} else {
|
||||
targetDate = time.Now()
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
log.Printf("Warning: Invalid timezone '%s': %v. Using UTC for today entries.", timezoneStr, locErr)
|
||||
loc = time.UTC
|
||||
}
|
||||
targetDate = time.Now().In(loc)
|
||||
}
|
||||
|
||||
// Запрос для получения entries с nodes за указанный день
|
||||
@@ -10758,8 +10788,14 @@ func (a *App) fitbitSyncHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Синхронизируем данные за сегодня
|
||||
err := a.syncFitbitData(userID, time.Now())
|
||||
// Синхронизируем данные за сегодня (в настроенном часовом поясе)
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
log.Printf("Warning: Invalid timezone '%s': %v. Using UTC for Fitbit sync.", timezoneStr, locErr)
|
||||
loc = time.UTC
|
||||
}
|
||||
err := a.syncFitbitData(userID, time.Now().In(loc))
|
||||
if err != nil {
|
||||
log.Printf("Fitbit sync error: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Sync failed: %v", err), http.StatusInternalServerError)
|
||||
@@ -10788,10 +10824,15 @@ func (a *App) getFitbitStatsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Получаем дату из query параметра (по умолчанию сегодня)
|
||||
// Получаем дату из query параметра (по умолчанию сегодня в настроенном часовом поясе)
|
||||
dateStr := r.URL.Query().Get("date")
|
||||
if dateStr == "" {
|
||||
dateStr = time.Now().Format("2006-01-02")
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
dateStr = time.Now().In(loc).Format("2006-01-02")
|
||||
}
|
||||
|
||||
var steps, floors, azm sql.NullInt64
|
||||
@@ -11084,7 +11125,8 @@ func (a *App) checkWishlistUnlock(itemID int, userID int) (bool, error) {
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
conditionMet = completed > 0
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
conditionMet = false
|
||||
}
|
||||
|
||||
} else if scoreConditionID.Valid {
|
||||
@@ -11472,8 +11514,9 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
||||
completedBool := true
|
||||
condition.TaskCompleted = &completedBool
|
||||
} else if err == nil {
|
||||
conditionMet = completed > 0
|
||||
completedBool := conditionMet
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
conditionMet = false
|
||||
completedBool := false
|
||||
condition.TaskCompleted = &completedBool
|
||||
}
|
||||
}
|
||||
@@ -11538,7 +11581,8 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
||||
completedBool := true
|
||||
condition.TaskCompleted = &completedBool
|
||||
} else if err == nil {
|
||||
completedBool := completed > 0
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
completedBool := false
|
||||
condition.TaskCompleted = &completedBool
|
||||
}
|
||||
}
|
||||
@@ -12381,9 +12425,14 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
condition.TaskID = &taskIDVal
|
||||
var taskCompleted int
|
||||
err := a.DB.QueryRow(`SELECT completed FROM tasks WHERE id = $1 AND user_id = $2 AND deleted = FALSE`, taskID.Int64, conditionOwnerID).Scan(&taskCompleted)
|
||||
if err == nil {
|
||||
isCompleted := taskCompleted > 0
|
||||
condition.TaskCompleted = &isCompleted
|
||||
if err == sql.ErrNoRows {
|
||||
// Задача удалена или не существует — условие выполнено
|
||||
t := true
|
||||
condition.TaskCompleted = &t
|
||||
} else if err == nil {
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
f := false
|
||||
condition.TaskCompleted = &f
|
||||
}
|
||||
}
|
||||
if taskNextShowAt.Valid {
|
||||
@@ -12777,9 +12826,14 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
condition.TaskID = &taskIDVal
|
||||
var taskCompleted int
|
||||
err := a.DB.QueryRow(`SELECT completed FROM tasks WHERE id = $1 AND user_id = $2 AND deleted = FALSE`, taskID.Int64, conditionOwnerID).Scan(&taskCompleted)
|
||||
if err == nil {
|
||||
isCompleted := taskCompleted > 0
|
||||
condition.TaskCompleted = &isCompleted
|
||||
if err == sql.ErrNoRows {
|
||||
// Задача удалена или не существует — условие выполнено
|
||||
t := true
|
||||
condition.TaskCompleted = &t
|
||||
} else if err == nil {
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
f := false
|
||||
condition.TaskCompleted = &f
|
||||
}
|
||||
}
|
||||
} else if scoreConditionID.Valid {
|
||||
@@ -14917,15 +14971,20 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
if taskName.Valid {
|
||||
condition.TaskName = &taskName.String
|
||||
}
|
||||
// Проверяем выполнена ли задача для владельца условия
|
||||
// Условие выполнено, если задача удалена или не существует
|
||||
if taskID.Valid {
|
||||
taskIDVal := int(taskID.Int64)
|
||||
condition.TaskID = &taskIDVal
|
||||
var taskCompleted int
|
||||
err := a.DB.QueryRow(`SELECT completed FROM tasks WHERE id = $1 AND user_id = $2 AND deleted = FALSE`, taskID.Int64, conditionOwnerID).Scan(&taskCompleted)
|
||||
if err == nil {
|
||||
isCompleted := taskCompleted > 0
|
||||
condition.TaskCompleted = &isCompleted
|
||||
if err == sql.ErrNoRows {
|
||||
// Задача удалена или не существует — условие выполнено
|
||||
t := true
|
||||
condition.TaskCompleted = &t
|
||||
} else if err == nil {
|
||||
// Задача существует и не удалена — условие не выполнено
|
||||
f := false
|
||||
condition.TaskCompleted = &f
|
||||
}
|
||||
}
|
||||
} else if scoreConditionID.Valid {
|
||||
@@ -15803,8 +15862,13 @@ func (a *App) proxyImageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// getWeeklyStatsDataForUserAndWeek получает данные о проектах для конкретного пользователя и недели
|
||||
func (a *App) getWeeklyStatsDataForUserAndWeek(userID int, year int, week int) (*WeeklyStatsResponse, error) {
|
||||
// Определяем, является ли это текущей неделей
|
||||
now := time.Now()
|
||||
// Определяем, является ли это текущей неделей (в настроенном часовом поясе)
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
now := time.Now().In(loc)
|
||||
currentYear, currentWeek := now.ISOWeek()
|
||||
isCurrentWeek := year == currentYear && week == currentWeek
|
||||
|
||||
@@ -16000,7 +16064,12 @@ func (a *App) getTrackingStatsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
weekStr := r.URL.Query().Get("week")
|
||||
|
||||
var year, week int
|
||||
now := time.Now()
|
||||
timezoneStr := getEnv("TIMEZONE", "UTC")
|
||||
loc, locErr := time.LoadLocation(timezoneStr)
|
||||
if locErr != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
now := time.Now().In(loc)
|
||||
|
||||
if yearStr == "" || weekStr == "" {
|
||||
// Если не указаны - текущая неделя
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "play-life-web",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.8",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
Reference in New Issue
Block a user