feat: бэкапы локальных баз данных

This commit is contained in:
2026-04-21 13:58:52 +03:00
parent b928f4d356
commit 830e9c31a8
5 changed files with 124 additions and 15 deletions

4
.gitignore vendored
View File

@@ -1,10 +1,8 @@
/renv /renv
/temp /temp
/_devel
scheme.rds scheme.rds
configs/schemas/d2tra_t.xlsx
configs/schemas/antifib.xlsx
/_devel
.Renviron .Renviron
.DS_Store .DS_Store
.lintr .lintr

View File

@@ -12,6 +12,45 @@
... ...
# Quick start
## локально:
Копирование содержимого репозитория
```bash
git clone https://gitea.madelirihs.ru/madeliri/shiny_form.git
```
Восстановление окружения
```r
renv::init()
```
# Насторйки
## переменные окружения
### работа с авторизацией
Пароль базы данных с авторизацией необходимо указать в `.Renviron`:
```
AUTH_DB_KEY = "this_is_your_password"
```
### бэкапы локальных баз
Для создания бэкапов локальных баз данных, необходимо указать путь куда будут сохранятся бэкапы в переменной окружения:
```
FORM_APP_LOCAL_DB_BACKUP_PATH="path_to_backups"
```
Проверка осуществляется при каждом запуске приложения, бэкапы создаются раз в день (при первом запуске).
Количество сохраняемых бэкапов:
```
FORM_APP_LOCAL_DB_BACKUP_LIMITS=3
```
# Cтруктура `schema.xlsx` # Cтруктура `schema.xlsx`
@@ -41,11 +80,6 @@
## Авторизация ## Авторизация
Пароль базы данных с авторизацией необходимо указать в `.Renviron`:
```
AUTH_DB_KEY = "this_is_your_password"
```
# trade-ofs # trade-ofs

View File

@@ -1,15 +1,13 @@
default: default:
form_app_version: 0.15.0 form_app_version: 0.15.0
form_id: new_formy
form_name: NEW FORMY
prod: prod:
form_app_configure_path: "." form_app_configure_path: "."
form_auth_enabled: true form_auth_enabled: true
form_id: new_formy
form_name: NEW FORMY
devel: devel:
form_app_configure_path: _devel/d2tra form_app_configure_path: _devel/d2tra
form_auth_enabled: false form_auth_enabled: false
form_id: new_formy
form_name: NEW FORMY
form_app_version: 0.15.0 dev form_app_version: 0.15.0 dev

View File

@@ -336,3 +336,71 @@ excel_to_db_dates_converter = function(date) {
fin_date <- as.character(format(fin_date, "%Y-%m-%d")) fin_date <- as.character(format(fin_date, "%Y-%m-%d"))
fin_date fin_date
} }
#' @export
local_db_backup <- function(
db_name,
backups_paths = Sys.getenv("FORM_APP_LOCAL_DB_BACKUP_PATH"),
backups_limit = as.integer(Sys.getenv("FORM_APP_LOCAL_DB_BACKUP_LIMITS", 5))
) {
db_path <- fs::path(config::get("form_app_configure_path"), "db")
db_full_path <- fs::path(db_path, db_name, ext = "sqlite")
backup_folder <- fs::path(backups_paths, db_name)
if (!dir.exists(backup_folder)) dir.create(backup_folder, recursive = TRUE)
date_mark <- format(Sys.time(), "%Y%m%d")
schedule <- c(
daily = 1,
weekly = 7,
monthly = 28
)
purrr::walk2(
.x = schedule,
.y = names(schedule),
.f = \(schedule_days, schedule_name) {
# daily
daily_folder <- fs::path(backup_folder, schedule_name)
todays_backup <- fs::path(daily_folder, paste0(db_name, "_", format(Sys.time(), "%Y%m%d")), ext = "sqlite")
if (!dir.exists(daily_folder)) dir.create(daily_folder)
existed_files <- fs::dir_ls(daily_folder, regexp = "((?:19|20)\\d\\d)(0?[1-9]|1[012])([12][0-9]|3[01]|0?[1-9])")
existed_files <- sort(existed_files, decreasing = TRUE)
# если бэкап для сегодняшнего дня есть - скипаем процедуру
if (todays_backup %in% existed_files) {
return()
}
# парсим даты
dates <- stringr::str_extract(existed_files, "((?:19|20)\\d\\d)(0?[1-9]|1[012])([12][0-9]|3[01]|0?[1-9])")
dates <- as.Date(dates, "%Y%m%d")
# если количество существующих бэкапов значимо превышает установленный лимит, удаляем лишнее
if (length(existed_files) > backups_limit) {
file.remove(utils::tail(existed_files, length(existed_files) - backups_limit))
}
# если количество существующих бэкапов равно имеющемуся и пора делать бэкап - делаем бэкап, удаляем послендий файл
if (length(existed_files) >= backups_limit & dates[1] + schedule_days == Sys.Date()) {
file.remove(utils::tail(existed_files, 1))
file.copy(db_full_path, todays_backup)
} else if(length(existed_files) == 0) {
file.copy(db_full_path, todays_backup)
}
}
)
}

View File

@@ -40,9 +40,13 @@ check_and_init_scheme = function() {
cli::cli_inform(c("*" = "проверка схемы...")) cli::cli_inform(c("*" = "проверка схемы..."))
options(box.path = here::here())
box::use(modules/db[local_db_backup])
options(box.path = config::get("form_app_configure_path")) options(box.path = config::get("form_app_configure_path"))
box::use(configs/enabled_schemes[enabled_schemes]) box::use(configs/enabled_schemes[enabled_schemes])
# список файлов, изменение которых, приведут к переинициализиации схемы
files_to_watch <- c( files_to_watch <- c(
fs::path(config::get("form_app_configure_path"), "configs", "enabled_schemes.R"), fs::path(config::get("form_app_configure_path"), "configs", "enabled_schemes.R"),
"modules/scheme_generator.R", "modules/scheme_generator.R",
@@ -53,7 +57,9 @@ check_and_init_scheme = function() {
scheme_file <- paste0(config::get("form_app_configure_path"), "/configs/schemas/", scheme_names, ".xlsx") scheme_file <- paste0(config::get("form_app_configure_path"), "/configs/schemas/", scheme_names, ".xlsx")
scheme_file <- stats::setNames(scheme_file, scheme_names) scheme_file <- stats::setNames(scheme_file, scheme_names)
if (!all(file.exists(scheme_file))) cli::cli_abort(c("Отсутствуют файлы схем для следующих наименований:", paste("-", names(scheme_file)[!file.exists(scheme_file)]))) if (!all(file.exists(scheme_file))) {
cli::cli_abort(c("Отсутствуют файлы схем для следующих наименований:", paste("-", names(scheme_file)[!file.exists(scheme_file)])))
}
db_files <- paste0(config::get("form_app_configure_path"), "/db/", scheme_names, ".sqlite") db_files <- paste0(config::get("form_app_configure_path"), "/db/", scheme_names, ".sqlite")
@@ -71,8 +77,6 @@ check_and_init_scheme = function() {
} else { } else {
saved_hash <- readRDS(hash_file) saved_hash <- readRDS(hash_file)
print(exist_hash)
print(saved_hash)
# если данные были изменены проводим реинициализацию таблицы и схемы # если данные были изменены проводим реинициализацию таблицы и схемы
if (!all(exist_hash == saved_hash)) { if (!all(exist_hash == saved_hash)) {
@@ -85,6 +89,13 @@ check_and_init_scheme = function() {
} }
} }
# MAKING BACKUPS
if (Sys.getenv("FORM_APP_LOCAL_DB_BACKUP_PATH") != "") {
cli::cli_inform(c("*" = "создание бэкапов баз данных..."))
purrr::walk(scheme_names, local_db_backup)
}
# перезаписываем файл # перезаписываем файл
if (!dir.exists("temp")) dir.create("temp") if (!dir.exists("temp")) dir.create("temp")
saveRDS(exist_hash, hash_file) saveRDS(exist_hash, hash_file)