#define RAPIDJSON_HAS_STDSTRING 1 #include #include #include "data_internal.hxx" #define KEY(x) writer.Key(#x, sizeof(#x)-1) using Writer = rapidjson::Writer; void save_node(Writer &writer, Node *node) { writer.StartObject(); KEY(id); writer.Uint64(node->id); KEY(name); writer.String(node->name); KEY(file); writer.Bool(node->file); KEY(preview); writer.Bool(node->preview); KEY(size); writer.Uint64(node->size); KEY(parent); writer.Uint64(node->parent == nullptr ? 0 : node->parent->id); KEY(children); writer.StartArray(); for (const auto &child : node->children) writer.Uint64(child->id); writer.EndArray(); writer.EndObject(); } void save_user(Writer &writer, User *user) { std::shared_lock lock{user->node_lock}; writer.StartObject(); KEY(id); writer.Uint64(user->id); KEY(name); writer.String(user->name); KEY(password); writer.String(user->password); KEY(tfa_secret); writer.String(user->tfa_secret); KEY(enabled); writer.Bool(user->enabled); KEY(admin); writer.Bool(user->admin); KEY(tfa_enabled); writer.Bool(user->tfa_enabled); KEY(tfa_mail); writer.Bool(user->tfa_mail); KEY(next_node_id); writer.Uint64(user->next_node_id); KEY(nodes); writer.StartArray(); for (const auto &node : user->nodes) save_node(writer, node.second.get()); writer.EndArray(); writer.EndObject(); } char save_buf[65536]; void save(Data* data) { data_logger->info("Saving data"); try { { FileWrapper f{data_new_file.c_str(), "w"}; rapidjson::FileWriteStream stream{f.f, save_buf, sizeof(save_buf)}; Writer writer{stream}; std::shared_lock lock{data->user_lock}; writer.StartObject(); KEY(version); writer.Uint64(data->version); KEY(next_user_id); writer.Uint64(data->next_user_id); KEY(users); writer.StartArray(); for (const auto &user : data->users) save_user(writer, user.second.get()); writer.EndArray(); writer.EndObject(); } data_logger->info("Finished writing data"); if (std::filesystem::exists(data_cur_file)) std::filesystem::copy_file(data_cur_file, data_old_file, std::filesystem::copy_options::overwrite_existing); std::filesystem::rename(data_new_file, data_cur_file); data_logger->info("Save done"); } catch (std::exception &e) { data_logger->error("Error while saving: {}, retrying...", e.what()); data->save_flag.test_and_set(); } } void save_worker(Data *data) { while (!data->shutdown_flag.test()) { data->save_flag.wait(false); do { data->save_flag.clear(); std::this_thread::sleep_for(std::chrono::seconds{2}); } while (data->save_flag.test()); save(data); } data_logger->info("Data saver stopping"); save(data); } void Data::start_save_thread() { save(); shutdown_flag.clear(); this->save_thread = std::thread{save_worker, this}; } void Data::save() { save_flag.test_and_set(); save_flag.notify_all(); }