Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions frontend/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ThemeProvider } from '@/components/theme/ThemeProvider';
import { getCachedBlogCategories } from '@/db/queries/blog/blog-categories';
import { AuthProvider } from '@/hooks/useAuth';
import { locales } from '@/i18n/config';
import { readServerEnv } from '@/lib/env/server-env';

export default async function LocaleLayout({
children,
Expand All @@ -32,8 +33,8 @@ export default async function LocaleLayout({

const enableAdmin =
(
process.env.ENABLE_ADMIN_API ??
process.env.NEXT_PUBLIC_ENABLE_ADMIN ??
readServerEnv('ENABLE_ADMIN_API') ??
readServerEnv('NEXT_PUBLIC_ENABLE_ADMIN') ??
''
).toLowerCase() === 'true';

Expand Down
13 changes: 9 additions & 4 deletions frontend/lib/auth/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'server-only';
import type { NextRequest } from 'next/server';

import { getCurrentUser } from '@/lib/auth';
import { readServerEnv } from '@/lib/env/server-env';

export class AdminApiDisabledError extends Error {
code = 'ADMIN_API_DISABLED' as const;
Expand All @@ -29,10 +30,14 @@ export class AdminForbiddenError extends Error {
}

export function assertAdminApiEnabled(): void {
if (
process.env.NODE_ENV === 'production' &&
process.env.ENABLE_ADMIN_API !== 'true'
) {
const enabled =
(
readServerEnv('ENABLE_ADMIN_API') ??
readServerEnv('NEXT_PUBLIC_ENABLE_ADMIN') ??
''
).toLowerCase() === 'true';

if (process.env.NODE_ENV === 'production' && !enabled) {
throw new AdminApiDisabledError();
}
}
Expand Down
4 changes: 3 additions & 1 deletion frontend/lib/email/sendPasswordResetEmail.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { readServerEnv } from '@/lib/env/server-env';

import { resetPasswordTemplate } from './templates/reset-password';
import { mailer } from './transporter';

Expand All @@ -7,7 +9,7 @@ type Params = {
};

export async function sendPasswordResetEmail({ to, resetUrl }: Params) {
const from = process.env.EMAIL_FROM;
const from = readServerEnv('EMAIL_FROM');

if (!from) {
throw new Error('EMAIL_FROM is not configured');
Expand Down
4 changes: 3 additions & 1 deletion frontend/lib/email/sendVerificationEmail.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { readServerEnv } from '@/lib/env/server-env';

import { verifyEmailTemplate } from './templates/verify-email';
import { mailer } from './transporter';

Expand All @@ -7,7 +9,7 @@ type Params = {
};

export async function sendVerificationEmail({ to, verifyUrl }: Params) {
const from = process.env.EMAIL_FROM;
const from = readServerEnv('EMAIL_FROM');

if (!from) {
throw new Error('EMAIL_FROM is not configured');
Expand Down
6 changes: 4 additions & 2 deletions frontend/lib/email/transporter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import nodemailer from 'nodemailer';

const user = process.env.GMAIL_USER;
const pass = process.env.GMAIL_APP_PASSWORD;
import { readServerEnv } from '@/lib/env/server-env';

const user = readServerEnv('GMAIL_USER');
const pass = readServerEnv('GMAIL_APP_PASSWORD');

if (!user || !pass) {
throw new Error('Missing Gmail SMTP credentials');
Expand Down
8 changes: 8 additions & 0 deletions frontend/lib/env/server-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ const GENERATED_FALLBACK_KEYS = new Set([
'GITHUB_CLIENT_ID_DEVELOP',
'GITHUB_CLIENT_SECRET_DEVELOP',
'GITHUB_CLIENT_REDIRECT_URI_DEVELOP',
'ENABLE_ADMIN_API',
'NEXT_PUBLIC_ENABLE_ADMIN',
'SHOP_STATUS_TOKEN_SECRET',
'APP_ORIGIN',
'APP_ADDITIONAL_ORIGINS',
'GMAIL_USER',
'GMAIL_APP_PASSWORD',
'EMAIL_FROM',
]);


Expand Down
6 changes: 4 additions & 2 deletions frontend/lib/security/origin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';

import { readServerEnv } from '@/lib/env/server-env';

const LOCALHOST_ORIGIN = 'http://localhost:3000';

function buildErrorResponse(
Expand Down Expand Up @@ -49,13 +51,13 @@ export function normalizeOrigin(input: string): string {
export function getAllowedOrigins(): string[] {
const allowed = new Set<string>();

const appOrigin = (process.env.APP_ORIGIN ?? '').trim();
const appOrigin = (readServerEnv('APP_ORIGIN') ?? '').trim();
if (appOrigin) {
const normalized = normalizeOrigin(appOrigin);
if (normalized) allowed.add(normalized);
}

const additionalRaw = (process.env.APP_ADDITIONAL_ORIGINS ?? '').trim();
const additionalRaw = (readServerEnv('APP_ADDITIONAL_ORIGINS') ?? '').trim();
if (additionalRaw) {
for (const entry of additionalRaw.split(',')) {
const candidate = entry.trim();
Expand Down
4 changes: 3 additions & 1 deletion frontend/lib/shop/status-token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import crypto from 'node:crypto';

import { readServerEnv } from '@/lib/env/server-env';

export const STATUS_TOKEN_SCOPES = [
'status_lite',
'order_payment_init',
Expand Down Expand Up @@ -27,7 +29,7 @@ type TokenPayload = {
const DEFAULT_TTL_SECONDS = 45 * 60;

function getSecret(): string {
const raw = process.env.SHOP_STATUS_TOKEN_SECRET ?? '';
const raw = readServerEnv('SHOP_STATUS_TOKEN_SECRET') ?? '';
const trimmed = raw.trim();
if (!trimmed) {
throw new Error('SHOP_STATUS_TOKEN_SECRET is not configured');
Expand Down
Loading