fileserver/src/data/data_load.cxx

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");
}