website with slop placeholders
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
#load "../config/settings.uce"
|
||||
#load "user.class.h"
|
||||
|
||||
String app_fs_root(Request& context)
|
||||
{
|
||||
String root = context.var["app"]["fs_root"].to_string();
|
||||
if(root == "")
|
||||
root = cwd_get();
|
||||
return(root);
|
||||
}
|
||||
|
||||
String app_script_url(Request& context)
|
||||
{
|
||||
String url = context.var["app"]["script_url"].to_string();
|
||||
if(url == "")
|
||||
url = first(context.params["DOCUMENT_URI"], context.params["SCRIPT_NAME"]);
|
||||
return(url);
|
||||
}
|
||||
|
||||
String app_base_url(Request& context)
|
||||
{
|
||||
String base = context.var["app"]["base_url"].to_string();
|
||||
if(base == "")
|
||||
{
|
||||
base = dirname(app_script_url(context));
|
||||
if(base == "")
|
||||
base = "/";
|
||||
if(base[base.length() - 1] != '/')
|
||||
base.append(1, '/');
|
||||
}
|
||||
return(base);
|
||||
}
|
||||
|
||||
String app_fs_path(String relative, Request& context)
|
||||
{
|
||||
return(path_join(app_fs_root(context), relative));
|
||||
}
|
||||
|
||||
String app_link(String path, Request& context);
|
||||
String app_link(String path, StringMap params, Request& context);
|
||||
void app_init(Request& context);
|
||||
|
||||
String app_first_route_segment(Request& context)
|
||||
{
|
||||
String query = context.params["QUERY_STRING"];
|
||||
for(auto part : split(query, "&"))
|
||||
{
|
||||
if(part == "")
|
||||
continue;
|
||||
if(part.find("=") == String::npos)
|
||||
return(uri_decode(part));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
DTree app_make_route(Request& context)
|
||||
{
|
||||
DTree route;
|
||||
String path = app_first_route_segment(context);
|
||||
path = trim(path);
|
||||
while(path.length() > 0 && path[0] == '/')
|
||||
path = path.substr(1);
|
||||
while(path.length() > 0 && path[path.length() - 1] == '/')
|
||||
path = path.substr(0, path.length() - 1);
|
||||
if(path == "")
|
||||
path = "index";
|
||||
route["l_path"] = path;
|
||||
route["page"] = nibble(path, "/");
|
||||
if(route["page"].to_string() == "")
|
||||
route["page"] = "index";
|
||||
return(route);
|
||||
}
|
||||
|
||||
DTree app_resolve_view(Request& context, String base_dir = "views")
|
||||
{
|
||||
DTree result;
|
||||
DTree route = context.var["app"]["route"];
|
||||
String lpath = first(route["l_path"].to_string(), "index");
|
||||
String base = trim(base_dir);
|
||||
if(base != "" && base[base.length() - 1] == '/')
|
||||
base = base.substr(0, base.length() - 1);
|
||||
|
||||
String exact = base + "/" + lpath + ".uce";
|
||||
if(file_exists(exact))
|
||||
{
|
||||
result["file"] = exact;
|
||||
return(result);
|
||||
}
|
||||
|
||||
String dir_index = base + "/" + lpath + "/index.uce";
|
||||
if(file_exists(dir_index))
|
||||
{
|
||||
result["file"] = dir_index;
|
||||
return(result);
|
||||
}
|
||||
|
||||
auto parts = split(lpath, "/");
|
||||
if(parts.size() > 1)
|
||||
{
|
||||
String param = parts.back();
|
||||
parts.pop_back();
|
||||
String parent = join(parts, "/");
|
||||
String parent_index = base + "/" + parent + "/index.uce";
|
||||
if(file_exists(parent_index))
|
||||
{
|
||||
result["file"] = parent_index;
|
||||
result["param"] = param;
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void app_register_css(String path, Request& context)
|
||||
{
|
||||
context.var["app"]["assets"]["css"][path] = path;
|
||||
}
|
||||
|
||||
void app_register_js(String path, Request& context)
|
||||
{
|
||||
context.var["app"]["assets"]["js"][path] = path;
|
||||
}
|
||||
|
||||
String app_asset_url(String path, Request& context)
|
||||
{
|
||||
String url = app_base_url(context) + path;
|
||||
String fs_path = app_fs_path(path, context);
|
||||
if(file_exists(fs_path))
|
||||
url += "?v=" + std::to_string((u64)file_mtime(fs_path));
|
||||
return(url);
|
||||
}
|
||||
|
||||
void app_render_registered_css(Request& context)
|
||||
{
|
||||
context.var["app"]["assets"]["css"].each([&](DTree item, String key) {
|
||||
String path = item.to_string();
|
||||
if(path != "")
|
||||
{
|
||||
<><link rel="stylesheet" href="<?= app_asset_url(path, context) ?>" /></>
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void app_render_registered_js(Request& context)
|
||||
{
|
||||
context.var["app"]["assets"]["js"].each([&](DTree item, String key) {
|
||||
String path = item.to_string();
|
||||
if(path != "")
|
||||
{
|
||||
<><script src="<?= app_asset_url(path, context) ?>"></script></>
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String app_link(String path, Request& context)
|
||||
{
|
||||
StringMap params;
|
||||
return(app_link(path, params, context));
|
||||
}
|
||||
|
||||
String app_link(String path, StringMap params, Request& context)
|
||||
{
|
||||
String query = "";
|
||||
path = trim(path);
|
||||
if(path != "")
|
||||
query = uri_encode(path);
|
||||
String extra = encode_query(params);
|
||||
if(query != "" && extra != "")
|
||||
query += "&" + extra;
|
||||
else if(query == "")
|
||||
query = extra;
|
||||
String url = app_script_url(context);
|
||||
if(query != "")
|
||||
url += "?" + query;
|
||||
return(url);
|
||||
}
|
||||
|
||||
void app_redirect(String path, Request& context)
|
||||
{
|
||||
context.set_status(302, "Found");
|
||||
context.header["Location"] = app_link(path, context);
|
||||
}
|
||||
|
||||
void app_not_found(String message, Request& context)
|
||||
{
|
||||
context.set_status(404, "Not Found");
|
||||
context.var["app"]["error"] = message;
|
||||
context.var["app"]["page_title"] = "404 Not Found";
|
||||
}
|
||||
|
||||
String app_menu_href(String menu_key, DTree menu_item, Request& context)
|
||||
{
|
||||
if(menu_item["external"].to_string() != "")
|
||||
return("/" + menu_key);
|
||||
return(app_link(menu_key, context));
|
||||
}
|
||||
|
||||
String app_html_class(Request& context)
|
||||
{
|
||||
String classes = "no-js";
|
||||
if(context.cfg.get_by_path("theme/mode").to_string() == "dark")
|
||||
classes += " dark-theme";
|
||||
return(classes);
|
||||
}
|
||||
|
||||
String app_page_main_html(Request& context)
|
||||
{
|
||||
String main_html = context.call["main_html"].to_string();
|
||||
if(main_html == "")
|
||||
main_html = context.var["app"]["fragments"]["main"].to_string();
|
||||
return(main_html);
|
||||
}
|
||||
|
||||
bool app_bool_value(DTree value, bool fallback = false)
|
||||
{
|
||||
String raw = trim(value.to_string());
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
return(
|
||||
raw != "0" &&
|
||||
raw != "false" &&
|
||||
raw != "FALSE" &&
|
||||
raw != "False" &&
|
||||
raw != "(false)" &&
|
||||
raw != "no" &&
|
||||
raw != "NO" &&
|
||||
raw != "No"
|
||||
);
|
||||
}
|
||||
|
||||
bool app_request_embed_mode(Request& context)
|
||||
{
|
||||
return(app_bool_value(context.var["app"]["embed_mode"]));
|
||||
}
|
||||
|
||||
bool app_page_embed_mode(Request& context)
|
||||
{
|
||||
String embed_value = context.call["embed_mode"].to_string();
|
||||
if(embed_value != "")
|
||||
return(app_bool_value(context.call["embed_mode"]));
|
||||
return(app_request_embed_mode(context));
|
||||
}
|
||||
|
||||
String app_theme_page_component(Request& context)
|
||||
{
|
||||
String page_type = first(context.var["app"]["page_type"].to_string(), "html");
|
||||
if(page_type == "blank")
|
||||
return("themes/common/page.blank.uce");
|
||||
if(page_type == "json")
|
||||
return("themes/common/page.json.uce");
|
||||
|
||||
String theme_path = context.cfg.get_by_path("theme/path").to_string();
|
||||
if(theme_path == "")
|
||||
return("");
|
||||
if(theme_path[theme_path.length() - 1] != '/')
|
||||
theme_path.append(1, '/');
|
||||
return(theme_path + "page.html.uce");
|
||||
}
|
||||
|
||||
void app_render_page(Request& context)
|
||||
{
|
||||
if(context.flags.status >= 300 && context.flags.status < 400)
|
||||
return;
|
||||
|
||||
DTree page_props;
|
||||
page_props["main_html"] = context.var["app"]["fragments"]["main"];
|
||||
if(context.var["app"]["json"].get_type_name() == "array")
|
||||
page_props["json"] = context.var["app"]["json"];
|
||||
|
||||
String page_component = app_theme_page_component(context);
|
||||
if(page_component != "" && file_exists(page_component))
|
||||
{
|
||||
print(component(page_component, page_props, context));
|
||||
return;
|
||||
}
|
||||
|
||||
context.set_status(500, "Internal Server Error");
|
||||
context.header["Content-Type"] = "text/plain; charset=utf-8";
|
||||
print("UCE app is missing the page template component: " + page_component);
|
||||
}
|
||||
|
||||
// Backward-compatible aliases for older starter example units.
|
||||
using StarterUser = AppUser;
|
||||
|
||||
void starter_boot(Request& context)
|
||||
{
|
||||
app_init(context);
|
||||
}
|
||||
|
||||
String starter_link(String path, Request& context)
|
||||
{
|
||||
return(app_link(path, context));
|
||||
}
|
||||
|
||||
String starter_link(String path, StringMap params, Request& context)
|
||||
{
|
||||
return(app_link(path, params, context));
|
||||
}
|
||||
|
||||
void starter_register_css(String path, Request& context)
|
||||
{
|
||||
app_register_css(path, context);
|
||||
}
|
||||
|
||||
void starter_register_js(String path, Request& context)
|
||||
{
|
||||
app_register_js(path, context);
|
||||
}
|
||||
|
||||
String starter_asset_url(String path, Request& context)
|
||||
{
|
||||
return(app_asset_url(path, context));
|
||||
}
|
||||
|
||||
void starter_render_registered_css(Request& context)
|
||||
{
|
||||
app_render_registered_css(context);
|
||||
}
|
||||
|
||||
void starter_render_registered_js(Request& context)
|
||||
{
|
||||
app_render_registered_js(context);
|
||||
}
|
||||
|
||||
bool starter_page_embed_mode(Request& context)
|
||||
{
|
||||
return(app_page_embed_mode(context));
|
||||
}
|
||||
|
||||
String starter_page_main_html(Request& context)
|
||||
{
|
||||
return(app_page_main_html(context));
|
||||
}
|
||||
|
||||
String starter_html_class(Request& context)
|
||||
{
|
||||
return(app_html_class(context));
|
||||
}
|
||||
|
||||
String starter_menu_href(String menu_key, DTree menu_item, Request& context)
|
||||
{
|
||||
return(app_menu_href(menu_key, menu_item, context));
|
||||
}
|
||||
|
||||
void starter_render_page(Request& context)
|
||||
{
|
||||
app_render_page(context);
|
||||
}
|
||||
|
||||
void starter_redirect(String path, Request& context)
|
||||
{
|
||||
app_redirect(path, context);
|
||||
}
|
||||
|
||||
void starter_not_found(String message, Request& context)
|
||||
{
|
||||
app_not_found(message, context);
|
||||
}
|
||||
|
||||
DTree starter_make_route(Request& context)
|
||||
{
|
||||
return(app_make_route(context));
|
||||
}
|
||||
|
||||
DTree starter_resolve_view(Request& context, String base_dir = "views")
|
||||
{
|
||||
return(app_resolve_view(context, base_dir));
|
||||
}
|
||||
|
||||
void app_init(Request& context)
|
||||
{
|
||||
if(context.var["app"]["booted"].to_string() == "1")
|
||||
return;
|
||||
|
||||
context.var["app"]["booted"] = "1";
|
||||
context.var["app"]["fs_root"] = cwd_get();
|
||||
context.var["app"]["script_url"] = first(context.params["DOCUMENT_URI"], context.params["SCRIPT_NAME"]);
|
||||
|
||||
String base_url = dirname(context.var["app"]["script_url"].to_string());
|
||||
if(base_url == "")
|
||||
base_url = "/";
|
||||
if(base_url[base_url.length() - 1] != '/')
|
||||
base_url.append(1, '/');
|
||||
context.var["app"]["base_url"] = base_url;
|
||||
|
||||
DTree config = get_config();
|
||||
String requested_theme = first(context.get["theme"], context.cookies["app_theme"], config["theme"]["key"].to_string());
|
||||
if(config["theme"]["options"][requested_theme].to_string() == "" && config["theme"]["options"][requested_theme].get_type_name() != "array")
|
||||
requested_theme = config["theme"]["key"].to_string();
|
||||
|
||||
config["theme"]["options"][requested_theme].each([&](DTree item, String key) {
|
||||
config["theme"][key] = item;
|
||||
});
|
||||
config["theme"]["key"] = requested_theme;
|
||||
context.cfg = config;
|
||||
context.var["cfg"].set_reference(&context.cfg);
|
||||
context.var["app"]["config"].set_reference(&context.cfg);
|
||||
context.var["app"]["route"] = app_make_route(context);
|
||||
context.var["app"]["page_type"] = "html";
|
||||
context.var["app"]["page_title"] = first(config["menu"][context.var["app"]["route"]["l_path"].to_string()]["title"].to_string(), config["site"]["default_page_title"].to_string(), "Home");
|
||||
context.var["app"]["embed_mode"].set_bool(context.get["embed"] != "");
|
||||
|
||||
if(context.get["theme"] != "" && context.get["theme"] == requested_theme)
|
||||
{
|
||||
set_cookie("app_theme", requested_theme, time() + (86400 * 180));
|
||||
context.cookies["app_theme"] = requested_theme;
|
||||
}
|
||||
|
||||
session_start();
|
||||
AppUser(context).is_signed_in();
|
||||
|
||||
app_register_css(context.cfg.get_by_path("theme/path").to_string() + "css/style.css", context);
|
||||
app_register_css("themes/common/fontawesome/css/all.min.css", context);
|
||||
app_register_js("js/u-query.js", context);
|
||||
app_register_js("js/morphdom.js", context);
|
||||
app_register_js("js/site.js", context);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
struct AppUser
|
||||
{
|
||||
|
||||
Request& context;
|
||||
|
||||
explicit AppUser(Request& request)
|
||||
: context(request)
|
||||
{
|
||||
}
|
||||
|
||||
static String session_key()
|
||||
{
|
||||
return("app_user_id");
|
||||
}
|
||||
|
||||
static String normalize_email(String email)
|
||||
{
|
||||
return(to_lower(trim(email)));
|
||||
}
|
||||
|
||||
static String hash_id(String raw)
|
||||
{
|
||||
raw = normalize_email(raw);
|
||||
return(gen_sha1("uce-app:" + raw).substr(0, 12));
|
||||
}
|
||||
|
||||
String root_path()
|
||||
{
|
||||
return(first(context.cfg.get_by_path("filebase/path").to_string(), "/tmp/uce-app-data"));
|
||||
}
|
||||
|
||||
String dir(String email)
|
||||
{
|
||||
String bucket = hash_id(email);
|
||||
return(path_join(root_path(), "users/" + bucket.substr(0, 2) + "/" + bucket));
|
||||
}
|
||||
|
||||
String file_name(String email)
|
||||
{
|
||||
return(path_join(dir(email), "account.json"));
|
||||
}
|
||||
|
||||
static String password_hash(String password, String salt)
|
||||
{
|
||||
String hash = "uce-app:" + password + ":" + salt;
|
||||
for(s32 i = 0; i < 2048; i++)
|
||||
hash = gen_sha1(hash + ":" + std::to_string(i));
|
||||
return(hash);
|
||||
}
|
||||
|
||||
static DTree read_json_file(String file_name)
|
||||
{
|
||||
DTree result;
|
||||
if(!file_exists(file_name))
|
||||
return(result);
|
||||
String raw = trim(file_get_contents(file_name));
|
||||
if(raw == "")
|
||||
return(result);
|
||||
return(json_decode(raw));
|
||||
}
|
||||
|
||||
static bool write_json_file(String file_name, DTree data)
|
||||
{
|
||||
mkdir(dirname(file_name));
|
||||
return(file_put_contents(file_name, json_encode(data)));
|
||||
}
|
||||
|
||||
DTree load(String email)
|
||||
{
|
||||
DTree user = read_json_file(file_name(email));
|
||||
if(user.get_type_name() != "array")
|
||||
return(DTree());
|
||||
user["id"] = normalize_email(email);
|
||||
return(user);
|
||||
}
|
||||
|
||||
DTree create(String email, String password)
|
||||
{
|
||||
DTree result;
|
||||
email = normalize_email(email);
|
||||
password = trim(password);
|
||||
result["message"] = "";
|
||||
result["result"].set_bool(false);
|
||||
|
||||
if(email == "" || password == "")
|
||||
{
|
||||
result["message"] = "email_and_password_required";
|
||||
return(result);
|
||||
}
|
||||
if(email.find("@") == String::npos)
|
||||
{
|
||||
result["message"] = "invalid_email";
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree existing = load(email);
|
||||
if(existing.get_type_name() == "array" && existing["email"].to_string() != "")
|
||||
{
|
||||
result["message"] = "user_exists";
|
||||
return(result);
|
||||
}
|
||||
|
||||
String salt = gen_sha1(std::to_string((u64)time()) + ":" + std::to_string((u64)(time_precise() * 1000000.0)) + ":" + session_id_create()).substr(0, 24);
|
||||
DTree user;
|
||||
user["email"] = email;
|
||||
user["salt"] = salt;
|
||||
user["password_hash"] = password_hash(password, salt);
|
||||
user["created"] = std::to_string((u64)time());
|
||||
DTree role;
|
||||
role = "user";
|
||||
user["roles"].push(role);
|
||||
|
||||
write_json_file(file_name(email), user);
|
||||
user["id"] = email;
|
||||
result["result"].set_bool(true);
|
||||
result["id"] = email;
|
||||
result["profile"] = user;
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree sign_in(String email, String password)
|
||||
{
|
||||
DTree result;
|
||||
result["result"].set_bool(false);
|
||||
email = normalize_email(email);
|
||||
password = trim(password);
|
||||
|
||||
DTree user = load(email);
|
||||
if(user.get_type_name() != "array" || user["email"].to_string() == "")
|
||||
{
|
||||
result["message"] = "no_such_user";
|
||||
return(result);
|
||||
}
|
||||
if(user["password_hash"].to_string() == "")
|
||||
{
|
||||
result["message"] = "no_password_set";
|
||||
return(result);
|
||||
}
|
||||
|
||||
String expected = password_hash(password, user["salt"].to_string());
|
||||
if(expected != user["password_hash"].to_string())
|
||||
{
|
||||
result["message"] = "invalid_password";
|
||||
return(result);
|
||||
}
|
||||
|
||||
session_start();
|
||||
context.session[session_key()] = email;
|
||||
context.var["app"]["current_user"] = user;
|
||||
result["result"].set_bool(true);
|
||||
result["profile"] = user;
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool is_signed_in()
|
||||
{
|
||||
if(context.var["app"]["current_user"]["email"].to_string() != "")
|
||||
return(true);
|
||||
String user_id = context.session[session_key()];
|
||||
if(user_id == "")
|
||||
return(false);
|
||||
DTree user = load(user_id);
|
||||
if(user["email"].to_string() == "")
|
||||
{
|
||||
context.session.erase(session_key());
|
||||
return(false);
|
||||
}
|
||||
context.var["app"]["current_user"] = user;
|
||||
return(true);
|
||||
}
|
||||
|
||||
DTree current()
|
||||
{
|
||||
if(is_signed_in())
|
||||
return(context.var["app"]["current_user"]);
|
||||
return(DTree());
|
||||
}
|
||||
|
||||
void logout()
|
||||
{
|
||||
context.session.erase(session_key());
|
||||
context.var["app"]["current_user"].clear();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user