Compare commits
8 commits
44dfcd6bd0
...
1269aeeac1
Author | SHA1 | Date | |
---|---|---|---|
1269aeeac1 | |||
0f5751e7ed | |||
8edf661afe | |||
434b0e07d0 | |||
620608964f | |||
07413747c2 | |||
a49131f6bf | |||
03463de743 |
6 changed files with 122 additions and 7 deletions
|
@ -465,7 +465,16 @@ pub fn Mount(comptime route: []const u8) type {
|
||||||
var new_ctx = ctx;
|
var new_ctx = ctx;
|
||||||
new_ctx.path = args.path;
|
new_ctx.path = args.path;
|
||||||
|
|
||||||
return next.handle(req, res, new_ctx, {});
|
if (@hasField(@TypeOf(new_ctx), "mounted_at")) {
|
||||||
|
new_ctx.mounted_at = try std.fmt.allocPrint(ctx.allocator, "/{s}/{s}", .{
|
||||||
|
std.mem.trim(u8, ctx.mounted_at, "/"),
|
||||||
|
std.mem.trim(u8, route, "/"),
|
||||||
|
});
|
||||||
|
defer ctx.allocator.free(new_ctx.mounted_at);
|
||||||
|
return next.handle(req, res, new_ctx, {});
|
||||||
|
} else {
|
||||||
|
return next.handle(req, res, addField(new_ctx, "mounted_at", std.mem.trim(u8, route, "/")), {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,9 @@ pub const Response = struct {
|
||||||
pub fn template(self: *Self, status_code: http.Status, srv: anytype, comptime templ: []const u8, data: anytype) !void {
|
pub fn template(self: *Self, status_code: http.Status, srv: anytype, comptime templ: []const u8, data: anytype) !void {
|
||||||
try self.headers.put("Content-Type", "text/html");
|
try self.headers.put("Content-Type", "text/html");
|
||||||
|
|
||||||
|
const user = if (srv.user_id) |uid| try srv.getUser(uid) else null;
|
||||||
|
defer util.deepFree(srv.allocator, user);
|
||||||
|
|
||||||
var stream = try self.open(status_code);
|
var stream = try self.open(status_code);
|
||||||
defer stream.close();
|
defer stream.close();
|
||||||
|
|
||||||
|
@ -237,6 +240,7 @@ pub const Response = struct {
|
||||||
data,
|
data,
|
||||||
.{
|
.{
|
||||||
.community = srv.community,
|
.community = srv.community,
|
||||||
|
.user = user,
|
||||||
.user_id = srv.user_id,
|
.user_id = srv.user_id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub const routes = .{
|
||||||
controllers.apiEndpoint(cluster.overview),
|
controllers.apiEndpoint(cluster.overview),
|
||||||
controllers.apiEndpoint(cluster.communities.create.page),
|
controllers.apiEndpoint(cluster.communities.create.page),
|
||||||
controllers.apiEndpoint(cluster.communities.create.submit),
|
controllers.apiEndpoint(cluster.communities.create.submit),
|
||||||
|
controllers.apiEndpoint(drive.details),
|
||||||
};
|
};
|
||||||
|
|
||||||
const static = struct {
|
const static = struct {
|
||||||
|
@ -229,6 +230,29 @@ const user_details = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const drive = struct {
|
||||||
|
const details = struct {
|
||||||
|
pub const path = "/drive/:path*";
|
||||||
|
pub const method = .GET;
|
||||||
|
|
||||||
|
pub const Args = struct {
|
||||||
|
path: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const dir_tmpl = @embedFile("./web/drive/directory.tmpl.html");
|
||||||
|
|
||||||
|
pub fn handler(req: anytype, res: anytype, srv: anytype) !void {
|
||||||
|
const info = try srv.driveGet(req.args.path);
|
||||||
|
defer util.deepFree(srv.allocator, info);
|
||||||
|
|
||||||
|
switch (info) {
|
||||||
|
.dir => |dir| try res.template(.ok, srv, dir_tmpl, .{ .dir = dir }),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const cluster = struct {
|
const cluster = struct {
|
||||||
const overview = struct {
|
const overview = struct {
|
||||||
pub const path = "/cluster/overview";
|
pub const path = "/cluster/overview";
|
||||||
|
|
34
src/main/controllers/web/drive/directory.tmpl.html
Normal file
34
src/main/controllers/web/drive/directory.tmpl.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<div class="drive">
|
||||||
|
<table class="directory-listing">
|
||||||
|
{#for .dir.children.? |$child| =}
|
||||||
|
<tr>
|
||||||
|
{#switch $child case dir |$dir| =}
|
||||||
|
<td class="icon"></td>
|
||||||
|
<td class="icon"><i class="fa-solid fa-folder-closed"></i></td>
|
||||||
|
<td class="name"><a href="./{$dir.name.?}">{$dir.name.?}</a></td>
|
||||||
|
{#case file |$file|}
|
||||||
|
<td class="icon">
|
||||||
|
{= #if %user |$u|}
|
||||||
|
{#if $u.avatar_file_id == $file.meta.id =}
|
||||||
|
<i class="fa-solid fa-user"></i>
|
||||||
|
{= #elif $u.header_file_id == $file.meta.id =}
|
||||||
|
<i class="fa-solid fa-heading"></i>
|
||||||
|
{= /if =}
|
||||||
|
{= /if =}
|
||||||
|
</td>
|
||||||
|
<td class="icon">
|
||||||
|
{= #if $file.meta.sensitive =}
|
||||||
|
<i class="fa-solid fa-eye-slash"></i>
|
||||||
|
{= #else =}
|
||||||
|
<i class="fa-solid fa-file"></i>
|
||||||
|
{= /if =}
|
||||||
|
</td>
|
||||||
|
<td class="name"><a href="./{$file.name.?}">{$file.name.?}</a></td>
|
||||||
|
<td class="content-type">{#if $file.meta.content_type |$t|}{$t}{/if}</td>
|
||||||
|
<td class="size">{$file.meta.size}</td>
|
||||||
|
<td class="created-at">{$file.meta.created_at}</td>
|
||||||
|
{/switch =}
|
||||||
|
</tr>
|
||||||
|
{/for=}
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -8,11 +8,13 @@ pub fn main() !void {
|
||||||
@embedFile("./test.tmp.html"),
|
@embedFile("./test.tmp.html"),
|
||||||
.{
|
.{
|
||||||
.community = .{ .name = "<abcd>" },
|
.community = .{ .name = "<abcd>" },
|
||||||
.foo = [_][]const u8{ "5", "4", "3", "2", "1" },
|
.foo = &[_][]const u8{ "5", "4", "3", "2", "1" },
|
||||||
.baz = [_][]const []const u8{
|
.baz = &[_][]const []const u8{
|
||||||
&.{ "5", "4", "3", "2", "1" },
|
&.{ "5", "4", "3", "2", "1" },
|
||||||
&.{ "5", "4", "3", "2", "1" },
|
&.{ "5", "4", "3", "2", "1" },
|
||||||
},
|
},
|
||||||
|
.start = 1,
|
||||||
|
.end = 3,
|
||||||
.bar = .{ .x = "x" },
|
.bar = .{ .x = "x" },
|
||||||
.qux = false,
|
.qux = false,
|
||||||
.quxx = true,
|
.quxx = true,
|
||||||
|
@ -236,7 +238,7 @@ fn deref(arg: anytype, comptime names: []const DerefDecl) DerefError!Deref(@Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExpressionError = DerefError;
|
const ExpressionError = error{IndexOutOfBounds} || DerefError;
|
||||||
|
|
||||||
fn EvaluateExpression(
|
fn EvaluateExpression(
|
||||||
comptime expression: Expression,
|
comptime expression: Expression,
|
||||||
|
@ -251,6 +253,7 @@ fn EvaluateExpression(
|
||||||
.equals => bool,
|
.equals => bool,
|
||||||
.builtin => |call| switch (call.*) {
|
.builtin => |call| switch (call.*) {
|
||||||
.isTag => bool,
|
.isTag => bool,
|
||||||
|
.slice => |sl| []const std.meta.Elem(EvaluateExpression(sl.iterable, Args, Captures, Context)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -273,13 +276,22 @@ fn evaluateExpression(
|
||||||
return std.mem.eql(u8, lhs, rhs);
|
return std.mem.eql(u8, lhs, rhs);
|
||||||
} else if (comptime std.meta.trait.isContainer(T) and @hasDecl(T, "eql")) {
|
} else if (comptime std.meta.trait.isContainer(T) and @hasDecl(T, "eql")) {
|
||||||
return T.eql(lhs, rhs);
|
return T.eql(lhs, rhs);
|
||||||
} else return lhs == rhs;
|
} else return std.meta.eql(lhs, rhs);
|
||||||
},
|
},
|
||||||
.builtin => |call| switch (call.*) {
|
.builtin => |call| switch (call.*) {
|
||||||
.isTag => |hdr| {
|
.isTag => |hdr| {
|
||||||
const val = try evaluateExpression(hdr.expression, args, captures, context);
|
const val = try evaluateExpression(hdr.expression, args, captures, context);
|
||||||
return std.meta.isTag(val, hdr.tag);
|
return std.meta.isTag(val, hdr.tag);
|
||||||
},
|
},
|
||||||
|
.slice => |sl| {
|
||||||
|
const iterable = try evaluateExpression(sl.iterable, args, captures, context);
|
||||||
|
const start = try evaluateExpression(sl.start, args, captures, context);
|
||||||
|
const end = try evaluateExpression(sl.end, args, captures, context);
|
||||||
|
|
||||||
|
if (comptime std.meta.trait.is(.Array)(@TypeOf(iterable))) @compileError("Cannot slice an array, pass a slice or pointer to array instead");
|
||||||
|
if (start > iterable.len or end > iterable.len) return error.IndexOutOfBounds;
|
||||||
|
return iterable[start..end];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -304,7 +316,11 @@ fn AddCapture(comptime Root: type, comptime name: []const u8, comptime Val: type
|
||||||
|
|
||||||
fn addCapture(root: anytype, comptime name: []const u8, val: anytype) AddCapture(@TypeOf(root), name, @TypeOf(val)) {
|
fn addCapture(root: anytype, comptime name: []const u8, val: anytype) AddCapture(@TypeOf(root), name, @TypeOf(val)) {
|
||||||
if (comptime std.mem.eql(u8, name, "_")) return root;
|
if (comptime std.mem.eql(u8, name, "_")) return root;
|
||||||
var result = std.mem.zeroInit(AddCapture(@TypeOf(root), name, @TypeOf(val)), root);
|
|
||||||
|
var result: AddCapture(@TypeOf(root), name, @TypeOf(val)) = undefined;
|
||||||
|
inline for (std.meta.fields(@TypeOf(root))) |f| {
|
||||||
|
@field(result, f.name) = @field(root, f.name);
|
||||||
|
}
|
||||||
@field(result, name) = val;
|
@field(result, name) = val;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -631,7 +647,7 @@ fn parseBuiltin(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter,
|
||||||
const call = switch (builtin) {
|
const call = switch (builtin) {
|
||||||
.isTag => blk: {
|
.isTag => blk: {
|
||||||
const expr = parseExpression(iter);
|
const expr = parseExpression(iter);
|
||||||
iter = expr.new_iter;
|
iter = skipWhitespace(expr.new_iter);
|
||||||
expectToken(iter.next(), .comma);
|
expectToken(iter.next(), .comma);
|
||||||
iter = skipWhitespace(iter);
|
iter = skipWhitespace(iter);
|
||||||
const tag = iter.next();
|
const tag = iter.next();
|
||||||
|
@ -643,6 +659,26 @@ fn parseBuiltin(comptime tokens: ControlTokenIter) ParseResult(ControlTokenIter,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
.slice => blk: {
|
||||||
|
const expr = parseExpression(iter);
|
||||||
|
iter = skipWhitespace(expr.new_iter);
|
||||||
|
expectToken(iter.next(), .comma);
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
const start = parseExpression(iter);
|
||||||
|
iter = skipWhitespace(start.new_iter);
|
||||||
|
expectToken(iter.next(), .comma);
|
||||||
|
iter = skipWhitespace(iter);
|
||||||
|
const end = parseExpression(iter);
|
||||||
|
iter = skipWhitespace(end.new_iter);
|
||||||
|
|
||||||
|
break :blk .{
|
||||||
|
.slice = .{
|
||||||
|
.iterable = expr.item,
|
||||||
|
.start = start.item,
|
||||||
|
.end = end.item,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
iter = skipWhitespace(iter);
|
iter = skipWhitespace(iter);
|
||||||
expectToken(iter.next(), .close_paren);
|
expectToken(iter.next(), .close_paren);
|
||||||
|
@ -1161,6 +1197,7 @@ const EndKeyword = enum {
|
||||||
|
|
||||||
const Builtin = enum {
|
const Builtin = enum {
|
||||||
isTag,
|
isTag,
|
||||||
|
slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BuiltinCall = union(Builtin) {
|
const BuiltinCall = union(Builtin) {
|
||||||
|
@ -1168,6 +1205,11 @@ const BuiltinCall = union(Builtin) {
|
||||||
tag: []const u8,
|
tag: []const u8,
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
},
|
},
|
||||||
|
slice: struct {
|
||||||
|
iterable: Expression,
|
||||||
|
start: Expression,
|
||||||
|
end: Expression,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlToken = union(enum) {
|
const ControlToken = union(enum) {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
neither
|
neither
|
||||||
{=/if}
|
{=/if}
|
||||||
|
|
||||||
|
sliced: {#for @slice(.foo, .start, .end) |$s|}{$s}, {/for}
|
||||||
|
|
||||||
format: {#format "s" .x}
|
format: {#format "s" .x}
|
||||||
|
|
||||||
{#switch .snap case foo =}
|
{#switch .snap case foo =}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue