Compare commits
2 commits
1b379513f7
...
252c12403a
Author | SHA1 | Date | |
---|---|---|---|
252c12403a | |||
4e85c0225b |
7 changed files with 98 additions and 1 deletions
|
@ -14,6 +14,12 @@ const services = struct {
|
|||
const follows = @import("./services/follows.zig");
|
||||
};
|
||||
|
||||
pub const ClusterMeta = struct {
|
||||
community_count: usize,
|
||||
user_count: usize,
|
||||
note_count: usize,
|
||||
};
|
||||
|
||||
pub const RegistrationOptions = struct {
|
||||
invite_code: ?[]const u8 = null,
|
||||
email: ?[]const u8 = null,
|
||||
|
@ -485,5 +491,27 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
const result = try services.follows.create(self.db, self.user_id orelse return error.NoToken, followee, self.arena.allocator());
|
||||
defer util.deepFree(self.arena.allocator(), result);
|
||||
}
|
||||
|
||||
pub fn unfollow(self: *Self, followee: Uuid) !void {
|
||||
const result = try services.follows.delete(self.db, self.user_id orelse return error.NoToken, followee, self.arena.allocator());
|
||||
defer util.deepFree(self.arena.allocator(), result);
|
||||
}
|
||||
|
||||
pub fn getClusterMeta(self: *Self) !ClusterMeta {
|
||||
return try self.db.queryRow(
|
||||
ClusterMeta,
|
||||
\\SELECT
|
||||
\\ COUNT(DISTINCT note.id) AS note_count,
|
||||
\\ COUNT(DISTINCT actor.id) AS user_count,
|
||||
\\ COUNT(DISTINCT community.id) AS community_count
|
||||
\\FROM note, actor, community
|
||||
\\WHERE
|
||||
\\ actor.community_id = community.id AND
|
||||
\\ community.kind != 'admin'
|
||||
,
|
||||
.{},
|
||||
self.arena.allocator(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,6 +33,17 @@ pub fn create(db: anytype, followed_by_id: Uuid, followee_id: Uuid, alloc: std.m
|
|||
};
|
||||
}
|
||||
|
||||
pub fn delete(db: anytype, followed_by_id: Uuid, followee_id: Uuid, alloc: std.mem.Allocator) !void {
|
||||
// TODO: Measure count and report success
|
||||
db.exec(
|
||||
\\DELETE FROM follow
|
||||
\\WHERE followed_by_id = $1 AND followee_id = $2
|
||||
,
|
||||
.{ followed_by_id, followee_id },
|
||||
alloc,
|
||||
) catch return error.DatabaseFailure;
|
||||
}
|
||||
|
||||
const max_max_items = 100;
|
||||
|
||||
pub const QueryArgs = struct {
|
||||
|
|
|
@ -50,6 +50,7 @@ const routes = .{
|
|||
timelines.local,
|
||||
timelines.home,
|
||||
follows.create,
|
||||
follows.delete,
|
||||
follows.query_followers,
|
||||
follows.query_following,
|
||||
} ++ web.routes;
|
||||
|
|
|
@ -19,6 +19,21 @@ pub const create = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const delete = struct {
|
||||
pub const method = .DELETE;
|
||||
pub const path = "/api/v0/users/:id/follow";
|
||||
|
||||
pub const Args = struct {
|
||||
id: Uuid,
|
||||
};
|
||||
|
||||
pub fn handler(req: anytype, res: anytype, srv: anytype) !void {
|
||||
try srv.unfollow(req.args.id);
|
||||
|
||||
try res.json(.ok, .{});
|
||||
}
|
||||
};
|
||||
|
||||
pub const query_followers = struct {
|
||||
pub const method = .GET;
|
||||
pub const path = "/api/v0/users/:id/followers";
|
||||
|
|
|
@ -5,6 +5,7 @@ pub const routes = .{
|
|||
about,
|
||||
login,
|
||||
global_timeline,
|
||||
cluster.overview,
|
||||
};
|
||||
|
||||
const index = struct {
|
||||
|
@ -70,3 +71,18 @@ const global_timeline = struct {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
const cluster = struct {
|
||||
const overview = struct {
|
||||
pub const path = "/cluster/overview";
|
||||
pub const method = .GET;
|
||||
|
||||
pub fn handler(_: anytype, res: anytype, srv: anytype) !void {
|
||||
const meta = try srv.getClusterMeta();
|
||||
try res.template(.ok, @embedFile("./web/cluster/overview.tmpl.html"), .{
|
||||
.community = srv.community,
|
||||
.meta = meta,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
24
src/main/controllers/web/cluster/overview.tmpl.html
Normal file
24
src/main/controllers/web/cluster/overview.tmpl.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{ .community.name }</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{ .community.name }</h1>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="communities">Communities</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<h2>Cluster Overview</h2>
|
||||
<div>
|
||||
Home to <span>{ .meta.community_count }</span> communities
|
||||
hosting <span>{ .meta.user_count }</span> users.
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -59,7 +59,9 @@ fn executeStatement(writer: anytype, comptime stmt: Statement, args: anytype, ca
|
|||
}
|
||||
|
||||
fn print(writer: anytype, arg: anytype) !void {
|
||||
if (comptime std.meta.trait.isZigString(@TypeOf(arg))) return writer.writeAll(arg);
|
||||
const T = @TypeOf(arg);
|
||||
if (comptime std.meta.trait.isZigString(T)) return writer.writeAll(arg);
|
||||
if (comptime std.meta.trait.isNumber(T)) return std.fmt.format(writer, "{}", .{arg});
|
||||
@compileLog(@TypeOf(arg));
|
||||
|
||||
@compileError("TODO");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue