Add reference

This commit is contained in:
2025-07-24 14:43:16 -05:00
parent 92f21917b1
commit 6a8c8f38ff
44 changed files with 2082 additions and 1287 deletions

View File

@@ -81,7 +81,7 @@ func Pages.Account(ctx)
<if(TypeOf(user) == "Dictionary")>
<true>
<h1>{user.accountName}</h1>
<a href={$"./account_packages?name={Net.Http.UrlEncode(name)}"}>Packages</a>|<a href={$"./reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>Reserved Prefixes</a>
<a href={$"./packages?account={Net.Http.UrlEncode(name)}"}>Packages</a>|<a href={$"./reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>Reserved Prefixes</a>
<if(active.active)>
<true>
@@ -94,6 +94,11 @@ func Pages.Account(ctx)
</if>
<input class="btn btn-primary" type="submit" value="Save">
<input type="hidden" name="csrf" value={DB.CreateCSRF(ctx)}>
<if(active.admin)>
<true>
<a class="btn btn-secondary" href="./admin">Admin</a>
</true>
</if>
<a class="btn btn-danger" href="./logout">Logout</a>
</form>

View File

@@ -0,0 +1,235 @@
func Pages.Admin(ctx)
{
var active = DB.LoginButton(ctx,false,"");
var csrf="";
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
active
];
if(!active.admin) ctx.StatusCode = 401;
else csrf = DB.CreateCSRF(ctx);
if(ctx.Method == "POST")
{
var csrf2 = ctx.QueryParams.TryGetFirst("csrf");
var action2 = ctx.QueryParams.TryGetFirst("action");
if(TypeOf(action2) != "String") {ctx.StatusCode = 501; return Shell("Unsupported operation", pages,<h1>Unsupported Operation</h1>);}
if(!active.admin) {ctx.StatusCode = 401; return Shell("Not an admin", pages,<h1>Not an admin</h1>);}
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
if(DB.VerifyCSRF(active.session, csrf2))
{
//we have authorization
switch(action2)
{
case "server_config":
{
var prefix = ctx.QueryParams.TryGetFirst("prefix");
var port = ctx.QueryParams.TryGetFirstInt("port");
var allowRegistering = ctx.QueryParams.GetFirstBoolean("allowRegistering");
if(TypeOf(prefix) == "String" && TypeOf(port) == "Long" && port >= 1 && port <= 65534)
{
DB.Config.Prefix = prefix;
DB.Config.Port = port;
DB.Config.AllowRegister = allowRegistering;
FS.WriteAllText(FS.Local, DB.working / "conf.json", Json.Encode(DB.Config,true));
}
else {
ctx.StatusCode = 400;
return Shell("Invalid input", pages,<h1>Invalid Input</h1>);
}
}
break;
case "smtp_config":
{
var host = ctx.QueryParams.TryGetFirst("host");
var port = ctx.QueryParams.TryGetFirstInt("port");
var tls = ctx.QueryParams.GetFirstBoolean("tls");
var username = ctx.QueryParams.TryGetFirst("username");
var password = ctx.QueryParams.TryGetFirst("password");
var email = ctx.QueryParams.TryGetFirst("email");
var fromname = ctx.QueryParams.TryGetFirst("fromname");
var domain = ctx.QueryParams.TryGetFirst("domain");
if(TypeOf(host) == "String" && TypeOf(username) == "String" && TypeOf(username) == "String" && TypeOf(password) == "String" && TypeOf(email) == "String" && TypeOf(fromname) == "String" && TypeOf(domain) == "String" && TypeOf(port) == "Long" && port >= 0 && port <= 65534)
{
if(TypeOf(DB.Config.MailConfig) != "Dictionary")
DB.Config.MailConfig={};
if(TypeOf(DB.Config.MailConfig.Server) != "Dictionary")
DB.Config.MailConfig.Server={};
if(TypeOf(DB.Config.MailConfig.Auth) != "Dictionary")
DB.Config.MailConfig.Auth={};
if(TypeOf(DB.Config.MailConfig.From) != "Dictionary")
DB.Config.MailConfig.From={};
DB.Config.MailConfig.Server.host = host;
DB.Config.MailConfig.Server.port = port == 0 ? undefined : port;
DB.Config.MailConfig.Server.tls = tls;
DB.Config.MailConfig.Auth.username = username;
if(password.Length != 0)
DB.Config.MailConfig.Auth.password = password;
DB.Config.MailConfig.Domain = domain;
DB.Config.MailConfig.From.email = email;
DB.Config.MailConfig.From.name = fromname;
FS.WriteAllText(FS.Local, DB.working / "conf.json", Json.Encode(DB.Config,true));
}
else {
ctx.StatusCode = 400;
return Shell("Invalid input", pages,<h1>Invalid Input</h1>);
}
}
break;
default:
ctx.StatusCode=501;
return Shell($"Unsupported operation {action2}", pages,<h1>Unsupported operation {action2}</h1>);
}
}
else
{
ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);
}
}
var html = <div class="container"><if(active.admin)>
<true>
/*<if(ctx.Method == "POST")>
<true>
<ul>
<each(var item : ctx.QueryParams.ToList())>
<li><b>{item.Key}</b>: {item.Value}</li>
</each>
</ul>
</true>
</if>*/
<fieldset class="border p-2">
<legend class="float-none w-auto">Cache Package</legend>
<form action="./admin" method="post">
<input type="hidden" name="action" value="cache_package">
<input type="hidden" name="csrf" value={csrf}>
<input class="form-control" name="url" type="url" placeholder="Package Url" aria-label="Package Url">
<input type="submit" class="btn btn-primary" value="Add Package">
</form>
</fieldset>
<fieldset class="border p-2">
<legend class="float-none w-auto">Accounts</legend>
<form action="./admin_account" method="get">
<div class="mb-3">
<label for="account" class="form-label">Account Name</label>
<input class="form-control" name="account" id="account" type="text" placeholder="Account Name" aria-label="Account Name">
</div>
<input type="submit" class="btn btn-primary" value="Manage Account">
</form>
<a class="btn btn-primary" href="/admin_accounts">View Accounts</a>
<a class="btn btn-primary" href="/admin_register">Register Account</a>
</fieldset>
<fieldset class="border p-2">
<legend class="float-none w-auto">Server Configuration</legend>
<form action="./admin" method="post">
<input type="hidden" name="csrf" value={csrf}>
<input type="hidden" name="action" value="server_config">
<div class="mb-3">
<label for="prefix" class="form-label">Prefix Url (url prefix in emails)</label>
<input class="form-control" name="prefix" id="prefix" type="text" placeholder="Prefix Url" aria-label="Prefix Url (url prefix in emails)" value={TypeOf(DB.Config.Prefix) == "String" ? DB.Config.Prefix : ""}>
</div>
<div class="mb-3">
<label for="port" class="form-label">Server Listening Port</label>
<input class="form-control" name="port" id="port" type="number" placeholder="Port" aria-label="Server Listening Port" value={TypeOf(DB.Config.Port) == "Long" ? DB.Config.Port : 4206}>
</div>
<div class="form-check">
<if(DB.Config.AllowRegister)>
<true>
<input class="form-check-input" type="checkbox" name="allowRegistering" id="allowRegistering" checked>
</true>
<false>
<input class="form-check-input" type="checkbox" name="allowRegistering" id="allowRegistering">
</false>
</if>
//DB.Config.Prefix
<label class="form-check-label" for="allowRegistering">
Allow Registering
</label>
</div>
<input type="submit" class="btn btn-primary" value="Save">
</form>
</fieldset>
<fieldset class="border p-2">
<legend class="float-none w-auto">SMTP Configuration</legend>
<form action="./admin" method="post">
<input type="hidden" name="csrf" value={csrf}>
<input type="hidden" name="action" value="smtp_config">
<div class="mb-3">
<label for="host" class="form-label">Host</label>
<input class="form-control" name="host" id="host" type="text" placeholder="Host" aria-label="Host" value={TypeOf(DB.Config.MailConfig.Server.host) == "String" ? DB.Config.MailConfig.Server.host : ""}>
</div>
<div class="mb-3">
<label for="smtpport" class="form-label">Port (0 for default)</label>
<input class="form-control" name="port" id="smtpport" type="number" placeholder="Port" aria-label="Port" value={TypeOf(DB.Config.MailConfig.Server.port) == "Long" ? DB.Config.MailConfig.Server.port : 0}>
</div>
<div class="form-check">
<if(TypeOf(DB.Config.MailConfig.Server.tls) != "Boolean" || DB.Config.MailConfig.Server.tls)>
<true>
<input class="form-check-input" type="checkbox" name="tls" id="tls" checked>
</true>
<false>
<input class="form-check-input" type="checkbox" name="tls" id="tls">
</false>
</if>
//DB.Config.Prefix
<label class="form-check-label" for="tls">
Enable TLS (you should definitely use this, unless you can{'\''}t in your setup)
</label>
</div>
<div class="mb-3">
<label for="username" class="form-label">Username (without domain)</label>
<input class="form-control" name="username" id="username" type="text" placeholder="Username" aria-label="Username" value={TypeOf(DB.Config.MailConfig.Auth.username) == "String" ? DB.Config.MailConfig.Auth.username : ""}>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password (WARNING this password is stored plaintext)</label>
<input class="form-control" name="password" id="password" type="password" placeholder="Password" aria-label="Password">
</div>
<div class="mb-3">
<label for="email" class="form-label">Email (the email you want in From header)</label>
<input class="form-control" name="email" id="email" type="email" placeholder="Email" aria-label="Email" value={TypeOf(DB.Config.MailConfig.From.email) == "String" ? DB.Config.MailConfig.From.email : ""}>
</div>
<div class="mb-3">
<label for="fromname" class="form-label">Name (the name you want in From header)</label>
<input class="form-control" name="fromname" id="fromname" type="text" placeholder="Name" aria-label="Name" value={TypeOf(DB.Config.MailConfig.From.name) == "String" ? DB.Config.MailConfig.From.name : ""}>
</div>
<div class="mb-3">
<label for="domain" class="form-label">Email Domain (if email is user@example.com it will be example.com)</label>
<input class="form-control" name="domain" id="domain" type="text" placeholder="Domain" aria-label="Domain" value={TypeOf(DB.Config.MailConfig.Domain) == "String" ? DB.Config.MailConfig.Domain : ""}>
</div>
<input type="submit" class="btn btn-primary" value="Save">
</form>
</fieldset>
</true>
<false>
<h1>You are not authorized in the admin panel</h1>
</false>
</if></div>;
return Shell("Admin", pages,html);
}

View File

@@ -0,0 +1,96 @@
func Pages.AdminRegister(ctx)
{
var active = DB.LoginButton(ctx,false,"");
var csrf="";
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
active
];
if(!active.admin) ctx.StatusCode = 401;
else csrf = DB.CreateCSRF(ctx);
if(ctx.Method == "POST")
{
var csrf2 = ctx.QueryParams.TryGetFirst("csrf");
if(!active.admin) {ctx.StatusCode = 401; return Shell("Not an admin", pages,<h1>Not an admin</h1>);}
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
if(DB.VerifyCSRF(active.session, csrf2))
{
var email = ctx.QueryParams.TryGetFirst("email");
var displayName = ctx.QueryParams.TryGetFirst("displayName");
var password = ctx.QueryParams.TryGetFirst("password");
var confirm = ctx.QueryParams.TryGetFirst("confirm");
var flags = ctx.QueryParams.GetFirstBoolean("verified") ? DB.FLAG_VERIFIED : DB.FLAG_FLAG_VERIFY;
flags |= (ctx.QueryParams.GetFirstBoolean("admin") ? DB.FLAG_ADMIN : 0);
if(TypeOf(email) != "String" || TypeOf(displayName) != "String" || TypeOf(password) != "String" || TypeOf(confirm) != "String")
return Shell("Invalid input",pages,<h1>Invalid input</h1>);
if(password != confirm)
return Shell("Passwords do not match",pages,<h1>Passwords do not match</h1>);
var res = DB.CreateUserFromAdmin(email, displayName, password, flags);
if(!res.Success)
{
return Shell(res.Reason, pages, <h1>{res.Reason}</h1>);
}
}
else
{
ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);
}
}
var html = <div class="container">
<if(active.admin)>
<true>
<form method="POST" action="./admin_register">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input class="form-control" name="email" id="email" type="email" placeholder="Email" aria-label="Email">
</div>
<div class="mb-3">
<label for="displayName" class="form-label">Display Name</label>
<input class="form-control" name="displayName" id="displayName" type="text" placeholder="Display Name" aria-label="Display Name">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input class="form-control" name="password" id="password" type="password" placeholder="Password" aria-label="Password">
</div>
<div class="mb-3">
<label for="confirm" class="form-label">Confirm Password</label>
<input class="form-control" name="confirm" id="confirm" type="password" placeholder="Confirm Password" aria-label="Confirm Password">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="admin" id="admin">
<label class="form-check-label" for="admin">Administrator</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="verified" id="verified" checked>
<label class="form-check-label" for="verified">Verified</label>
</div>
<input type="hidden" name="csrf" value={csrf}>
<input type="submit" class="btn btn-primary" value="Register">
</form>
</true>
<false>
<h1>You are not authorized in the admin panel</h1>
</false>
</if>
</div>;
return Shell("Admin Register", pages,html);
}

View File

@@ -0,0 +1,20 @@
func Pages.ResetPasswordFailed(reason)
{
var active = DB.LoginButton(ctx,false,"");
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
active
];
return Shell($"Reset Password Failed {reason}", pages,<h1>Reset Password Failed {reason}</h1>);
}

View File

@@ -0,0 +1,27 @@
func Pages.ForgotPassword(ctx)
{
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
DB.LoginButton(ctx,true)
];
var html = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
<form action={"./forgot_password"} method={"post"} enctype={"application/x-www-form-urlencoded"}>
<div class={"mb-3"}>
<label for={"email"} class={"form-label"}>Email address</label>
<input type={"email"} class={"form-control"} id={"email"} name={"email"} aria-describedby={"emailHelp"}>
<div id={"emailHelp"} class={"form-text"}>{"We'll"} never share your email with anyone else.</div>
</div>
<button type={"submit"} class={"btn btn-primary"}>Reset Password</button>
</form>
</div>;
return Shell("Forgot Password",pages,html);
}

View File

@@ -1,5 +1,6 @@
func Pages.Index(ctx)
{
var repo = TypeOf(DB.Config.RepoCommitPrefix) == "String" ? DB.Config.RepoCommitPrefix : "https://onedev.site.tesses.net/crosslang/crosslangextras/~files/";
var pages = [
{
@@ -14,6 +15,12 @@ func Pages.Index(ctx)
},
DB.LoginButton(ctx,false)
];
var commit = comptime {
return BuildTime.Git.Hash;
};
var html = <article><h1 class={"display-5"} style={"text-align: center;"}>Make crosslang development faster and more convenient with CPKG</h1><form action={"./packages"} method={"get"}><div class={"container text-center"}><div class={"row"}><div class={"col"}>
</div>
@@ -23,7 +30,8 @@ func Pages.Index(ctx)
<button class={"btn btn-outline-secondary"} type={"submit"} id={"button-search"}>Search</button>
</div>
<a href="./api">API</a>
<a href="./api">API</a><br>
<a href={$"{repo}{commit}"}>Commit {commit.Remove(8)}</a>
</div>
<div class={"col"}>

View File

@@ -25,6 +25,7 @@ func Pages.Login(ctx)
<input type={"password"} class={"form-control"} name={"password"} id={"password"}>
</div>
<a href={"./signup"}>Sign up</a>
<a href={"./forgot_password"}>Forgot Password</a>
<button type={"submit"} class={"btn btn-primary"}>Login</button>
</form>
</div>;

View File

@@ -0,0 +1,38 @@
func Pages.NewPassword(ctx)
{
var code = ctx.QueryParams.TryGetFirst("code");
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
DB.LoginButton(ctx,true)
];
var html = <div class={"container min-vh-100 d-flex justify-content-center align-items-center"}>
<form action={"./new_password"} method={"post"} enctype={"application/x-www-form-urlencoded"}>
<input type="hidden" name="code" value={code}>
<div class={"mb-3"}>
<label for={"password"} class={"form-label"}>Password</label>
<input type={"password"} class={"form-control"} name={"password"} id={"password"}>
</div>
<div class={"mb-3"}>
<label for={"confirm"} class={"form-label"}>Confirm Password</label>
<input type={"password"} class={"form-control"} name={"confirm"} id={"confirm"}>
</div>
<button type={"submit"} class={"btn btn-primary"}>Reset Password</button>
</form>
</div>;
return Shell("Reset your password", pages, html);
}

View File

@@ -113,6 +113,7 @@ func Pages.Package(ctx,name)
<li>Latest version: {package[0].version}</li>
<li>Type: {package[0].type}</li>
<li><a href={package[0].download}>Download</a></li>
<li><a href={package[0].docs}>View Docs</a></li>
</ul>
<if(package.Length > 1)>
<true>
@@ -153,6 +154,8 @@ func Pages.Package(ctx,name)
<li>Uploaded: {new DateTime(package[i].uploadTime).ToString("%Y/%m/%d %H:%M:%S UTC")}</li>
<li>Type: {package[i].type}</li>
<li><a href={package[i].download}>Download</a></li>
<li><a href={package[i].docs}>View Docs</a></li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,52 @@
func Pages.PackageDocs(ctx)
{
var name = ctx.QueryParams.TryGetFirst("name");
if(TypeOf(name) != "String") name = "";
var version = ctx.QueryParams.TryGetFirst("version");
if(TypeOf(version) != "String") version = "";
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
DB.LoginButton(ctx,false)
];
var res = DB.GetPackageFileData(name,version);
var classes = res.Classes;
var functions = res.Functions;
var chunks = res.Chunks;
Console.WriteLine(classes);
each(var item : functions)
{
item.Arguments = chunks[item.ChunkId].Arguments;
}
var html = <div class="container">
<h1>Functions</h1>
<ul>
<each(var fn : functions)>
<li><raw(Components.DocFunction(fn))></li>
</each>
</ul>
<h1>Classes</h1>
<ul>
<each(var cls : classes)>
<li><raw(Components.DocClass(cls))></li>
</each>
</ul>
</div>;
return Shell("Documentation", pages, html);
}

View File

@@ -0,0 +1,97 @@
func Pages.ReservedPrefixes(ctx)
{
var name = ctx.QueryParams.TryGetFirst("name");
if(TypeOf(name) != "String") name = "";
var active = DB.LoginButton(ctx,false,name);
var csrf="";
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
active
];
var user = DB.GetAccountInfo(name);
if(!active.active) ctx.StatusCode = 401;
else csrf = DB.CreateCSRF(ctx);
if(ctx.Method == "POST")
{
var csrf2 = ctx.QueryParams.TryGetFirst("csrf");
var action2 = ctx.QueryParams.TryGetFirst("action");
if(TypeOf(action2) != "String") {ctx.StatusCode = 501; return Shell("Unsupported operation", pages,<h1>Unsupported Operation</h1>);}
if(!active.active) {ctx.StatusCode = 401; return Shell("Not an", pages,<h1>Not the correct user</h1>);}
if(TypeOf(csrf2) != "String") {ctx.StatusCode = 401; return Shell("Invalid CSRF", pages,<h1>Invalid CSRF</h1>);}
if(DB.VerifyCSRF(active.session, csrf2))
{
switch(action2)
{
case "add":
{
var prefix = ctx.QueryParams.TryGetFirst("prefix");
var res = DB.CreatePrefix(name, prefix);
if(!res.Success)
{
ctx.StatusCode=403;
return Shell(res.Reason, pages, <h1>{res.Reason}</h1>);
}
}
break;
case "remove":
var prefix = ctx.QueryParams.TryGetFirst("prefix");
var res = DB.RemovePrefix(name, prefix);
if(!res.Success)
{
ctx.StatusCode=403;
return Shell(res.Reason, pages, <h1>{res.Reason}</h1>);
}
break;
}
}
}
var items = DB.QueryReservedPrefixes(name);
var html = <div class="container">
<if(active.active)>
<true>
<form method="POST" action={$"/reserved_prefixes?name={Net.Http.UrlEncode(name)}"}>
<input type="hidden" name="action" value="add">
<input type="hidden" name="csrf" value={csrf}>
<div class="mb-3">
<label for="prefix" class="form-label">Prefix</label>
<input type="text" class="form-control" id="prefix" name="prefix" placeholder="Prefix">
</div>
<input type="submit" class="btn btn-primary" value="Add">
</form>
</true>
</if>
<if(items.Success)>
<true>
<ul>
<each(var item : items.Items)>
<li>
<raw(Components.Prefix(item,active.active,name,csrf))>
</li>
</each>
</ul>
</true>
<false>
<h1>Failed to get prefixes {items.Reason}</h1>
</false>
</if>
</div>;
return Shell("Reserved Prefixes", pages, html);
}

View File

@@ -1,8 +1,23 @@
func Pages.Packages(ctx)
{
var q = ctx.QueryParams.TryGetFirst("q");
var account = ctx.QueryParams.TryGetFirst("account");
var page = ParseLong(ctx.QueryParams.TryGetFirst("page"));
q = TypeOf(q) != "String" ? "" : q;
page = TypeOf(page) != "Long" ? 1 : page;
var cur = (page - 1) % 3;
var firstPage = (page-1) - cur;
var path = $"./packages?q={Net.Http.UrlEncode(q)}";
if(TypeOf(account) == "String")
{
path += $"&account={Net.Http.UrlEncode(account)}";
}
if(TypeOf(q) != "String") q = "";
var pages = [
{
@@ -25,6 +40,11 @@ func Pages.Packages(ctx)
<input type={"search"} name={"q"} class={"form-control"} placeholder={"Search for packages..."} aria-label={"Search for packages..."} aria-describedby={"button-search"} value={q}>
<button class={"btn btn-outline-secondary"} type={"submit"} id={"button-search"}>Search</button>
<if(TypeOf(account) == "String")>
<true>
<input type="hidden" name="account" value={account}>
</true>
</if>
</div>
</div>
<div class={"col"}>
@@ -36,9 +56,33 @@ func Pages.Packages(ctx)
</article>
+
<ul>
<each(var item : DB.QueryPackages(q,(page-1)*20,20))>
<each(var item : DB.QueryPackages(q,(page-1)*20,20,account))>
<raw(Components.Package(item.packageName,item.version,item.accountName,new DateTime(item.uploadTime).ToString("%Y/%m/%d"),item.description))>
</each>
</ul>;
</ul>
+
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
<if(page == 1)>
<true>
<li class="page-item disabled">
<a class="page-link">Previous</a>
</li>
</true>
<false>
<li class="page-item">
<a class="page-link" href={$"{path}&page={page-1}"}>Previous</a>
</li>
</false>
</if>
<for(var i = 1; i <= 3; i++)>
<li class={i == cur + 1 ? "page-item active" : "page-item"}><a class="page-link" href={$"{path}&page={i+firstPage}"}>{i+firstPage}</a></li>
</for>
<li class="page-item">
<a class="page-link" href={$"{path}&page={page+1}"}>Next</a>
</li>
</ul>
</nav>;
return Shell("Packages",pages,html);
}

View File

@@ -0,0 +1,20 @@
func Pages.VerificationFailed(reason)
{
var active = DB.LoginButton(ctx,false,"");
var pages = [
{
active = false,
route = "/packages",
text = "Packages"
},
{
active = false,
route = "/upload",
text = "Upload"
},
active
];
return Shell($"Verification Failed {reason}", pages,<h1>Verification Failed {reason}</h1>);
}