116 lines
3.8 KiB
Zig
116 lines
3.8 KiB
Zig
const std = @import("std");
|
|
const config = @import("config.zig");
|
|
const Cache = @import("cache/Cache.zig");
|
|
const MetNo = @import("weather/MetNo.zig");
|
|
const types = @import("weather/types.zig");
|
|
const Server = @import("http/Server.zig");
|
|
const RateLimiter = @import("http/RateLimiter.zig");
|
|
const GeoIP = @import("location/GeoIP.zig");
|
|
const GeoCache = @import("location/GeoCache.zig");
|
|
const Airports = @import("location/Airports.zig");
|
|
const Resolver = @import("location/resolver.zig").Resolver;
|
|
const geolite_downloader = @import("location/geolite_downloader.zig");
|
|
|
|
pub const std_options: std.Options = .{
|
|
.log_level = .info,
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
var stdout_buffer: [4096]u8 = undefined;
|
|
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
|
const stdout = &stdout_writer.interface;
|
|
|
|
const cfg = try config.Config.load(allocator);
|
|
defer cfg.deinit(allocator);
|
|
|
|
try stdout.print("wttr starting on {s}:{d}\n", .{ cfg.listen_host, cfg.listen_port });
|
|
try stdout.print("Cache size: {d}\n", .{cfg.cache_size});
|
|
try stdout.print("Cache dir: {s}\n", .{cfg.cache_dir});
|
|
try stdout.print("GeoLite2 path: {s}\n", .{cfg.geolite_path});
|
|
if (cfg.geocache_file) |f| {
|
|
try stdout.print("Geocache file: {s}\n", .{f});
|
|
} else {
|
|
try stdout.print("Geocache: in-memory only\n", .{});
|
|
}
|
|
if (cfg.airports_dat_path) |f| {
|
|
try stdout.print("Airports database: {s}\n", .{f});
|
|
}
|
|
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});
|
|
std.log.warn("IP-based location resolution will be unavailable", .{});
|
|
return err;
|
|
};
|
|
defer geoip.deinit();
|
|
|
|
// Initialize geocoding cache
|
|
var geocache = try GeoCache.init(allocator, cfg.geocache_file);
|
|
defer geocache.deinit();
|
|
|
|
// Initialize airports database
|
|
var airports_db: ?Airports = null;
|
|
if (cfg.airports_dat_path) |path| {
|
|
airports_db = Airports.initFromFile(allocator, path) catch |err| blk: {
|
|
std.log.warn("Failed to load airports database: {}", .{err});
|
|
break :blk null;
|
|
};
|
|
}
|
|
if (airports_db) |*db| {
|
|
defer db.deinit();
|
|
}
|
|
|
|
// Initialize location resolver
|
|
var resolver = Resolver.init(allocator, &geoip, &geocache, if (airports_db) |*db| db else null);
|
|
|
|
var cache = try Cache.init(allocator, .{
|
|
.max_entries = cfg.cache_size,
|
|
.cache_dir = cfg.cache_dir,
|
|
});
|
|
defer cache.deinit();
|
|
|
|
var rate_limiter = try RateLimiter.init(allocator, .{
|
|
.capacity = 300,
|
|
.refill_rate = 5,
|
|
.refill_interval_ms = 200,
|
|
});
|
|
defer rate_limiter.deinit();
|
|
|
|
var metno = try MetNo.init(allocator);
|
|
defer metno.deinit();
|
|
|
|
var server = try Server.init(allocator, cfg.listen_host, cfg.listen_port, .{
|
|
.cache = &cache,
|
|
.provider = metno.provider(),
|
|
.resolver = &resolver,
|
|
.geoip = &geoip,
|
|
}, &rate_limiter);
|
|
|
|
try server.listen();
|
|
}
|
|
|
|
test {
|
|
std.testing.refAllDecls(@This());
|
|
_ = @import("config.zig");
|
|
_ = @import("cache/LRU.zig");
|
|
_ = @import("weather/mock.zig");
|
|
_ = @import("http/RateLimiter.zig");
|
|
_ = @import("http/query.zig");
|
|
_ = @import("http/help.zig");
|
|
_ = @import("render/line.zig");
|
|
_ = @import("render/json.zig");
|
|
_ = @import("render/v2.zig");
|
|
_ = @import("render/custom.zig");
|
|
_ = @import("location/GeoIP.zig");
|
|
_ = @import("location/GeoCache.zig");
|
|
_ = @import("location/Airports.zig");
|
|
_ = @import("location/resolver.zig");
|
|
}
|