website with slop placeholders
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
unit_call()
|
||||
</h1>
|
||||
<pre><?
|
||||
unit_call("call_file_funcs.uce", "test_func");
|
||||
print("\n");
|
||||
unit_render("call_file_funcs.uce", context);
|
||||
?></pre>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Minimal exported helper used by the `unit_call()` demo page.
|
||||
|
||||
EXPORT DTree* test_func(DTree* call_param)
|
||||
{
|
||||
print("HELLO FROM TEST FUNCTION");
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree card_props;
|
||||
card_props["title"] = "Component Example";
|
||||
card_props["body"] = "This card body comes from context.call and is rendered through component().";
|
||||
|
||||
DTree named_props;
|
||||
named_props["title"] = "Named Render Example";
|
||||
named_props["body"] = "This content is rendered through the COMPONENT:BODY(Request& context) entry point.";
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Components
|
||||
</h1>
|
||||
<p>
|
||||
component_exists("components/card"):
|
||||
<strong><?= component_exists("components/card") ? "true" : "false" ?></strong>
|
||||
</p>
|
||||
<p>
|
||||
component_resolve("components/card"):
|
||||
<code><?= component_resolve("components/card") ?></code>
|
||||
</p>
|
||||
<h2>Default Component</h2>
|
||||
<div>
|
||||
<?: component("components/card", card_props, context) ?>
|
||||
</div>
|
||||
<h2>Named Component</h2>
|
||||
<? component_render("components/card:BODY", named_props, context); ?>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
<>
|
||||
<section style="border:1px solid #ccc;padding:1em;margin:1em 0;">
|
||||
<? component_render(":TITLE", context.call, context); ?>
|
||||
<? component_render(":BODY", context.call, context); ?>
|
||||
</section>
|
||||
</>
|
||||
}
|
||||
|
||||
COMPONENT:TITLE(Request& context)
|
||||
{
|
||||
<>
|
||||
<h3><?= first(context.call["title"].to_string(), "Component Title") ?></h3>
|
||||
</>
|
||||
}
|
||||
|
||||
COMPONENT:BODY(Request& context)
|
||||
{
|
||||
<>
|
||||
<p><?= first(context.call["body"].to_string(), "Component Body") ?></p>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
String lang = first(context.call["lang"].to_string(), "plain");
|
||||
<>
|
||||
<section>
|
||||
<p><strong>Code block:</strong> <?= lang ?></p>
|
||||
<div><?: context.call["default_html"].to_string() ?></div>
|
||||
</section>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
String title = first(
|
||||
context.call["node"]["attrs"]["title"].to_string(),
|
||||
context.call["argument"].to_string(),
|
||||
"Notice"
|
||||
);
|
||||
|
||||
<>
|
||||
<aside>
|
||||
<p><strong><?= title ?></strong></p>
|
||||
<div><?: context.call["children_html"].to_string() ?></div>
|
||||
</aside>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Cookies
|
||||
</h1>
|
||||
Set
|
||||
<pre><?
|
||||
|
||||
set_cookie("test-cookie-1", "test-value-1", time() + 30*60);
|
||||
set_cookie("test-cookie-2", "test-value-2", time() + 30*60*24);
|
||||
print(var_dump(context.set_cookies));
|
||||
|
||||
?></pre>
|
||||
Get
|
||||
<pre><?
|
||||
|
||||
print(var_dump(context.cookies));
|
||||
|
||||
?></pre>
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
String test_demo_normalize_ip(String ip)
|
||||
{
|
||||
if(ip.find(",") != String::npos)
|
||||
ip = trim(nibble(ip, ","));
|
||||
ip = trim(ip);
|
||||
if(str_starts_with(ip, "::ffff:"))
|
||||
ip = ip.substr(7);
|
||||
return(ip);
|
||||
}
|
||||
|
||||
bool test_demo_ip_is_private(String ip)
|
||||
{
|
||||
ip = trim(ip);
|
||||
if(ip == "" || ip == "localhost" || ip == "::1")
|
||||
return(true);
|
||||
if(str_starts_with(ip, "127."))
|
||||
return(true);
|
||||
if(str_starts_with(ip, "10."))
|
||||
return(true);
|
||||
if(str_starts_with(ip, "192.168."))
|
||||
return(true);
|
||||
if(str_starts_with(ip, "fc") || str_starts_with(ip, "fd") || str_starts_with(ip, "fe80:"))
|
||||
return(true);
|
||||
|
||||
auto parts = split(ip, ".");
|
||||
if(parts.size() == 4 && parts[0] == "172")
|
||||
{
|
||||
s64 second = int_val(parts[1]);
|
||||
if(second >= 16 && second <= 31)
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
String test_demo_request_ip(Request& context)
|
||||
{
|
||||
String remote_ip = test_demo_normalize_ip(context.params["REMOTE_ADDR"]);
|
||||
String forwarded_ip = test_demo_normalize_ip(first(context.params["HTTP_X_FORWARDED_FOR"], context.params["HTTP_X_REAL_IP"]));
|
||||
|
||||
if(remote_ip != "" && !test_demo_ip_is_private(remote_ip))
|
||||
return(remote_ip);
|
||||
if(forwarded_ip != "")
|
||||
return(forwarded_ip);
|
||||
return(remote_ip);
|
||||
}
|
||||
|
||||
bool test_demo_request_allowed(Request& context)
|
||||
{
|
||||
return(test_demo_ip_is_private(test_demo_request_ip(context)));
|
||||
}
|
||||
|
||||
void test_demo_render_restricted_html(Request& context, String title, String risk, String style_href = "style.css", String back_href = "index.uce")
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
String request_ip = first(test_demo_request_ip(context), "unknown");
|
||||
print("<html><head>");
|
||||
print("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></meta>");
|
||||
print("<link rel=\"stylesheet\" href='", html_escape(style_href), "?v=", time(), "'></link>");
|
||||
print("</head><body>");
|
||||
print("<h1><a href=\"", html_escape(back_href), "\">UCE Test</a>: ", html_escape(title), "</h1>");
|
||||
print("<p>This test page is disabled for public access because it can ", html_escape(risk), ".</p>");
|
||||
print("<p>It remains available from localhost or a private network for local development and server administration.</p>");
|
||||
print("<p>Detected request source: <code>", html_escape(request_ip), "</code></p>");
|
||||
print("</body></html>");
|
||||
}
|
||||
|
||||
void test_demo_render_restricted_text(Request& context, String title, String risk)
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
context.header["Content-Type"] = "text/plain; charset=utf-8";
|
||||
print(title, ": restricted on public access\n");
|
||||
print("Reason: this test can ", risk, ".\n");
|
||||
print("Detected request source: ", first(test_demo_request_ip(context), "unknown"), "\n");
|
||||
}
|
||||
|
||||
void test_demo_render_restricted_json(Request& context, String title, String risk)
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
context.header["Content-Type"] = "application/json; charset=utf-8";
|
||||
DTree payload;
|
||||
payload["ok"].set_bool(false);
|
||||
payload["error"] = "restricted";
|
||||
payload["title"] = title;
|
||||
payload["reason"] = "This test is disabled for public access because it can " + risk + ".";
|
||||
payload["request_ip"] = first(test_demo_request_ip(context), "unknown");
|
||||
print(json_encode(payload));
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
DTree
|
||||
</h1>
|
||||
|
||||
<h2>Value Types</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
t.set("String test");
|
||||
print("String: ", t.to_string(), "\n");
|
||||
|
||||
t.set_bool(true);
|
||||
print("bool: ", t.to_string(), "\n");
|
||||
|
||||
t.set(1234.5678);
|
||||
print("float: ", t.to_string(), "\n");
|
||||
|
||||
t.set(&t);
|
||||
print("pointer: ", t.to_string(), "\n");
|
||||
|
||||
?></pre>
|
||||
|
||||
<h2>Typed Conversions</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree scalar_text;
|
||||
scalar_text = "42.75";
|
||||
print("text -> f64: ", scalar_text.to_f64(), "\n");
|
||||
print("text -> u64: ", scalar_text.to_u64(), "\n");
|
||||
print("text -> s64: ", scalar_text.to_s64(), "\n");
|
||||
|
||||
DTree bool_text;
|
||||
bool_text = "yes";
|
||||
print("yes -> bool: ", bool_text.to_bool() ? "true" : "false", "\n");
|
||||
|
||||
DTree single_value_map;
|
||||
single_value_map["value"] = "123";
|
||||
print("{value:123} -> u64: ", single_value_map.to_u64(), "\n");
|
||||
|
||||
DTree headers;
|
||||
headers["Content-Type"] = "text/plain";
|
||||
headers["X-Test"] = "123";
|
||||
print("map -> StringMap:\n", var_dump(headers.to_stringmap()), "\n");
|
||||
|
||||
?></pre>
|
||||
|
||||
<h2>Lookup Helpers</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree lookup;
|
||||
print("has(user) before create: ", lookup.has("user") ? "true" : "false", "\n");
|
||||
print("key(user) before create: ", lookup.key("user") ? "present" : "missing", "\n");
|
||||
lookup.get_or_create("user")->set("Ada");
|
||||
print("has(user) after create: ", lookup.has("user") ? "true" : "false", "\n");
|
||||
if(DTree* user = lookup.key("user"))
|
||||
print("key(user) after create: ", user->to_string(), "\n");
|
||||
|
||||
?></pre>
|
||||
|
||||
<h2>Nested Tree</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
t.get_or_create("a")->set("valueA");
|
||||
t.get_or_create("b")->set("valueB");
|
||||
t.get_or_create("c")->get_or_create("c-d")->set("valueCD");
|
||||
t.get_or_create("c")->get_or_create("c-e")->set("valueCE");
|
||||
t.get_or_create("c")->get_or_create("c-f")->set(&t);
|
||||
t.get_or_create("g")->get_or_create("g-h")->get_or_create("h-i")->set("valueHI");
|
||||
t["g"]["x"]["y1"] = "XYZ1";
|
||||
t["g"]["x"]["y2"] = &t;
|
||||
t["g"]["x"]["y3"] = time();
|
||||
t["g"]["x"]["y4"] = "Ünicödä";
|
||||
print(var_dump(t));
|
||||
|
||||
?></pre>
|
||||
|
||||
<h2>Array Merge — Associative</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree assoc_left;
|
||||
assoc_left["name"] = "left";
|
||||
assoc_left["shared"] = "left-value";
|
||||
|
||||
DTree assoc_right;
|
||||
assoc_right["shared"] = "right-value";
|
||||
assoc_right["extra"] = "new-value";
|
||||
|
||||
print("left:\n", var_dump(assoc_left), "\n");
|
||||
print("right:\n", var_dump(assoc_right), "\n");
|
||||
print("merged:\n", var_dump(array_merge(assoc_left, assoc_right)));
|
||||
|
||||
?></pre>
|
||||
|
||||
<h2>Array Merge — List</h2>
|
||||
|
||||
<pre><?
|
||||
|
||||
DTree list_left;
|
||||
list_left.set_array();
|
||||
DTree a; a = "A"; list_left.push(a);
|
||||
DTree b; b = "B"; list_left.push(b);
|
||||
|
||||
DTree list_right;
|
||||
list_right.set_array();
|
||||
DTree c; c = "C"; list_right.push(c);
|
||||
DTree d; d = "D"; list_right.push(d);
|
||||
|
||||
print("left:\n", var_dump(list_left), "\n");
|
||||
print("right:\n", var_dump(list_right), "\n");
|
||||
print("merged:\n", var_dump(array_merge(list_left, list_right)));
|
||||
|
||||
?></pre>
|
||||
|
||||
<details>
|
||||
<summary>Request Parameters</summary>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</details>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
void hello()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "Error reporting", "intentionally crash or abort request workers");
|
||||
return;
|
||||
}
|
||||
String mode = context.get["mode"];
|
||||
|
||||
if(mode == "exception")
|
||||
throw std::runtime_error("Intentional test exception from /test/error-reporting.uce");
|
||||
if(mode == "abort")
|
||||
raise(SIGABRT);
|
||||
if(mode == "segfault")
|
||||
raise(SIGSEGV);
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Error reporting
|
||||
</h1>
|
||||
<p>These actions intentionally trigger failures so you can verify that UCE returns a usable `500` response instead of dropping the upstream connection.</p>
|
||||
<ul>
|
||||
<li><a href="?mode=exception">Trigger uncaught exception</a></li>
|
||||
<li><a href="?mode=abort">Trigger SIGABRT</a></li>
|
||||
<li><a href="?mode=segfault">Trigger SIGSEGV</a></li>
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "File Append", "write to server-side files");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
File Append
|
||||
</h1>
|
||||
|
||||
File Append:
|
||||
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
if(context.get["cmd"] == "clear")
|
||||
file_put_contents("/tmp/test.txt", "");
|
||||
|
||||
file_append("/tmp/test.txt", context.server->request_count, "\thello world\t", 2, "\t", time(), "\t", time_precise(), "\n");
|
||||
|
||||
print(file_get_contents("/tmp/test.txt"));
|
||||
|
||||
?></pre>
|
||||
|
||||
<button onclick="document.location.href='?cmd=add';">Add Line</button>
|
||||
<button onclick="document.location.href='?cmd=clear';">Clear File</button>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
FileI/O
|
||||
</h1>
|
||||
|
||||
File IO:
|
||||
|
||||
<pre style="white-space: pre-wrap"><?= file_get_contents("fileio.uce") ?></pre>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Header
|
||||
</h1>
|
||||
|
||||
Response Headers
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
print(var_dump(context.header));
|
||||
|
||||
?></pre>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
|
||||
|
||||
void show_stuff(Request& context)
|
||||
{
|
||||
//context.header["Content-Type"] = "text/plain";
|
||||
|
||||
<><html>
|
||||
Mwahahaaha <?= time() ?>
|
||||
<div>hello world: <?= context.params["HTTP_HOST"] ?></div>
|
||||
<pre>
|
||||
Display the date using any or all of the following elements:
|
||||
|
||||
%a: abbreviated day name (i.e. mon, tue, wed)
|
||||
%A: full day name (i.e. Monday, Tuesday, Wednesday)
|
||||
%b or %h: abbreviated month name (i.e. jan, feb, mar)
|
||||
%B: full month name (January, February, March)
|
||||
%c: locales date and time (full date and time)
|
||||
%C: century - displays the first two numbers of the year (i.e 19 for 1999 and 20 for 2020)
|
||||
%d: day of month (i.e. 01, 02, 03)
|
||||
%D: same as M/D/Y (i.e. 04/20/16)
|
||||
%e: day of month padded (i.e. ' 1', ' 2')
|
||||
%F: full date, same as yyyy-mm-dd
|
||||
%H: hour (00, 01, 02, 21, 22, 23)
|
||||
%I: hour (1,2,3,10,11,12)
|
||||
%j: day of year (i.e. 243)
|
||||
%k: hour padded (i.e. '1' becomes ' 1'
|
||||
%l: hour padded (12 hour clock)
|
||||
%m: month number (1,2,3)
|
||||
%M: minute (1,2,3,57,58,59)
|
||||
%n: new line
|
||||
%N: nanoseconds
|
||||
%p: AM or PM
|
||||
%P: like %p but lowercase (ironically)
|
||||
%r: locales 12 hour clock time
|
||||
%R: 24 hour version of hour and minute
|
||||
%s: seconds since 1970-01-01 00:00:00
|
||||
%S: second (01,02,03, 57, 58, 59)
|
||||
%t: a tab
|
||||
%T: time same as %H:%M:%S
|
||||
%u: day of week (1 is Monday, 2 is Tuesday etc)
|
||||
%U: week number of year (assuming Sunday as first day of the week)
|
||||
%V: ISO week number with Monday as the first day of the week
|
||||
%w: day of week (0 is Sunday)
|
||||
%W: week number of the year with Monday as the first day of the week
|
||||
%x: locales date representation (12/31/2015)
|
||||
%X: locales time representation (14:44:44)
|
||||
%y: last two digits of year
|
||||
%Y: year
|
||||
%z: numeric time zone (i.e. -0400)
|
||||
%:z: numeric time zone as follows (i.e. -04:00)
|
||||
%::z: numeric time zone as follows (i.e. -04:00:00)
|
||||
%Z: alphabetic time zone abbreviation (GMT)
|
||||
-: a single hyphen prevents zero padding
|
||||
_: a single underscore pads with spaces
|
||||
0: pads with zeroes
|
||||
^: use uppercase if possible
|
||||
#: use opposite case if possible
|
||||
To display just the time use the following:
|
||||
|
||||
date +%T
|
||||
Alternatively, use the following:
|
||||
|
||||
date +%H:%M:%S
|
||||
Attach the date, as well, using the command:
|
||||
|
||||
date +%d/%m/%Y%t%H:%M:%S
|
||||
Alternatively, use the follow (since %T is equivalent to %H:%M:%S):
|
||||
|
||||
date +$d/%m/%Y%t%T
|
||||
The : and / characters are optional and can be whatever you want. For example:
|
||||
|
||||
date +%dc%mc%Y
|
||||
|
||||
outputs: 24c09c2020, if you wanted to use 'c' as a delimiter for some reason.
|
||||
|
||||
Use any combination of the above switches after the plus symbol to output the date as you so wish. If you want to add spaces you can use quotes around the date.
|
||||
|
||||
date +'%d/%m/%Y %H:%M:%S'
|
||||
How to Show the UTC Date
|
||||
View the UTC date for your computer using the following command:
|
||||
|
||||
date -u
|
||||
If you are in the UK you will notice that instead of showing "18:58:20" as the time it will show "17:58:20" as the time.
|
||||
|
||||
How to Show the RFC Date
|
||||
View the RFC date for your computer using the following command:
|
||||
|
||||
date --rfc-2822
|
||||
This displays the date in the following format:
|
||||
|
||||
Wed, 20 Apr 2018 19:56:52 +0100
|
||||
This flag is useful as it shows that you are an hour ahead of GMT.
|
||||
|
||||
Some Useful Date Commands
|
||||
Do you want to know the date next Monday? Try this:
|
||||
|
||||
date -d "next Monday"
|
||||
|
||||
At the point of writing this returns "Mon 25 Apr 00:00:00 BST 2016"
|
||||
|
||||
The -d basically prints a date in the future or the past. So, you can use "next Monday" or "last Friday".
|
||||
|
||||
Using the same command you can find out which day of the week your birthday or Christmas falls upon.
|
||||
|
||||
date -d 12/25/2016
|
||||
|
||||
The result is Sun Dec 25.
|
||||
|
||||
Summary
|
||||
It is worth checking out the manual page for the date command using the following command:
|
||||
|
||||
man date
|
||||
Was this page helpful?
|
||||
|
||||
More from Lifewire
|
||||
Businessman checking the time on his watch
|
||||
How to Understand the Date and Time in Email Headers
|
||||
Calendar next to an Apple keyboard
|
||||
How to Change the Date and Time on a Mac Manually
|
||||
Turning on automatic date and time settings in Windows 10.
|
||||
Change the Date and Time Zone on Your Windows Laptop
|
||||
People comparing the calendars on their iPhones
|
||||
How to Change Date on iPhone
|
||||
Woman walking with Black Friday shopping bag
|
||||
What Is Black Friday?
|
||||
Clock On White Wall
|
||||
Find the Sent Timestamp on Gmail Messages
|
||||
Homescreen with clock on Android
|
||||
How to Change the Time on Android
|
||||
Person taking a photo of a flower with a smartphone
|
||||
How to Adjust the Date, Time, and Location of Photos in iOS 15
|
||||
Dark office with many computers, one lit up
|
||||
Understanding the Linux Command: Ar
|
||||
A human hand pressing an old-fashioned alarm clock
|
||||
Learn the Linux Command 'at'
|
||||
Close-Up Of Thumbtack On Calendar Date
|
||||
Using the DATE Function in Google Sheets
|
||||
DATE function in Excel
|
||||
How to Use the Excel DATE Function
|
||||
Tux the penguin is the official Linux mascot.
|
||||
Delete Files Using the Linux Command Line
|
||||
Close-Up Of Clock Against Calendar
|
||||
Serial Number and Serial Date in Excel
|
||||
Person running Linux sleep command for 20 seconds on a laptop
|
||||
How to Use the Linux Sleep Command to Pause a BASH Script
|
||||
Cropped Hand Of Person Using Laptop By Alarm Clock At Table
|
||||
Excel's Volatile NOW Function for the Date and Time
|
||||
Lifewire
|
||||
Tech for Humans
|
||||
Follow Us
|
||||
Subscribe to our newsletter and get tech’s top stories in 30 seconds.
|
||||
Email Address
|
||||
enter email
|
||||
SUBMIT
|
||||
News
|
||||
Best Products
|
||||
Mobile Phones
|
||||
Computers
|
||||
About Us
|
||||
Advertise
|
||||
Privacy Policy
|
||||
Cookie Policy
|
||||
Careers
|
||||
Editorial Guidelines
|
||||
Contact
|
||||
Terms of Use
|
||||
EU Privacy
|
||||
California Privacy Notice
|
||||
Lifewire is part of the Dotdash publishing family.
|
||||
|
||||
</pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<><html>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Index
|
||||
</h1>
|
||||
<? show_stuff(context); ?>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
void test_output()
|
||||
{
|
||||
print("hello from include!");
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
void render_card(String url, String title, String desc)
|
||||
{
|
||||
<><a class="test-card" href="<?= url ?>">
|
||||
<strong><?= title ?></strong>
|
||||
<span><?= desc ?></span>
|
||||
</a></>
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree p;
|
||||
p.set(context.params);
|
||||
bool allow_server_demos = test_demo_request_allowed(context);
|
||||
|
||||
<><html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"></meta>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test Suite</a>
|
||||
<a class="docs-link" href="../doc/index.uce">API Docs →</a>
|
||||
</h1>
|
||||
<div class="test-grid">
|
||||
<div class="grid-heading">Coverage</div>
|
||||
<? render_card("../tests/index.uce", "Site Tests", "Grouped coverage pages for broad runtime and API validation"); ?>
|
||||
|
||||
<div class="grid-heading">Basics</div>
|
||||
<? render_card("hello.uce", "Hello World", "Basic output and server time"); ?>
|
||||
<? render_card("header.uce", "Headers", "HTTP response headers"); ?>
|
||||
<? if(allow_server_demos) { render_card("working-dir.uce", "Working Directory", "Server paths and cwd"); } ?>
|
||||
<? if(allow_server_demos) { render_card("error-reporting.uce", "Error Reporting", "Error handling and output"); } ?>
|
||||
|
||||
<div class="grid-heading">Data Types & Parsing</div>
|
||||
<? render_card("dtree.uce", "DTree", "Dynamic hierarchical data tree"); ?>
|
||||
<? render_card("json.uce", "JSON", "Parse and encode JSON data"); ?>
|
||||
<? render_card("preprocessor-comments.uce", "Preprocessor Comments", "Regression coverage for comment parsing in templates"); ?>
|
||||
<? render_card("string.uce", "String", "String operations"); ?>
|
||||
<? render_card("str_replace.uce", "String Replace", "Search and replace in strings"); ?>
|
||||
<? render_card("utf8.uce", "UTF-8", "Unicode string handling"); ?>
|
||||
<? render_card("random.uce", "RNG / Noise", "Random generation and noise"); ?>
|
||||
<? render_card("parse_time.uce", "time_parse()", "Date and time parsing"); ?>
|
||||
|
||||
<div class="grid-heading">HTTP & Forms</div>
|
||||
<? render_card("post.uce", "Form POST", "URL-encoded form submission"); ?>
|
||||
<? render_card("post-multipart.uce", "Multipart POST", "File upload and multipart forms"); ?>
|
||||
<? render_card("uri.uce", "URI", "URI encoding and decoding"); ?>
|
||||
<? render_card("cookie.uce", "Cookies", "Read and write browser cookies"); ?>
|
||||
<? render_card("session.uce", "Session", "Server-side session state"); ?>
|
||||
|
||||
<div class="grid-heading">Storage & I/O</div>
|
||||
<? render_card("fileio.uce", "File I/O", "Read and write files"); ?>
|
||||
<? if(allow_server_demos) { render_card("file_append.uce", "File Append", "Append data to files"); } ?>
|
||||
<? if(allow_server_demos) { render_card("shell.uce", "Shell", "Execute shell commands"); } ?>
|
||||
<? if(allow_server_demos) { render_card("memcached.uce", "Memcached", "Memcached key-value store"); } ?>
|
||||
<? if(allow_server_demos) { render_card("mysql.uce", "MySQL", "MySQL database connector"); } ?>
|
||||
|
||||
<div class="grid-heading">Advanced</div>
|
||||
<? render_card("call_file.uce", "unit_call()", "Dynamic file inclusion"); ?>
|
||||
<? render_card("components.uce", "Components", "Reusable component system"); ?>
|
||||
<? render_card("markdown.uce", "Markdown", "Markdown parsing with components"); ?>
|
||||
<? render_card("script.uce", "Script", "UCE script integration"); ?>
|
||||
<? render_card("websockets.ws.uce", "WebSockets", "Real-time WebSocket chat"); ?>
|
||||
<? render_card("unit-browser.uce", "Unit Browser", "units_list(), unit_info(), unit_compile()"); ?>
|
||||
<? if(allow_server_demos) { render_card("task.uce", "Task", "Background task execution"); } ?>
|
||||
<? if(allow_server_demos) { render_card("task_repeat.uce", "Task Repeat", "Recurring task scheduling"); } ?>
|
||||
</div>
|
||||
<? if(!allow_server_demos) { ?>
|
||||
<div class="system-info">
|
||||
<h3>Restricted On Public Access</h3>
|
||||
<pre>Local-only demos are hidden here because they can execute shell commands, access infrastructure services, mutate server state, compile units, or expose internal runtime details.</pre>
|
||||
</div>
|
||||
<? } else { ?>
|
||||
<div class="system-info">
|
||||
<h3>System Info</h3>
|
||||
<pre><?
|
||||
print("Worker PID: ", my_pid, "\n");
|
||||
print("Parent PID: ", parent_pid, "\n");
|
||||
print("Output buffer size: ", context.ob->str().length(), "\n");
|
||||
print("Request #", context.server->request_count, "\n");
|
||||
?></pre>
|
||||
</div>
|
||||
<? } ?>
|
||||
<details>
|
||||
<summary>Request Parameters</summary>
|
||||
<pre><?= (var_dump(p)) ?></pre>
|
||||
</details>
|
||||
</body>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
DTree/JSON
|
||||
</h1>
|
||||
|
||||
JSON:
|
||||
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
t.get_or_create("a")->set("valueA");
|
||||
t.get_or_create("b")->set("valueB");
|
||||
t.get_or_create("c")->get_or_create("c-dt")->set_bool(true);
|
||||
t.get_or_create("c")->get_or_create("c-df")->set_bool(false);
|
||||
t.get_or_create("c")->get_or_create("c-e")->set("valueCE");
|
||||
t.get_or_create("c")->get_or_create("c-f")->set(&t);
|
||||
t.get_or_create("g")->get_or_create("g-h")->get_or_create("h-i")->set("valueHI");
|
||||
t["g"]["x"]["y1"] = "XYZ1";
|
||||
t["g"]["x"]["y2"] = &t;
|
||||
t["g"]["x"]["y3"] = time();
|
||||
t["g"]["x"]["y4"] = "Ünicödä";
|
||||
t["l"] = context.params;
|
||||
|
||||
String j;
|
||||
print(j = json_encode(t));
|
||||
|
||||
?></pre>
|
||||
|
||||
Parsed:
|
||||
|
||||
<pre><?
|
||||
print(var_dump(
|
||||
json_decode(j)
|
||||
));
|
||||
?></pre>
|
||||
|
||||
Compare:
|
||||
|
||||
<pre><?
|
||||
print(var_dump(
|
||||
t
|
||||
));
|
||||
?></pre>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# Markdown Demo
|
||||
|
||||
This page exercises **strong**, *emphasis*, ~~strikethrough~~, `code spans`, and a bare URL: https://uce.openfu.com/doc/index.uce
|
||||
|
||||
## Task List
|
||||
|
||||
- [x] Parse markdown into an AST
|
||||
- [x] Render markdown into HTML
|
||||
- [ ] Add even more extensions later
|
||||
|
||||
## Table
|
||||
|
||||
| Feature | Status | Notes |
|
||||
| :--- | :---: | ---: |
|
||||
| Headings | Ready | 1 |
|
||||
| Tables | Ready | 2 |
|
||||
| Components | Ready | 3 |
|
||||
|
||||
## Quote
|
||||
|
||||
> Markdown in UCE should be composable.
|
||||
>
|
||||
> Components make that much more interesting.
|
||||
|
||||
:::warning title="Component-backed directive"
|
||||
This `:::warning` block is rendered through a normal UCE component selected from `options["components"]`.
|
||||
:::
|
||||
|
||||
## Code
|
||||
|
||||
```cpp
|
||||
RENDER(Request& context)
|
||||
{
|
||||
print(markdown_to_html("# hello"));
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree options;
|
||||
options["components"][":::warning"] = "components/markdown/warning";
|
||||
options["components"]["node.code_block"] = "components/markdown/code_block";
|
||||
|
||||
String markdown_src = first(
|
||||
context.post["markdown"],
|
||||
file_get_contents("markdown-example.md")
|
||||
);
|
||||
|
||||
DTree ast = markdown_to_ast(markdown_src, options);
|
||||
String html = markdown_to_html(markdown_src, options);
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Markdown
|
||||
</h1>
|
||||
<p>
|
||||
This page exercises `markdown_to_ast()` and `markdown_to_html()` with component hooks for `:::warning` directives and fenced code blocks.
|
||||
</p>
|
||||
<form method="post" action="?">
|
||||
<div>
|
||||
<section>
|
||||
<h2>Source</h2>
|
||||
<textarea name="markdown"><?= markdown_src ?></textarea>
|
||||
<div>
|
||||
<input type="submit" value="Render Markdown"/>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Rendered HTML</h2>
|
||||
<div>
|
||||
<?: html ?>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</form>
|
||||
<details>
|
||||
<summary>AST JSON</summary>
|
||||
<pre><?= json_encode(ast) ?></pre>
|
||||
</details>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "MemcacheD", "inspect and mutate local memcached state");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
MemcacheD
|
||||
</h1>
|
||||
|
||||
Stats:
|
||||
|
||||
<pre><?
|
||||
|
||||
auto sfd = memcache_connect();
|
||||
|
||||
print(memcache_command(sfd, "stats"));
|
||||
|
||||
?></pre>
|
||||
|
||||
Set/Get:
|
||||
|
||||
<pre><?
|
||||
|
||||
memcache_set(sfd, "test_key", "test_value");
|
||||
memcache_set(sfd, "test_key2", "test_value2");
|
||||
print("raw:: "+memcache_command(sfd, "get test_key")+"\n");
|
||||
print("get:: "+memcache_get(sfd, "test_key")+"\n");
|
||||
print("multiple::\n"+var_dump(memcache_get_multiple(sfd, {"test_key", "test_key2"}))+"\n");
|
||||
|
||||
?></pre>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "MySQL Connector Test", "connect to local database services and expose database contents");
|
||||
return;
|
||||
}
|
||||
|
||||
<><html>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
MySQL Connector Test
|
||||
</h1>
|
||||
|
||||
<label>MySQL Connection</label>
|
||||
<pre><?
|
||||
|
||||
String query = "SELECT * FROM :table WHERE x = :val";
|
||||
StringMap params;
|
||||
params["table"] = "TableName";
|
||||
params["val"] = "Dubious\\Value'Name\nwith;breaks";
|
||||
|
||||
MySQL con;
|
||||
if(con.connect("localhost", "root", ""))
|
||||
{
|
||||
print("error: "+con.error()+"\n");
|
||||
print("quotation test: "+con.escape("‘\"' or 1=1;–.")+"\n");
|
||||
print("query parse: "+con.parse_query_parameters(query, params)+"\n");
|
||||
print(var_dump(con.query("SHOW DATABASES")));
|
||||
con.query("USE mysql");
|
||||
|
||||
?></pre>
|
||||
|
||||
SELECT * FROM user table
|
||||
|
||||
<pre><?
|
||||
|
||||
print(var_dump(con.query("SELECT * FROM user")));
|
||||
|
||||
}
|
||||
|
||||
|
||||
?></pre>
|
||||
|
||||
<div><?
|
||||
print("connection status: ", con.statement_info, "\n");
|
||||
?></div>
|
||||
|
||||
<label>CGI Params</label>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
String raw = first(context.post["raw"], "today");
|
||||
u64 parsed = time_parse(raw);
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
time_parse
|
||||
</h1>
|
||||
<form action="?" method="post">
|
||||
<div>
|
||||
<label>Time string</label>
|
||||
<input type="text" name="raw" value="<?= raw ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Parse"/>
|
||||
</div>
|
||||
</form>
|
||||
<pre><?
|
||||
|
||||
print("local: ", time_format_local("%Y-%m-%d %H:%M", parsed), "\n");
|
||||
print("relative: ", time_format_relative(parsed), "\n");
|
||||
print("delta: ", time_format_local("%deltaHh %deltaMm %deltaSs", parsed), "\n");
|
||||
|
||||
?></pre>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
void show_form(Request& context)
|
||||
{
|
||||
|
||||
<><form action="?" method="post" enctype="multipart/form-data">
|
||||
<div>
|
||||
<label>Some text:</label>
|
||||
<input type="text" name="fühld" value="<?= context.post["fühld"] ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Some multiline text:</label>
|
||||
<textarea name="field2"><?= context.post["field2"] ?></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</div>
|
||||
</form></>
|
||||
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Multipart-Encoded Form POST
|
||||
</h1>
|
||||
|
||||
<? show_form(context); ?>
|
||||
|
||||
<div>
|
||||
|
||||
</div>
|
||||
|
||||
<label>Parsed POST fields</label>
|
||||
<pre><?= var_dump(context.post) ?></pre>
|
||||
|
||||
<label>Raw POST content</label>
|
||||
<pre><?= context.in ?></pre>
|
||||
|
||||
<label>CGI Params</label>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
EXPORT void show_form(Request& context)
|
||||
{
|
||||
|
||||
<><form action="?" method="post">
|
||||
<div>
|
||||
<label>Some text:</label>
|
||||
<input type="text" name="field" value="<?= context.post["field"] ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Some multiline text:</label>
|
||||
<textarea name="field2"><?= context.post["field2"] ?></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</div>
|
||||
</form></>
|
||||
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<><html>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Form POST
|
||||
</h1>
|
||||
|
||||
<? show_form(context); ?>
|
||||
|
||||
<label>Parsed POST fields</label>
|
||||
<pre><?= var_dump(context.post) ?></pre>
|
||||
|
||||
<label>Raw POST content</label>
|
||||
<pre><?= context.in ?></pre>
|
||||
|
||||
<label>CGI Params</label>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
RENDER(Request& context)
|
||||
{
|
||||
// Regression: this comment contains <> and an apostrophe that shouldn't start a literal block: <>
|
||||
String outer_status = "outer parser ok";
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Preprocessor Comments
|
||||
</h1>
|
||||
|
||||
<p>This page exists to prove the template parser ignores quotes and template markers that appear inside C++ comments.</p>
|
||||
|
||||
<?
|
||||
// Regression: this comment's apostrophe must not swallow the later ?> marker.
|
||||
String inline_status = "inline comment ok";
|
||||
?>
|
||||
|
||||
<?
|
||||
/*
|
||||
Regression: block comments may contain "quotes", apostrophe's, and <> markers
|
||||
without confusing the parser or the generated .cpp output.
|
||||
*/
|
||||
String block_status = "block comment ok";
|
||||
?>
|
||||
|
||||
<ul>
|
||||
<li>Outer code scan: <code><?= outer_status ?></code></li>
|
||||
<li>Markup code island with // comment: <code><?= inline_status ?></code></li>
|
||||
<li>Markup code island with /* */ comment: <code><?= block_status ?></code></li>
|
||||
</ul>
|
||||
|
||||
<details>
|
||||
<summary>Request Parameters</summary>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</details>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Random
|
||||
</h1>
|
||||
</>
|
||||
|
||||
<>
|
||||
Generate Some Numbers
|
||||
<pre><?
|
||||
|
||||
u64 max_64 = 0xffffffffffffffff;
|
||||
u64 max_32 = 0xffffffff;
|
||||
|
||||
for(auto i = 0; i < 100; i++)
|
||||
{
|
||||
print(i, ": ", (float)gen_noise32(i)/(float)max_32, " / ", (float)gen_noise64(i)/(float)max_64, " / ", gen_noise01(i), " / ",
|
||||
gen_int(0, 4, i), " / ", gen_float(0.0, 4.0, i), "\n");
|
||||
}
|
||||
|
||||
?></pre>
|
||||
Draw Some Numbers
|
||||
<div>
|
||||
Seed <?= context.random_seed ?>
|
||||
</div>
|
||||
<pre><?
|
||||
|
||||
for(auto i = 0; i < 100; i++)
|
||||
{
|
||||
print(i, ": ", draw_int(0, 4), " / ", draw_float(0.0, 4.0), "\n");
|
||||
}
|
||||
|
||||
?></pre>
|
||||
|
||||
<div>
|
||||
Sha1 of '1234': <?= gen_sha1("1234") ?>
|
||||
</div>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
v1 : string = "";
|
||||
|
||||
v2 := 'BLA';
|
||||
i1 := 123.2;
|
||||
|
||||
for(v3 => c)
|
||||
print(c);
|
||||
|
||||
v1 = "This is an interpolated string {=i1}
|
||||
which also contains line breaks";
|
||||
|
||||
?><div>
|
||||
It's now time for some HTML!
|
||||
</div>
|
||||
<?
|
||||
|
||||
print("And, we're back!");
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
enum TokenType{ TT_UNKNOWN, TT_STRING, TT_INT, TT_FLOAT, TT_IDENTIFIER, TT_OPERATOR };
|
||||
String TokenTypeName[] = { "TT_UNKNOWN", "TT_STRING", "TT_INT", "TT_FLOAT", "TT_IDENTIFIER", "TT_OPERATOR" };
|
||||
|
||||
struct Token
|
||||
{
|
||||
String literal = "";
|
||||
TokenType type = TT_UNKNOWN;
|
||||
char delim = ' ';
|
||||
};
|
||||
|
||||
#define NEW_TOKEN { result.push_back(ctok); ctok = new Token(); goto eval_char; }
|
||||
|
||||
std::vector<Token*> script_tokenize(String code)
|
||||
{
|
||||
std::vector<Token*> result;
|
||||
auto ctok = new Token();
|
||||
|
||||
for(auto c : code)
|
||||
{
|
||||
eval_char:
|
||||
if(ctok->type == TT_UNKNOWN)
|
||||
{
|
||||
if(isspace(c))
|
||||
{
|
||||
|
||||
}
|
||||
else if(isdigit(c))
|
||||
{
|
||||
ctok->type = TT_INT;
|
||||
}
|
||||
else if(isalpha(c))
|
||||
{
|
||||
ctok->type = TT_IDENTIFIER;
|
||||
}
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
ctok->type = TT_STRING;
|
||||
ctok->delim = c;
|
||||
continue;
|
||||
}
|
||||
else if(ispunct(c))
|
||||
{
|
||||
ctok->type = TT_OPERATOR;
|
||||
}
|
||||
}
|
||||
switch(ctok->type)
|
||||
{
|
||||
case(TT_IDENTIFIER):
|
||||
{
|
||||
if(isalnum(c) || c == '_')
|
||||
ctok->literal += c;
|
||||
else
|
||||
NEW_TOKEN
|
||||
break;
|
||||
}
|
||||
case(TT_INT):
|
||||
{
|
||||
if(isdigit(c))
|
||||
ctok->literal += c;
|
||||
else if(c == '.')
|
||||
{
|
||||
ctok->type = TT_FLOAT;
|
||||
ctok->literal += c;
|
||||
}
|
||||
else
|
||||
NEW_TOKEN
|
||||
break;
|
||||
}
|
||||
case(TT_FLOAT):
|
||||
{
|
||||
if(isdigit(c))
|
||||
ctok->literal += c;
|
||||
else
|
||||
NEW_TOKEN
|
||||
break;
|
||||
}
|
||||
case(TT_OPERATOR):
|
||||
{
|
||||
ctok->literal += c;
|
||||
c = ' ';
|
||||
NEW_TOKEN
|
||||
break;
|
||||
}
|
||||
case(TT_STRING):
|
||||
{
|
||||
if(c == ctok->delim)
|
||||
{
|
||||
c = ' ';
|
||||
NEW_TOKEN
|
||||
}
|
||||
else
|
||||
ctok->literal += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push_back(ctok);
|
||||
return(result);
|
||||
}
|
||||
|
||||
String to_string(std::vector<Token*> t)
|
||||
{
|
||||
String result = "";
|
||||
for(auto& ti : t)
|
||||
{
|
||||
result += concat(TokenTypeName[ti->type], "(", ti->literal, ") ");
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
// "
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Script
|
||||
</h1>
|
||||
</>
|
||||
|
||||
String script_src = first(context.post["code"], file_get_contents("script-example1.usp"));
|
||||
|
||||
<>
|
||||
<h3>Code</h3>
|
||||
<form action="?" method="post">
|
||||
<textarea name="code"><?= script_src ?></textarea>
|
||||
<input type="submit" value="parse"/>
|
||||
</form>
|
||||
<pre><?
|
||||
|
||||
auto tokens = script_tokenize(script_src);
|
||||
print(html_escape(to_string(tokens)));
|
||||
|
||||
|
||||
?></pre>
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Session
|
||||
</h1>
|
||||
</>
|
||||
|
||||
|
||||
String action = context.get["action"];
|
||||
if(context.cookies["uce-session"].length() > 0)
|
||||
session_start();
|
||||
if(action == "start")
|
||||
{
|
||||
session_start();
|
||||
print("action: starting session");
|
||||
}
|
||||
else if(action == "store")
|
||||
{
|
||||
context.session["stored-value"] = session_id_create();
|
||||
print("action: storing value "+context.session["stored-value"]);
|
||||
}
|
||||
else if(action == "destroy")
|
||||
{
|
||||
session_destroy();
|
||||
print("action: destroying session");
|
||||
}
|
||||
|
||||
<>
|
||||
<br/>
|
||||
<a href="?action=start" class="button">Start Session</a> |
|
||||
<a href="?" class="button">Refresh</a> |
|
||||
<a href="?action=destroy" class="button">Destroy Session</a> |
|
||||
<a href="?action=store" class="button">Store Value</a> |
|
||||
Info
|
||||
<pre><?
|
||||
|
||||
print(var_dump(context.cookies));
|
||||
print("SESSION:\n");
|
||||
print(var_dump(context.session));
|
||||
|
||||
?></pre>
|
||||
Params
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
auto p = compiler_load_shared_unit(&context, "post.uce");
|
||||
if(p)
|
||||
print(to_string(p));
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "Shell Stuff", "execute shell commands and expose repository state");
|
||||
return;
|
||||
}
|
||||
|
||||
<><html>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Shell Stuff
|
||||
</h1>
|
||||
|
||||
<div>shell_exec()</div>
|
||||
|
||||
<pre><?
|
||||
|
||||
print(shell_exec("ls -l"));
|
||||
|
||||
?></pre>
|
||||
|
||||
<div>shell_escape()</div>
|
||||
|
||||
<pre style="height:80px"><?
|
||||
|
||||
print(shell_exec("echo "+shell_escape("kasjdf 1ölkasj öflkjasdö\\lkfjsöa'ldkfj 23487692\"bla83746")));
|
||||
|
||||
?></pre>
|
||||
|
||||
<div>Git</div>
|
||||
|
||||
<pre><?
|
||||
|
||||
print(shell_exec("git show"));
|
||||
|
||||
?></pre>
|
||||
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
void str_replace_test(String s, String sr, String rp)
|
||||
{
|
||||
print("in '", s, "' replace all '", sr, "' with '", rp, "' => ");
|
||||
print(replace(s, sr, rp));
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<><html>
|
||||
<link rel="stylesheet" href='style.css?v=1'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
String Replace
|
||||
</h1>
|
||||
|
||||
<div>
|
||||
<? str_replace_test("abcdefgh", "c", "C"); ?>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<? str_replace_test("abcccdefgh", "c", "C"); ?>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<? str_replace_test("abcccdefgh", "ccc", "C"); ?>
|
||||
</div>
|
||||
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</html></>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void test_row(String label, String result)
|
||||
{
|
||||
<><tr><td class="test-label"><?= label ?></td><td class="test-result"><code><?= result ?></code></td></tr></>
|
||||
}
|
||||
|
||||
void test_row(String label, String result, String expect)
|
||||
{
|
||||
bool pass = (result == expect);
|
||||
<><tr>
|
||||
<td class="test-label"><?= label ?></td>
|
||||
<td class="test-result"><code><?= result ?></code></td>
|
||||
<td class="test-status <?= pass ? "pass" : "fail" ?>"><?= pass ? "✓" : "✗ expected: " + expect ?></td>
|
||||
</tr></>
|
||||
}
|
||||
|
||||
void test_num(String label, s64 result, s64 expect)
|
||||
{
|
||||
bool pass = (result == expect);
|
||||
<><tr>
|
||||
<td class="test-label"><?= label ?></td>
|
||||
<td class="test-result"><code><? print(result); ?></code></td>
|
||||
<td class="test-status <?= pass ? "pass" : "fail" ?>"><? if(!pass) { print("✗ expected: "); print(expect); } else { print("✓"); } ?></td>
|
||||
</tr></>
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
String sample = " Hello UCE World ";
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<style>
|
||||
table.tests { width: 100%; border-collapse: collapse; margin-bottom: 8px; }
|
||||
table.tests td { padding: 6px 12px; border-bottom: 1px solid rgba(255,255,255,0.06); font-family: var(--font-mono); font-size: 0.88rem; }
|
||||
.test-label { color: var(--text-dim); white-space: nowrap; width: 1%; }
|
||||
.test-result code { background: var(--bg-code); padding: 2px 8px; border-radius: 4px; }
|
||||
.test-status.pass { color: #6f6; width: 1%; }
|
||||
.test-status.fail { color: #f66; }
|
||||
</style>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Strings
|
||||
</h1>
|
||||
|
||||
<p>sample = <code>"<?= sample ?>"</code></p>
|
||||
|
||||
<h2>trim</h2>
|
||||
<table class="tests"><?
|
||||
test_row("trim(sample)", "[" + trim(sample) + "]", "[Hello UCE World]");
|
||||
?></table>
|
||||
|
||||
<h2>substr</h2>
|
||||
<table class="tests"><?
|
||||
test_row("substr(sample, 2, 5)", substr(sample, 2, 5), "Hello");
|
||||
test_row("substr(sample, -7)", substr(sample, -7), "World ");
|
||||
test_row("substr(sample, 2, -2)", substr(sample, 2, -2), "Hello UCE World");
|
||||
?></table>
|
||||
|
||||
<h2>strpos</h2>
|
||||
<table class="tests"><?
|
||||
test_num("strpos(sample, \"UCE\")", strpos(sample, "UCE"), 8);
|
||||
test_num("strpos(sample, \"missing\")", strpos(sample, "missing"), -1);
|
||||
?></table>
|
||||
|
||||
<h2>contains</h2>
|
||||
<table class="tests"><?
|
||||
test_row("contains(sample, \"World\")", contains(sample, "World") ? "true" : "false", "true");
|
||||
test_row("contains(sample, \"xyz\")", contains(sample, "xyz") ? "true" : "false", "false");
|
||||
?></table>
|
||||
|
||||
<h2>str_starts_with / str_ends_with</h2>
|
||||
<table class="tests"><?
|
||||
test_row("str_starts_with(trim(sample), \"Hello\")", str_starts_with(trim(sample), "Hello") ? "true" : "false", "true");
|
||||
test_row("str_starts_with(trim(sample), \"World\")", str_starts_with(trim(sample), "World") ? "true" : "false", "false");
|
||||
test_row("str_ends_with(trim(sample), \"World\")", str_ends_with(trim(sample), "World") ? "true" : "false", "true");
|
||||
test_row("str_ends_with(trim(sample), \"Hello\")", str_ends_with(trim(sample), "Hello") ? "true" : "false", "false");
|
||||
?></table>
|
||||
|
||||
<h2>replace / split / join</h2>
|
||||
<table class="tests"><?
|
||||
test_row("replace(sample, \"UCE\", \"C++\")", replace(sample, "UCE", "C++"), " Hello C++ World ");
|
||||
test_row("join(split(trim(sample), \" \"), \"-\")", join(split(trim(sample), " "), "-"), "Hello-UCE-World");
|
||||
?></table>
|
||||
|
||||
<h2>to_upper / to_lower</h2>
|
||||
<table class="tests"><?
|
||||
test_row("to_upper(trim(sample))", to_upper(trim(sample)), "HELLO UCE WORLD");
|
||||
test_row("to_lower(trim(sample))", to_lower(trim(sample)), "hello uce world");
|
||||
?></table>
|
||||
|
||||
<details>
|
||||
<summary>Request Parameters</summary>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</details>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
/* UCE Test Suite — Theme
|
||||
Color palette: deep blue gradient · warm gold accents · frosted glass surfaces */
|
||||
@font-face {
|
||||
font-family: 'default_sans';
|
||||
src: url('../examples/uce-starter/themes/common/fonts/b612/b612-regular.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'default_sans';
|
||||
src: url('../examples/uce-starter/themes/common/fonts/b612/b612-bold.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'default_mono';
|
||||
src: url('../examples/uce-starter/themes/common/fonts/b612/b612-mono-regular.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'default_mono';
|
||||
src: url('../examples/uce-starter/themes/common/fonts/b612/b612-mono-bold.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg: #113399;
|
||||
--bg-grad: linear-gradient(168deg, #1a3daa 0%, #113399 40%, #0d2880 100%);
|
||||
--bg-surface: rgba(255, 255, 255, 0.05);
|
||||
--bg-surface-hover: rgba(255, 255, 255, 0.09);
|
||||
--bg-inset: rgba(0, 0, 0, 0.28);
|
||||
--bg-code: rgba(0, 0, 0, 0.3);
|
||||
--text: #dfe5f2;
|
||||
--text-dim: rgba(200, 210, 235, 0.6);
|
||||
--text-muted: rgba(200, 210, 235, 0.35);
|
||||
--accent: #f0c430;
|
||||
--accent-dim: rgba(240, 196, 48, 0.08);
|
||||
--accent-hover: #ffd95c;
|
||||
--accent-glow: rgba(240, 196, 48, 0.15);
|
||||
--border: rgba(255, 255, 255, 0.06);
|
||||
--border-strong: rgba(255, 255, 255, 0.14);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.25);
|
||||
--font-sans: 'default_sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
--font-mono: 'default_mono', 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;
|
||||
--radius: 10px;
|
||||
--radius-lg: 16px;
|
||||
--ease: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* ── Reset ── */
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* ── Base ── */
|
||||
|
||||
html {
|
||||
font-size: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 64px;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1rem;
|
||||
line-height: 1.65;
|
||||
background: var(--bg-grad);
|
||||
background-attachment: fixed;
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Typography ── */
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
padding: 32px 0 20px;
|
||||
border-bottom: 2px solid transparent;
|
||||
border-image: linear-gradient(90deg, var(--accent) 0%, rgba(240, 196, 48, 0.15) 100%) 1;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
h1 a {
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
h1 a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 24px 0 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--text-dim);
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--accent-hover);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ── Page sections ── */
|
||||
|
||||
body > section,
|
||||
body > div:not(.test-grid):not(.system-info):not(.grid-heading),
|
||||
body > p {
|
||||
background: var(--bg-surface);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px 24px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* ── Test Card Grid ── */
|
||||
|
||||
.test-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.test-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 16px 20px;
|
||||
background: var(--bg-surface);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: all 200ms var(--ease);
|
||||
}
|
||||
|
||||
.test-card:hover {
|
||||
background: var(--bg-surface-hover);
|
||||
border-color: var(--border-strong);
|
||||
text-decoration: none;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.test-card strong {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.92rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.test-card span {
|
||||
font-size: 0.82rem;
|
||||
color: var(--text-dim);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.grid-heading {
|
||||
grid-column: 1 / -1;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
padding: 14px 0 2px;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.grid-heading:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
float: right;
|
||||
font-size: 0.55em;
|
||||
font-weight: 400;
|
||||
opacity: 0.5;
|
||||
transition: opacity 150ms var(--ease);
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dim {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* ── Forms ── */
|
||||
|
||||
form > div, label {
|
||||
display: block;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
input, textarea, select {
|
||||
background: var(--bg-inset);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
font-family: var(--font-sans);
|
||||
transition: border-color 150ms var(--ease), background 150ms var(--ease);
|
||||
}
|
||||
|
||||
input[type=text], textarea {
|
||||
padding: 10px 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text]:hover, textarea:hover,
|
||||
input[type=text]:focus, textarea:focus {
|
||||
border-color: var(--accent);
|
||||
outline: none;
|
||||
background: rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
input[type=submit], button {
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
background: var(--accent-dim);
|
||||
border: 1px solid rgba(255, 214, 68, 0.25);
|
||||
color: var(--accent);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
input[type=submit]:hover, button:hover {
|
||||
background: rgba(255, 214, 68, 0.2);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-hover);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* ── Code / Output ── */
|
||||
|
||||
pre {
|
||||
padding: 16px 20px;
|
||||
overflow: auto;
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9em;
|
||||
padding: 2px 6px;
|
||||
background: var(--bg-inset);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* ── Lists ── */
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
li a {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
/* ── System Info ── */
|
||||
|
||||
.system-info {
|
||||
margin-top: 32px;
|
||||
padding: 16px 20px;
|
||||
background: var(--bg-code);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.system-info h3 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.system-info pre {
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
/* ── Details / Summary ── */
|
||||
|
||||
details {
|
||||
background: var(--bg-surface);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 16px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
summary {
|
||||
padding: 14px 20px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
summary:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
details pre {
|
||||
border: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── Sections (markdown, etc.) ── */
|
||||
|
||||
section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
border-bottom: none;
|
||||
margin: 0 0 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ── Unit Browser ── */
|
||||
|
||||
.unit-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.unit-toolbar p {
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.unit-count {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.82rem;
|
||||
color: var(--text-dim);
|
||||
padding: 6px 10px;
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-code);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-ok {
|
||||
color: #9ff0b3;
|
||||
border-color: rgba(159, 240, 179, 0.25);
|
||||
background: rgba(38, 105, 58, 0.22);
|
||||
}
|
||||
|
||||
.status-warn {
|
||||
color: #ffd97a;
|
||||
border-color: rgba(255, 217, 122, 0.26);
|
||||
background: rgba(120, 88, 22, 0.25);
|
||||
}
|
||||
|
||||
.status-error {
|
||||
color: #ff9e9e;
|
||||
border-color: rgba(255, 158, 158, 0.3);
|
||||
background: rgba(122, 34, 34, 0.28);
|
||||
}
|
||||
|
||||
.status-muted {
|
||||
color: var(--text-dim);
|
||||
border-color: var(--border);
|
||||
background: var(--bg-code);
|
||||
}
|
||||
|
||||
.unit-browser-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(250px, 320px) minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.unit-sidebar,
|
||||
.unit-detail-pane {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.unit-sidebar-header,
|
||||
.unit-detail-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.unit-sidebar-header h2,
|
||||
.unit-detail-pane h2,
|
||||
.unit-detail-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.unit-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.unit-list-item {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.unit-list-item:hover {
|
||||
text-decoration: none;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.unit-list-item.is-selected {
|
||||
border-color: rgba(240, 196, 48, 0.32);
|
||||
background: rgba(240, 196, 48, 0.08);
|
||||
box-shadow: 0 0 0 1px rgba(240, 196, 48, 0.08), var(--shadow-sm);
|
||||
}
|
||||
|
||||
.unit-list-title-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.unit-list-title-row strong {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.78rem;
|
||||
color: var(--text);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.unit-list-path {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.72rem;
|
||||
color: var(--text-muted);
|
||||
word-break: break-word;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.unit-list-meta {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.74rem;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.unit-list-meta span {
|
||||
padding: 2px 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.unit-list-error {
|
||||
margin-top: 8px;
|
||||
font-size: 0.74rem;
|
||||
color: #ffb7b7;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.unit-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.unit-actions a {
|
||||
font-size: 0.82rem;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.unit-detail-path {
|
||||
margin: 6px 0 0;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
padding: 16px 18px;
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.detail-card strong {
|
||||
display: block;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.74rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-card span,
|
||||
.detail-card code {
|
||||
font-size: 0.88rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 920px) {
|
||||
.unit-browser-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.unit-sidebar-header,
|
||||
.unit-detail-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Scrollbar ── */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_text(context, "Task Status", "inspect background task state");
|
||||
return;
|
||||
}
|
||||
String task_name = first(context.get["task-name"], "example-task");
|
||||
|
||||
print("Task Name: ", task_name, "\n");
|
||||
print("Task ID: ", task_pid(task_name), "\n");
|
||||
print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "Tasks", "spawn and inspect background tasks");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
|
||||
String task_name = first(context.get["task-name"], "example-task");
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Tasks
|
||||
</h1>
|
||||
|
||||
<script>
|
||||
|
||||
function load(target, url) {
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", url, true);
|
||||
r.onreadystatechange = function () {
|
||||
if (r.readyState != 4 || r.status != 200) return;
|
||||
target.innerHTML = r.responseText;
|
||||
};
|
||||
r.send();
|
||||
}
|
||||
</script>
|
||||
|
||||
<form action="?">
|
||||
Task name
|
||||
<input type="text" name="task-name" value="<?= task_name ?>"/>
|
||||
<input type="submit" name="cmd" value="Get Info"/>
|
||||
<input type="submit" name="cmd" value="Execute"/>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
/*setInterval(() => {
|
||||
|
||||
load(document.getElementById('task-status'), 'task-status.uce?task-name=<?= uri_encode(task_name) ?>');
|
||||
|
||||
}, 200);*/
|
||||
|
||||
</script>
|
||||
|
||||
Task Status
|
||||
|
||||
<pre id="task-status" style="white-space: pre-wrap"><?
|
||||
|
||||
print("Task Name: ", task_name, "\n");
|
||||
print("Task ID: ", task_pid(task_name), "\n");
|
||||
print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");
|
||||
|
||||
|
||||
?></pre>
|
||||
|
||||
<?
|
||||
if(context.get["cmd"] == "Execute")
|
||||
{
|
||||
?>
|
||||
Task Start
|
||||
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
print("New Task ID: ", task("example-task", []() {
|
||||
|
||||
sleep(10);
|
||||
|
||||
}), "\n");
|
||||
|
||||
print("Task run time: 10 seconds");
|
||||
|
||||
?></pre><?
|
||||
}
|
||||
?>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.get) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "Task repeat", "schedule recurring background tasks");
|
||||
return;
|
||||
}
|
||||
DTree t;
|
||||
|
||||
String task_name = first(context.get["task-name"], "example-task");
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Task repeat
|
||||
</h1>
|
||||
|
||||
<script>
|
||||
|
||||
function load(target, url) {
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", url, true);
|
||||
r.onreadystatechange = function () {
|
||||
if (r.readyState != 4 || r.status != 200) return;
|
||||
target.innerHTML = r.responseText;
|
||||
};
|
||||
r.send();
|
||||
}
|
||||
</script>
|
||||
|
||||
<form action="?">
|
||||
Task name
|
||||
<input type="text" name="task-name" value="<?= task_name ?>"/>
|
||||
<input type="submit" name="cmd" value="Get Info"/>
|
||||
<input type="submit" name="cmd" value="Execute"/>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
/*setInterval(() => {
|
||||
|
||||
load(document.getElementById('task-status'), 'task-status.uce?task-name=<?= uri_encode(task_name) ?>');
|
||||
|
||||
}, 200);*/
|
||||
|
||||
</script>
|
||||
|
||||
Task Status
|
||||
|
||||
<pre id="task-status" style="white-space: pre-wrap"><?
|
||||
|
||||
print("Task Name: ", task_name, "\n");
|
||||
print("Task ID: ", task_pid(task_name), "\n");
|
||||
print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");
|
||||
|
||||
|
||||
?></pre>
|
||||
|
||||
<?
|
||||
if(context.get["cmd"] == "Execute")
|
||||
{
|
||||
?>
|
||||
Task Start
|
||||
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
print("New Task ID: ", task_repeat("example-task", 5, []() {
|
||||
|
||||
sleep(1);
|
||||
|
||||
}), "\n");
|
||||
|
||||
print("Task interval: 5 seconds");
|
||||
|
||||
?></pre><?
|
||||
}
|
||||
?>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context.get) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#include "../demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_text(context, "Working Directory Sub-Invoke", "reveal internal filesystem layout");
|
||||
return;
|
||||
}
|
||||
print("Sub-Invoke Working dir: ", cwd_get(), "\n");
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
#include "demo_guard.h"
|
||||
|
||||
String unit_query(String selected_path, String compile_path = "")
|
||||
{
|
||||
String result = "?";
|
||||
if(selected_path != "")
|
||||
result += "selected=" + uri_encode(selected_path);
|
||||
if(compile_path != "")
|
||||
{
|
||||
if(result.length() > 1)
|
||||
result += "&";
|
||||
result += "compile=" + uri_encode(compile_path);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String unit_status_class(String compile_status, String error_status)
|
||||
{
|
||||
if(trim(error_status) != "")
|
||||
return("status-error");
|
||||
if(compile_status == "loaded" || compile_status == "compiled")
|
||||
return("status-ok");
|
||||
if(compile_status == "stale")
|
||||
return("status-warn");
|
||||
if(compile_status == "missing_source")
|
||||
return("status-error");
|
||||
return("status-muted");
|
||||
}
|
||||
|
||||
String unit_flag_label(DTree value)
|
||||
{
|
||||
return(value.to_string() == "(true)" ? "✅" : "❌");
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
bool allow_mutation = test_demo_request_allowed(context);
|
||||
String compile_target = first(context.get["compile"], "");
|
||||
String selected_path = first(context.get["selected"], "");
|
||||
String compile_message = "";
|
||||
String compile_message_class = "status-muted";
|
||||
|
||||
if(compile_target != "")
|
||||
{
|
||||
selected_path = compile_target;
|
||||
if(allow_mutation)
|
||||
{
|
||||
bool compile_ok = unit_compile(compile_target);
|
||||
compile_message = (compile_ok ? "Compile succeeded for " : "Compile failed for ") + compile_target;
|
||||
compile_message_class = compile_ok ? "status-ok" : "status-error";
|
||||
}
|
||||
else
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
compile_message = "Manual compile is restricted to localhost and private-network access.";
|
||||
compile_message_class = "status-error";
|
||||
}
|
||||
}
|
||||
|
||||
auto unit_paths = units_list();
|
||||
if(selected_path == "" && unit_paths.size() > 0)
|
||||
selected_path = unit_paths.front();
|
||||
|
||||
DTree selected_info = unit_info(selected_path);
|
||||
String selected_compile_status = selected_info["compile_status"].to_string();
|
||||
String selected_error_status = selected_info["error_status"].to_string();
|
||||
String selected_status_class = unit_status_class(selected_compile_status, selected_error_status);
|
||||
String selected_export_count = std::to_string(selected_info["exports"]._map.size());
|
||||
String selected_compile_link = unit_query(selected_path, selected_path);
|
||||
|
||||
<><html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"></meta>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<title>UCE Test: Unit Browser</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Unit Browser
|
||||
</h1>
|
||||
|
||||
<? if(compile_message != "") { ?>
|
||||
<section>
|
||||
<p><span class="status-badge <?= compile_message_class ?>"><?= compile_message ?></span></p>
|
||||
</section>
|
||||
<? } ?>
|
||||
|
||||
<section>
|
||||
<div class="unit-browser-layout">
|
||||
<aside class="unit-sidebar">
|
||||
<div class="unit-sidebar-header">
|
||||
<h2>Units</h2>
|
||||
<span class="unit-count"><?= std::to_string(unit_paths.size()) ?> total</span>
|
||||
</div>
|
||||
<div class="unit-list">
|
||||
<?
|
||||
for(auto& unit_path : unit_paths)
|
||||
{
|
||||
auto info = unit_info(unit_path);
|
||||
String compile_status = info["compile_status"].to_string();
|
||||
String error_status = info["error_status"].to_string();
|
||||
String status_class = unit_status_class(compile_status, error_status);
|
||||
String select_link = unit_query(unit_path);
|
||||
String title = first(info["src_file_name"].to_string(), unit_path);
|
||||
String meta_line = std::to_string(info["exports"]._map.size()) + " exports";
|
||||
bool is_selected = (selected_path == unit_path);
|
||||
?>
|
||||
<a class="unit-list-item <?= is_selected ? "is-selected" : "" ?>" href="<?= select_link ?>">
|
||||
<div class="unit-list-title-row" title="<?= unit_path ?>">
|
||||
<strong><?= title ?></strong>
|
||||
<span class="status-badge <?= status_class ?>"><?= compile_status ?></span>
|
||||
</div>
|
||||
|
||||
<? if(trim(error_status) != "") { ?>
|
||||
<div class="unit-list-error"><?= error_status ?></div>
|
||||
<? } ?>
|
||||
</a>
|
||||
<?
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="unit-detail-pane">
|
||||
<h2>Unit Info</h2>
|
||||
<? if(selected_info["path"].to_string() == "") { ?>
|
||||
<p>No unit metadata is available for the current selection.</p>
|
||||
<? } else { ?>
|
||||
<div class="unit-detail-header">
|
||||
<div>
|
||||
<h3><?= first(selected_info["src_file_name"].to_string(), selected_info["path"].to_string()) ?></h3>
|
||||
<p class="unit-detail-path"><code><?= selected_info["path"].to_string() ?></code></p>
|
||||
</div>
|
||||
<div class="unit-actions">
|
||||
<span class="status-badge <?= selected_status_class ?>"><?= selected_compile_status ?></span>
|
||||
<? if(allow_mutation) { ?>
|
||||
<a href="<?= selected_compile_link ?>">compile</a>
|
||||
<? } else { ?>
|
||||
<span class="dim">compile disabled on public access</span>
|
||||
<? } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-grid">
|
||||
<div class="detail-card">
|
||||
<strong>Error Status</strong>
|
||||
<span><?= trim(selected_error_status) != "" ? selected_error_status : "none" ?></span>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<strong>Exports</strong>
|
||||
<span><?= selected_export_count ?></span>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<strong>Filesystem</strong>
|
||||
<span>source <?= unit_flag_label(selected_info["source_exists"]) ?>, compiled <?= unit_flag_label(selected_info["compiled_exists"]) ?>, metadata <?= unit_flag_label(selected_info["metadata_exists"]) ?></span>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<strong>Runtime Flags</strong>
|
||||
<span>loaded <?= unit_flag_label(selected_info["loaded"]) ?>, stale <?= unit_flag_label(selected_info["stale"]) ?>, current <?= unit_flag_label(selected_info["current_unit"]) ?></span>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<strong>Artifacts</strong>
|
||||
<code><?= selected_info["so_name"].to_string() ?></code>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<strong>Timestamps</strong>
|
||||
<span>Source file: <?= time_format_relative(selected_info["source_mtime"].to_u64()) ?><br>
|
||||
Binary: <?= time_format_relative(selected_info["compiled_mtime"].to_u64()) ?><br>
|
||||
Meta info: <?= time_format_relative(selected_info["metadata_mtime"].to_u64()) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details open>
|
||||
<summary><code>unit_info()</code> payload</summary>
|
||||
<pre><?= var_dump(selected_info) ?></pre>
|
||||
</details>
|
||||
<? } ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html></>
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "demo_guard.h"
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
bool allow_mutation = test_demo_request_allowed(context);
|
||||
DTree payload;
|
||||
|
||||
if(context.get["compile"] != "")
|
||||
{
|
||||
if(allow_mutation)
|
||||
payload["compile_ok"].set_bool(unit_compile(context.get["compile"]));
|
||||
else
|
||||
{
|
||||
context.set_status(403, "Restricted");
|
||||
payload["error"] = "restricted";
|
||||
payload["reason"] = "Manual compile is restricted to localhost and private-network access.";
|
||||
}
|
||||
}
|
||||
|
||||
payload["current"] = unit_info();
|
||||
|
||||
DTree units;
|
||||
units.set_array();
|
||||
for(auto& unit_path : units_list())
|
||||
{
|
||||
DTree item;
|
||||
item = unit_path;
|
||||
units.push(item);
|
||||
}
|
||||
payload["units"] = units;
|
||||
|
||||
context.header["Content-Type"] = "application/json; charset=utf-8";
|
||||
print(json_encode(payload));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
URI
|
||||
</h1>
|
||||
<form action="?" method="post">
|
||||
<div>
|
||||
<label>uri_encode</label>
|
||||
<input type="text" name="uri_encode" value="<?= context.post["uri_encode"] ?>"/>
|
||||
<br/>
|
||||
Encode: <?= uri_encode(context.post["uri_encode"]) ?>
|
||||
<br/>
|
||||
Decode: <?= uri_decode(uri_encode(context.post["uri_encode"])) ?>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</div>
|
||||
</form>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
String string_to_hex(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
result += to_hex(c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
|
||||
String raw = first(context.post["raw"], "■👪▧▲🏳️🌈😂👋🏽😆😏😱🇦🇽Udø島リZ̸̢̧̡̧̗̰̪͉̤͖͉̪̝̦͎̮̑͜a̸̧̝̱̹̲̗̪̰̦͒̃͋̿̿̃̈͑̐͑͗̚̕̚͝͠l͚̜͕̠̣ģ̸̧̘̜͇͚͈͙̓̌̅͑͊͊͋̓́͌̈́̿̈́͗͘̚ͅͅȏ̶̗̤̳͎̫̥͕̣͔̥̙̜̰̂͌̍͊͂́̅̇̒̕̕ルイ社もなく");
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
UTF-8
|
||||
</h1>
|
||||
<form action="?" method="post">
|
||||
<div>
|
||||
<label>split_utf8</label>
|
||||
<input type="text" name="raw" value="<?= raw ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</div>
|
||||
</form>
|
||||
Simple Characters
|
||||
<pre><?
|
||||
|
||||
u32 item_idx = 0;
|
||||
for(auto seg : split_utf8(raw))
|
||||
{
|
||||
item_idx++;
|
||||
print(string_to_hex(seg), " ", seg, "\t");
|
||||
if(item_idx % 4 == 0)
|
||||
print("\n");
|
||||
}
|
||||
|
||||
?></pre>
|
||||
Compound Characters
|
||||
<pre><?
|
||||
|
||||
item_idx = 0;
|
||||
for(auto seg : split_utf8(raw, true))
|
||||
{
|
||||
item_idx++;
|
||||
print(string_to_hex(seg), " ", seg, "\t");
|
||||
if(item_idx % 4 == 0)
|
||||
print("\n");
|
||||
}
|
||||
|
||||
?></pre>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
String clean_chat_field(String raw, u32 max_length)
|
||||
{
|
||||
String value = trim(raw);
|
||||
value = replace(value, "\r", " ");
|
||||
value = replace(value, "\n", " ");
|
||||
if(value.length() > max_length)
|
||||
value.resize(max_length);
|
||||
return(value);
|
||||
}
|
||||
|
||||
DTree chat_event(Request& context, String type, String body)
|
||||
{
|
||||
DTree event;
|
||||
event["type"] = type;
|
||||
event["body"] = body;
|
||||
event["connection_id"] = ws_connection_id();
|
||||
event["scope"] = first(context.params["DOCUMENT_URI"], ws_scope());
|
||||
event["online"] = (f64)ws_connection_count();
|
||||
event["at"] = time_format_utc("%H:%M:%S");
|
||||
event["name"] = context.connection["name"].to_string();
|
||||
event["message_count"] = context.connection["message_count"];
|
||||
return(event);
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
String ws_url = context.params["DOCUMENT_URI"];
|
||||
u64 online = ws_connection_count();
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
WebSocket Chat
|
||||
</h1>
|
||||
|
||||
<label>Chat Info</label>
|
||||
<pre>Page scope: <?= ws_url ?>
|
||||
Connected right now: <span id="online-count"><?= online ?></span>
|
||||
Status: <span id="status">Connecting...</span></pre>
|
||||
|
||||
<form id="chat-form" action="?" method="post">
|
||||
<div>
|
||||
<label>Display name:</label>
|
||||
<input id="chat-name" type="text" maxlength="32" value="guest-<?= draw_int(1000, 9999) ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Message:</label>
|
||||
<textarea id="chat-message" maxlength="500" placeholder="Say something to everyone connected to this same .ws.uce page"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<input id="send-button" type="submit" value="Send Message"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<label>Chat Log</label>
|
||||
<pre id="chat-log"></pre>
|
||||
|
||||
<script>
|
||||
const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}<?= ws_url ?>`;
|
||||
const statusEl = document.getElementById('status');
|
||||
const onlineEl = document.getElementById('online-count');
|
||||
const logEl = document.getElementById('chat-log');
|
||||
const formEl = document.getElementById('chat-form');
|
||||
const nameEl = document.getElementById('chat-name');
|
||||
const messageEl = document.getElementById('chat-message');
|
||||
const sendButtonEl = document.getElementById('send-button');
|
||||
const savedName = window.localStorage.getItem('uce-chat-name');
|
||||
if (savedName) nameEl.value = savedName;
|
||||
|
||||
let ws;
|
||||
let reconnectDelay = 1000;
|
||||
|
||||
function addLine(kind, title, body, meta = '') {
|
||||
const prefix = kind === 'system' ? '[system]' : `[${title}]`;
|
||||
const metaText = meta ? ` ${meta}` : '';
|
||||
logEl.append(`${prefix} ${body}${metaText}\n`);
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
|
||||
function setStatus(text, online) {
|
||||
statusEl.textContent = text;
|
||||
if (typeof online === 'number') onlineEl.textContent = String(online);
|
||||
}
|
||||
|
||||
function sendPayload(payload) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) return false;
|
||||
payload.name = (nameEl.value || '').trim().slice(0, 32) || 'guest';
|
||||
window.localStorage.setItem('uce-chat-name', payload.name);
|
||||
ws.send(JSON.stringify(payload));
|
||||
return true;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
ws = new WebSocket(wsUrl);
|
||||
setStatus(`Connecting to ${wsUrl}...`);
|
||||
sendButtonEl.disabled = true;
|
||||
|
||||
ws.addEventListener('open', () => {
|
||||
reconnectDelay = 1000;
|
||||
sendButtonEl.disabled = false;
|
||||
setStatus('Connected');
|
||||
addLine('system', 'system', 'socket connected', new Date().toLocaleTimeString());
|
||||
sendPayload({ type: 'join' });
|
||||
});
|
||||
|
||||
ws.addEventListener('message', (event) => {
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(event.data);
|
||||
} catch (error) {
|
||||
addLine('system', 'decode error', String(error), new Date().toLocaleTimeString());
|
||||
return;
|
||||
}
|
||||
if (typeof payload.online === 'number') onlineEl.textContent = String(payload.online);
|
||||
if (payload.type === 'message') {
|
||||
addLine('', payload.name || 'guest', payload.body || '', `${payload.at || ''} ${payload.connection_id || ''}`);
|
||||
} else {
|
||||
addLine('system', payload.type || 'system', payload.body || '', `${payload.at || ''} ${payload.connection_id || ''}`);
|
||||
}
|
||||
});
|
||||
|
||||
ws.addEventListener('close', () => {
|
||||
sendButtonEl.disabled = true;
|
||||
setStatus(`Disconnected, retrying in ${reconnectDelay / 1000}s`);
|
||||
addLine('system', 'system', 'socket closed', new Date().toLocaleTimeString());
|
||||
window.setTimeout(connect, reconnectDelay);
|
||||
reconnectDelay = Math.min(reconnectDelay * 2, 10000);
|
||||
});
|
||||
|
||||
ws.addEventListener('error', () => {
|
||||
setStatus('WebSocket error');
|
||||
});
|
||||
}
|
||||
|
||||
formEl.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
const body = (messageEl.value || '').trim();
|
||||
if (!body) return;
|
||||
if (sendPayload({ type: 'message', body })) {
|
||||
messageEl.value = '';
|
||||
messageEl.focus();
|
||||
}
|
||||
});
|
||||
|
||||
connect();
|
||||
</script>
|
||||
</>
|
||||
}
|
||||
|
||||
WS(Request& context)
|
||||
{
|
||||
if(ws_is_binary())
|
||||
{
|
||||
ws_send_to(
|
||||
ws_connection_id(),
|
||||
json_encode(chat_event(context, "notice", "Binary messages are not handled by this demo"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
DTree payload = json_decode(ws_message());
|
||||
String type = clean_chat_field(payload["type"].to_string(), 24);
|
||||
String name = clean_chat_field(payload["name"].to_string(), 32);
|
||||
String body = clean_chat_field(payload["body"].to_string(), 500);
|
||||
u64 message_count = int_val(context.connection["message_count"].to_string());
|
||||
|
||||
if(name == "")
|
||||
name = first(context.connection["name"].to_string(), "guest");
|
||||
|
||||
context.connection["name"] = name;
|
||||
|
||||
if(type == "join")
|
||||
{
|
||||
context.connection["joined_at"] = time_format_utc("%Y-%m-%d %H:%M:%S");
|
||||
context.connection["last_type"] = type;
|
||||
ws_send(json_encode(chat_event(context, "join", name + " joined the room")));
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == "message" && body != "")
|
||||
{
|
||||
context.connection["last_type"] = type;
|
||||
context.connection["last_body"] = body;
|
||||
context.connection["message_count"] = (f64)(message_count + 1);
|
||||
DTree event = chat_event(context, "message", body);
|
||||
ws_send(json_encode(event));
|
||||
return;
|
||||
}
|
||||
|
||||
if(type != "")
|
||||
{
|
||||
context.connection["last_type"] = type;
|
||||
ws_send_to(ws_connection_id(), json_encode(chat_event(context, "notice", "Unknown message type: " + type)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
#include "demo_guard.h"
|
||||
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
if(!test_demo_request_allowed(context))
|
||||
{
|
||||
test_demo_render_restricted_html(context, "Working Directory", "reveal internal filesystem layout");
|
||||
return;
|
||||
}
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Working Directory
|
||||
</h1>
|
||||
<pre><?
|
||||
print("Base WD: " + cwd_get() + "\n");
|
||||
unit_render("test2/working-dir-test.uce", context);
|
||||
?></pre>
|
||||
<pre><?= var_dump(context.params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user