129 {
131 if (!fs::exists(draftPath))
132 return nlohmann::json();
133
134 nlohmann::json j;
135
136
137 try {
138 std::ifstream f(draftPath);
139 f >> j;
140 f.close();
141
142 auto now = std::chrono::system_clock::now();
143 std::time_t now_c = std::chrono::system_clock::to_time_t(now);
144 std::stringstream ss;
145 ss << std::put_time(std::gmtime(&now_c), "%Y-%m-%dT%H:%M:%SZ");
146
147 if (!j.contains("meta"))
148 j["meta"] = nlohmann::json::object();
149 j["meta"]["created"] = ss.str();
150 j["meta"]["version"] = std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
151
152 std::ofstream out(draftPath, std::ios::trunc);
153 out << j.dump(2);
154 out.close();
155 } catch (const std::exception& e) {
156 VLOG(1) << "Failed to inject metadata into draft: " << e.what();
157 }
158
159
160 if (fs::exists(mapFilePath)) {
161 fs::path versionDir = fs::path(mapFilePath).parent_path() / "versions";
162 if (!fs::exists(versionDir)) {
163 fs::create_directories(versionDir);
164 }
165
166 auto now = std::chrono::system_clock::now();
167 auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
168 std::string filename = fs::path(mapFilePath).filename().string();
169 std::string backupPath = versionDir / (filename + "." + std::to_string(timestamp));
170
171 fs::copy_file(mapFilePath, backupPath, fs::copy_options::overwrite_existing);
172
173
174 try {
175 std::vector<fs::path> versions;
176 for (const auto& entry : fs::directory_iterator(versionDir)) {
177 if (entry.path().filename().string().starts_with(filename + ".")) {
178 versions.push_back(entry.path());
179 }
180 }
181 if (versions.size() > 50) {
182 std::sort(versions.begin(), versions.end(), [](const fs::path& a, const fs::path& b) {
183 return fs::last_write_time(a) < fs::last_write_time(b);
184 });
185 for (size_t i = 0; i < versions.size() - 50; ++i) {
186 fs::remove(versions[i]);
187 }
188 }
189 } catch (...) {
190 }
191
192
193 fs::rename(draftPath, mapFilePath);
194 } else {
195 fs::remove(draftPath);
196 }
197
198 return j;
199 }
static std::string getDraftPath(const std::string &mapFilePath)