Static files + welcome page
This commit is contained in:
parent
7543dd9af0
commit
3cd05291d0
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#include "shared-bindings/mdns/RemoteService.h"
|
#include "shared-bindings/mdns/RemoteService.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/ipaddress/IPv4Address.h"
|
||||||
|
|
||||||
const char *common_hal_mdns_remoteservice_get_service_type(mdns_remoteservice_obj_t *self) {
|
const char *common_hal_mdns_remoteservice_get_service_type(mdns_remoteservice_obj_t *self) {
|
||||||
if (self->result == NULL) {
|
if (self->result == NULL) {
|
||||||
return "";
|
return "";
|
||||||
@ -61,6 +63,32 @@ mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self)
|
|||||||
return self->result->port;
|
return self->result->port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t mdns_remoteservice_get_ipv4(mdns_remoteservice_obj_t *self) {
|
||||||
|
if (self->result == NULL ||
|
||||||
|
self->result->ip_protocol != MDNS_IP_PROTOCOL_V4 ||
|
||||||
|
self->result->addr == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mdns_ip_addr_t *cur = self->result->addr;
|
||||||
|
while (cur != NULL) {
|
||||||
|
if (cur->addr.type == ESP_IPADDR_TYPE_V4) {
|
||||||
|
return cur->addr.u_addr.ip4.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_mdns_remoteservice_get_ipv4(mdns_remoteservice_obj_t *self) {
|
||||||
|
uint32_t addr = mdns_remoteservice_get_ipv4(self);
|
||||||
|
if (addr == 0) {
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
return common_hal_ipaddress_new_ipv4address(addr);
|
||||||
|
}
|
||||||
|
|
||||||
void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self) {
|
void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self) {
|
||||||
mdns_query_results_free(self->result);
|
mdns_query_results_free(self->result);
|
||||||
self->result = NULL;
|
self->result = NULL;
|
||||||
|
@ -38,4 +38,8 @@ const char *common_hal_mdns_remoteservice_get_protocol(mdns_remoteservice_obj_t
|
|||||||
const char *common_hal_mdns_remoteservice_get_instance_name(mdns_remoteservice_obj_t *self);
|
const char *common_hal_mdns_remoteservice_get_instance_name(mdns_remoteservice_obj_t *self);
|
||||||
const char *common_hal_mdns_remoteservice_get_hostname(mdns_remoteservice_obj_t *self);
|
const char *common_hal_mdns_remoteservice_get_hostname(mdns_remoteservice_obj_t *self);
|
||||||
mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self);
|
mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self);
|
||||||
|
mp_obj_t common_hal_mdns_remoteservice_get_ipv4(mdns_remoteservice_obj_t *self);
|
||||||
void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self);
|
void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self);
|
||||||
|
|
||||||
|
// For internal use.
|
||||||
|
uint32_t mdns_remoteservice_get_ipv4(mdns_remoteservice_obj_t *self);
|
||||||
|
BIN
supervisor/shared/web_workflow/static/blinka_16x16.ico
Normal file
BIN
supervisor/shared/web_workflow/static/blinka_16x16.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html><html><head><title></title><meta charset="UTF-8"></head>
|
<!DOCTYPE html><html><head><title></title><meta charset="UTF-8"></head>
|
||||||
<script src="http://127.0.0.1:8000/circuitpython.js" defer=true></script>
|
<script src="/directory.js" defer=true></script>
|
||||||
<body><h1></h1><template id="row"><tr><td></td><td></td><td><a></a></td><td></td><td><button class="delete">🗑️</button></td></tr></template><table><thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th></th></tr></thead><tbody></tbody></table><hr><input type="file" id="files" multiple><button type="submit" id="upload">Upload</button><hr>+🗀 <input type="text" id="name"><button type="submit" id="mkdir">Create Directory</button>
|
<body><h1></h1><template id="row"><tr><td></td><td></td><td><a></a></td><td></td><td><button class="delete">🗑️</button></td></tr></template><table><thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th></th></tr></thead><tbody></tbody></table><hr><input type="file" id="files" multiple><button type="submit" id="upload">Upload</button><hr>+🗀 <input type="text" id="name"><button type="submit" id="mkdir">Create Directory</button>
|
||||||
</body></html>
|
</body></html>
|
@ -18,7 +18,6 @@ async function refresh_list() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log(data);
|
|
||||||
var new_children = [];
|
var new_children = [];
|
||||||
var template = document.querySelector('#row');
|
var template = document.querySelector('#row');
|
||||||
|
|
||||||
@ -76,12 +75,17 @@ async function refresh_list() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function find_devices() {
|
async function find_devices() {
|
||||||
|
const version_response = await fetch("/cp/version.json");
|
||||||
|
if (version_response.ok) {
|
||||||
|
url_base = new URL("/", window.location).href;
|
||||||
|
} else {
|
||||||
|
// TODO: Remove this when we've settled things. It is only used when this file isn't hosted
|
||||||
|
// by a CP device.
|
||||||
const response = await fetch("http://circuitpython.local/cp/devices.json");
|
const response = await fetch("http://circuitpython.local/cp/devices.json");
|
||||||
let url = new URL("/", response.url);
|
let url = new URL("/", response.url);
|
||||||
console.log(response, url);
|
|
||||||
url_base = url.href;
|
url_base = url.href;
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log(data);
|
}
|
||||||
refresh_list();
|
refresh_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,9 +107,7 @@ async function mkdir(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function upload(e) {
|
async function upload(e) {
|
||||||
console.log("upload");
|
|
||||||
for (const file of files.files) {
|
for (const file of files.files) {
|
||||||
console.log(file);
|
|
||||||
let file_path = new URL("/fs" + current_path + file.name, url_base);
|
let file_path = new URL("/fs" + current_path + file.name, url_base);
|
||||||
const response = await fetch(file_path,
|
const response = await fetch(file_path,
|
||||||
{
|
{
|
||||||
@ -119,7 +121,6 @@ async function upload(e) {
|
|||||||
)
|
)
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
refresh_list();
|
refresh_list();
|
||||||
console.log(files);
|
|
||||||
files.value = "";
|
files.value = "";
|
||||||
upload_button.disabled = true;
|
upload_button.disabled = true;
|
||||||
}
|
}
|
||||||
@ -127,8 +128,6 @@ async function upload(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function del(e) {
|
async function del(e) {
|
||||||
console.log("delete");
|
|
||||||
console.log(e);
|
|
||||||
let fn = new URL(e.target.value);
|
let fn = new URL(e.target.value);
|
||||||
var prompt = "Delete " + fn.pathname.substr(3);
|
var prompt = "Delete " + fn.pathname.substr(3);
|
||||||
if (e.target.value.endsWith("/")) {
|
if (e.target.value.endsWith("/")) {
|
21
supervisor/shared/web_workflow/static/welcome.html
Normal file
21
supervisor/shared/web_workflow/static/welcome.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CircuitPython</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<script src="/welcome.js" defer=true></script>
|
||||||
|
<body>
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
Welcome to CircuitPython's Web API. Through your browser you can <a href="/fs/">work with files</a>. Make sure you've set <code>CIRCUITPY_WEB_API_PASSWORD</code> in <code>/.env</code> and provide it when the browser prompts for it. Leave the username blank.
|
||||||
|
<h2>Device Info</h2>
|
||||||
|
Board: <a id="board"></a><br>
|
||||||
|
Version: <span id="version"></span><br>
|
||||||
|
Hostname: <a id="hostname"></a><br>
|
||||||
|
IP: <a id="ip"></a>
|
||||||
|
<h2>Other Devices</h2>
|
||||||
|
Here are other CircuitPython devices on your network:
|
||||||
|
<ul id="devices">
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
62
supervisor/shared/web_workflow/static/welcome.js
Normal file
62
supervisor/shared/web_workflow/static/welcome.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
var url_base = window.location;
|
||||||
|
var current_path;
|
||||||
|
|
||||||
|
var mdns_works = window.location.hostname.endsWith(".local");
|
||||||
|
|
||||||
|
async function find_devices() {
|
||||||
|
var version_response = await fetch("/cp/version.json");
|
||||||
|
if (version_response.ok) {
|
||||||
|
url_base = new URL("/", window.location).href;
|
||||||
|
} else {
|
||||||
|
// TODO: Remove this when we've settled things. It is only used when this file isn't hosted
|
||||||
|
// by a CP device.
|
||||||
|
version_response = await fetch("http://circuitpython.local/cp/version.json", {mode: "cors"});
|
||||||
|
mdns_works = mdns_works || version_response.redirected;
|
||||||
|
if (!version_response.ok && version_response.redirected) {
|
||||||
|
version_response = await fetch(version_response.url);
|
||||||
|
}
|
||||||
|
let url = new URL("/", version_response.url);
|
||||||
|
url_base = url.href;
|
||||||
|
}
|
||||||
|
const version_info = await version_response.json();
|
||||||
|
let version_span = document.querySelector("#version");
|
||||||
|
version_span.textContent = version_info.version;
|
||||||
|
let board_link = document.querySelector("#board");
|
||||||
|
board_link.href = "https://circuitpython.org/board/" + version_info.board_id + "/";
|
||||||
|
board_link.textContent = version_info.board_name;
|
||||||
|
let hostname = document.querySelector("#hostname");
|
||||||
|
var port = "";
|
||||||
|
if (version_info.port != 80) {
|
||||||
|
port = ":" + version_info.port;
|
||||||
|
}
|
||||||
|
hostname.href = "http://" + version_info.hostname + ".local" + port + "/";
|
||||||
|
hostname.textContent = version_info.hostname;
|
||||||
|
let ip = document.querySelector("#ip");
|
||||||
|
ip.href = "http://" + version_info.ip + port + "/";
|
||||||
|
ip.textContent = version_info.ip;
|
||||||
|
const response = await fetch(new URL("/cp/devices.json", url_base));
|
||||||
|
const data = await response.json();
|
||||||
|
let device_list = document.querySelector("#devices");
|
||||||
|
let new_devices = [];
|
||||||
|
for (device of data.devices) {
|
||||||
|
let li = document.createElement("li");
|
||||||
|
let a = document.createElement("a");
|
||||||
|
li.appendChild(a);
|
||||||
|
var port = "";
|
||||||
|
if (device.port != 80) {
|
||||||
|
port = ":" + version_info.port;
|
||||||
|
}
|
||||||
|
var server;
|
||||||
|
if (mdns_works) {
|
||||||
|
server = device.hostname + ".local";
|
||||||
|
} else {
|
||||||
|
server = device.ip;
|
||||||
|
}
|
||||||
|
a.href = "http://" + server + port + "/";
|
||||||
|
a.textContent = device.instance_name + " (" + device.hostname + ")";
|
||||||
|
new_devices.push(li);
|
||||||
|
}
|
||||||
|
device_list.replaceChildren(...new_devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
find_devices();
|
@ -250,29 +250,10 @@ void supervisor_start_web_workflow(void) {
|
|||||||
active.num = -1;
|
active.num = -1;
|
||||||
active.connected = false;
|
active.connected = false;
|
||||||
|
|
||||||
// Accept a connection and start parsing:
|
// TODO:
|
||||||
// * HTTP method
|
|
||||||
// * HTTP path
|
|
||||||
// * Headers we care about:
|
|
||||||
// * Authentication
|
|
||||||
// * Must match CIRCUITPY_WEB_AUTH
|
|
||||||
// * Host
|
|
||||||
// * IP - ok
|
|
||||||
// * cpy-mac.local - ok
|
|
||||||
// * circuitpython.local - redirect
|
|
||||||
// * Content-Length
|
|
||||||
//
|
|
||||||
// PUT /fs/<filename>
|
|
||||||
// GET /fs/<filename>
|
|
||||||
// - File contents
|
|
||||||
// - JSON directory representation
|
|
||||||
// GET /cp/devices.json
|
|
||||||
// - JSON list of MDNS results
|
|
||||||
// GET /cp/version.json
|
|
||||||
// - JSON version info
|
|
||||||
// GET /cp/serial.txt
|
// GET /cp/serial.txt
|
||||||
// - Most recent 1k of serial output.
|
// - Most recent 1k of serial output.
|
||||||
// GET /
|
// GET /edit/
|
||||||
// - Super basic editor
|
// - Super basic editor
|
||||||
// GET /ws/circuitpython
|
// GET /ws/circuitpython
|
||||||
// GET /ws/user
|
// GET /ws/user
|
||||||
@ -313,18 +294,33 @@ static bool _endswith(const char *str, const char *suffix) {
|
|||||||
return strcmp(str + (strlen(str) - strlen(suffix)), suffix) == 0;
|
return strcmp(str + (strlen(str) - strlen(suffix)), suffix) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _cors_header(socketpool_socket_obj_t *socket, _request *request) {
|
const char *ok_hosts[] = {"code.circuitpython.org"};
|
||||||
bool origin_ok = false;
|
|
||||||
|
static bool _origin_ok(const char *origin) {
|
||||||
#if CIRCUITPY_DEBUG
|
#if CIRCUITPY_DEBUG
|
||||||
origin_ok = true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
const char *localhost = "127.0.0.1:";
|
||||||
|
const char *http = "http://";
|
||||||
|
// This is a prefix check.
|
||||||
|
if (memcmp(origin + strlen(http), localhost, strlen(localhost)) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < MP_ARRAY_SIZE(ok_hosts); i++) {
|
||||||
|
// This checks exactly.
|
||||||
|
if (strcmp(origin + strlen(http), ok_hosts[i]) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cors_header(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
_send_str(socket, "Access-Control-Allow-Credentials: true\r\nVary: Origin\r\n");
|
_send_str(socket, "Access-Control-Allow-Credentials: true\r\nVary: Origin\r\n");
|
||||||
if (origin_ok) {
|
|
||||||
_send_str(socket, "Access-Control-Allow-Origin: ");
|
_send_str(socket, "Access-Control-Allow-Origin: ");
|
||||||
_send_str(socket, request->origin);
|
_send_str(socket, request->origin);
|
||||||
_send_str(socket, "\r\n");
|
_send_str(socket, "\r\n");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void _reply_continue(socketpool_socket_obj_t *socket, _request *request) {
|
static void _reply_continue(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
const char *response = "HTTP/1.1 100 Continue\r\n";
|
const char *response = "HTTP/1.1 100 Continue\r\n";
|
||||||
@ -367,6 +363,14 @@ static void _reply_missing(socketpool_socket_obj_t *socket, _request *request) {
|
|||||||
_send_str(socket, "\r\n");
|
_send_str(socket, "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _reply_method_not_allowed(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
|
const char *response = "HTTP/1.1 405 Method Not Allowed\r\n"
|
||||||
|
"Content-Length: 0\r\n";
|
||||||
|
_send_str(socket, response);
|
||||||
|
_cors_header(socket, request);
|
||||||
|
_send_str(socket, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
static void _reply_forbidden(socketpool_socket_obj_t *socket, _request *request) {
|
static void _reply_forbidden(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
const char *response = "HTTP/1.1 403 Forbidden\r\n"
|
const char *response = "HTTP/1.1 403 Forbidden\r\n"
|
||||||
"Content-Length: 0\r\n";
|
"Content-Length: 0\r\n";
|
||||||
@ -486,22 +490,6 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
|
|||||||
_send_chunk(socket, "");
|
_send_chunk(socket, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _reply_directory_html(socketpool_socket_obj_t *socket, _request *request, FF_DIR *dir, const char *request_path, const char *path) {
|
|
||||||
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
|
|
||||||
"Content-Type: text/html\r\n";
|
|
||||||
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
|
||||||
_cors_header(socket, request);
|
|
||||||
_send_str(socket, "\r\n");
|
|
||||||
_send_chunk(socket, "<!DOCTYPE html><html><head><title></title><meta charset=\"UTF-8\"></head>");
|
|
||||||
_send_chunk(socket, "<script src=\"http://127.0.0.1:8000/circuitpython.js\" defer=true></script>");
|
|
||||||
_send_chunk(socket, "<body><h1></h1><template id=\"row\"><tr><td></td><td></td><td><a></a></td><td></td><td><button class=\"delete\">🗑️</button></td></tr></template><table><thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th></th></tr></thead><tbody>");
|
|
||||||
_send_chunk(socket, "</tbody></table><hr><input type=\"file\" id=\"files\" multiple><button type=\"submit\" id=\"upload\">Upload</button>");
|
|
||||||
|
|
||||||
_send_chunk(socket, "<hr>+🗀 <input type=\"text\" id=\"name\"><button type=\"submit\" id=\"mkdir\">Create Directory</button>");
|
|
||||||
_send_chunk(socket, "</body></html>");
|
|
||||||
_send_chunk(socket, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _reply_with_file(socketpool_socket_obj_t *socket, _request *request, const char *filename, FIL *active_file) {
|
static void _reply_with_file(socketpool_socket_obj_t *socket, _request *request, const char *filename, FIL *active_file) {
|
||||||
uint32_t total_length = f_size(active_file);
|
uint32_t total_length = f_size(active_file);
|
||||||
char encoded_len[10];
|
char encoded_len[10];
|
||||||
@ -577,7 +565,14 @@ static void _reply_with_devices_json(socketpool_socket_obj_t *socket, _request *
|
|||||||
int port = common_hal_mdns_remoteservice_get_port(&found_devices[i]);
|
int port = common_hal_mdns_remoteservice_get_port(&found_devices[i]);
|
||||||
snprintf(port_encoded, sizeof(port_encoded), "%d", port);
|
snprintf(port_encoded, sizeof(port_encoded), "%d", port);
|
||||||
_send_chunk(socket, port_encoded);
|
_send_chunk(socket, port_encoded);
|
||||||
_send_chunk(socket, "}");
|
_send_chunk(socket, ", \"ip\": \"");
|
||||||
|
|
||||||
|
char ip_encoded[4 * 4];
|
||||||
|
uint32_t ipv4_address = mdns_remoteservice_get_ipv4(&found_devices[i]);
|
||||||
|
uint8_t *octets = (uint8_t *)&ipv4_address;
|
||||||
|
snprintf(ip_encoded, sizeof(ip_encoded), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]);
|
||||||
|
_send_chunk(socket, ip_encoded);
|
||||||
|
_send_chunk(socket, "\"}");
|
||||||
common_hal_mdns_remoteservice_deinit(&found_devices[i]);
|
common_hal_mdns_remoteservice_deinit(&found_devices[i]);
|
||||||
}
|
}
|
||||||
_send_chunk(socket, "]}");
|
_send_chunk(socket, "]}");
|
||||||
@ -607,7 +602,17 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request *
|
|||||||
_send_chunk(socket, ", \"creation_id\": ");
|
_send_chunk(socket, ", \"creation_id\": ");
|
||||||
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATION_ID);
|
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATION_ID);
|
||||||
_send_chunk(socket, encoded_id);
|
_send_chunk(socket, encoded_id);
|
||||||
_send_chunk(socket, "}");
|
_send_chunk(socket, ", \"hostname\": \"");
|
||||||
|
_send_chunk(socket, common_hal_mdns_server_get_hostname(&mdns));
|
||||||
|
_send_chunk(socket, "\", \"port\": 80");
|
||||||
|
_send_chunk(socket, ", \"ip\": \"");
|
||||||
|
|
||||||
|
char ip_encoded[4 * 4];
|
||||||
|
uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj);
|
||||||
|
uint8_t *octets = (uint8_t *)&ipv4_address;
|
||||||
|
snprintf(ip_encoded, sizeof(ip_encoded), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]);
|
||||||
|
_send_chunk(socket, ip_encoded);
|
||||||
|
_send_chunk(socket, "\"}");
|
||||||
// Empty chunk signals the end of the response.
|
// Empty chunk signals the end of the response.
|
||||||
_send_chunk(socket, "");
|
_send_chunk(socket, "");
|
||||||
}
|
}
|
||||||
@ -730,9 +735,36 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define STATIC_FILE(filename) extern uint32_t filename##_length; extern uint8_t filename[]; extern const char *filename##_content_type;
|
||||||
|
|
||||||
|
STATIC_FILE(directory_html);
|
||||||
|
STATIC_FILE(directory_js);
|
||||||
|
STATIC_FILE(welcome_html);
|
||||||
|
STATIC_FILE(welcome_js);
|
||||||
|
STATIC_FILE(blinka_16x16_ico);
|
||||||
|
|
||||||
|
static void _reply_static(socketpool_socket_obj_t *socket, _request *request, const uint8_t *response, size_t response_len, const char *content_type) {
|
||||||
|
uint32_t total_length = response_len;
|
||||||
|
char encoded_len[10];
|
||||||
|
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
|
||||||
|
|
||||||
|
_send_str(socket, "HTTP/1.1 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: ");
|
||||||
|
_send_str(socket, encoded_len);
|
||||||
|
_send_str(socket, "\r\n");
|
||||||
|
_send_str(socket, "Content-Type: ");
|
||||||
|
_send_str(socket, content_type);
|
||||||
|
_send_str(socket, "\r\n");
|
||||||
|
_send_str(socket, "\r\n");
|
||||||
|
_send_raw(socket, response, response_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _REPLY_STATIC(socket, request, filename) _reply_static(socket, request, filename, filename##_length, filename##_content_type)
|
||||||
|
|
||||||
static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
||||||
if (request->redirect) {
|
if (request->redirect) {
|
||||||
_reply_redirect(socket, request, request->path);
|
_reply_redirect(socket, request, request->path);
|
||||||
|
} else if (strlen(request->origin) > 0 && !_origin_ok(request->origin)) {
|
||||||
|
_reply_forbidden(socket, request);
|
||||||
} else if (memcmp(request->path, "/fs/", 4) == 0) {
|
} else if (memcmp(request->path, "/fs/", 4) == 0) {
|
||||||
if (!request->authenticated) {
|
if (!request->authenticated) {
|
||||||
ESP_LOGW(TAG, "not authed");
|
ESP_LOGW(TAG, "not authed");
|
||||||
@ -800,8 +832,10 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||||||
}
|
}
|
||||||
if (request->json) {
|
if (request->json) {
|
||||||
_reply_directory_json(socket, request, &dir, request->path, path);
|
_reply_directory_json(socket, request, &dir, request->path, path);
|
||||||
|
} else if (pathlen == 1) {
|
||||||
|
_REPLY_STATIC(socket, request, directory_html);
|
||||||
} else {
|
} else {
|
||||||
_reply_directory_html(socket, request, &dir, request->path, path);
|
_reply_missing(socket, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
f_closedir(&dir);
|
f_closedir(&dir);
|
||||||
@ -857,10 +891,24 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||||||
} else if (strcmp(path, "/version.json") == 0) {
|
} else if (strcmp(path, "/version.json") == 0) {
|
||||||
_reply_with_version_json(socket, request);
|
_reply_with_version_json(socket, request);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(request->method, "GET") != 0) {
|
||||||
|
_reply_method_not_allowed(socket, request);
|
||||||
} else {
|
} else {
|
||||||
const char *ok_response = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/plain\r\n\r\nHello World";
|
if (strcmp(request->path, "/") == 0) {
|
||||||
int sent = socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
|
// TODO: Autogenerate this based on the blinka bitmap and change the
|
||||||
ESP_LOGW(TAG, "sent ok %d", sent);
|
// palette based on MAC address.
|
||||||
|
_REPLY_STATIC(socket, request, welcome_html);
|
||||||
|
} else if (strcmp(request->path, "/directory.js") == 0) {
|
||||||
|
_REPLY_STATIC(socket, request, directory_js);
|
||||||
|
} else if (strcmp(request->path, "/welcome.js") == 0) {
|
||||||
|
_REPLY_STATIC(socket, request, welcome_js);
|
||||||
|
} else if (strcmp(request->path, "/favicon.ico") == 0) {
|
||||||
|
// TODO: Autogenerate this based on the blinka bitmap and change the
|
||||||
|
// palette based on MAC address.
|
||||||
|
_REPLY_STATIC(socket, request, blinka_16x16_ico);
|
||||||
|
} else {
|
||||||
|
_reply_missing(socket, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -159,8 +159,18 @@ ifeq ($(CIRCUITPY_USB),1)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
STATIC_RESOURCES = $(wildcard $(TOP)/supervisor/shared/web_workflow/static/*)
|
||||||
|
|
||||||
|
$(BUILD)/autogen_web_workflow_static.c: ../../tools/gen_web_workflow_static.py $(STATIC_RESOURCES)
|
||||||
|
$(STEPECHO) "GEN $@"
|
||||||
|
$(Q)$(PYTHON) $< \
|
||||||
|
--output_c_file $@ \
|
||||||
|
$(STATIC_RESOURCES)
|
||||||
|
|
||||||
ifeq ($(CIRCUITPY_WEB_WORKFLOW),1)
|
ifeq ($(CIRCUITPY_WEB_WORKFLOW),1)
|
||||||
SRC_SUPERVISOR += supervisor/shared/web_workflow/web_workflow.c
|
SRC_SUPERVISOR += supervisor/shared/web_workflow/web_workflow.c
|
||||||
|
|
||||||
|
SRC_SUPERVISOR += $(BUILD)/autogen_web_workflow_static.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SRC_TINYUSB = $(filter lib/tinyusb/%.c, $(SRC_SUPERVISOR))
|
SRC_TINYUSB = $(filter lib/tinyusb/%.c, $(SRC_SUPERVISOR))
|
||||||
@ -217,4 +227,4 @@ $(BUILD)/autogen_display_resources-$(TRANSLATION).c: ../../tools/gen_display_res
|
|||||||
$(Q)$(PYTHON) ../../tools/gen_display_resources.py \
|
$(Q)$(PYTHON) ../../tools/gen_display_resources.py \
|
||||||
--font $(CIRCUITPY_DISPLAY_FONT) \
|
--font $(CIRCUITPY_DISPLAY_FONT) \
|
||||||
--sample_file $(TOP)/locale/$(TRANSLATION).po \
|
--sample_file $(TOP)/locale/$(TRANSLATION).po \
|
||||||
--output_c_file $(BUILD)/autogen_display_resources-$(TRANSLATION).c
|
--output_c_file $@
|
||||||
|
36
tools/gen_web_workflow_static.py
Normal file
36
tools/gen_web_workflow_static.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import gzip
|
||||||
|
import mimetypes
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Generate displayio resources.")
|
||||||
|
parser.add_argument("--output_c_file", type=argparse.FileType("w"), required=True)
|
||||||
|
parser.add_argument("files", metavar="FILE", type=argparse.FileType("rb"), nargs="+")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
c_file = args.output_c_file
|
||||||
|
|
||||||
|
c_file.write(f"// Autogenerated by tools/gen_web_workflow_static.py\n")
|
||||||
|
c_file.write(f"#include <stdint.h>\n\n")
|
||||||
|
|
||||||
|
for f in args.files:
|
||||||
|
path = pathlib.Path(f.name)
|
||||||
|
variable = path.name.replace(".", "_")
|
||||||
|
uncompressed = f.read()
|
||||||
|
ulen = len(uncompressed)
|
||||||
|
compressed = gzip.compress(uncompressed)
|
||||||
|
clen = len(compressed)
|
||||||
|
compressed = ", ".join([hex(x) for x in compressed])
|
||||||
|
mime = mimetypes.guess_type(f.name)[0]
|
||||||
|
|
||||||
|
c_file.write(f"// {f.name}\n")
|
||||||
|
c_file.write(f"// Original length: {ulen} Compressed length: {clen}\n")
|
||||||
|
c_file.write(f"const uint32_t {variable}_length = {clen};\n")
|
||||||
|
c_file.write(f'const char* {variable}_content_type = "{mime}";\n')
|
||||||
|
c_file.write(f"const uint8_t {variable}[{clen}] = {{{compressed}}};\n\n")
|
Loading…
x
Reference in New Issue
Block a user