diff --git a/public/banner.jpg b/public/banner.jpg new file mode 100644 index 0000000..4faf4de Binary files /dev/null and b/public/banner.jpg differ diff --git a/public/favicon.ico b/public/favicon.ico index 0ccc92b..8f3e128 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/app/database/Schema.zig b/src/app/database/Schema.zig index eccdcea..8b64b95 100644 --- a/src/app/database/Schema.zig +++ b/src/app/database/Schema.zig @@ -6,6 +6,7 @@ pub const Blog = jetquery.Model( struct { id: i32, title: []const u8, + blob: []const u8, content: ?[]const u8, created_at: jetquery.DateTime, updated_at: jetquery.DateTime, diff --git a/src/app/database/migrations/2025-05-04_05-05-24_create_blogs.zig b/src/app/database/migrations/2025-05-04_05-05-24_create_blogs.zig index c9e2d5d..33d05a6 100644 --- a/src/app/database/migrations/2025-05-04_05-05-24_create_blogs.zig +++ b/src/app/database/migrations/2025-05-04_05-05-24_create_blogs.zig @@ -9,6 +9,7 @@ pub fn up(repo: anytype) !void { t.primaryKey("id", .{}), t.column("title", .string, .{}), t.column("content", .text, .{ .optional = true }), + t.column("blob", .text, .{}), t.timestamps(.{}), }, .{}, diff --git a/src/app/rss.zig b/src/app/rss.zig new file mode 100644 index 0000000..006ff69 --- /dev/null +++ b/src/app/rss.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const jetzig = @import("jetzig"); + +const RssItem = struct { + id: i32, + title: []const u8, + blob: []const u8, + content: []const u8, + created_at: jetzig.jetquery.DateTime, + updated_at: jetzig.jetquery.DateTime, +}; + +fn generateRss(items: []const RssItem, allocator: std.mem.Allocator) ![]u8 { + var list = std.ArrayList(u8).init(allocator); + const writer = list.writer(); + + try writer.print( + \\ + \\ + \\ + \\yuzucchii.xyz + \\yuzucchii.xyz + \\Personal blog of Yuzu with all kinds of different articles + , .{}); + + for (items) |item| { + try writer.print( + \\ + \\{s} + \\yuzucchii.xyz/blogs/{d} + \\{s} + , .{ item.title, item.id, item.blob }); + + try item.created_at.strftime(writer, "Day, DD Mon YYYY HH:MM:SS GMT"); + + try writer.print( + \\ + , .{}); + } + + try writer.writeAll(""); + return list.toOwnedSlice(); +} + + +// we'll send xml instead +pub fn index(request: *jetzig.Request) !void { + try request.headers.append("Content-Type", "application/rss+xml"); + + const query = jetzig.database.Query(.Blog).orderBy(.{.created_at = .desc}); + + const blogs = try request.repo.all(query); + + var items: std.ArrayList(RssItem) = .init(request.allocator); + + for (blogs) |blog| { + try items.append(RssItem{ + .id = blog.id, + .title = blog.title, + .blob = blog.blob, + .content = blog.content orelse "", + .created_at = blog.created_at, + .updated_at = blog.updated_at, + }); + } + + // TODO: wait until jetzig adds xml support + _ = try generateRss(try items.toOwnedSlice(), request.allocator); + + return request.render(.ok); +} + diff --git a/src/app/views/blogs.zig b/src/app/views/blogs.zig index 254e83d..8907e3d 100644 --- a/src/app/views/blogs.zig +++ b/src/app/views/blogs.zig @@ -153,7 +153,15 @@ pub fn post(request: *jetzig.Request) !jetzig.View { return request.fail(.unprocessable_entity); }; - try request.repo.insert(.Blog, .{ .title = title, .content = content }); + const preview = params.getT(.string, "preview") orelse { + return request.fail(.unprocessable_entity); + }; + + try request.repo.insert(.Blog, .{ + .title = title, + .blob = preview, + .content = content, + }); return request.redirect("/blogs", .moved_permanently); } else { diff --git a/src/app/views/blogs/index.zmpl b/src/app/views/blogs/index.zmpl index ba43ea8..2b006e0 100644 --- a/src/app/views/blogs/index.zmpl +++ b/src/app/views/blogs/index.zmpl @@ -1,6 +1,24 @@ @args allowed: bool
@for (.blogs) |blog| { + @if ($.allowed) + + + @end {{blog.title}} {{zmpl.fmt.datetime(blog.get("created_at"), "%Y-%m-%d %H:%M")}}
diff --git a/src/app/views/blogs/new.zmpl b/src/app/views/blogs/new.zmpl index 62adba1..4a85475 100644 --- a/src/app/views/blogs/new.zmpl +++ b/src/app/views/blogs/new.zmpl @@ -1,11 +1,12 @@
+ {{context.authenticityFormElement()}} - - + +
diff --git a/src/app/views/root.zig b/src/app/views/root.zig index 200ac33..81919b3 100644 --- a/src/app/views/root.zig +++ b/src/app/views/root.zig @@ -61,6 +61,10 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { try root.put("allowed", allowed); + try root.put("bsky_link", "empty for now"); + try root.put("discord_link", "https://discord.gg/pvraBkepUP"); + try root.put("codeberg_link", "https://codeberg.org/yuzu"); + try root.put("message_param", params.get("message")); // Set arbitrary response headers as required. `content-type` is automatically assigned for diff --git a/src/app/views/root/_article_blob.zmpl b/src/app/views/root/_article_blob.zmpl index 1bfc872..3fd038d 100644 --- a/src/app/views/root/_article_blob.zmpl +++ b/src/app/views/root/_article_blob.zmpl @@ -1,4 +1,8 @@ -@args title: []const u8 +@args title: []const u8, id: i32, blob: []const u8

{{title}}

+
+ {{blob}} +
+ Read more
diff --git a/src/app/views/root/index.zmpl b/src/app/views/root/index.zmpl index 370cc3a..355b321 100644 --- a/src/app/views/root/index.zmpl +++ b/src/app/views/root/index.zmpl @@ -9,10 +9,10 @@ {{.title}} - +
+

Hello, I like doing things.

-

I created this website using Jetzig in order to store things I do.

Blog @@ -21,10 +21,11 @@ @else Login @end - Gitea instance - IRC - Tor url - RSS feed + Gitea instance + IRC + RSS feed + SearX + Files E-mail
+ +
+ Banner +
+

I created this website in order to store things I do, but don't be scared of the simple layout, this website is featureful and full of things to do, please enjoy your visit.

@for (.articles) |article| { - @partial root/article_blob(title: article.title, blob: article.content) + @partial root/article_blob(title: article.title, blob: article.content, id: article.id) }
- +
+
+
+

+ This website is not reliant on JavaScript, and all scripts are optional and publicly available. +

+
+ + + +

© 2025 yuzucchii.xyz

+
+
+