@startup-api/cloudflare 0.3.2 → 0.4.0
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 +42 -0
- package/package.json +1 -1
- package/public/users/power-strip.js +23 -0
- package/public/users/profile.html +4 -0
- package/src/auth/AtprotoProvider.ts +282 -0
- package/src/auth/OAuthProvider.ts +103 -3
- package/src/auth/atproto/crypto.ts +119 -0
- package/src/auth/atproto/identity.ts +178 -0
- package/src/auth/index.ts +195 -195
- package/src/auth/providers.ts +2 -0
- package/src/createStartupAPI.ts +4 -4
- package/src/handlers/admin.ts +3 -1
- package/src/handlers/ssr.ts +6 -2
- package/src/handlers/utils.ts +7 -1
- package/src/schemas/config.ts +6 -0
- package/worker-configuration.d.ts +1 -5
package/src/handlers/admin.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StartupAPIEnv } from '../StartupAPIEnv';
|
|
2
2
|
import { CookieManager } from '../CookieManager';
|
|
3
3
|
import { getUserFromSession, checkAndClearStaleSession, isAdmin, parseCookies, getActiveProviders } from './utils';
|
|
4
|
+
import type { ProviderConfigs } from '../auth/providers';
|
|
4
5
|
import { Plan } from '../billing/Plan';
|
|
5
6
|
import { UserProfileSchema } from '../schemas/user';
|
|
6
7
|
import { SystemAccountSchema, MemberSchema } from '../schemas/account';
|
|
@@ -11,6 +12,7 @@ export async function handleAdmin(
|
|
|
11
12
|
env: StartupAPIEnv,
|
|
12
13
|
usersPath: string,
|
|
13
14
|
cookieManager: CookieManager,
|
|
15
|
+
providerConfigs: ProviderConfigs = {},
|
|
14
16
|
): Promise<Response> {
|
|
15
17
|
const user = await getUserFromSession(request, env, cookieManager);
|
|
16
18
|
if (!user || !isAdmin(user, env)) {
|
|
@@ -31,7 +33,7 @@ export async function handleAdmin(
|
|
|
31
33
|
html = html.replace(/\{\{ssr:([a-z0-9_]+)\}\}/g, (match, key) => {
|
|
32
34
|
const replacements: Record<string, string> = {
|
|
33
35
|
plans_json: JSON.stringify(Plan.getAll()).replace(/"/g, '"'),
|
|
34
|
-
providers: getActiveProviders(env).join(','),
|
|
36
|
+
providers: getActiveProviders(env, providerConfigs).join(','),
|
|
35
37
|
};
|
|
36
38
|
return replacements[key] !== undefined ? replacements[key] : match;
|
|
37
39
|
});
|
package/src/handlers/ssr.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StartupAPIEnv } from '../StartupAPIEnv';
|
|
2
2
|
import { CookieManager } from '../CookieManager';
|
|
3
3
|
import { getUserFromSession, checkAndClearStaleSession, isAdmin, getActiveProviders } from './utils';
|
|
4
|
+
import type { ProviderConfigs } from '../auth/providers';
|
|
4
5
|
import { Plan } from '../billing/Plan';
|
|
5
6
|
|
|
6
7
|
export async function handleSSR(
|
|
@@ -9,6 +10,7 @@ export async function handleSSR(
|
|
|
9
10
|
url: URL,
|
|
10
11
|
usersPath: string,
|
|
11
12
|
cookieManager: CookieManager,
|
|
13
|
+
providerConfigs: ProviderConfigs = {},
|
|
12
14
|
): Promise<Response> {
|
|
13
15
|
const user = await getUserFromSession(request, env, cookieManager);
|
|
14
16
|
if (!user) {
|
|
@@ -90,7 +92,7 @@ export async function handleSSR(
|
|
|
90
92
|
// Prepare SSR values
|
|
91
93
|
const replacements: Record<string, string> = {
|
|
92
94
|
plans_json: JSON.stringify(Plan.getAll()).replace(/"/g, '"'),
|
|
93
|
-
providers: getActiveProviders(env).join(','),
|
|
95
|
+
providers: getActiveProviders(env, providerConfigs).join(','),
|
|
94
96
|
profile_json: JSON.stringify(data).replace(/"/g, '"'),
|
|
95
97
|
credentials_json: JSON.stringify(credentials).replace(/"/g, '"'),
|
|
96
98
|
profile_name: data.profile.name || 'Anonymous',
|
|
@@ -105,7 +107,7 @@ export async function handleSSR(
|
|
|
105
107
|
: '',
|
|
106
108
|
nav_account_display: account && (account.role === 1 || data.is_admin) ? 'display: block;' : 'display: none;',
|
|
107
109
|
credentials_list_html: renderCredentialsList(credentials, data.credential?.provider),
|
|
108
|
-
link_credentials_html: renderLinkCredentialsList(getActiveProviders(env), url.href),
|
|
110
|
+
link_credentials_html: renderLinkCredentialsList(getActiveProviders(env, providerConfigs), url.href),
|
|
109
111
|
};
|
|
110
112
|
|
|
111
113
|
if (account) {
|
|
@@ -212,6 +214,8 @@ function getProviderIcon(provider: string): string {
|
|
|
212
214
|
return '<svg viewBox="0 0 24 24" width="24" height="24" class="twitch-icon"><path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z" fill="currentColor"/></svg>';
|
|
213
215
|
} else if (provider === 'patreon') {
|
|
214
216
|
return '<svg viewBox="0 0 24 24" width="24" height="24" class="patreon-icon"><path d="M14.82 2.41c3.96 0 7.18 3.24 7.18 7.21 0 3.96-3.22 7.18-7.18 7.18-3.97 0-7.21-3.22-7.21-7.18 0-3.97 3.24-7.21 7.21-7.21M2 21.6h3.5V2.41H2V21.6z" fill="currentColor"/></svg>';
|
|
217
|
+
} else if (provider === 'atproto') {
|
|
218
|
+
return '<svg viewBox="0 0 24 24" width="24" height="24" class="atproto-icon"><path d="M12 10.5C10.9 8.4 8.2 6.3 6.3 6c-1.5-.2-1.8.7-1.5 2 .2 1 1.5 5 2.3 6 .9 1.2 2 1.4 3 1.2-1.7.3-3.2 1-1.2 3 .9.9 1.6.3 2.1-.6.5-1 .8-2.1 1-2.6.2.5.5 1.6 1 2.6.5.9 1.2 1.5 2.1.6 2-2 .5-2.7-1.2-3 1 .2 2.1 0 3-1.2.8-1 2.1-5 2.3-6 .3-1.3 0-2.2-1.5-2-1.9.3-4.6 2.4-5.7 4.5z" fill="#0085FF"/></svg>';
|
|
215
219
|
}
|
|
216
220
|
return '';
|
|
217
221
|
}
|
package/src/handlers/utils.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { StartupAPIEnv } from '../StartupAPIEnv';
|
|
2
2
|
import { CookieManager } from '../CookieManager';
|
|
3
|
+
import { isAtprotoEnabled } from '../auth/AtprotoProvider';
|
|
4
|
+
import type { ProviderConfigs } from '../auth/providers';
|
|
3
5
|
|
|
4
|
-
export function getActiveProviders(env: StartupAPIEnv): string[] {
|
|
6
|
+
export function getActiveProviders(env: StartupAPIEnv, providerConfigs: ProviderConfigs = {}): string[] {
|
|
5
7
|
const providers: string[] = [];
|
|
6
8
|
if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
|
|
7
9
|
providers.push('google');
|
|
@@ -12,6 +14,10 @@ export function getActiveProviders(env: StartupAPIEnv): string[] {
|
|
|
12
14
|
if (env.PATREON_CLIENT_ID && env.PATREON_CLIENT_SECRET) {
|
|
13
15
|
providers.push('patreon');
|
|
14
16
|
}
|
|
17
|
+
// atproto has no env credentials; it is enabled purely via factory config.
|
|
18
|
+
if (isAtprotoEnabled(providerConfigs.atproto)) {
|
|
19
|
+
providers.push('atproto');
|
|
20
|
+
}
|
|
15
21
|
return providers;
|
|
16
22
|
}
|
|
17
23
|
|
package/src/schemas/config.ts
CHANGED
|
@@ -28,6 +28,12 @@ export const ProviderOptionsSchema = z.object({
|
|
|
28
28
|
scopes: z.union([z.string(), z.array(z.string())]).optional(),
|
|
29
29
|
/** Patreon only: restrict entitlements to a single campaign id. */
|
|
30
30
|
campaignId: z.string().optional(),
|
|
31
|
+
/** atproto only: display name advertised in the client-metadata document. Default: "StartupAPI". */
|
|
32
|
+
clientName: z.string().optional(),
|
|
33
|
+
/** atproto only: override the PLC directory used to resolve did:plc identities. Default: https://plc.directory. */
|
|
34
|
+
plcUrl: z.string().optional(),
|
|
35
|
+
/** atproto only: override the DNS-over-HTTPS resolver used for handle resolution. */
|
|
36
|
+
dohUrl: z.string().optional(),
|
|
31
37
|
freshness: ProviderFreshnessSchema.optional(),
|
|
32
38
|
});
|
|
33
39
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
// Generated by Wrangler by running `wrangler types` (hash:
|
|
2
|
+
// Generated by Wrangler by running `wrangler types` (hash: 21e6c5c76a7bbde8583fcbcc839c03e7)
|
|
3
3
|
// Runtime types generated with workerd@1.20260120.0 2025-09-27 global_fetch_strictly_public
|
|
4
4
|
declare namespace Cloudflare {
|
|
5
5
|
interface GlobalProps {
|
|
@@ -15,7 +15,6 @@ declare namespace Cloudflare {
|
|
|
15
15
|
ORIGIN_URL: "https://startup-api-demo-origin.sergeychernyshev.workers.dev/";
|
|
16
16
|
TWITCH_CLIENT_ID: "";
|
|
17
17
|
TWITCH_CLIENT_SECRET: "";
|
|
18
|
-
GITHUB_PROJECT_ID: string;
|
|
19
18
|
USER: DurableObjectNamespace<import("./src/index").UserDO>;
|
|
20
19
|
ACCOUNT: DurableObjectNamespace<import("./src/index").AccountDO>;
|
|
21
20
|
SYSTEM: DurableObjectNamespace<import("./src/index").SystemDO>;
|
|
@@ -36,7 +35,6 @@ declare namespace Cloudflare {
|
|
|
36
35
|
PATREON_CLIENT_ID: "";
|
|
37
36
|
PATREON_CLIENT_SECRET: "";
|
|
38
37
|
PATREON_WEBHOOK_SECRET: "";
|
|
39
|
-
GITHUB_PROJECT_ID: string;
|
|
40
38
|
USER: DurableObjectNamespace<import("./src/index").UserDO>;
|
|
41
39
|
ACCOUNT: DurableObjectNamespace<import("./src/index").AccountDO>;
|
|
42
40
|
SYSTEM: DurableObjectNamespace<import("./src/index").SystemDO>;
|
|
@@ -56,14 +54,12 @@ declare namespace Cloudflare {
|
|
|
56
54
|
PATREON_CLIENT_ID: "patreon-id";
|
|
57
55
|
PATREON_CLIENT_SECRET: "patreon-secret";
|
|
58
56
|
PATREON_WEBHOOK_SECRET: "whsec-test";
|
|
59
|
-
GITHUB_PROJECT_ID: string;
|
|
60
57
|
USER: DurableObjectNamespace<import("./src/index").UserDO>;
|
|
61
58
|
ACCOUNT: DurableObjectNamespace<import("./src/index").AccountDO>;
|
|
62
59
|
SYSTEM: DurableObjectNamespace<import("./src/index").SystemDO>;
|
|
63
60
|
CREDENTIAL: DurableObjectNamespace<import("./src/index").CredentialDO>;
|
|
64
61
|
}
|
|
65
62
|
interface Env {
|
|
66
|
-
GITHUB_PROJECT_ID: string;
|
|
67
63
|
IMAGE_STORAGE: R2Bucket;
|
|
68
64
|
ASSETS: Fetcher;
|
|
69
65
|
ENVIRONMENT?: "preview" | "" | "test";
|