@rimelight/security 0.0.18 → 0.0.20
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/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/integration.d.mts +26 -0
- package/dist/integration.mjs +20 -0
- package/package.json +13 -4
- package/src/components/ConstructionPage.astro +166 -0
- package/src/components/ConstructionSignIn.astro +9 -9
- package/src/config/index.ts +2 -0
- package/src/csp.ts +97 -0
- package/src/index.ts +5 -0
- package/src/integration.ts +51 -0
- package/src/layouts/ConstructionLayout.astro +50 -0
- package/src/middleware/construction.ts +315 -0
- package/src/middleware/dev-only.ts +16 -0
- package/src/middleware/index.ts +4 -0
- package/src/middleware/ratelimit.ts +120 -0
- package/src/middleware/security.ts +288 -0
- package/src/pages/construction.astro +5 -0
- package/src/types.ts +207 -0
- package/src/virtual.d.ts +16 -0
- package/src/vite.ts +114 -0
package/dist/index.d.mts
CHANGED
|
@@ -2,4 +2,5 @@ import { i as SecurityOptions, n as RateLimitOptions, r as RateLimiterBinding, t
|
|
|
2
2
|
import { buildCspHeader } from "./csp.mjs";
|
|
3
3
|
import { RimelightSecurityPlugin, SecurityPluginOptions, security } from "./vite.mjs";
|
|
4
4
|
import { CONSTRUCTION_GUEST_COOKIE, construction, isConstructionGuest, signInConstructionGuest } from "./middleware/construction.mjs";
|
|
5
|
-
|
|
5
|
+
import rimelightSecurity, { RimelightSecurityOptions } from "./integration.mjs";
|
|
6
|
+
export { CONSTRUCTION_GUEST_COOKIE, ConstructionOptions, RateLimitOptions, RateLimiterBinding, RimelightSecurityOptions, RimelightSecurityPlugin, SecurityOptions, SecurityPluginOptions, buildCspHeader, construction, isConstructionGuest, rimelightSecurity, security, security as sri, signInConstructionGuest };
|
package/dist/index.mjs
CHANGED
|
@@ -2,4 +2,5 @@ import "./types.mjs";
|
|
|
2
2
|
import { buildCspHeader } from "./csp.mjs";
|
|
3
3
|
import { security } from "./vite.mjs";
|
|
4
4
|
import { CONSTRUCTION_GUEST_COOKIE, construction, isConstructionGuest, signInConstructionGuest } from "./middleware/construction.mjs";
|
|
5
|
-
|
|
5
|
+
import rimelightSecurity from "./integration.mjs";
|
|
6
|
+
export { CONSTRUCTION_GUEST_COOKIE, buildCspHeader, construction, isConstructionGuest, rimelightSecurity, security, security as sri, signInConstructionGuest };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { i as SecurityOptions, t as ConstructionOptions } from "./types-Dcvwj-af.mjs";
|
|
2
|
+
import { AstroIntegration } from "astro";
|
|
3
|
+
//#region src/integration.d.ts
|
|
4
|
+
export interface RimelightSecurityOptions extends SecurityOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Construction mode options & route injection settings
|
|
7
|
+
*/
|
|
8
|
+
construction?: ConstructionOptions & {
|
|
9
|
+
/**
|
|
10
|
+
* Whether to automatically inject the construction page route. Defaults to true.
|
|
11
|
+
*/
|
|
12
|
+
injectRoute?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Pattern to inject for the construction route. Defaults to "/[locale]/construction" or
|
|
15
|
+
* "/construction".
|
|
16
|
+
*/
|
|
17
|
+
routePattern?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Astro Integration for Rimelight Security. Integrates Vite security plugins (CSP, SRI) and
|
|
22
|
+
* automatically injects the construction mode page route.
|
|
23
|
+
*/
|
|
24
|
+
export declare function rimelightSecurity(options?: RimelightSecurityOptions): AstroIntegration;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { rimelightSecurity as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { security } from "./vite.mjs";
|
|
2
|
+
//#region src/integration.ts
|
|
3
|
+
/**
|
|
4
|
+
* Astro Integration for Rimelight Security. Integrates Vite security plugins (CSP, SRI) and
|
|
5
|
+
* automatically injects the construction mode page route.
|
|
6
|
+
*/
|
|
7
|
+
function rimelightSecurity(options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
name: "@rimelight/security",
|
|
10
|
+
hooks: { "astro:config:setup": ({ injectRoute, updateConfig }) => {
|
|
11
|
+
if (options.construction?.injectRoute !== false) injectRoute({
|
|
12
|
+
pattern: options.construction?.routePattern || "/[locale]/construction",
|
|
13
|
+
entrypoint: "@rimelight/security/pages/construction.astro"
|
|
14
|
+
});
|
|
15
|
+
updateConfig({ vite: { plugins: [security(options)] } });
|
|
16
|
+
} }
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { rimelightSecurity as default, rimelightSecurity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelight/security",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Rimelight Entertainment's Security Package",
|
|
6
6
|
"homepage": "https://rimelight.com/docs",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
|
-
"src
|
|
20
|
+
"src"
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
23
|
"exports": {
|
|
@@ -33,17 +33,26 @@
|
|
|
33
33
|
"types": "./dist/config/index.d.mts",
|
|
34
34
|
"import": "./dist/config/index.mjs"
|
|
35
35
|
},
|
|
36
|
+
"./integration": {
|
|
37
|
+
"types": "./dist/integration.d.mts",
|
|
38
|
+
"import": "./dist/integration.mjs"
|
|
39
|
+
},
|
|
40
|
+
"./components/*": "./src/components/*",
|
|
41
|
+
"./layouts/*": "./src/layouts/*",
|
|
42
|
+
"./pages/*": "./src/pages/*",
|
|
36
43
|
"./*": "./src/*"
|
|
37
44
|
},
|
|
38
45
|
"publishConfig": {
|
|
39
46
|
"access": "public"
|
|
40
47
|
},
|
|
41
48
|
"dependencies": {
|
|
42
|
-
"@rimelight/
|
|
49
|
+
"@rimelight/i18n": "0.0.17",
|
|
50
|
+
"@rimelight/seo": "0.0.13",
|
|
51
|
+
"@rimelight/ui": "0.0.57"
|
|
43
52
|
},
|
|
44
53
|
"devDependencies": {
|
|
45
54
|
"@astrojs/check": "0.9.10",
|
|
46
|
-
"@rimelight/config": "0.0.
|
|
55
|
+
"@rimelight/config": "0.0.16",
|
|
47
56
|
"astro": "7.3.3",
|
|
48
57
|
"hono": "4.13.8",
|
|
49
58
|
"typescript": "6.0.3"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
import ConstructionLayout from "../layouts/ConstructionLayout.astro";
|
|
3
|
+
import ConstructionSignIn from "./ConstructionSignIn.astro";
|
|
4
|
+
import RLALogo from "@rimelight/ui/components/logo/RLALogo.astro";
|
|
5
|
+
import RLALocaleSelector from "@rimelight/ui/components/locale-selector/RLALocaleSelector.astro";
|
|
6
|
+
import { t, getLocale } from "@rimelight/i18n";
|
|
7
|
+
import { siteConfig } from "virtual:rimelight/seo";
|
|
8
|
+
|
|
9
|
+
export interface LocaleOption {
|
|
10
|
+
code: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
href?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
title?: string | undefined;
|
|
17
|
+
description?: string | undefined;
|
|
18
|
+
metaTitle?: string | undefined;
|
|
19
|
+
metaDescription?: string | undefined;
|
|
20
|
+
noindex?: boolean | undefined;
|
|
21
|
+
favicon?: string | undefined;
|
|
22
|
+
|
|
23
|
+
// Branding & Logo
|
|
24
|
+
logoAlt?: string | undefined;
|
|
25
|
+
siteName?: string | undefined;
|
|
26
|
+
copyright?: string | undefined;
|
|
27
|
+
|
|
28
|
+
// Locales
|
|
29
|
+
locales?: (LocaleOption | string)[] | undefined;
|
|
30
|
+
currentLocale?: string | undefined;
|
|
31
|
+
showLocaleSelector?: boolean | undefined;
|
|
32
|
+
|
|
33
|
+
// Guest Passphrase Form
|
|
34
|
+
allowAuth?: boolean | undefined;
|
|
35
|
+
endpoint?: string | undefined;
|
|
36
|
+
passphraseLabel?: string | undefined;
|
|
37
|
+
passphrasePlaceholder?: string | undefined;
|
|
38
|
+
rememberMeLabel?: string | undefined;
|
|
39
|
+
signInButtonLabel?: string | undefined;
|
|
40
|
+
loadingButtonLabel?: string | undefined;
|
|
41
|
+
returnLaterText?: string | undefined;
|
|
42
|
+
|
|
43
|
+
// Layout & Styling
|
|
44
|
+
class?: string | undefined;
|
|
45
|
+
containerClass?: string | undefined;
|
|
46
|
+
bodyClass?: string | undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
title,
|
|
51
|
+
description,
|
|
52
|
+
metaTitle,
|
|
53
|
+
metaDescription,
|
|
54
|
+
noindex = true,
|
|
55
|
+
favicon,
|
|
56
|
+
logoAlt,
|
|
57
|
+
siteName = (siteConfig as any)?.["name"] || "Rimelight Entertainment",
|
|
58
|
+
copyright,
|
|
59
|
+
locales = [
|
|
60
|
+
{ code: "en", label: "English" },
|
|
61
|
+
{ code: "pt", label: "Português" },
|
|
62
|
+
],
|
|
63
|
+
currentLocale,
|
|
64
|
+
showLocaleSelector = true,
|
|
65
|
+
allowAuth = true,
|
|
66
|
+
endpoint = "/api/construction-guest",
|
|
67
|
+
passphraseLabel,
|
|
68
|
+
passphrasePlaceholder,
|
|
69
|
+
rememberMeLabel,
|
|
70
|
+
signInButtonLabel,
|
|
71
|
+
loadingButtonLabel,
|
|
72
|
+
returnLaterText,
|
|
73
|
+
class: className = "flex min-h-screen flex-col items-center justify-center bg-black px-4 py-12",
|
|
74
|
+
containerClass = "z-10 w-full max-w-md space-y-8 text-center",
|
|
75
|
+
bodyClass = "isolate bg-black"
|
|
76
|
+
} = Astro.props;
|
|
77
|
+
|
|
78
|
+
// Safe i18n translation helper with fallback
|
|
79
|
+
const translate = (key: string, fallback: string): string => {
|
|
80
|
+
try {
|
|
81
|
+
const val = t(key);
|
|
82
|
+
if (val && val !== key) return val;
|
|
83
|
+
} catch {}
|
|
84
|
+
return fallback;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const resolvedMetaTitle = metaTitle ?? translate("page_construction.meta_title", title ?? "Under Construction");
|
|
88
|
+
const resolvedMetaDescription = metaDescription ?? translate("page_construction.meta_description", description ?? "This site is currently undergoing scheduled maintenance.");
|
|
89
|
+
const resolvedTitle = title ?? translate("page_construction.title", "Under Construction");
|
|
90
|
+
const resolvedDescription = description ?? translate("page_construction.description", "This site is currently undergoing scheduled maintenance and is restricted to authorized personnel.");
|
|
91
|
+
|
|
92
|
+
const resolvedPassphraseLabel = passphraseLabel ?? translate("page_construction.form_passphrase_label", "Passphrase");
|
|
93
|
+
const resolvedPassphrasePlaceholder = passphrasePlaceholder ?? translate("page_construction.form_passphrase_placeholder", "Enter passphrase...");
|
|
94
|
+
const resolvedRememberMeLabel = rememberMeLabel ?? translate("page_construction.form_remember_me_label", "Remember me");
|
|
95
|
+
const resolvedSignInButtonLabel = signInButtonLabel ?? translate("page_construction.form_sign_in_button", "Sign In");
|
|
96
|
+
const resolvedCopyright = copyright ?? `© ${new Date().getFullYear()} ${siteName}`;
|
|
97
|
+
|
|
98
|
+
let resolvedCurrentLocale = currentLocale;
|
|
99
|
+
if (!resolvedCurrentLocale) {
|
|
100
|
+
try {
|
|
101
|
+
resolvedCurrentLocale = getLocale();
|
|
102
|
+
} catch {
|
|
103
|
+
resolvedCurrentLocale = "en";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
<ConstructionLayout
|
|
109
|
+
title={resolvedMetaTitle}
|
|
110
|
+
description={resolvedMetaDescription}
|
|
111
|
+
favicon={favicon}
|
|
112
|
+
noindex={noindex}
|
|
113
|
+
bodyClass={bodyClass}
|
|
114
|
+
>
|
|
115
|
+
<slot name="head" slot="head" />
|
|
116
|
+
|
|
117
|
+
<div class={className}>
|
|
118
|
+
<div class={containerClass}>
|
|
119
|
+
<slot name="header">
|
|
120
|
+
<div class="flex flex-col items-center gap-4">
|
|
121
|
+
<slot name="logo">
|
|
122
|
+
<RLALogo class="h-12 w-auto text-white" variant="logomark" mode="white" alt={logoAlt || siteName} />
|
|
123
|
+
</slot>
|
|
124
|
+
<slot name="title">
|
|
125
|
+
<h1 class="m-0 text-3xl font-bold text-white sm:text-4xl">
|
|
126
|
+
{resolvedTitle}
|
|
127
|
+
</h1>
|
|
128
|
+
</slot>
|
|
129
|
+
<slot name="description">
|
|
130
|
+
<p class="m-0 text-neutral-400">
|
|
131
|
+
{resolvedDescription}
|
|
132
|
+
</p>
|
|
133
|
+
</slot>
|
|
134
|
+
</div>
|
|
135
|
+
</slot>
|
|
136
|
+
|
|
137
|
+
<slot name="header-extra" />
|
|
138
|
+
|
|
139
|
+
<slot name="form">
|
|
140
|
+
<ConstructionSignIn
|
|
141
|
+
allowAuth={allowAuth}
|
|
142
|
+
endpoint={endpoint}
|
|
143
|
+
passphraseLabel={resolvedPassphraseLabel}
|
|
144
|
+
passphrasePlaceholder={resolvedPassphrasePlaceholder}
|
|
145
|
+
rememberMeLabel={resolvedRememberMeLabel}
|
|
146
|
+
signInButtonLabel={resolvedSignInButtonLabel}
|
|
147
|
+
loadingButtonLabel={loadingButtonLabel}
|
|
148
|
+
returnLaterText={returnLaterText}
|
|
149
|
+
/>
|
|
150
|
+
</slot>
|
|
151
|
+
|
|
152
|
+
<slot name="footer">
|
|
153
|
+
<div class="flex flex-col items-center gap-2 text-xs text-neutral-500">
|
|
154
|
+
<slot name="locale-selector">
|
|
155
|
+
{showLocaleSelector && locales && locales.length > 1 && (
|
|
156
|
+
<RLALocaleSelector locales={locales as any} currentLocale={resolvedCurrentLocale} />
|
|
157
|
+
)}
|
|
158
|
+
</slot>
|
|
159
|
+
<slot name="copyright">
|
|
160
|
+
<span>{resolvedCopyright}</span>
|
|
161
|
+
</slot>
|
|
162
|
+
</div>
|
|
163
|
+
</slot>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</ConstructionLayout>
|
|
@@ -6,15 +6,15 @@ import RLAButton from "@rimelight/ui/components/button/RLAButton.astro"
|
|
|
6
6
|
import RLACheckbox from "@rimelight/ui/components/checkbox/RLACheckbox.astro"
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
|
-
allowAuth?: boolean
|
|
10
|
-
endpoint?: string
|
|
11
|
-
passphraseLabel?: string
|
|
12
|
-
passphrasePlaceholder?: string
|
|
13
|
-
rememberMeLabel?: string
|
|
14
|
-
signInButtonLabel?: string
|
|
15
|
-
loadingButtonLabel?: string
|
|
16
|
-
returnLaterText?: string
|
|
17
|
-
class?: string
|
|
9
|
+
allowAuth?: boolean | undefined
|
|
10
|
+
endpoint?: string | undefined
|
|
11
|
+
passphraseLabel?: string | undefined
|
|
12
|
+
passphrasePlaceholder?: string | undefined
|
|
13
|
+
rememberMeLabel?: string | undefined
|
|
14
|
+
signInButtonLabel?: string | undefined
|
|
15
|
+
loadingButtonLabel?: string | undefined
|
|
16
|
+
returnLaterText?: string | undefined
|
|
17
|
+
class?: string | undefined
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const {
|
package/src/csp.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { SecurityOptions } from "./types"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds the Content-Security-Policy header string dynamically for a given request nonce.
|
|
5
|
+
*/
|
|
6
|
+
export function buildCspHeader(options: SecurityOptions = {}, nonce?: string): string {
|
|
7
|
+
const domain = options.domain || ""
|
|
8
|
+
|
|
9
|
+
const imgSources = Array.from(
|
|
10
|
+
new Set([
|
|
11
|
+
"'self'",
|
|
12
|
+
"data:",
|
|
13
|
+
...(domain ? [`https://cdn.${domain}`] : []),
|
|
14
|
+
"https://i3.ytimg.com",
|
|
15
|
+
"https://www.youtube.com",
|
|
16
|
+
"https://www.youtube-nocookie.com",
|
|
17
|
+
...(options.imgSrc || [])
|
|
18
|
+
])
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const connectSources = Array.from(
|
|
22
|
+
new Set(["'self'", "https://cloudflareinsights.com", ...(options.connectSrc || [])])
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const frameSources = Array.from(
|
|
26
|
+
new Set([
|
|
27
|
+
"https://www.youtube.com",
|
|
28
|
+
"https://www.youtube-nocookie.com",
|
|
29
|
+
...(options.frameSrc || [])
|
|
30
|
+
])
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const scriptSources = Array.from(
|
|
34
|
+
new Set([
|
|
35
|
+
"'self'",
|
|
36
|
+
...(nonce ? [`'nonce-${nonce}'`] : []),
|
|
37
|
+
"https://static.cloudflareinsights.com",
|
|
38
|
+
"https://betterlytics.io/analytics.js",
|
|
39
|
+
"'inline-speculation-rules'",
|
|
40
|
+
// Astro ClientRouter (View Transitions inline script sha)
|
|
41
|
+
"'sha256-ZuMSxilKU+4KIM8LWna4lqBEKzd6WaiAjz6gamhm7zM='",
|
|
42
|
+
...(options.scriptResources || [])
|
|
43
|
+
])
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const styleSources = Array.from(
|
|
47
|
+
new Set([
|
|
48
|
+
"'self'",
|
|
49
|
+
// Astro ClientRouter (View Transitions inline style sha)
|
|
50
|
+
"'sha256-SKuaOGnks7NAUq37nvw1PfGEE0lOAs5ERbiYRbGFIbw='",
|
|
51
|
+
...(options.styleResources || [])
|
|
52
|
+
])
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const defaultDirectives: Record<string, string[]> = {
|
|
56
|
+
"default-src": ["'none'"],
|
|
57
|
+
"img-src": imgSources,
|
|
58
|
+
"font-src": ["'self'"],
|
|
59
|
+
"connect-src": connectSources,
|
|
60
|
+
"frame-ancestors": ["'none'"],
|
|
61
|
+
"frame-src": frameSources,
|
|
62
|
+
"script-src": scriptSources,
|
|
63
|
+
"script-src-attr": ["'none'"],
|
|
64
|
+
"style-src": styleSources,
|
|
65
|
+
"base-uri": ["'self'"],
|
|
66
|
+
"form-action": ["'self'"],
|
|
67
|
+
"object-src": ["'none'"],
|
|
68
|
+
"manifest-src": ["'self'"],
|
|
69
|
+
"upgrade-insecure-requests": []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (options.trustedTypes) {
|
|
73
|
+
defaultDirectives["require-trusted-types-for"] = ["'script'"]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Merge custom directive overrides
|
|
77
|
+
if (options.directives) {
|
|
78
|
+
for (const [key, value] of Object.entries(options.directives)) {
|
|
79
|
+
if (value === false) {
|
|
80
|
+
delete defaultDirectives[key]
|
|
81
|
+
} else if (value === true) {
|
|
82
|
+
defaultDirectives[key] = []
|
|
83
|
+
} else if (Array.isArray(value)) {
|
|
84
|
+
defaultDirectives[key] = value
|
|
85
|
+
} else if (typeof value === "string") {
|
|
86
|
+
defaultDirectives[key] = value.split(" ").filter(Boolean)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return Object.entries(defaultDirectives)
|
|
92
|
+
.map(([directive, values]) => {
|
|
93
|
+
if (!values || values.length === 0) return directive
|
|
94
|
+
return `${directive} ${values.join(" ")}`
|
|
95
|
+
})
|
|
96
|
+
.join("; ")
|
|
97
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AstroIntegration } from "astro"
|
|
2
|
+
import type { SecurityOptions, ConstructionOptions } from "./types"
|
|
3
|
+
import { security } from "./vite"
|
|
4
|
+
|
|
5
|
+
export interface RimelightSecurityOptions extends SecurityOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Construction mode options & route injection settings
|
|
8
|
+
*/
|
|
9
|
+
construction?: ConstructionOptions & {
|
|
10
|
+
/**
|
|
11
|
+
* Whether to automatically inject the construction page route. Defaults to true.
|
|
12
|
+
*/
|
|
13
|
+
injectRoute?: boolean
|
|
14
|
+
/**
|
|
15
|
+
* Pattern to inject for the construction route. Defaults to "/[locale]/construction" or
|
|
16
|
+
* "/construction".
|
|
17
|
+
*/
|
|
18
|
+
routePattern?: string
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Astro Integration for Rimelight Security. Integrates Vite security plugins (CSP, SRI) and
|
|
24
|
+
* automatically injects the construction mode page route.
|
|
25
|
+
*/
|
|
26
|
+
export function rimelightSecurity(options: RimelightSecurityOptions = {}): AstroIntegration {
|
|
27
|
+
return {
|
|
28
|
+
name: "@rimelight/security",
|
|
29
|
+
hooks: {
|
|
30
|
+
"astro:config:setup": ({ injectRoute, updateConfig }) => {
|
|
31
|
+
// 1. Inject Construction Page Route by default (unless explicitly disabled)
|
|
32
|
+
if (options.construction?.injectRoute !== false) {
|
|
33
|
+
const pattern = options.construction?.routePattern || "/[locale]/construction"
|
|
34
|
+
injectRoute({
|
|
35
|
+
pattern,
|
|
36
|
+
entrypoint: "@rimelight/security/pages/construction.astro"
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Add Vite security plugin
|
|
41
|
+
updateConfig({
|
|
42
|
+
vite: {
|
|
43
|
+
plugins: [security(options)]
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default rimelightSecurity
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
import SecurityHead from "../components/SecurityHead.astro";
|
|
3
|
+
import SEOHead from "@rimelight/seo/components/SEOHead.astro";
|
|
4
|
+
import UIHead from "@rimelight/ui/components/head/UIHead.astro";
|
|
5
|
+
import RLAMain from "@rimelight/ui/components/main/RLAMain.astro";
|
|
6
|
+
import { siteConfig } from "virtual:rimelight/seo";
|
|
7
|
+
import { Font } from "astro:assets";
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
title?: string | undefined;
|
|
11
|
+
description?: string | undefined;
|
|
12
|
+
favicon?: string | undefined;
|
|
13
|
+
noindex?: boolean | undefined;
|
|
14
|
+
class?: string | undefined;
|
|
15
|
+
bodyClass?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
title = "Under Construction",
|
|
20
|
+
description = "This site is currently undergoing scheduled construction.",
|
|
21
|
+
favicon,
|
|
22
|
+
noindex = true,
|
|
23
|
+
class: className = "",
|
|
24
|
+
bodyClass = "isolate bg-black"
|
|
25
|
+
} = Astro.props;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<!doctype html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<SecurityHead />
|
|
32
|
+
<SEOHead
|
|
33
|
+
title={title}
|
|
34
|
+
description={description}
|
|
35
|
+
config={siteConfig}
|
|
36
|
+
favicon={favicon}
|
|
37
|
+
noindex={noindex}
|
|
38
|
+
/>
|
|
39
|
+
<UIHead />
|
|
40
|
+
<Font cssVariable="--font-sans" preload={true} />
|
|
41
|
+
<Font cssVariable="--font-serif" preload={true} />
|
|
42
|
+
<Font cssVariable="--font-mono" preload={true} />
|
|
43
|
+
<slot name="head" />
|
|
44
|
+
</head>
|
|
45
|
+
<body class={bodyClass}>
|
|
46
|
+
<RLAMain class={className}>
|
|
47
|
+
<slot />
|
|
48
|
+
</RLAMain>
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|