@tknf/matchbox 0.2.6 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -46
- package/dist/cgi.d.ts +10 -21
- package/dist/cgi.js +26 -97
- package/dist/htaccess/access-control.d.ts +9 -0
- package/dist/htaccess/access-control.js +91 -0
- package/dist/htaccess/error-document.d.ts +9 -0
- package/dist/htaccess/error-document.js +16 -0
- package/dist/htaccess/headers.d.ts +32 -0
- package/dist/htaccess/headers.js +98 -0
- package/dist/htaccess/index.d.ts +8 -0
- package/dist/htaccess/index.js +20 -0
- package/dist/htaccess/parser.d.ts +9 -0
- package/dist/htaccess/parser.js +365 -0
- package/dist/htaccess/rewrite.d.ts +13 -0
- package/dist/htaccess/rewrite.js +69 -0
- package/dist/htaccess/types.d.ts +156 -0
- package/dist/htaccess/types.js +0 -0
- package/dist/htaccess/utils.d.ts +21 -0
- package/dist/htaccess/utils.js +69 -0
- package/dist/html.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/middleware/auth.d.ts +8 -0
- package/dist/middleware/auth.js +28 -0
- package/dist/middleware/htaccess.d.ts +13 -0
- package/dist/middleware/htaccess.js +42 -0
- package/dist/middleware/index.d.ts +10 -0
- package/dist/middleware/index.js +14 -0
- package/dist/middleware/protected-files.d.ts +8 -0
- package/dist/middleware/protected-files.js +14 -0
- package/dist/middleware/session.d.ts +16 -0
- package/dist/middleware/session.js +37 -0
- package/dist/middleware/trailing-slash.d.ts +8 -0
- package/dist/middleware/trailing-slash.js +12 -0
- package/dist/with-defaults.d.ts +2 -1
- package/dist/with-defaults.js +17 -28
- package/package.json +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main configuration type for .htaccess files (replaces RewriteMap)
|
|
5
|
+
* Maps directory paths to their respective configurations
|
|
6
|
+
*/
|
|
7
|
+
type HtaccessConfig = Record<string, DirectoryConfig>;
|
|
8
|
+
/**
|
|
9
|
+
* Configuration for a single directory
|
|
10
|
+
*/
|
|
11
|
+
interface DirectoryConfig {
|
|
12
|
+
rewriteRules: RewriteRuleConfig[];
|
|
13
|
+
redirects: RedirectConfig[];
|
|
14
|
+
errorDocuments: ErrorDocumentConfig[];
|
|
15
|
+
headers: HeaderConfig[];
|
|
16
|
+
authConfig?: AuthConfig;
|
|
17
|
+
accessControl?: AccessControlConfig;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* RewriteRule configuration
|
|
21
|
+
*/
|
|
22
|
+
interface RewriteRuleConfig {
|
|
23
|
+
type: "rewrite";
|
|
24
|
+
pattern: string;
|
|
25
|
+
target: string;
|
|
26
|
+
flags: RewriteFlags;
|
|
27
|
+
conditions: RewriteCondition[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* RewriteCond configuration
|
|
31
|
+
*/
|
|
32
|
+
interface RewriteCondition {
|
|
33
|
+
testString: string;
|
|
34
|
+
pattern: string;
|
|
35
|
+
flags: ConditionFlags;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* RewriteRule flags
|
|
39
|
+
*/
|
|
40
|
+
interface RewriteFlags {
|
|
41
|
+
last?: boolean;
|
|
42
|
+
redirect?: number;
|
|
43
|
+
forbidden?: boolean;
|
|
44
|
+
gone?: boolean;
|
|
45
|
+
noCase?: boolean;
|
|
46
|
+
qsAppend?: boolean;
|
|
47
|
+
qsDiscard?: boolean;
|
|
48
|
+
noEscape?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* RewriteCond flags
|
|
52
|
+
*/
|
|
53
|
+
interface ConditionFlags {
|
|
54
|
+
noCase?: boolean;
|
|
55
|
+
or?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* ErrorDocument configuration
|
|
59
|
+
*/
|
|
60
|
+
interface ErrorDocumentConfig {
|
|
61
|
+
statusCode: number;
|
|
62
|
+
target: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Header directive configuration
|
|
66
|
+
*/
|
|
67
|
+
interface HeaderConfig {
|
|
68
|
+
action: "set" | "append" | "unset";
|
|
69
|
+
name: string;
|
|
70
|
+
value?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Redirect configuration (legacy support)
|
|
74
|
+
*/
|
|
75
|
+
interface RedirectConfig {
|
|
76
|
+
type: "redirect";
|
|
77
|
+
code: number;
|
|
78
|
+
source: string;
|
|
79
|
+
target: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Authentication configuration
|
|
83
|
+
*/
|
|
84
|
+
interface AuthConfig {
|
|
85
|
+
authType?: "Basic" | "Digest";
|
|
86
|
+
authName?: string;
|
|
87
|
+
authUserFile?: string;
|
|
88
|
+
authGroupFile?: string;
|
|
89
|
+
authDigestProvider?: string;
|
|
90
|
+
require?: RequireConfig[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Require directive configuration
|
|
94
|
+
*/
|
|
95
|
+
interface RequireConfig {
|
|
96
|
+
type: "valid-user" | "user" | "group" | "ip" | "host" | "all";
|
|
97
|
+
value?: string | string[];
|
|
98
|
+
granted?: boolean;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Access Control configuration (Apache 2.2 style - Order/Allow/Deny)
|
|
102
|
+
*/
|
|
103
|
+
interface AccessControlConfig {
|
|
104
|
+
order?: "allow,deny" | "deny,allow" | "mutual-failure";
|
|
105
|
+
allow: AccessRule[];
|
|
106
|
+
deny: AccessRule[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Access rule for Allow/Deny directives
|
|
110
|
+
*/
|
|
111
|
+
interface AccessRule {
|
|
112
|
+
type: "all" | "ip" | "host" | "env";
|
|
113
|
+
value?: string | string[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Variable context for RewriteCond evaluation
|
|
117
|
+
*/
|
|
118
|
+
interface VariableContext {
|
|
119
|
+
HTTP_HOST: string;
|
|
120
|
+
HTTP_USER_AGENT: string;
|
|
121
|
+
REQUEST_URI: string;
|
|
122
|
+
QUERY_STRING: string;
|
|
123
|
+
HTTPS: string;
|
|
124
|
+
REMOTE_ADDR: string;
|
|
125
|
+
REQUEST_METHOD: string;
|
|
126
|
+
HTTP_REFERER: string;
|
|
127
|
+
HTTP_ACCEPT: string;
|
|
128
|
+
HTTP_COOKIE: string;
|
|
129
|
+
SERVER_NAME: string;
|
|
130
|
+
SERVER_PORT: string;
|
|
131
|
+
DOCUMENT_ROOT: string;
|
|
132
|
+
REQUEST_FILENAME: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Result of applying rewrite flags
|
|
136
|
+
*/
|
|
137
|
+
type RewriteResult = {
|
|
138
|
+
type: "continue";
|
|
139
|
+
} | {
|
|
140
|
+
type: "redirect";
|
|
141
|
+
url: string;
|
|
142
|
+
status: number;
|
|
143
|
+
} | {
|
|
144
|
+
type: "forbidden";
|
|
145
|
+
} | {
|
|
146
|
+
type: "gone";
|
|
147
|
+
} | {
|
|
148
|
+
type: "rewrite";
|
|
149
|
+
path: string;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Hono Context (for type compatibility)
|
|
153
|
+
*/
|
|
154
|
+
type HonoContext = Context;
|
|
155
|
+
|
|
156
|
+
export type { AccessControlConfig, AccessRule, AuthConfig, ConditionFlags, DirectoryConfig, ErrorDocumentConfig, HeaderConfig, HonoContext, HtaccessConfig, RedirectConfig, RequireConfig, RewriteCondition, RewriteFlags, RewriteResult, RewriteRuleConfig, VariableContext };
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { VariableContext, RewriteFlags, RewriteResult, RewriteCondition } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build variable context from Hono context
|
|
6
|
+
*/
|
|
7
|
+
declare function buildVariableContext(c: Context): VariableContext;
|
|
8
|
+
/**
|
|
9
|
+
* Expand variables like %{HTTP_HOST} in test strings
|
|
10
|
+
*/
|
|
11
|
+
declare function expandVariables(testString: string, context: VariableContext): string;
|
|
12
|
+
/**
|
|
13
|
+
* Test a single RewriteCond
|
|
14
|
+
*/
|
|
15
|
+
declare function testCondition(condition: RewriteCondition, context: VariableContext): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Apply rewrite flags to determine result
|
|
18
|
+
*/
|
|
19
|
+
declare function applyRewriteFlags(target: string, flags: RewriteFlags, context: Context): RewriteResult;
|
|
20
|
+
|
|
21
|
+
export { applyRewriteFlags, buildVariableContext, expandVariables, testCondition };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
function buildVariableContext(c) {
|
|
2
|
+
const url = new URL(c.req.url);
|
|
3
|
+
return {
|
|
4
|
+
HTTP_HOST: c.req.header("host") || "",
|
|
5
|
+
HTTP_USER_AGENT: c.req.header("user-agent") || "",
|
|
6
|
+
REQUEST_URI: c.req.path,
|
|
7
|
+
QUERY_STRING: url.search.slice(1),
|
|
8
|
+
HTTPS: url.protocol === "https:" ? "on" : "off",
|
|
9
|
+
REMOTE_ADDR: c.req.header("x-forwarded-for")?.split(",")[0].trim() || "127.0.0.1",
|
|
10
|
+
REQUEST_METHOD: c.req.method,
|
|
11
|
+
HTTP_REFERER: c.req.header("referer") || "",
|
|
12
|
+
HTTP_ACCEPT: c.req.header("accept") || "",
|
|
13
|
+
HTTP_COOKIE: c.req.header("cookie") || "",
|
|
14
|
+
SERVER_NAME: c.req.header("host")?.split(":")[0] || "localhost",
|
|
15
|
+
SERVER_PORT: url.port || (url.protocol === "https:" ? "443" : "80"),
|
|
16
|
+
DOCUMENT_ROOT: "",
|
|
17
|
+
REQUEST_FILENAME: c.req.path
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function expandVariables(testString, context) {
|
|
21
|
+
return testString.replace(/%\{([^}]+)\}/g, (match, varName) => {
|
|
22
|
+
const value = context[varName];
|
|
23
|
+
return value !== void 0 ? String(value) : match;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function testCondition(condition, context) {
|
|
27
|
+
const testValue = expandVariables(condition.testString, context);
|
|
28
|
+
const pattern = new RegExp(condition.pattern, condition.flags.noCase ? "i" : "");
|
|
29
|
+
return pattern.test(testValue);
|
|
30
|
+
}
|
|
31
|
+
function applyRewriteFlags(target, flags, context) {
|
|
32
|
+
if (flags.forbidden) {
|
|
33
|
+
return { type: "forbidden" };
|
|
34
|
+
}
|
|
35
|
+
if (flags.gone) {
|
|
36
|
+
return { type: "gone" };
|
|
37
|
+
}
|
|
38
|
+
if (flags.redirect) {
|
|
39
|
+
let finalTarget = target;
|
|
40
|
+
if (flags.qsAppend) {
|
|
41
|
+
const currentQs = new URL(context.req.url).search.slice(1);
|
|
42
|
+
if (currentQs) {
|
|
43
|
+
const separator = finalTarget.includes("?") ? "&" : "?";
|
|
44
|
+
finalTarget += separator + currentQs;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (flags.qsDiscard) {
|
|
48
|
+
const qsIndex = finalTarget.indexOf("?");
|
|
49
|
+
if (qsIndex !== -1) {
|
|
50
|
+
finalTarget = finalTarget.slice(0, qsIndex);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
type: "redirect",
|
|
55
|
+
url: finalTarget,
|
|
56
|
+
status: flags.redirect
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
type: "rewrite",
|
|
61
|
+
path: target
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
applyRewriteFlags,
|
|
66
|
+
buildVariableContext,
|
|
67
|
+
expandVariables,
|
|
68
|
+
testCondition
|
|
69
|
+
};
|
package/dist/html.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as hono_utils_html from 'hono/utils/html';
|
|
|
2
2
|
import { CgiContext } from './cgi.js';
|
|
3
3
|
import 'hono/types';
|
|
4
4
|
import 'hono';
|
|
5
|
+
import './htaccess/types.js';
|
|
5
6
|
|
|
6
7
|
declare const generateCgiInfo: ({ $_SERVER, $_SESSION, $_REQUEST, config, }: Pick<CgiContext, "$_SERVER" | "$_SESSION" | "$_REQUEST" | "config">) => () => hono_utils_html.HtmlEscapedString | Promise<hono_utils_html.HtmlEscapedString>;
|
|
7
8
|
declare const generateCgiError: ({ error, $_SERVER, }: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { CgiContext, ConfigObject, MatchboxOptions, ModuleInfo, Page, SessionCookieOptions } from './cgi.js';
|
|
2
|
+
export { HtaccessConfig } from './htaccess/types.js';
|
|
3
|
+
export { parseHtaccess } from './htaccess/parser.js';
|
|
4
|
+
export { corsHeaders, securityHeaders } from './htaccess/headers.js';
|
|
2
5
|
export { createCgi } from './with-defaults.js';
|
|
3
6
|
import 'hono/types';
|
|
4
7
|
import 'hono';
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { basicAuth } from "hono/basic-auth";
|
|
2
|
+
function createBasicAuthMiddleware(htpasswdContent, realm = "Restricted Area") {
|
|
3
|
+
const credentials = htpasswdContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => {
|
|
4
|
+
const [username, password] = line.split(":");
|
|
5
|
+
return { username, password };
|
|
6
|
+
});
|
|
7
|
+
if (credentials.length === 0) {
|
|
8
|
+
return async (_c, next) => {
|
|
9
|
+
await next();
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return async (c, next) => {
|
|
13
|
+
const handler = basicAuth({
|
|
14
|
+
verifyUser: (u, p) => credentials.some((cred) => cred.username === u && cred.password === p),
|
|
15
|
+
realm
|
|
16
|
+
});
|
|
17
|
+
return handler(c, next);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function applyBasicAuth(app, authMap) {
|
|
21
|
+
Object.entries(authMap).forEach(([dir, content]) => {
|
|
22
|
+
const authPath = dir === "/" ? "*" : `${dir.replace(/\/$/, "")}/*`;
|
|
23
|
+
app.use(authPath, createBasicAuthMiddleware(content));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
applyBasicAuth
|
|
28
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { HtaccessConfig } from '../htaccess/types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Apply htaccess middleware to Hono app in the correct order
|
|
6
|
+
*/
|
|
7
|
+
declare function applyHtaccessMiddleware(app: Hono, htaccessConfig: HtaccessConfig): void;
|
|
8
|
+
/**
|
|
9
|
+
* Apply error document middleware (must be applied last)
|
|
10
|
+
*/
|
|
11
|
+
declare function applyErrorDocumentMiddleware(app: Hono, htaccessConfig: HtaccessConfig): void;
|
|
12
|
+
|
|
13
|
+
export { applyErrorDocumentMiddleware, applyHtaccessMiddleware };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createRewriteMiddleware } from "../htaccess/rewrite.js";
|
|
2
|
+
import { createHeaderMiddleware } from "../htaccess/headers.js";
|
|
3
|
+
import { createErrorDocumentMiddleware } from "../htaccess/error-document.js";
|
|
4
|
+
import { createAccessControlMiddleware } from "../htaccess/access-control.js";
|
|
5
|
+
function applyHtaccessMiddleware(app, htaccessConfig) {
|
|
6
|
+
Object.entries(htaccessConfig).forEach(([dir, config]) => {
|
|
7
|
+
if (config.headers.length === 0) return;
|
|
8
|
+
const basePath = dir === "/" ? "" : dir.replace(/\/$/, "");
|
|
9
|
+
app.use(`${basePath}/*`, createHeaderMiddleware(config.headers));
|
|
10
|
+
});
|
|
11
|
+
Object.entries(htaccessConfig).forEach(([dir, config]) => {
|
|
12
|
+
const allRules = [
|
|
13
|
+
...config.rewriteRules,
|
|
14
|
+
...config.redirects.map((r) => ({
|
|
15
|
+
type: "rewrite",
|
|
16
|
+
pattern: `^${r.source}$`,
|
|
17
|
+
target: r.target,
|
|
18
|
+
flags: { redirect: r.code },
|
|
19
|
+
conditions: []
|
|
20
|
+
}))
|
|
21
|
+
];
|
|
22
|
+
if (allRules.length === 0) return;
|
|
23
|
+
const basePath = dir === "/" ? "" : dir.replace(/\/$/, "");
|
|
24
|
+
app.use(`${basePath}/*`, createRewriteMiddleware(allRules, basePath));
|
|
25
|
+
});
|
|
26
|
+
Object.entries(htaccessConfig).forEach(([dir, config]) => {
|
|
27
|
+
if (!config.accessControl) return;
|
|
28
|
+
const basePath = dir === "/" ? "" : dir.replace(/\/$/, "");
|
|
29
|
+
app.use(`${basePath}/*`, createAccessControlMiddleware(config.accessControl));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function applyErrorDocumentMiddleware(app, htaccessConfig) {
|
|
33
|
+
Object.entries(htaccessConfig).forEach(([dir, config]) => {
|
|
34
|
+
if (config.errorDocuments.length === 0) return;
|
|
35
|
+
const basePath = dir === "/" ? "" : dir.replace(/\/$/, "");
|
|
36
|
+
app.use(`${basePath}/*`, createErrorDocumentMiddleware(config.errorDocuments, basePath));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
applyErrorDocumentMiddleware,
|
|
41
|
+
applyHtaccessMiddleware
|
|
42
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { applyBasicAuth } from './auth.js';
|
|
2
|
+
export { applyErrorDocumentMiddleware, applyHtaccessMiddleware } from './htaccess.js';
|
|
3
|
+
export { getSessionFromCookie, saveSessionToCookie } from './session.js';
|
|
4
|
+
export { applyProtectedFilesMiddleware } from './protected-files.js';
|
|
5
|
+
export { applyTrailingSlashMiddleware } from './trailing-slash.js';
|
|
6
|
+
import 'hono';
|
|
7
|
+
import '../htaccess/types.js';
|
|
8
|
+
import '../cgi.js';
|
|
9
|
+
import 'hono/types';
|
|
10
|
+
import 'hono/utils/html';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { applyBasicAuth } from "./auth.js";
|
|
2
|
+
import { applyHtaccessMiddleware, applyErrorDocumentMiddleware } from "./htaccess.js";
|
|
3
|
+
import { getSessionFromCookie, saveSessionToCookie } from "./session.js";
|
|
4
|
+
import { applyProtectedFilesMiddleware } from "./protected-files.js";
|
|
5
|
+
import { applyTrailingSlashMiddleware } from "./trailing-slash.js";
|
|
6
|
+
export {
|
|
7
|
+
applyBasicAuth,
|
|
8
|
+
applyErrorDocumentMiddleware,
|
|
9
|
+
applyHtaccessMiddleware,
|
|
10
|
+
applyProtectedFilesMiddleware,
|
|
11
|
+
applyTrailingSlashMiddleware,
|
|
12
|
+
getSessionFromCookie,
|
|
13
|
+
saveSessionToCookie
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const PROTECTED_FILES = [".htaccess", ".htpasswd", ".htdigest", ".htgroup"];
|
|
2
|
+
function applyProtectedFilesMiddleware(app) {
|
|
3
|
+
app.use("*", async (c, next) => {
|
|
4
|
+
const path = c.req.path;
|
|
5
|
+
const lastSegment = path.slice(path.lastIndexOf("/") + 1);
|
|
6
|
+
if (PROTECTED_FILES.some((file) => lastSegment === file)) {
|
|
7
|
+
return c.text("Forbidden", 403);
|
|
8
|
+
}
|
|
9
|
+
await next();
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
applyProtectedFilesMiddleware
|
|
14
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
import { SessionCookieOptions } from '../cgi.js';
|
|
3
|
+
import 'hono/types';
|
|
4
|
+
import 'hono/utils/html';
|
|
5
|
+
import '../htaccess/types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get session data from cookie
|
|
9
|
+
*/
|
|
10
|
+
declare function getSessionFromCookie(c: Context, sessionCookieName?: string): Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Save session data to cookie
|
|
13
|
+
*/
|
|
14
|
+
declare function saveSessionToCookie(c: Context, session: Record<string, unknown>, options?: SessionCookieOptions): void;
|
|
15
|
+
|
|
16
|
+
export { getSessionFromCookie, saveSessionToCookie };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getCookie, setCookie } from "hono/cookie";
|
|
2
|
+
const DEFAULT_SESSION_COOKIE_NAME = "_SESSION_ID";
|
|
3
|
+
function getSessionFromCookie(c, sessionCookieName) {
|
|
4
|
+
const cookieName = sessionCookieName || DEFAULT_SESSION_COOKIE_NAME;
|
|
5
|
+
const sessionCookie = getCookie(c, cookieName);
|
|
6
|
+
if (sessionCookie) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(decodeURIComponent(sessionCookie));
|
|
9
|
+
} catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
function saveSessionToCookie(c, session, options) {
|
|
16
|
+
const cookieName = options?.name || DEFAULT_SESSION_COOKIE_NAME;
|
|
17
|
+
const sessionValue = encodeURIComponent(JSON.stringify(session));
|
|
18
|
+
const sessionOptions = {
|
|
19
|
+
path: options?.path || "/",
|
|
20
|
+
httpOnly: true,
|
|
21
|
+
sameSite: options?.sameSite || "Lax"
|
|
22
|
+
};
|
|
23
|
+
if (options?.secure !== void 0) {
|
|
24
|
+
sessionOptions.secure = options.secure;
|
|
25
|
+
}
|
|
26
|
+
if (options?.domain) {
|
|
27
|
+
sessionOptions.domain = options.domain;
|
|
28
|
+
}
|
|
29
|
+
if (options?.maxAge !== void 0) {
|
|
30
|
+
sessionOptions.maxAge = options.maxAge;
|
|
31
|
+
}
|
|
32
|
+
setCookie(c, cookieName, sessionValue, sessionOptions);
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
getSessionFromCookie,
|
|
36
|
+
saveSessionToCookie
|
|
37
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function applyTrailingSlashMiddleware(app) {
|
|
2
|
+
app.use("*", async (c, next) => {
|
|
3
|
+
const path = c.req.path;
|
|
4
|
+
if (!path.endsWith("/") && !path.includes(".")) {
|
|
5
|
+
return c.redirect(`${path}/`, 301);
|
|
6
|
+
}
|
|
7
|
+
await next();
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
applyTrailingSlashMiddleware
|
|
12
|
+
};
|
package/dist/with-defaults.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { MatchboxOptions } from './cgi.js';
|
|
2
1
|
import * as hono from 'hono';
|
|
3
2
|
import * as hono_types from 'hono/types';
|
|
3
|
+
import { MatchboxOptions } from './cgi.js';
|
|
4
4
|
import 'hono/utils/html';
|
|
5
|
+
import './htaccess/types.js';
|
|
5
6
|
|
|
6
7
|
declare const createCgi: (options?: MatchboxOptions) => hono.Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
7
8
|
|
package/dist/with-defaults.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCgiWithPages } from "./cgi.js";
|
|
2
|
+
import { parseHtaccess } from "./htaccess/parser.js";
|
|
2
3
|
const loadPagesFromPublic = () => {
|
|
3
4
|
const modules = import.meta.glob("/public/**/*.cgi.{tsx,jsx}", {
|
|
4
5
|
eager: true
|
|
@@ -38,40 +39,28 @@ const loadPagesFromPublic = () => {
|
|
|
38
39
|
},
|
|
39
40
|
{}
|
|
40
41
|
);
|
|
41
|
-
const
|
|
42
|
+
const htaccessConfig = Object.keys(htaccessFiles).reduce((acc, key) => {
|
|
42
43
|
const dir = key.replace(basePathRegex, "").replace(/\.htaccess$/, "") || "/";
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
if (parts[0] === "Redirect") {
|
|
57
|
-
return {
|
|
58
|
-
type: "redirect",
|
|
59
|
-
code: parts[1],
|
|
60
|
-
source: parts[2],
|
|
61
|
-
target: parts[3]
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}).filter(Boolean);
|
|
66
|
-
acc[dir] = rules;
|
|
44
|
+
const content = htaccessFiles[key];
|
|
45
|
+
try {
|
|
46
|
+
acc[dir] = parseHtaccess(content);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(`Error parsing .htaccess in ${dir}:`, error.message);
|
|
49
|
+
acc[dir] = {
|
|
50
|
+
rewriteRules: [],
|
|
51
|
+
redirects: [],
|
|
52
|
+
errorDocuments: [],
|
|
53
|
+
headers: []
|
|
54
|
+
};
|
|
55
|
+
}
|
|
67
56
|
return acc;
|
|
68
57
|
}, {});
|
|
69
|
-
return { pages, authMap,
|
|
58
|
+
return { pages, authMap, htaccessConfig };
|
|
70
59
|
};
|
|
71
60
|
const createCgi = (options) => {
|
|
72
61
|
const resolvedConfig = typeof __MATCHBOX_CONFIG__ === "undefined" ? {} : __MATCHBOX_CONFIG__;
|
|
73
|
-
const { pages, authMap,
|
|
74
|
-
return createCgiWithPages(pages, resolvedConfig, authMap,
|
|
62
|
+
const { pages, authMap, htaccessConfig } = loadPagesFromPublic();
|
|
63
|
+
return createCgiWithPages(pages, resolvedConfig, authMap, htaccessConfig, options);
|
|
75
64
|
};
|
|
76
65
|
export {
|
|
77
66
|
createCgi
|