144 lines
5.0 KiB
C++
144 lines
5.0 KiB
C++
#define RAPIDJSON_HAS_STDSTRING 1
|
|
#include <rapidjson/filereadstream.h>
|
|
#include <rapidjson/document.h>
|
|
#include <rapidjson/error/en.h>
|
|
#include "data_internal.hxx"
|
|
|
|
#define FIND_MEMBER(name, ty) m = doc.FindMember(#name); \
|
|
if (m == doc.MemberEnd() || !m->value.Is##ty()) { data_logger->error("Missing or invalid "#name); throw std::exception{}; }
|
|
#define ASSIGN_MEMBER(lhs, name, ty) FIND_MEMBER(name, ty) lhs = m->value.Get##ty()
|
|
|
|
SaveNode load_node(const rapidjson::Value &doc) {
|
|
rapidjson::Value::ConstMemberIterator m;
|
|
auto node = std::make_unique<Node>();
|
|
std::uint64_t id, parent;
|
|
|
|
node->parent = nullptr;
|
|
|
|
ASSIGN_MEMBER(node->id, id, Uint64);
|
|
id = node->id;
|
|
data_logger->debug("Loading node {}", id);
|
|
|
|
ASSIGN_MEMBER(node->name, name, String);
|
|
ASSIGN_MEMBER(node->file, file, Bool);
|
|
ASSIGN_MEMBER(node->preview, preview, Bool);
|
|
ASSIGN_MEMBER(node->size, size, Uint64);
|
|
|
|
ASSIGN_MEMBER(parent, parent, Uint64);
|
|
|
|
std::list<std::uint64_t> children;
|
|
FIND_MEMBER(children, Array)
|
|
for (const auto &v : m->value.GetArray())
|
|
if (!v.IsUint64()) { data_logger->error("Invalid child id"); throw std::exception{}; }
|
|
else children.push_back(v.GetUint64());
|
|
|
|
return {
|
|
.node = std::move(node),
|
|
.id = id,
|
|
.parent_id = parent,
|
|
.children_ids = children
|
|
};
|
|
}
|
|
|
|
std::unique_ptr<User> load_user(const rapidjson::Value &doc) {
|
|
rapidjson::Value::ConstMemberIterator m;
|
|
auto user = std::make_unique<User>();
|
|
|
|
ASSIGN_MEMBER(user->id, id, Uint64);
|
|
data_logger->debug("Loading user {}", user->id);
|
|
|
|
ASSIGN_MEMBER(user->name, name, String);
|
|
ASSIGN_MEMBER(user->password, password, String);
|
|
ASSIGN_MEMBER(user->tfa_secret, tfa_secret, String);
|
|
ASSIGN_MEMBER(user->enabled, enabled, Bool);
|
|
ASSIGN_MEMBER(user->admin, admin, Bool);
|
|
ASSIGN_MEMBER(user->tfa_enabled, tfa_enabled, Bool);
|
|
ASSIGN_MEMBER(user->tfa_mail, tfa_mail, Bool);
|
|
ASSIGN_MEMBER(user->next_node_id, next_node_id, Uint64);
|
|
|
|
std::list<SaveNode> nodes;
|
|
FIND_MEMBER(nodes, Array)
|
|
for (const auto &v : m->value.GetArray())
|
|
nodes.push_back(load_node(v));
|
|
|
|
for (auto &node : nodes)
|
|
user->nodes.emplace(node.id, node.node.release());
|
|
|
|
for (const auto &snode : nodes) {
|
|
auto &node = user->nodes.at(snode.id);
|
|
if (node->id != 0)
|
|
node->parent = user->nodes.at(snode.parent_id);
|
|
for (const auto &child : snode.children_ids)
|
|
node->children.push_back(user->nodes.at(child));
|
|
}
|
|
|
|
user->user_dir = files_dir / std::to_string(user->id);
|
|
|
|
return user;
|
|
}
|
|
|
|
bool load_from_file(Data &data, const std::string &file) {
|
|
char buf[65536];
|
|
bool success = false;
|
|
data_logger->info("Loading data from {}", file);
|
|
data.users.clear();
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
try {
|
|
FileWrapper f{file.c_str(), "r"};
|
|
rapidjson::FileReadStream fstream{f.f, buf, sizeof(buf)};
|
|
rapidjson::Document doc;
|
|
rapidjson::Document::MemberIterator m;
|
|
doc.ParseStream(fstream);
|
|
if (doc.HasParseError()) {
|
|
data_logger->error("Failed to parse data");
|
|
auto e = rapidjson::GetParseError_En(doc.GetParseError());
|
|
data_logger->error("{}", e);
|
|
throw std::exception{};
|
|
}
|
|
|
|
ASSIGN_MEMBER(data.version, version, Uint64);
|
|
if (data.version != Data::current_version) {
|
|
data_logger->error("Version is {}, current version is {}. Refusing to load!", data.version, Data::current_version);
|
|
throw std::exception{};
|
|
}
|
|
|
|
ASSIGN_MEMBER(data.next_user_id, next_user_id, Uint64);
|
|
|
|
FIND_MEMBER(users, Array)
|
|
for (const auto &v : m->value.GetArray()) {
|
|
auto user = load_user(v);
|
|
data.users.emplace(user->id, std::move(user));
|
|
}
|
|
|
|
success = true;
|
|
} catch (std::exception &_) {}
|
|
auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
|
data_logger->info("Data loading took {} ms", dur.count());
|
|
return success;
|
|
}
|
|
|
|
void Data::load() {
|
|
if (std::filesystem::exists(data_cur_file)) {
|
|
bool ok = load_from_file(*this, data_cur_file);
|
|
if (!ok) {
|
|
data_logger->warn("Retrying from old file");
|
|
if (!std::filesystem::exists(data_old_file)) {
|
|
data_logger->critical("Old file missing");
|
|
crash();
|
|
}
|
|
ok = load_from_file(*this, data_old_file);
|
|
if (!ok) {
|
|
data_logger->critical("Failed to load data");
|
|
crash();
|
|
}
|
|
}
|
|
} else if (std::filesystem::exists(data_old_file)) {
|
|
data_logger->warn("Data file missing, loading from old file");
|
|
bool ok = load_from_file(*this, data_old_file);
|
|
if (!ok) {
|
|
data_logger->critical("Failed to load data");
|
|
crash();
|
|
}
|
|
} else data_logger->info("No data file found for loading");
|
|
}
|