╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │10 const evt = @import("./event.zig"); │ │11 │ │12 pub const Options = struct { │ │13 - http_listen: ?[]const u8 = "127.0.0.1:8080", │ │13 + web_listen: []const u8 = "127.0.0.1:8080", │ │14 ssh_listen: ?[]const u8 = "127.0.0.1:8022", │ │15 - wui_listen: []const u8 = "127.0.0.1:8000", │ │15 + // the public urls shown to users, defaulting to localhost and the bound port│ │16 + web_url: ?[]const u8 = null, │ │17 + ssh_url: ?[]const u8 = null, │ │18 data_dir: []const u8 = ".", │ │19 git_ssh_prefix: []const u8 = "", │ │20 fallback_on_address_in_use: bool = false, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │83 }; │ │84 defer admin_repo.deinit(io, allocator); │ │85 } │ │84 - │ │85 - // create http listener │ │86 │ │87 const port_policy: PortPolicy = if (options.fallback_on_address_in_use) .fallback else .exact;│ │88 - var http_maybe: ?BoundListener = if (options.http_listen) |value| blk: { │ │89 - const address = try parseListenAddress(value); │ │90 - break :blk .{ .address = address, .server = try listen(io, address, port_policy) }; │ │91 - } else null; │ │92 - defer if (http_maybe) |*http| http.deinit(io); │ │88 │ │89 // disk-backed store for web login sessions │ │90 const session_store = try web.SessionStore.init(io, data_dir); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 91 defer session_store.deinit(); │ │ 92 │ │ 98 - // create wui listener │ │ 93 + // create web listener │ │ 94 │ │100 - const wui_listen_address = try parseListenAddress(options.wui_listen); │ │101 - var wui_server = try listen(io, wui_listen_address, port_policy); │ │102 - defer wui_server.deinit(io); │ │ 95 + const web_listen_address = try parseListenAddress(options.web_listen); │ │ 96 + var web_server = try listen(io, web_listen_address, port_policy); │ │ 97 + defer web_server.deinit(io); │ │ 98 │ │ 99 + const web_url = options.web_url orelse try allocator.print("http://localhost:{d}", .{web_server.socket.address.getPort()});│ │100 + defer if (options.web_url == null) allocator.free(web_url); │ │101 + │ │102 // create ssh listener and its session state │ │103 │ │106 - const git_http_port: ?u16 = if (http_maybe) |*http| http.port() else null; │ │104 + var ssh_url_owned: ?[]const u8 = null; │ │105 + defer if (ssh_url_owned) |url| allocator.free(url); │ │106 var ssh_maybe: ?SshService = if (options.ssh_listen) |value| blk: { │ │107 const address = try parseListenAddress(value); │ │108 var listener = BoundListener{ .address = address, .server = try listen(io, address, port_policy) }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │109 errdefer listener.deinit(io); │ │111 - const ssh_port = listener.port(); │ │110 + const ssh_url = options.ssh_url orelse url_blk: { │ │111 + const url = try allocator.print("ssh://localhost:{d}", .{listener.port()}); │ │112 + ssh_url_owned = url; │ │113 + break :url_blk url; │ │114 + }; │ │115 break :blk .{ │ │116 .listener = listener, │ │117 .host_key = try serve_ssh_protocol.HostKey.loadOrGenerate(io, allocator, data_dir_path),│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │118 .session_handler = .{ │ │119 .admin_repo_path = admin_repo_path, │ │120 .server = .{ .users_dir = users_dir, .admin_repo_path = admin_repo_path },│ │118 - .wui_port = wui_server.socket.address.getPort(), │ │119 - .git_http_port = git_http_port, │ │120 - .git_ssh_port = ssh_port, │ │121 + .web_url = web_url, │ │122 + .ssh_url = ssh_url, │ │123 .git_ssh_prefix = options.git_ssh_prefix, │ │124 .err = err, │ │125 }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │134 │ │135 // run listeners, printing the ports actually bound │ │136 │ │135 - const git_ssh_port: ?u16 = if (ssh_maybe) |*ssh| ssh.listener.port() else null; │ │136 - │ │137 - if (http_maybe) |*http| { │ │138 - try err.print("serving HTTP on {s}:{d}, users dir {s}\n", .{ http.address.host, http.port(), users_dir }); │ │139 - try err.flush(); │ │140 - serve_http.runListener(repo_kind, any_repo_opts, io, allocator, users_dir, admin_repo_path, &http.server, &tasks, err); │ │141 - } │ │137 + const ssh_url: ?[]const u8 = if (ssh_maybe) |*ssh| ssh.session_handler.ssh_url else null; │ │138 │ │139 if (ssh_maybe) |*ssh| { │ │140 try err.print("serving SSH on {s}:{d}, host key ED25519 {s}\n", .{ ssh.listener.address.host, ssh.listener.port(), try ssh.host_key.fingerprint(allocator) });│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │142 serve_ssh.runListener(io, allocator, &ssh.host_key, &ssh.session_handler, &ssh.watchdog, &ssh.listener.server, &tasks, err); │ │143 } │ │144 │ │149 - try err.print("serving web UI on http://{s}:{d}/\n", .{ wui_listen_address.host, wui_server.socket.address.getPort() }); │ │145 + try err.print("serving web UI and git over HTTP on http://{s}:{d}/, users dir {s}\n", .{ web_listen_address.host, web_server.socket.address.getPort(), users_dir });│ │146 try err.flush(); │ │147 │ │152 - runWebListener(io, allocator, &wui_server, &tasks, .{ .server = .{ │ │148 + runWebListener(io, allocator, &web_server, &tasks, .{ .server = .{ │ │149 .admin_repo_path = admin_repo_path, │ │150 .users_dir = users_dir, │ │151 .session_store = session_store, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │156 - .git_http_port = git_http_port, │ │157 - .git_ssh_port = git_ssh_port, │ │152 + .web_url = web_url, │ │153 + .ssh_url = ssh_url, │ │154 .git_ssh_prefix = options.git_ssh_prefix, │ │155 + .serve_git = serve_http.gitHandler(repo_kind, any_repo_opts),│ │156 } }, err); │ │157 │ │158 if (@TypeOf(runnable) != void) { │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │162 - try runnable.run(wui_server.socket.address.getPort(), git_http_port, git_ssh_port);│ │159 + try runnable.run(web_url, ssh_url); │ │160 } else { │ │161 try tasks.await(io); │ │162 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │165 // serve just the web UI for a single local repo, running `runnable` (the │ │166 // local TUI) in the foreground while the listener runs in the background. │ │167 // when the default port is taken, a random ephemeral port is used instead;│ │171 -// the bound port is passed to `runnable.run`. │ │168 +// the web UI's url is passed to `runnable.run`. │ │169 pub fn runLocal( │ │170 io: std.Io, │ │171 allocator: std.mem.Allocator, │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯