@alteran/astro 0.1.8 → 0.1.10
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/index.js +55 -0
- package/package.json +1 -1
- package/src/_worker.ts +2 -1
- package/src/entrypoints/server.ts +19 -0
- package/src/lib/jwt.ts +18 -5
- package/src/lib/secrets.ts +50 -2
- package/types/env.d.ts +3 -2
package/index.js
CHANGED
|
@@ -62,6 +62,7 @@ export default function alteran(options = {}) {
|
|
|
62
62
|
|
|
63
63
|
const middlewareEntrypoint = resolvePackagePath('./src/middleware.ts');
|
|
64
64
|
const serverEntrypoint = resolvePackagePath('./src/_worker.ts');
|
|
65
|
+
const cloudflareServerAdapter = resolvePackagePath('./src/entrypoints/server.ts');
|
|
65
66
|
|
|
66
67
|
const routes = CORE_ROUTES.slice();
|
|
67
68
|
if (includeRootEndpoint) {
|
|
@@ -79,6 +80,60 @@ export default function alteran(options = {}) {
|
|
|
79
80
|
updateConfig({ output: 'server' });
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
const existingAlias = config.vite?.resolve?.alias ?? [];
|
|
84
|
+
const aliasArray = Array.isArray(existingAlias)
|
|
85
|
+
? existingAlias.slice()
|
|
86
|
+
: Object.entries(existingAlias).map(([find, replacement]) => ({ find, replacement }));
|
|
87
|
+
|
|
88
|
+
const hasCloudflareAlias = aliasArray.some(
|
|
89
|
+
(entry) => entry && entry.find === '@astrojs/cloudflare/entrypoints/server.js'
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (!hasCloudflareAlias) {
|
|
93
|
+
aliasArray.push({
|
|
94
|
+
find: '@astrojs/cloudflare/entrypoints/server.js',
|
|
95
|
+
replacement: cloudflareServerAdapter,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const vitePlugins = Array.isArray(config.vite?.plugins)
|
|
100
|
+
? config.vite.plugins.slice().filter(Boolean)
|
|
101
|
+
: [];
|
|
102
|
+
|
|
103
|
+
vitePlugins.push({
|
|
104
|
+
name: 'alteran-sequencer-export',
|
|
105
|
+
enforce: 'post',
|
|
106
|
+
apply: 'build',
|
|
107
|
+
transform(code, id) {
|
|
108
|
+
if (!id.includes('@astrojs-ssr-virtual-entry')) return null;
|
|
109
|
+
if (!code.includes('const _exports = createExports')) return null;
|
|
110
|
+
if (code.includes("const Sequencer = _exports['Sequencer'];")) return null;
|
|
111
|
+
|
|
112
|
+
const replacedInit = code.replace(
|
|
113
|
+
'const __astrojsSsrVirtualEntry = _exports.default;',
|
|
114
|
+
"const Sequencer = _exports['Sequencer'];\nconst __astrojsSsrVirtualEntry = _exports.default;"
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (replacedInit === code) return null;
|
|
118
|
+
|
|
119
|
+
const replacedExport = replacedInit.replace(
|
|
120
|
+
'export { __astrojsSsrVirtualEntry as default, pageMap };',
|
|
121
|
+
'export { Sequencer, __astrojsSsrVirtualEntry as default, pageMap };'
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return { code: replacedExport, map: null };
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
updateConfig({
|
|
129
|
+
vite: {
|
|
130
|
+
resolve: {
|
|
131
|
+
alias: aliasArray,
|
|
132
|
+
},
|
|
133
|
+
plugins: vitePlugins,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
82
137
|
if (injectServerEntry) {
|
|
83
138
|
if (config.build?.serverEntry && config.build.serverEntry !== serverEntrypoint) {
|
|
84
139
|
logger.info(
|
package/package.json
CHANGED
package/src/_worker.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SSRManifest } from 'astro';
|
|
2
|
+
import { App } from 'astro/app';
|
|
3
|
+
import { handle } from '@astrojs/cloudflare/handler';
|
|
4
|
+
import { Sequencer } from '../worker/sequencer';
|
|
5
|
+
|
|
6
|
+
export function createExports(manifest: SSRManifest) {
|
|
7
|
+
const app = new App(manifest);
|
|
8
|
+
const fetch = async (request: Request, env: unknown, context: unknown) => {
|
|
9
|
+
// Delegate to the Cloudflare adapter handler while preserving Alteran additions.
|
|
10
|
+
return await handle(manifest, app, request, env as any, context as any);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
default: { fetch },
|
|
15
|
+
Sequencer,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Sequencer };
|
package/src/lib/jwt.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Env } from '../env';
|
|
2
|
+
import { getRuntimeString } from './secrets';
|
|
2
3
|
|
|
3
4
|
export interface JwtClaims {
|
|
4
5
|
sub: string; // DID
|
|
@@ -31,7 +32,14 @@ export async function signJwt(env: Env, claims: JwtClaims, kind: 'access' | 'ref
|
|
|
31
32
|
if (claims.scope) payload.scope = claims.scope;
|
|
32
33
|
if (claims.jti) payload.jti = claims.jti;
|
|
33
34
|
|
|
34
|
-
const secret =
|
|
35
|
+
const secret = await getRuntimeString(
|
|
36
|
+
env,
|
|
37
|
+
kind === 'access' ? 'ACCESS_TOKEN_SECRET' : 'REFRESH_TOKEN_SECRET',
|
|
38
|
+
kind === 'access' ? 'dev-access' : 'dev-refresh'
|
|
39
|
+
);
|
|
40
|
+
if (!secret) {
|
|
41
|
+
throw new Error(`Missing ${kind === 'access' ? 'ACCESS_TOKEN_SECRET' : 'REFRESH_TOKEN_SECRET'}`);
|
|
42
|
+
}
|
|
35
43
|
const algorithm = (env.JWT_ALGORITHM as string | undefined) ?? 'HS256';
|
|
36
44
|
|
|
37
45
|
if (algorithm === 'EdDSA') {
|
|
@@ -50,7 +58,12 @@ export async function verifyJwt(env: Env, token: string): Promise<{ valid: boole
|
|
|
50
58
|
|
|
51
59
|
let ok = false;
|
|
52
60
|
if (header.alg === 'HS256' && header.typ === 'JWT') {
|
|
53
|
-
const secret =
|
|
61
|
+
const secret = await getRuntimeString(
|
|
62
|
+
env,
|
|
63
|
+
payload.t === 'refresh' ? 'REFRESH_TOKEN_SECRET' : 'ACCESS_TOKEN_SECRET',
|
|
64
|
+
payload.t === 'refresh' ? 'dev-refresh' : 'dev-access'
|
|
65
|
+
);
|
|
66
|
+
if (!secret) return null;
|
|
54
67
|
ok = await hmacJwtVerify(parts[0] + '.' + parts[1], parts[2], secret);
|
|
55
68
|
} else if (header.alg === 'EdDSA' && header.typ === 'JWT') {
|
|
56
69
|
ok = await eddsaJwtVerify(parts[0] + '.' + parts[1], parts[2], env);
|
|
@@ -90,9 +103,9 @@ async function eddsaJwtSign(payload: any, env: Env): Promise<string> {
|
|
|
90
103
|
const data = `${h}.${p}`;
|
|
91
104
|
|
|
92
105
|
// Import Ed25519 private key from env
|
|
93
|
-
const keyData = env
|
|
106
|
+
const keyData = await getRuntimeString(env, 'REPO_SIGNING_KEY');
|
|
94
107
|
if (!keyData) {
|
|
95
|
-
throw new Error('
|
|
108
|
+
throw new Error('REPO_SIGNING_KEY not configured for EdDSA JWTs');
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
// Decode base64 private key
|
|
@@ -114,7 +127,7 @@ async function eddsaJwtVerify(data: string, sigB64: string, env: Env): Promise<b
|
|
|
114
127
|
const enc = new TextEncoder();
|
|
115
128
|
|
|
116
129
|
// Import Ed25519 public key from env
|
|
117
|
-
const keyData = env
|
|
130
|
+
const keyData = await getRuntimeString(env, 'REPO_SIGNING_PUBLIC_KEY');
|
|
118
131
|
if (!keyData) {
|
|
119
132
|
return false;
|
|
120
133
|
}
|
package/src/lib/secrets.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setGetEnv } from 'astro/env/setup';
|
|
1
2
|
import type { Env } from '../env';
|
|
2
3
|
import type { SecretsStoreSecret } from '../../types/env';
|
|
3
4
|
|
|
@@ -9,8 +10,6 @@ const SECRET_KEYS = [
|
|
|
9
10
|
'REFRESH_TOKEN_SECRET',
|
|
10
11
|
'REPO_SIGNING_KEY',
|
|
11
12
|
'REPO_SIGNING_PUBLIC_KEY',
|
|
12
|
-
'JWT_ED25519_PRIVATE_KEY',
|
|
13
|
-
'JWT_ED25519_PUBLIC_KEY',
|
|
14
13
|
] as const satisfies readonly (keyof Env)[];
|
|
15
14
|
|
|
16
15
|
function isSecretStoreBinding(value: unknown): value is SecretsStoreSecret {
|
|
@@ -42,6 +41,55 @@ export async function resolveEnvSecrets<E extends Env>(env: E): Promise<E> {
|
|
|
42
41
|
})
|
|
43
42
|
);
|
|
44
43
|
|
|
44
|
+
setGetEnv((key) => {
|
|
45
|
+
const local = resolved[key];
|
|
46
|
+
if (typeof local === 'string') return local;
|
|
47
|
+
if (typeof local === 'number' || typeof local === 'boolean') return String(local);
|
|
48
|
+
const fallback = process.env[key];
|
|
49
|
+
return typeof fallback === 'string' ? fallback : undefined;
|
|
50
|
+
});
|
|
51
|
+
|
|
45
52
|
return resolved as E;
|
|
46
53
|
}
|
|
47
54
|
|
|
55
|
+
type AstroGetSecret = (key: string) => string | undefined;
|
|
56
|
+
|
|
57
|
+
let astroGetSecret: AstroGetSecret | null | undefined;
|
|
58
|
+
|
|
59
|
+
async function loadAstroGetSecret(): Promise<AstroGetSecret | null> {
|
|
60
|
+
if (astroGetSecret !== undefined) return astroGetSecret;
|
|
61
|
+
try {
|
|
62
|
+
const mod = await import('astro:env/server');
|
|
63
|
+
astroGetSecret = mod.getSecret as AstroGetSecret;
|
|
64
|
+
} catch {
|
|
65
|
+
astroGetSecret = null;
|
|
66
|
+
}
|
|
67
|
+
return astroGetSecret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function getRuntimeString<K extends keyof Env>(
|
|
71
|
+
env: Env,
|
|
72
|
+
key: K,
|
|
73
|
+
fallback?: string
|
|
74
|
+
): Promise<string | undefined> {
|
|
75
|
+
const current = env[key];
|
|
76
|
+
if (typeof current === 'string' && current !== '') {
|
|
77
|
+
return current;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const secretFn = await loadAstroGetSecret();
|
|
81
|
+
if (secretFn) {
|
|
82
|
+
try {
|
|
83
|
+
const value = secretFn(String(key));
|
|
84
|
+
if (typeof value === 'string' && value !== '') {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (fallback === undefined) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return fallback;
|
|
95
|
+
}
|
package/types/env.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ declare global {
|
|
|
24
24
|
PDS_HANDLE?: string | SecretsStoreSecret;
|
|
25
25
|
PDS_DID?: string | SecretsStoreSecret;
|
|
26
26
|
PDS_HOSTNAME?: string;
|
|
27
|
+
PDS_ALLOWED_MIME?: string;
|
|
27
28
|
USER_PASSWORD?: string | SecretsStoreSecret;
|
|
28
29
|
PDS_MAX_BLOB_SIZE?: string;
|
|
29
30
|
ACCESS_TOKEN_SECRET?: string | SecretsStoreSecret;
|
|
@@ -31,13 +32,13 @@ declare global {
|
|
|
31
32
|
PDS_ACCESS_TTL_SEC?: string;
|
|
32
33
|
PDS_REFRESH_TTL_SEC?: string;
|
|
33
34
|
JWT_ALGORITHM?: string;
|
|
34
|
-
JWT_ED25519_PRIVATE_KEY?: string | SecretsStoreSecret;
|
|
35
|
-
JWT_ED25519_PUBLIC_KEY?: string | SecretsStoreSecret;
|
|
36
35
|
REPO_SIGNING_KEY?: string | SecretsStoreSecret;
|
|
37
36
|
REPO_SIGNING_PUBLIC_KEY?: string | SecretsStoreSecret;
|
|
38
37
|
PDS_RATE_LIMIT_PER_MIN?: string;
|
|
39
38
|
PDS_MAX_JSON_BYTES?: string;
|
|
40
39
|
PDS_CORS_ORIGIN?: string;
|
|
40
|
+
PDS_SEQ_WINDOW?: string;
|
|
41
|
+
ENVIRONMENT?: string;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
namespace App {
|