updates
This commit is contained in:
parent
056b8918d3
commit
d0a8d8d969
BIN
public/banner.jpg
Normal file
BIN
public/banner.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 126 KiB |
@ -6,6 +6,7 @@ pub const Blog = jetquery.Model(
|
|||||||
struct {
|
struct {
|
||||||
id: i32,
|
id: i32,
|
||||||
title: []const u8,
|
title: []const u8,
|
||||||
|
blob: []const u8,
|
||||||
content: ?[]const u8,
|
content: ?[]const u8,
|
||||||
created_at: jetquery.DateTime,
|
created_at: jetquery.DateTime,
|
||||||
updated_at: jetquery.DateTime,
|
updated_at: jetquery.DateTime,
|
||||||
|
@ -9,6 +9,7 @@ pub fn up(repo: anytype) !void {
|
|||||||
t.primaryKey("id", .{}),
|
t.primaryKey("id", .{}),
|
||||||
t.column("title", .string, .{}),
|
t.column("title", .string, .{}),
|
||||||
t.column("content", .text, .{ .optional = true }),
|
t.column("content", .text, .{ .optional = true }),
|
||||||
|
t.column("blob", .text, .{}),
|
||||||
t.timestamps(.{}),
|
t.timestamps(.{}),
|
||||||
},
|
},
|
||||||
.{},
|
.{},
|
||||||
|
72
src/app/rss.zig
Normal file
72
src/app/rss.zig
Normal file
@ -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(
|
||||||
|
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
\\<rss version="2.0">
|
||||||
|
\\<channel>
|
||||||
|
\\<title>yuzucchii.xyz</title>
|
||||||
|
\\<link>yuzucchii.xyz</link>
|
||||||
|
\\<description>Personal blog of Yuzu with all kinds of different articles</description>
|
||||||
|
, .{});
|
||||||
|
|
||||||
|
for (items) |item| {
|
||||||
|
try writer.print(
|
||||||
|
\\<item>
|
||||||
|
\\<title>{s}</title>
|
||||||
|
\\<link>yuzucchii.xyz/blogs/{d}</link>
|
||||||
|
\\<description>{s}</description>
|
||||||
|
, .{ item.title, item.id, item.blob });
|
||||||
|
|
||||||
|
try item.created_at.strftime(writer, "<pubDate>Day, DD Mon YYYY HH:MM:SS GMT</pubDate>");
|
||||||
|
|
||||||
|
try writer.print(
|
||||||
|
\\</item>
|
||||||
|
, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("</channel></rss>");
|
||||||
|
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 "<empty>",
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
@ -153,7 +153,15 @@ pub fn post(request: *jetzig.Request) !jetzig.View {
|
|||||||
return request.fail(.unprocessable_entity);
|
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);
|
return request.redirect("/blogs", .moved_permanently);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,24 @@
|
|||||||
@args allowed: bool
|
@args allowed: bool
|
||||||
<div>
|
<div>
|
||||||
@for (.blogs) |blog| {
|
@for (.blogs) |blog| {
|
||||||
|
@if ($.allowed)
|
||||||
|
<button id="delete-post">
|
||||||
|
Delete post
|
||||||
|
</button>
|
||||||
|
<script>
|
||||||
|
document.getElementById('delete-post').addEventListener('click', function () {
|
||||||
|
fetch(`/blogs/${blog.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error during logout:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@end
|
||||||
<a href="/blogs/{{blog.id}}">{{blog.title}}</a>
|
<a href="/blogs/{{blog.id}}">{{blog.title}}</a>
|
||||||
{{zmpl.fmt.datetime(blog.get("created_at"), "%Y-%m-%d %H:%M")}}
|
{{zmpl.fmt.datetime(blog.get("created_at"), "%Y-%m-%d %H:%M")}}
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<form action="/blogs" method="POST">
|
<form action="/blogs" method="POST">
|
||||||
|
{{context.authenticityFormElement()}}
|
||||||
<label>Title</label>
|
<label>Title</label>
|
||||||
<input name="title" />
|
<input name="title" />
|
||||||
|
|
||||||
<label>Content</label>
|
<label>Content</label>
|
||||||
<textarea name="content"></textarea>
|
<textarea name="content"></textarea>
|
||||||
|
<label>Preview</label>
|
||||||
|
<textarea name="preview"></textarea>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,6 +61,10 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|||||||
|
|
||||||
try root.put("allowed", allowed);
|
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"));
|
try root.put("message_param", params.get("message"));
|
||||||
|
|
||||||
// Set arbitrary response headers as required. `content-type` is automatically assigned for
|
// Set arbitrary response headers as required. `content-type` is automatically assigned for
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
@args title: []const u8
|
@args title: []const u8, id: i32, blob: []const u8
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h2 class="text-2xl font-semibold mb-2">{{title}}</h2>
|
<h2 class="text-2xl font-semibold mb-2">{{title}}</h2>
|
||||||
|
<div class="text-gray-700">
|
||||||
|
{{blob}}
|
||||||
|
</div>
|
||||||
|
<a href="/blogs/{{id}}" class="text-blue-600 underline">Read more</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
<title> {{.title}} </title
|
<title> {{.title}} </title
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="mx-auto max-w-3xl mt-sm">
|
<div class="mx-auto max-w-3xl mt-sm">
|
||||||
|
<br/>
|
||||||
<h1 class="text-3xl font-bold mb-4">Hello, I like doing things.</h1>
|
<h1 class="text-3xl font-bold mb-4">Hello, I like doing things.</h1>
|
||||||
<p class="text-lg mb-6">I created this website using Jetzig in order to store things I do.</p>
|
|
||||||
|
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<a href="/blogs" class="text-blue-500 hover:underline">Blog</a>
|
<a href="/blogs" class="text-blue-500 hover:underline">Blog</a>
|
||||||
@ -21,10 +21,11 @@
|
|||||||
@else
|
@else
|
||||||
<a href="/login" class="text-blue-500 hover:underline ml-4">Login</a>
|
<a href="/login" class="text-blue-500 hover:underline ml-4">Login</a>
|
||||||
@end
|
@end
|
||||||
<a href="#" class="text-blue-500 hover:underline ml-4">Gitea instance</a>
|
<a href="git.yuzucchii.xyz" class="text-blue-500 hover:underline ml-4">Gitea instance</a>
|
||||||
<a href="#" class="text-blue-500 hover:underline ml-4">IRC</a>
|
<a href="chat.yuzucchii.xyz" class="text-blue-500 hover:underline ml-4">IRC</a>
|
||||||
<a href="#" class="text-blue-500 hover:underline ml-4">Tor url</a>
|
<a href="/rss" class="text-blue-500 hover:underline ml-4">RSS feed</a>
|
||||||
<a href="#" class="text-blue-500 hover:underline ml-4">RSS feed</a>
|
<a href="searx.yuzucchii.xyz" class="text-blue-500 hover:underline ml-4">SearX</a>
|
||||||
|
<a href="yuzucchii.xyz/files" class="text-blue-500 hover:underline ml-4">Files</a>
|
||||||
<a href="mailto:me@yuzucchii.xyz" class="text-blue-500 hover:underline ml-4">E-mail</a>
|
<a href="mailto:me@yuzucchii.xyz" class="text-blue-500 hover:underline ml-4">E-mail</a>
|
||||||
<button
|
<button
|
||||||
id="copy-button"
|
id="copy-button"
|
||||||
@ -49,12 +50,35 @@
|
|||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Banner Image -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<img src="/banner.jpg" alt="Banner" class="w-full rounded-xl shadow-md">
|
||||||
|
</div>
|
||||||
|
<p class="text-lg mb-6">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.</p>
|
||||||
|
|
||||||
@for (.articles) |article| {
|
@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)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<footer class="mt-12 border-t pt-6 text-center text-sm text-gray-600">
|
||||||
|
<div class="max-w-xl mx-auto px-4">
|
||||||
|
<div class="bg-yellow-100 text-yellow-800 border border-yellow-200 rounded-md px-4 py-3 mb-6">
|
||||||
|
<p class="text-sm md:text-base">
|
||||||
|
This website is not reliant on JavaScript, and all scripts are optional and publicly available.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-center flex-wrap gap-4 mb-4 text-blue-500">
|
||||||
|
<a href="{{.bsky_link}}" class="hover:underline" target="_blank" rel="noopener">Bluesky</a>
|
||||||
|
<a href="{{.discord_link}}" class="hover:underline" target="_blank" rel="noopener">Discord</a>
|
||||||
|
<a href="{{.codeberg_link}}" class="hover:underline" target="_blank" rel="noopener">Codeberg</a>
|
||||||
|
<a href="https://yuzucchii.xyz" class="hover:underline" target="_blank" rel="noopener">yuzucchii.xyz</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-xs text-gray-500">© 2025 yuzucchii.xyz</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function showCopiedMessage() {
|
function showCopiedMessage() {
|
||||||
|
@ -215,8 +215,6 @@ pub const jetzig_options = struct {
|
|||||||
|
|
||||||
pub fn init(app: *jetzig.App) !void {
|
pub fn init(app: *jetzig.App) !void {
|
||||||
_ = app;
|
_ = app;
|
||||||
// Example custom route:
|
|
||||||
// app.route(.GET, "/custom/:id/foo/bar", @import("app/views/custom/foo.zig"), .bar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user