feature: add ability to drop a .spa.static into the tree
This commit is contained in:
parent
75439172c9
commit
d07991bc60
6 changed files with 175 additions and 70 deletions
|
|
@ -3,19 +3,34 @@
|
|||
* @module
|
||||
*/
|
||||
|
||||
import * as path from '@std/path';
|
||||
import { PRECHECK, SERVER } from '../server.ts';
|
||||
import { getCookies } from '@std/http/cookie';
|
||||
import { load_html_with_ssi } from './html.ts';
|
||||
import * as path from "@std/path";
|
||||
import { PRECHECK, SERVER } from "../server.ts";
|
||||
import { getCookies } from "@std/http/cookie";
|
||||
import { load_html_with_ssi } from "./html.ts";
|
||||
|
||||
async function find_spa_file_root(request_path: string): Promise<string | undefined> {
|
||||
let current_path = Deno.cwd();
|
||||
const relative_path = path.relative(current_path, request_path);
|
||||
const path_elements = relative_path.split('/').slice(0, -1);
|
||||
const cwd = Deno.cwd();
|
||||
|
||||
const relative_path = path.relative(cwd, request_path);
|
||||
const path_elements = relative_path.split("/").slice(0, -1);
|
||||
|
||||
do {
|
||||
const current_path = path.join(cwd, ...path_elements);
|
||||
|
||||
try {
|
||||
const spa_file_stat = await Deno.stat(path.join(current_path, '.spa'));
|
||||
const spa_static_file_stat = await Deno.stat(path.join(current_path, ".spa.static"));
|
||||
|
||||
if (spa_static_file_stat.isFile) {
|
||||
return; // return nothing, we want files under this path to be handled as if static
|
||||
}
|
||||
} catch (error) {
|
||||
if (!(error instanceof Deno.errors.NotFound)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const spa_file_stat = await Deno.stat(path.join(current_path, ".spa"));
|
||||
|
||||
if (spa_file_stat.isFile) {
|
||||
return current_path;
|
||||
|
|
@ -25,39 +40,41 @@ async function find_spa_file_root(request_path: string): Promise<string | undefi
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
current_path = path.join(current_path, path_elements.shift() ?? '');
|
||||
} while (path_elements.length);
|
||||
} while (path_elements.pop());
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export type HTTP_METHOD = 'GET' | 'PUT' | 'POST' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
||||
export type HTTP_METHOD = "GET" | "PUT" | "POST" | "DELETE" | "HEAD" | "OPTIONS";
|
||||
export type HANDLER_METHOD = (
|
||||
request: Request,
|
||||
normalized_path: string,
|
||||
server: SERVER
|
||||
server: SERVER,
|
||||
) => Promise<Response | undefined> | Response | undefined;
|
||||
export const PRECHECKS: Partial<Record<HTTP_METHOD, PRECHECK[]>> = {};
|
||||
export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
||||
HEAD: async (_request: Request, normalized_path: string, _server: SERVER): Promise<Response | undefined> => {
|
||||
HEAD: async (
|
||||
_request: Request,
|
||||
normalized_path: string,
|
||||
_server: SERVER,
|
||||
): Promise<Response | undefined> => {
|
||||
const spa_root = await find_spa_file_root(normalized_path);
|
||||
if (!spa_root) {
|
||||
return;
|
||||
}
|
||||
|
||||
for await (const index_filename of ['index.html', 'index.htm']) {
|
||||
for await (const index_filename of ["index.html", "index.htm"]) {
|
||||
try {
|
||||
const index_file_path = path.join(spa_root, index_filename);
|
||||
const index_file_stat = await Deno.stat(index_file_path);
|
||||
|
||||
if (index_file_stat.isFile) {
|
||||
return new Response('', {
|
||||
return new Response("", {
|
||||
headers: {
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': `${index_file_stat.size}`,
|
||||
'Last-Modified': `${index_file_stat.mtime ?? index_file_stat.ctime}`
|
||||
}
|
||||
"Content-Type": "text/html",
|
||||
"Content-Length": `${index_file_stat.size}`,
|
||||
"Last-Modified": `${index_file_stat.mtime ?? index_file_stat.ctime}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -70,13 +87,17 @@ export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
|||
}
|
||||
},
|
||||
|
||||
GET: async (request: Request, normalized_path: string, _server: SERVER): Promise<Response | undefined> => {
|
||||
GET: async (
|
||||
request: Request,
|
||||
normalized_path: string,
|
||||
_server: SERVER,
|
||||
): Promise<Response | undefined> => {
|
||||
const spa_root = await find_spa_file_root(normalized_path);
|
||||
if (!spa_root) {
|
||||
return;
|
||||
}
|
||||
|
||||
for await (const index_filename of ['index.html', 'index.htm']) {
|
||||
for await (const index_filename of ["index.html", "index.htm"]) {
|
||||
try {
|
||||
const index_file_path = path.join(spa_root, index_filename);
|
||||
const index_file_stat = await Deno.stat(index_file_path);
|
||||
|
|
@ -85,8 +106,11 @@ export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
|||
const processed: string = await load_html_with_ssi(index_file_path);
|
||||
return new Response(processed, {
|
||||
headers: {
|
||||
'Content-Type': request.headers.get('accept')?.indexOf('text/plain') !== -1 ? 'text/plain' : 'text/html'
|
||||
}
|
||||
"Content-Type":
|
||||
request.headers.get("accept")?.indexOf("text/plain") !== -1
|
||||
? "text/plain"
|
||||
: "text/html",
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -105,19 +129,20 @@ export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
|||
return;
|
||||
}
|
||||
|
||||
for await (const index_filename of ['index.html', 'index.htm']) {
|
||||
for await (const index_filename of ["index.html", "index.htm"]) {
|
||||
try {
|
||||
const index_file_path = path.join(spa_root, index_filename);
|
||||
const index_file_stat = await Deno.stat(index_file_path);
|
||||
|
||||
if (index_file_stat.isFile) {
|
||||
const allowed = ['GET', 'HEAD', 'OPTIONS'];
|
||||
const allowed = ["GET", "HEAD", "OPTIONS"];
|
||||
|
||||
return new Response('', {
|
||||
return new Response("", {
|
||||
headers: {
|
||||
'Allow': allowed.sort().join(','),
|
||||
'Access-Control-Allow-Origin': Deno.env.get('SERVERUS_ACCESS_CONTROL_ALLOW_ORIGIN') ?? '*'
|
||||
}
|
||||
Allow: allowed.sort().join(","),
|
||||
"Access-Control-Allow-Origin":
|
||||
Deno.env.get("SERVERUS_ACCESS_CONTROL_ALLOW_ORIGIN") ?? "*",
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -128,7 +153,7 @@ export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -137,7 +162,10 @@ export const HANDLERS: Partial<Record<HTTP_METHOD, HANDLER_METHOD>> = {
|
|||
* @param request The incoming HTTP request
|
||||
* @returns Either a response (a static file was requested and returned properly) or undefined if unhandled.
|
||||
*/
|
||||
export default async function handle_spa_files_in_path(request: Request, server: SERVER): Promise<Response | undefined> {
|
||||
export default async function handle_spa_files_in_path(
|
||||
request: Request,
|
||||
server: SERVER,
|
||||
): Promise<Response | undefined> {
|
||||
const method: HTTP_METHOD = request.method.toUpperCase() as HTTP_METHOD;
|
||||
const handler: HANDLER_METHOD | undefined = HANDLERS[method];
|
||||
|
||||
|
|
@ -146,7 +174,9 @@ export default async function handle_spa_files_in_path(request: Request, server:
|
|||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const normalized_path = path.resolve(path.normalize(decodeURIComponent(url.pathname)).replace(/^\/+/, ''));
|
||||
const normalized_path = path.resolve(
|
||||
path.normalize(decodeURIComponent(url.pathname)).replace(/^\/+/, ""),
|
||||
);
|
||||
|
||||
// if they're requesting something outside the working dir, just bail
|
||||
if (!normalized_path.startsWith(Deno.cwd())) {
|
||||
|
|
@ -160,7 +190,7 @@ export default async function handle_spa_files_in_path(request: Request, server:
|
|||
|
||||
const metadata = {
|
||||
cookies,
|
||||
query
|
||||
query,
|
||||
};
|
||||
|
||||
for await (const precheck of prechecks) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue