bsky feeds about music
music-atmosphere-feed.plyr.fm/
bsky
feed
zig
1const std = @import("std");
2const net = std.net;
3const posix = std.posix;
4const Thread = std.Thread;
5
6const http = @import("server/http.zig");
7const db = @import("store/db.zig");
8const jetstream = @import("jetstream.zig");
9const stats = @import("server/stats.zig");
10
11const log = std.log.scoped(.main);
12
13const MAX_HTTP_WORKERS = 16;
14const SOCKET_TIMEOUT_SECS = 30;
15
16pub fn main() !void {
17 var gpa = std.heap.GeneralPurposeAllocator(.{
18 .thread_safe = true,
19 }){};
20 defer _ = gpa.deinit();
21 const allocator = gpa.allocator();
22
23 // init stats
24 stats.init();
25
26 // init database
27 try db.init();
28
29 // start jetstream consumer in background (runs forever, reconnects on error)
30 const js_thread = try Thread.spawn(.{}, jetstream.consumer, .{allocator});
31 js_thread.detach();
32
33 // init thread pool for http connections
34 var pool: Thread.Pool = undefined;
35 try pool.init(.{
36 .allocator = allocator,
37 .n_jobs = MAX_HTTP_WORKERS,
38 });
39 defer pool.deinit();
40
41 // start http server
42 const port: u16 = blk: {
43 const port_str = posix.getenv("PORT") orelse "3000";
44 break :blk std.fmt.parseInt(u16, port_str, 10) catch 3000;
45 };
46
47 const address = try net.Address.parseIp("0.0.0.0", port);
48 var server = try address.listen(.{ .reuse_address = true });
49 defer server.deinit();
50
51 log.info("listening on http://0.0.0.0:{d}", .{port});
52
53 while (true) {
54 const conn = server.accept() catch |err| {
55 log.err("accept error: {}", .{err});
56 continue;
57 };
58
59 setSocketTimeout(conn.stream.handle, SOCKET_TIMEOUT_SECS) catch |err| {
60 log.warn("failed to set socket timeout: {}", .{err});
61 };
62
63 pool.spawn(http.handleConnection, .{conn}) catch |err| {
64 log.err("pool spawn error: {}", .{err});
65 conn.stream.close();
66 };
67 }
68}
69
70fn setSocketTimeout(fd: posix.fd_t, secs: u32) !void {
71 const timeout = std.mem.toBytes(posix.timeval{
72 .sec = @intCast(secs),
73 .usec = 0,
74 });
75 try posix.setsockopt(fd, posix.SOL.SOCKET, posix.SO.RCVTIMEO, &timeout);
76 try posix.setsockopt(fd, posix.SOL.SOCKET, posix.SO.SNDTIMEO, &timeout);
77}
78
79test {
80 _ = @import("feed/filter.zig");
81 _ = @import("feed/skeleton.zig");
82 _ = @import("server/stats.zig");
83 _ = @import("store/posts.zig");
84}