110 lines
3.2 KiB
C++
110 lines
3.2 KiB
C++
#define RAPIDJSON_HAS_STDSTRING 1
|
|
#include <rapidjson/filewritestream.h>
|
|
#include <rapidjson/writer.h>
|
|
#include "data_internal.hxx"
|
|
|
|
#define KEY(x) writer.Key(#x, sizeof(#x)-1)
|
|
|
|
using Writer = rapidjson::Writer<rapidjson::FileWriteStream>;
|
|
|
|
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();
|
|
}
|