Create signup page
This commit is contained in:
parent
d191391aff
commit
783e3699e8
3 changed files with 157 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const util = @import("util");
|
const util = @import("util");
|
||||||
|
const http = @import("http");
|
||||||
const controllers = @import("../controllers.zig");
|
const controllers = @import("../controllers.zig");
|
||||||
|
|
||||||
pub const routes = .{
|
pub const routes = .{
|
||||||
|
@ -10,6 +11,9 @@ pub const routes = .{
|
||||||
controllers.apiEndpoint(cluster.overview),
|
controllers.apiEndpoint(cluster.overview),
|
||||||
controllers.apiEndpoint(media),
|
controllers.apiEndpoint(media),
|
||||||
controllers.apiEndpoint(static),
|
controllers.apiEndpoint(static),
|
||||||
|
controllers.apiEndpoint(signup.page),
|
||||||
|
controllers.apiEndpoint(signup.with_invite),
|
||||||
|
controllers.apiEndpoint(signup.submit),
|
||||||
};
|
};
|
||||||
|
|
||||||
const static = struct {
|
const static = struct {
|
||||||
|
@ -94,6 +98,101 @@ const login = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const signup = struct {
|
||||||
|
const tmpl = @embedFile("./web/signup.tmpl.html");
|
||||||
|
|
||||||
|
fn servePage(
|
||||||
|
invite_code: ?[]const u8,
|
||||||
|
error_msg: ?[]const u8,
|
||||||
|
status: http.Status,
|
||||||
|
res: anytype,
|
||||||
|
srv: anytype,
|
||||||
|
) !void {
|
||||||
|
const invite = if (invite_code) |code| srv.validateInvite(code) catch |err| switch (err) {
|
||||||
|
error.InvalidInvite => return servePage(null, "Invite is not valid", .bad_request, res, srv),
|
||||||
|
else => |e| return e,
|
||||||
|
} else null;
|
||||||
|
defer util.deepFree(srv.allocator, invite);
|
||||||
|
|
||||||
|
try res.template(status, srv, tmpl, .{
|
||||||
|
.error_msg = error_msg,
|
||||||
|
.invite = invite,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = struct {
|
||||||
|
pub const path = "/signup";
|
||||||
|
pub const method = .GET;
|
||||||
|
|
||||||
|
pub fn handler(_: anytype, res: anytype, srv: anytype) !void {
|
||||||
|
try servePage(null, null, .ok, res, srv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const with_invite = struct {
|
||||||
|
pub const path = "/invite/:code";
|
||||||
|
pub const method = .GET;
|
||||||
|
|
||||||
|
pub const Args = struct {
|
||||||
|
code: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn handler(req: anytype, res: anytype, srv: anytype) !void {
|
||||||
|
std.log.debug("{s}", .{req.args.code});
|
||||||
|
try servePage(req.args.code, null, .ok, res, srv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = struct {
|
||||||
|
pub const path = "/signup";
|
||||||
|
pub const method = .POST;
|
||||||
|
|
||||||
|
pub const Body = struct {
|
||||||
|
username: []const u8,
|
||||||
|
password: []const u8,
|
||||||
|
email: ?[]const u8 = null,
|
||||||
|
invite_code: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn handler(req: anytype, res: anytype, srv: anytype) !void {
|
||||||
|
const user = srv.register(req.body.username, req.body.password, .{
|
||||||
|
.email = req.body.email,
|
||||||
|
.invite_code = req.body.invite_code,
|
||||||
|
}) catch |err| {
|
||||||
|
var status: http.Status = .bad_request;
|
||||||
|
const err_msg = switch (err) {
|
||||||
|
error.UsernameEmpty => "Username cannot be empty",
|
||||||
|
error.UsernameContainsInvalidChar => "Username must be composed of alphanumeric characters and underscore",
|
||||||
|
error.UsernameTooLong => "Username too long",
|
||||||
|
error.PasswordTooShort => "Password too short, must be at least 12 chars",
|
||||||
|
|
||||||
|
error.UsernameTaken => blk: {
|
||||||
|
status = .unprocessable_entity;
|
||||||
|
break :blk "Username is already registered";
|
||||||
|
},
|
||||||
|
else => blk: {
|
||||||
|
status = .internal_server_error;
|
||||||
|
break :blk "an internal error occurred";
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return servePage(req.body.invite_code, err_msg, status, res, srv);
|
||||||
|
};
|
||||||
|
defer util.deepFree(srv.allocator, user);
|
||||||
|
|
||||||
|
const token = try srv.login(req.body.username, req.body.password);
|
||||||
|
|
||||||
|
try res.headers.put("Location", index.path);
|
||||||
|
var buf: [64]u8 = undefined;
|
||||||
|
const cookie_name = try std.fmt.bufPrint(&buf, "token.{s}", .{req.body.username});
|
||||||
|
try res.headers.setCookie(cookie_name, token.token, .{});
|
||||||
|
try res.headers.setCookie("active_account", req.body.username, .{ .HttpOnly = false });
|
||||||
|
|
||||||
|
try res.status(.see_other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const global_timeline = struct {
|
const global_timeline = struct {
|
||||||
pub const path = "/timelines/global";
|
pub const path = "/timelines/global";
|
||||||
pub const method = .GET;
|
pub const method = .GET;
|
||||||
|
|
54
src/main/controllers/web/signup.tmpl.html
Normal file
54
src/main/controllers/web/signup.tmpl.html
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<header>
|
||||||
|
<h2>{ %community.name }</h2>
|
||||||
|
</header>
|
||||||
|
<form action="/signup" method="post">
|
||||||
|
<h3>Sign Up</h3>
|
||||||
|
{#if .error_msg |$msg| =}
|
||||||
|
<div class="error">Error: {$msg}</div>
|
||||||
|
{= /if}
|
||||||
|
{#if .invite |$invite| =}
|
||||||
|
<div>
|
||||||
|
<div>You are about to accept an invite from:</div>
|
||||||
|
<div class="user-mini">
|
||||||
|
<div>
|
||||||
|
{=#if $invite.creator.display_name |$name|=}
|
||||||
|
{$name}
|
||||||
|
{= #else =}
|
||||||
|
{$invite.creator.username}
|
||||||
|
{= /if =}
|
||||||
|
</div>
|
||||||
|
<div>@{$invite.creator.username}@{$invite.creator.host}</div>
|
||||||
|
</div>
|
||||||
|
{#if @isTag($invite.kind, community_owner) =}
|
||||||
|
<div>This act will make your new account the owner of { %community.name }</div>
|
||||||
|
{/if =}
|
||||||
|
</div>
|
||||||
|
{=/if}
|
||||||
|
<label>
|
||||||
|
<div>Username <span class="required">(required)</span></div>
|
||||||
|
<div class="textinput username">
|
||||||
|
<span class="prefix">@</span>
|
||||||
|
<input type="text" name="username" placeholder="xion" />
|
||||||
|
<span class="suffix">@{ %community.host }</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-helpinfo">Up to 32 characters, allows alphanumeric characters and underscores.</div>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<div>Password <span class="required">(required)</span></div>
|
||||||
|
<div class="textinput">
|
||||||
|
<span class="prefix"><i class="fa-solid fa-key fa-fw"></i></span>
|
||||||
|
<input type="password" name="password" placeholder="hunter2" />
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<div>Email</div>
|
||||||
|
<div class="textinput">
|
||||||
|
<span class="prefix"><i class="fa-solid fa-envelope fa-fw"></i></span>
|
||||||
|
<input type="email" name="email" placeholder="me@exmaple.com" />
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
{#if .invite |$invite| =}
|
||||||
|
<input style="display: none" type="text" name="invite_code" value="{$invite.code}" />
|
||||||
|
{/if =}
|
||||||
|
<button type="submit">Sign up</button>
|
||||||
|
</form>
|
|
@ -92,6 +92,10 @@ form[action*=login] .textinput span.suffix {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-helpinfo {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
button, a.button {
|
button, a.button {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
Loading…
Reference in a new issue