From 0b07947b8a2ce8140a08fb9d699fca4d43969ab8 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Wed, 18 Jun 2025 02:10:51 -0400 Subject: [PATCH 1/2] Create urlDecode function for redirects --- src/date_fmt.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/date_fmt.zig b/src/date_fmt.zig index 7e44da7..9746fe6 100644 --- a/src/date_fmt.zig +++ b/src/date_fmt.zig @@ -33,3 +33,20 @@ pub fn scrobbleToRow(allocator: std.mem.Allocator, scrobble: Data.Scrobble) !Dat .date = try dateFmt(allocator, scrobble.date), }; } + +pub fn urlDecode(allocator: std.mem.Allocator, str: []const u8) ![]const u8 { + var decoded = std.ArrayList(u8).init(allocator); + var i: usize = 0; + while (i < str.len) : (i += 1) { + const v = str[i]; + if (v == '%') { + if (i + 2 < str.len) { + const hex = str[i + 1 .. i + 3]; + const char = try std.fmt.parseInt(u8, hex, 16); + try decoded.append(char); + i += 2; + } else return error.InvalidInput; + } else try decoded.append(v); + } + return decoded.toOwnedSlice(); +} From 2d7d2835fd7f4d026fea9863b39d6b60150c89c8 Mon Sep 17 00:00:00 2001 From: mitteneer Date: Wed, 18 Jun 2025 02:11:30 -0400 Subject: [PATCH 2/2] Proof of concept artist name in url string Some of my favorite code in the project. Just need a disambiguation page, and we're in business here --- src/app/views/artists.zig | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/app/views/artists.zig b/src/app/views/artists.zig index 3ec8eba..12be28c 100644 --- a/src/app/views/artists.zig +++ b/src/app/views/artists.zig @@ -2,9 +2,10 @@ const std = @import("std"); const jetzig = @import("jetzig"); const jetquery = @import("jetzig").jetquery; const TableRow = @import("../../types.zig").TableRow; -const dateFmt = @import("../../date_fmt.zig").dateFmt; -const ordinalFmt = @import("../../ordinal_fmt.zig").ordinalFmt; +//const dateFmt = @import("../../date_fmt.zig").dateFmt; +//const ordinalFmt = @import("../../ordinal_fmt.zig").ordinalFmt; const queries = @import("../../queries.zig"); +const decode = @import("../../date_fmt.zig").urlDecode; pub fn index(request: *jetzig.Request) !jetzig.View { var root = try request.data(.object); @@ -16,21 +17,45 @@ pub fn index(request: *jetzig.Request) !jetzig.View { } pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View { + const parse_err = blk: { + const rdr_id = std.fmt.parseInt(i64, id, 10) catch |err| break :blk err; + const artist = try jetzig.database.Query(.Artist).find(rdr_id).execute(request.repo); + if (artist == null) break :blk error.InvalidCharacter; + var name = std.ArrayList(u8).init(request.allocator); + try name.appendSlice("http://127.0.0.1:8080/artists/"); + try name.appendSlice(artist.?.name); + return request.redirect(try name.toOwnedSlice(), .found); + }; + + const id_int = switch (parse_err) { + error.Overflow => return request.fail(.not_found), + error.InvalidCharacter => blk: { + const rn = try decode(request.allocator, id); + std.log.debug("{s}", .{rn}); + const artists = try jetzig.database.Query(.Artist).where(.{ .name = rn }).all(request.repo); + + if (artists.len == 0) return request.fail(.not_found); + if (artists.len > 1) return request.redirect("http://127.0.0.1:8080", .found); + break :blk artists[0].id; + }, + else => unreachable, + }; + var root = try request.data(.object); - const artist = try queries.entityQueryResult(request, queries.loadQuery(.artist, .entity_info), .{id}); + const artist = try queries.entityQueryResult(request, queries.loadQuery(.artist, .entity_info), .{id_int}); try root.put("artist", artist); - const albums = try queries.entityQueryResult(request, queries.loadQuery(.artist, .get_albums), .{id}); + const albums = try queries.entityQueryResult(request, queries.loadQuery(.artist, .get_albums), .{id_int}); try root.put("albums", albums); - const appears = try queries.entityQueryResult(request, queries.loadQuery(.artist, .appears), .{id}); + const appears = try queries.entityQueryResult(request, queries.loadQuery(.artist, .appears), .{id_int}); try root.put("appears", appears); - const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.artist, .firstlast), .{id}); + const firstlast = try queries.entityQueryResult(request, queries.loadQuery(.artist, .firstlast), .{id_int}); try root.put("firstlast", firstlast); - const timescale = try queries.entityQueryResult(request, queries.loadQuery(.artist, .timescale), .{id}); + const timescale = try queries.entityQueryResult(request, queries.loadQuery(.artist, .timescale), .{id_int}); try root.put("yearly", timescale); return request.render(.ok);