AI: download geoip on startup if it does not exist
This commit is contained in:
parent
3a7d65eeb7
commit
58d09b1f7e
2 changed files with 76 additions and 0 deletions
72
zig/src/location/geolite_downloader.zig
Normal file
72
zig/src/location/geolite_downloader.zig
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn ensureDatabase(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
std.fs.cwd().access(path, .{}) catch {
|
||||
std.log.info("GeoLite2 database not found at {s}, downloading...", .{path});
|
||||
try downloadDatabase(allocator, path);
|
||||
std.log.info("GeoLite2 database downloaded successfully", .{});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
fn downloadDatabase(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
const latest_url = try getLatestReleaseUrl(allocator);
|
||||
defer allocator.free(latest_url);
|
||||
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
const uri = try std.Uri.parse(latest_url);
|
||||
const response_buf = try allocator.alloc(u8, 64 * 1024 * 1024);
|
||||
defer allocator.free(response_buf);
|
||||
|
||||
var writer = std.Io.Writer.fixed(response_buf);
|
||||
const result = try client.fetch(.{
|
||||
.location = .{ .uri = uri },
|
||||
.method = .GET,
|
||||
.response_writer = &writer,
|
||||
});
|
||||
|
||||
if (result.status != .ok) return error.DownloadFailed;
|
||||
|
||||
const file = try std.fs.cwd().createFile(path, .{});
|
||||
defer file.close();
|
||||
try file.writeAll(response_buf[0..writer.end]);
|
||||
}
|
||||
|
||||
fn getLatestReleaseUrl(allocator: std.mem.Allocator) ![]const u8 {
|
||||
var client: std.http.Client = .{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
const api_url = "https://api.github.com/repos/P3TERX/GeoLite.mmdb/releases/latest";
|
||||
const uri = try std.Uri.parse(api_url);
|
||||
|
||||
const response_buf = try allocator.alloc(u8, 1024 * 1024);
|
||||
defer allocator.free(response_buf);
|
||||
|
||||
var writer = std.Io.Writer.fixed(response_buf);
|
||||
const result = try client.fetch(.{
|
||||
.location = .{ .uri = uri },
|
||||
.method = .GET,
|
||||
.response_writer = &writer,
|
||||
.extra_headers = &.{
|
||||
.{ .name = "User-Agent", .value = "wttr.in" },
|
||||
},
|
||||
});
|
||||
|
||||
if (result.status != .ok) return error.ApiFailed;
|
||||
|
||||
const json_data = response_buf[0..writer.end];
|
||||
const parsed = try std.json.parseFromSlice(std.json.Value, allocator, json_data, .{});
|
||||
defer parsed.deinit();
|
||||
|
||||
const assets = parsed.value.object.get("assets") orelse return error.NoAssets;
|
||||
for (assets.array.items) |asset| {
|
||||
const name = asset.object.get("name") orelse continue;
|
||||
if (std.mem.eql(u8, name.string, "GeoLite2-City.mmdb")) {
|
||||
const url = asset.object.get("browser_download_url") orelse return error.NoDownloadUrl;
|
||||
return allocator.dupe(u8, url.string);
|
||||
}
|
||||
}
|
||||
return error.DatabaseNotFound;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ const GeoIP = @import("location/geoip.zig").GeoIP;
|
|||
const GeoCache = @import("location/geocache.zig").GeoCache;
|
||||
const AirportDB = @import("location/airports.zig").AirportDB;
|
||||
const Resolver = @import("location/resolver.zig").Resolver;
|
||||
const geolite_downloader = @import("location/geolite_downloader.zig");
|
||||
|
||||
pub const std_options: std.Options = .{
|
||||
.log_level = .info,
|
||||
|
|
@ -40,6 +41,9 @@ pub fn main() !void {
|
|||
}
|
||||
try stdout.flush();
|
||||
|
||||
// Ensure GeoLite2 database exists
|
||||
try geolite_downloader.ensureDatabase(allocator, cfg.geolite_path);
|
||||
|
||||
// Initialize GeoIP database
|
||||
var geoip = GeoIP.init(cfg.geolite_path) catch |err| {
|
||||
std.log.warn("Failed to load GeoIP database: {}", .{err});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue