╭ changed ╮ │README.md│ ╰─────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │55 zig build try │ │56 ``` │ │57 │ │58 -This will launch a server with fake data that it stores in the `temp-try` directory. It will then launch the TUI directly in your terminal (you can exit by pressing escape). Additionally, you can view the web UI at http://localhost:8000.│ │58 +This will launch a server with fake data that it stores in the `temp-try` directory. It will then launch the TUI directly in your terminal (you can exit by pressing escape). Additionally, you can view the web UI at http://localhost:8080.│ │59 │ │60 A fun test is to push Haxy itself to the server. You can do that by running the following command, which will push it to the server using SSH: │ │61 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │63 GIT_SSH_COMMAND='ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i temp-try/key' git push localhost:admin:haxy HEAD:master│ │64 ``` │ │65 │ │66 -After that, go to the admin user in the UI and you'll see Haxy's repo page: http://localhost:8000/repo/admin/haxy/files/branch:master │ │66 +After that, go to the admin user in the UI and you'll see Haxy's repo page: http://localhost:8080/repo/admin/haxy/files/branch:master │ │67 │ │68 *"C'mon Alex! You always dreamt about going on a big adventure! Let this be our first!" -- Lunar: Silver Star Story* │ │69 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────╮ │src/command.zig│ ╰───────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │19 \\a long-running server forwarding receive-pack and upload-pack. │ │20 , │ │21 .example = │ │22 - \\haxy serve --http-listen 127.0.0.1:8080 --ssh-listen 127.0.0.1:8022 --wui-listen 127.0.0.1:8000 --data-dir /srv/git│ │23 - \\use "none" for --http-listen or --ssh-listen to disable that service. │ │22 + \\haxy serve --web-listen 127.0.0.1:8080 --ssh-listen 127.0.0.1:8022 --data-dir /srv/git │ │23 + \\use "none" for --ssh-listen to disable ssh. git clones over HTTP share the web UI's listener. │ │24 + \\--web-url and --ssh-url set the public urls shown to users, like https://example.com and ssh://example.com. │ │25 , │ │26 }, │ │27 }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────╮ │84 // flags that can have a value associated with them │ │85 // must be included here │ │86 const value_flags = std.StaticStringMap(void).initComptime(.{│ │86 - .{"--http-listen"}, │ │87 + .{"--web-listen"}, │ │88 .{"--ssh-listen"}, │ │88 - .{"--wui-listen"}, │ │89 + .{"--web-url"}, │ │90 + .{"--ssh-url"}, │ │91 .{"--data-dir"}, │ │92 }); │ │93 │ ╰─────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │203 if (cmd_args.positional_args.len != 0) return null; │ │204 │ │205 var options: srv.Options = .{}; │ │204 - if (cmd_args.get("--http-listen")) |value_maybe| { │ │205 - const value = value_maybe orelse return error.HttpListenNeedsValue; │ │206 - options.http_listen = if (std.mem.eql(u8, value, "none")) null else value; │ │207 - } │ │206 + if (cmd_args.get("--web-listen")) |value_maybe| options.web_listen = value_maybe orelse return error.WebListenNeedsValue;│ │207 if (cmd_args.get("--ssh-listen")) |value_maybe| { │ │208 const value = value_maybe orelse return error.SshListenNeedsValue; │ │209 options.ssh_listen = if (std.mem.eql(u8, value, "none")) null else value; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │210 } │ │212 - if (cmd_args.get("--wui-listen")) |value_maybe| options.wui_listen = value_maybe orelse return error.WuiListenNeedsValue; │ │211 + // urls are joined with "/" + a path │ │212 + if (cmd_args.get("--web-url")) |value_maybe| options.web_url = std.mem.trimEnd(u8, value_maybe orelse return error.WebUrlNeedsValue, "/");│ │213 + if (cmd_args.get("--ssh-url")) |value_maybe| options.ssh_url = std.mem.trimEnd(u8, value_maybe orelse return error.SshUrlNeedsValue, "/");│ │214 if (cmd_args.get("--data-dir")) |value_maybe| options.data_dir = value_maybe orelse return error.DataDirNeedsValue; │ │215 return options; │ │216 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/main.zig│ ╰────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │147 allocator: std.mem.Allocator, │ │148 session: *ui.Session, │ │149 │ │150 - pub fn run(self: @This(), web_port: u16) !void { │ │151 - self.session.web_port = web_port; │ │150 + pub fn run(self: @This(), web_url: []const u8) !void { │ │151 + self.session.web_url = web_url; │ │152 try ui.run(self.io, self.allocator, self.session, null);│ │153 } │ │154 }; │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │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) }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯