mirror of
https://github.com/wahyd4/heygo.git
synced 2026-08-08 21:05:48 +10:00
update dev env
This commit is contained in:
@@ -123,6 +123,21 @@ The Worker reads `PUBLIC_HOST`, `PRIVATE_HOST`, `APP_BASE_URL`, and
|
||||
`COOKIE_DOMAIN` from env vars (set in each wrangler config), so the same code
|
||||
serves every environment without hardcoded hostnames.
|
||||
|
||||
Admin users are configured with `ADMIN_EMAILS` in the matching Wrangler config:
|
||||
|
||||
- `wrangler.dev.jsonc` for `dev.heygo.cc`
|
||||
- `wrangler.prod.jsonc` for `heygo.cc`
|
||||
- `wrangler.jsonc` for local development
|
||||
|
||||
Use a comma, semicolon, space, or newline separated list, for example:
|
||||
|
||||
```jsonc
|
||||
"ADMIN_EMAILS": "owner@example.com, ops@example.com"
|
||||
```
|
||||
|
||||
When a signed-in user's email matches this list, the app treats that user as
|
||||
`admin` even if the stored database role is still `user`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A Cloudflare account with the `heygo.cc` zone added.
|
||||
|
||||
+66
-1
@@ -3,7 +3,9 @@ import {
|
||||
AUTH_SESSION_COOKIE_NAME,
|
||||
AuthError,
|
||||
getCurrentUser,
|
||||
getConfiguredAdminEmails,
|
||||
hashSessionToken,
|
||||
isConfiguredAdminEmail,
|
||||
parseCookieHeader,
|
||||
requireAdmin,
|
||||
requireUser,
|
||||
@@ -64,7 +66,7 @@ class FakeSessionStatement {
|
||||
}
|
||||
}
|
||||
|
||||
function makeEnv(rows: SessionRow[] = []): { env: Env; db: FakeSessionD1 } {
|
||||
function makeEnv(rows: SessionRow[] = [], overrides: Partial<Env> = {}): { env: Env; db: FakeSessionD1 } {
|
||||
const db = new FakeSessionD1(rows);
|
||||
return {
|
||||
env: {
|
||||
@@ -73,6 +75,7 @@ function makeEnv(rows: SessionRow[] = []): { env: Env; db: FakeSessionD1 } {
|
||||
PRIVATE_HOST: 'my.heygo.cc',
|
||||
APP_BASE_URL: 'https://heygo.cc',
|
||||
COOKIE_DOMAIN: '.heygo.cc',
|
||||
...overrides,
|
||||
},
|
||||
db,
|
||||
};
|
||||
@@ -129,6 +132,19 @@ describe('hashSessionToken', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('configured admin emails', () => {
|
||||
it('parses comma, semicolon, and whitespace separated admin emails case-insensitively', () => {
|
||||
const emails = getConfiguredAdminEmails({
|
||||
ADMIN_EMAILS: ' Boss@Heygo.cc, owner@example.com; ops@example.com\n',
|
||||
});
|
||||
|
||||
expect([...emails]).toEqual(['boss@heygo.cc', 'owner@example.com', 'ops@example.com']);
|
||||
expect(isConfiguredAdminEmail('boss@heygo.cc', { ADMIN_EMAILS: 'Boss@Heygo.cc' })).toBe(true);
|
||||
expect(isConfiguredAdminEmail('BOSS@HEYGO.CC', { ADMIN_EMAILS: 'boss@heygo.cc' })).toBe(true);
|
||||
expect(isConfiguredAdminEmail('person@heygo.cc', { ADMIN_EMAILS: 'boss@heygo.cc' })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCurrentUser', () => {
|
||||
it('returns null when there is no Cookie header', async () => {
|
||||
const { env } = makeEnv();
|
||||
@@ -176,6 +192,30 @@ describe('getCurrentUser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('treats a configured admin email as admin even when the database role is user', async () => {
|
||||
const token = 'configured-admin-token';
|
||||
const hash = await hashSessionToken(token);
|
||||
const { env } = makeEnv(
|
||||
[
|
||||
{
|
||||
id: 'user_configured_admin',
|
||||
email: 'Boss@Heygo.cc',
|
||||
name: 'Boss',
|
||||
image_url: null,
|
||||
role: 'user',
|
||||
expires_at: futureIso(),
|
||||
session_token_hash: hash,
|
||||
},
|
||||
],
|
||||
{ ADMIN_EMAILS: 'boss@heygo.cc' },
|
||||
);
|
||||
|
||||
const cookie = `${AUTH_SESSION_COOKIE_NAME}=${token}`;
|
||||
const user = await getCurrentUser(makeRequest(cookie), env);
|
||||
|
||||
expect(user?.role).toBe('admin');
|
||||
});
|
||||
|
||||
it('returns null for an expired session', async () => {
|
||||
const token = 'expired-session-token';
|
||||
const hash = await hashSessionToken(token);
|
||||
@@ -272,4 +312,29 @@ describe('requireAdmin', () => {
|
||||
expect(user.id).toBe('user_5');
|
||||
expect(user.role).toBe('admin');
|
||||
});
|
||||
|
||||
it('allows a configured admin email through requireAdmin', async () => {
|
||||
const token = 'configured-admin-require-token';
|
||||
const hash = await hashSessionToken(token);
|
||||
const { env } = makeEnv(
|
||||
[
|
||||
{
|
||||
id: 'user_6',
|
||||
email: 'owner@heygo.cc',
|
||||
name: 'Owner',
|
||||
image_url: null,
|
||||
role: 'user',
|
||||
expires_at: futureIso(),
|
||||
session_token_hash: hash,
|
||||
},
|
||||
],
|
||||
{ ADMIN_EMAILS: 'owner@heygo.cc' },
|
||||
);
|
||||
|
||||
const cookie = `${AUTH_SESSION_COOKIE_NAME}=${token}`;
|
||||
const user = await requireAdmin(makeRequest(cookie), env);
|
||||
|
||||
expect(user.id).toBe('user_6');
|
||||
expect(user.role).toBe('admin');
|
||||
});
|
||||
});
|
||||
|
||||
+27
-1
@@ -24,6 +24,7 @@ export interface AuthSession {
|
||||
}
|
||||
|
||||
const ENABLED_PROVIDER_SET = new Set<string>(AUTH_ENABLED_PROVIDERS);
|
||||
const ADMIN_EMAIL_SPLIT_PATTERN = /[\s,;]+/;
|
||||
|
||||
export function isAuthProviderEnabled(provider: string): provider is EnabledAuthProvider {
|
||||
return ENABLED_PROVIDER_SET.has(provider);
|
||||
@@ -96,6 +97,31 @@ export async function hashSessionToken(token: string): Promise<string> {
|
||||
return bufferToHex(digest);
|
||||
}
|
||||
|
||||
export function getConfiguredAdminEmails(env: Pick<Env, 'ADMIN_EMAILS'>): Set<string> {
|
||||
const raw = env.ADMIN_EMAILS?.trim();
|
||||
if (!raw) {
|
||||
return new Set();
|
||||
}
|
||||
|
||||
return new Set(
|
||||
raw
|
||||
.split(ADMIN_EMAIL_SPLIT_PATTERN)
|
||||
.map((email) => email.trim().toLowerCase())
|
||||
.filter(Boolean),
|
||||
);
|
||||
}
|
||||
|
||||
export function isConfiguredAdminEmail(
|
||||
email: string | null | undefined,
|
||||
env: Pick<Env, 'ADMIN_EMAILS'>,
|
||||
): boolean {
|
||||
if (!email) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getConfiguredAdminEmails(env).has(email.trim().toLowerCase());
|
||||
}
|
||||
|
||||
function bufferToHex(buffer: ArrayBuffer): string {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let hex = '';
|
||||
@@ -148,7 +174,7 @@ export async function getCurrentUser(request: Request, env: Env): Promise<AuthUs
|
||||
email: row.email,
|
||||
name: row.name,
|
||||
imageUrl: row.image_url,
|
||||
role: row.role,
|
||||
role: isConfiguredAdminEmail(row.email, env) ? 'admin' : row.role,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,4 +6,5 @@ export interface Env {
|
||||
PRIVATE_HOST: string;
|
||||
APP_BASE_URL: string;
|
||||
COOKIE_DOMAIN: string;
|
||||
ADMIN_EMAILS?: string;
|
||||
}
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@
|
||||
"PUBLIC_HOST": "dev.heygo.cc",
|
||||
"PRIVATE_HOST": "my.dev.heygo.cc",
|
||||
"APP_BASE_URL": "https://dev.heygo.cc",
|
||||
"COOKIE_DOMAIN": ".heygo.cc"
|
||||
"COOKIE_DOMAIN": ".heygo.cc",
|
||||
"ADMIN_EMAILS": "wahyd4@gmail.com"
|
||||
},
|
||||
"d1_databases": [
|
||||
{
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@
|
||||
"PUBLIC_HOST": "*",
|
||||
"PRIVATE_HOST": "*",
|
||||
"APP_BASE_URL": "http://localhost:8787",
|
||||
"COOKIE_DOMAIN": ""
|
||||
"COOKIE_DOMAIN": "",
|
||||
"ADMIN_EMAILS": "wahyd4@gmail.com"
|
||||
},
|
||||
"d1_databases": [
|
||||
{
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@
|
||||
"PUBLIC_HOST": "heygo.cc",
|
||||
"PRIVATE_HOST": "my.heygo.cc",
|
||||
"APP_BASE_URL": "https://heygo.cc",
|
||||
"COOKIE_DOMAIN": ".heygo.cc"
|
||||
"COOKIE_DOMAIN": ".heygo.cc",
|
||||
"ADMIN_EMAILS": "wahyd4@gmail.com"
|
||||
},
|
||||
"d1_databases": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user