Refactor environment variable handling and improve API key management
- Introduced `client.ts` and `server.ts` for structured environment variable management using `@t3-oss/env-nextjs`. - Updated references to environment variables in various files to use the new `clientEnv` and `serverEnv` objects. - Enhanced the `next.config.mjs` file to validate environment variables during build. - Added new dependencies: `@t3-oss/env-nextjs` and `jiti` for improved environment handling. - Cleaned up imports and ensured consistent usage of environment variables across the application.
This commit is contained in:
parent
adf96e516f
commit
186bd3c2cc
@ -1,9 +1,10 @@
|
|||||||
// app/actions.ts
|
// app/actions.ts
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
|
import { serverEnv } from '@/env/server';
|
||||||
|
import { xai } from '@ai-sdk/xai';
|
||||||
import { generateObject } from 'ai';
|
import { generateObject } from 'ai';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { xai } from '@ai-sdk/xai';
|
|
||||||
|
|
||||||
export async function suggestQuestions(history: any[]) {
|
export async function suggestQuestions(history: any[]) {
|
||||||
'use server';
|
'use server';
|
||||||
@ -37,7 +38,7 @@ Do not use pronouns like he, she, him, his, her, etc. in the questions as they b
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
|
const ELEVENLABS_API_KEY = serverEnv.ELEVENLABS_API_KEY;
|
||||||
|
|
||||||
export async function generateSpeech(text: string, voice: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer' = "alloy") {
|
export async function generateSpeech(text: string, voice: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer' = "alloy") {
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
// /app/api/chat/route.ts
|
// /app/api/chat/route.ts
|
||||||
import { z } from "zod";
|
import { getGroupConfig } from "@/app/actions";
|
||||||
import { xai } from '@ai-sdk/xai'
|
import { serverEnv } from "@/env/server";
|
||||||
import Exa from 'exa-js'
|
import { xai } from '@ai-sdk/xai';
|
||||||
import {
|
|
||||||
convertToCoreMessages,
|
|
||||||
streamText,
|
|
||||||
tool,
|
|
||||||
smoothStream
|
|
||||||
} from "ai";
|
|
||||||
import { BlobRequestAbortedError, put } from '@vercel/blob';
|
|
||||||
import CodeInterpreter from "@e2b/code-interpreter";
|
import CodeInterpreter from "@e2b/code-interpreter";
|
||||||
import FirecrawlApp from '@mendable/firecrawl-js';
|
import FirecrawlApp from '@mendable/firecrawl-js';
|
||||||
import { tavily } from '@tavily/core'
|
import { tavily } from '@tavily/core';
|
||||||
import { getGroupConfig } from "@/app/actions";
|
|
||||||
import { geolocation, ipAddress } from '@vercel/functions'
|
|
||||||
import { Ratelimit } from "@upstash/ratelimit"; // for deno: see above
|
import { Ratelimit } from "@upstash/ratelimit"; // for deno: see above
|
||||||
import { Redis } from "@upstash/redis"; // see below for cloudflare and fastly adapters
|
import { Redis } from "@upstash/redis"; // see below for cloudflare and fastly adapters
|
||||||
|
import { BlobRequestAbortedError, put } from '@vercel/blob';
|
||||||
|
import {
|
||||||
|
convertToCoreMessages,
|
||||||
|
smoothStream,
|
||||||
|
streamText,
|
||||||
|
tool
|
||||||
|
} from "ai";
|
||||||
|
import Exa from 'exa-js';
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
// Allow streaming responses up to 60 seconds
|
// Allow streaming responses up to 60 seconds
|
||||||
export const maxDuration = 120;
|
export const maxDuration = 120;
|
||||||
@ -182,7 +182,7 @@ export async function POST(req: Request) {
|
|||||||
searchDepth: ("basic" | "advanced")[];
|
searchDepth: ("basic" | "advanced")[];
|
||||||
exclude_domains?: string[];
|
exclude_domains?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const apiKey = process.env.TAVILY_API_KEY;
|
const apiKey = serverEnv.TAVILY_API_KEY;
|
||||||
const tvly = tavily({ apiKey });
|
const tvly = tavily({ apiKey });
|
||||||
const includeImageDescriptions = true;
|
const includeImageDescriptions = true;
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ export async function POST(req: Request) {
|
|||||||
}),
|
}),
|
||||||
execute: async ({ query }: { query: string }) => {
|
execute: async ({ query }: { query: string }) => {
|
||||||
try {
|
try {
|
||||||
const exa = new Exa(process.env.EXA_API_KEY as string);
|
const exa = new Exa(serverEnv.EXA_API_KEY as string);
|
||||||
|
|
||||||
const result = await exa.searchAndContents(
|
const result = await exa.searchAndContents(
|
||||||
query,
|
query,
|
||||||
@ -304,7 +304,7 @@ export async function POST(req: Request) {
|
|||||||
query: z.string().describe("The search query for movies/TV shows"),
|
query: z.string().describe("The search query for movies/TV shows"),
|
||||||
}),
|
}),
|
||||||
execute: async ({ query }: { query: string }) => {
|
execute: async ({ query }: { query: string }) => {
|
||||||
const TMDB_API_KEY = process.env.TMDB_API_KEY;
|
const TMDB_API_KEY = serverEnv.TMDB_API_KEY;
|
||||||
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -389,7 +389,7 @@ export async function POST(req: Request) {
|
|||||||
description: "Get trending movies from TMDB",
|
description: "Get trending movies from TMDB",
|
||||||
parameters: z.object({}),
|
parameters: z.object({}),
|
||||||
execute: async () => {
|
execute: async () => {
|
||||||
const TMDB_API_KEY = process.env.TMDB_API_KEY;
|
const TMDB_API_KEY = serverEnv.TMDB_API_KEY;
|
||||||
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -423,7 +423,7 @@ export async function POST(req: Request) {
|
|||||||
description: "Get trending TV shows from TMDB",
|
description: "Get trending TV shows from TMDB",
|
||||||
parameters: z.object({}),
|
parameters: z.object({}),
|
||||||
execute: async () => {
|
execute: async () => {
|
||||||
const TMDB_API_KEY = process.env.TMDB_API_KEY;
|
const TMDB_API_KEY = serverEnv.TMDB_API_KEY;
|
||||||
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
const TMDB_BASE_URL = 'https://api.themoviedb.org/3';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -460,7 +460,7 @@ export async function POST(req: Request) {
|
|||||||
}),
|
}),
|
||||||
execute: async ({ query }: { query: string }) => {
|
execute: async ({ query }: { query: string }) => {
|
||||||
try {
|
try {
|
||||||
const exa = new Exa(process.env.EXA_API_KEY as string);
|
const exa = new Exa(serverEnv.EXA_API_KEY as string);
|
||||||
|
|
||||||
// Search academic papers with content summary
|
// Search academic papers with content summary
|
||||||
const result = await exa.searchAndContents(
|
const result = await exa.searchAndContents(
|
||||||
@ -516,7 +516,7 @@ export async function POST(req: Request) {
|
|||||||
}),
|
}),
|
||||||
execute: async ({ query, no_of_results }: { query: string, no_of_results: number }) => {
|
execute: async ({ query, no_of_results }: { query: string, no_of_results: number }) => {
|
||||||
try {
|
try {
|
||||||
const exa = new Exa(process.env.EXA_API_KEY as string);
|
const exa = new Exa(serverEnv.EXA_API_KEY as string);
|
||||||
|
|
||||||
// Simple search to get YouTube URLs only
|
// Simple search to get YouTube URLs only
|
||||||
const searchResult = await exa.search(
|
const searchResult = await exa.search(
|
||||||
@ -545,17 +545,17 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
// Fetch detailed info from our endpoints
|
// Fetch detailed info from our endpoints
|
||||||
const [detailsResponse, captionsResponse, timestampsResponse] = await Promise.all([
|
const [detailsResponse, captionsResponse, timestampsResponse] = await Promise.all([
|
||||||
fetch(`${process.env.YT_ENDPOINT}/video-data`, {
|
fetch(`${serverEnv.YT_ENDPOINT}/video-data`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url: result.url })
|
body: JSON.stringify({ url: result.url })
|
||||||
}).then(res => res.ok ? res.json() : null),
|
}).then(res => res.ok ? res.json() : null),
|
||||||
fetch(`${process.env.YT_ENDPOINT}/video-captions`, {
|
fetch(`${serverEnv.YT_ENDPOINT}/video-captions`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url: result.url })
|
body: JSON.stringify({ url: result.url })
|
||||||
}).then(res => res.ok ? res.text() : null),
|
}).then(res => res.ok ? res.text() : null),
|
||||||
fetch(`${process.env.YT_ENDPOINT}/video-timestamps`, {
|
fetch(`${serverEnv.YT_ENDPOINT}/video-timestamps`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url: result.url })
|
body: JSON.stringify({ url: result.url })
|
||||||
@ -595,7 +595,7 @@ export async function POST(req: Request) {
|
|||||||
url: z.string().describe("The URL to retrieve the information from."),
|
url: z.string().describe("The URL to retrieve the information from."),
|
||||||
}),
|
}),
|
||||||
execute: async ({ url }: { url: string }) => {
|
execute: async ({ url }: { url: string }) => {
|
||||||
const app = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY });
|
const app = new FirecrawlApp({ apiKey: serverEnv.FIRECRAWL_API_KEY });
|
||||||
try {
|
try {
|
||||||
const content = await app.scrapeUrl(url);
|
const content = await app.scrapeUrl(url);
|
||||||
if (!content.success || !content.metadata) {
|
if (!content.success || !content.metadata) {
|
||||||
@ -625,7 +625,7 @@ export async function POST(req: Request) {
|
|||||||
lon: z.number().describe("The longitude of the location."),
|
lon: z.number().describe("The longitude of the location."),
|
||||||
}),
|
}),
|
||||||
execute: async ({ lat, lon }: { lat: number; lon: number }) => {
|
execute: async ({ lat, lon }: { lat: number; lon: number }) => {
|
||||||
const apiKey = process.env.OPENWEATHER_API_KEY;
|
const apiKey = serverEnv.OPENWEATHER_API_KEY;
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}`,
|
`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}`,
|
||||||
);
|
);
|
||||||
@ -645,7 +645,7 @@ export async function POST(req: Request) {
|
|||||||
console.log("Title:", title);
|
console.log("Title:", title);
|
||||||
console.log("Icon:", icon);
|
console.log("Icon:", icon);
|
||||||
|
|
||||||
const sandbox = await CodeInterpreter.create(process.env.SANDBOX_TEMPLATE_ID!);
|
const sandbox = await CodeInterpreter.create(serverEnv.SANDBOX_TEMPLATE_ID!);
|
||||||
const execution = await sandbox.runCode(code);
|
const execution = await sandbox.runCode(code);
|
||||||
let message = "";
|
let message = "";
|
||||||
let images = [];
|
let images = [];
|
||||||
@ -729,14 +729,14 @@ export async function POST(req: Request) {
|
|||||||
execute: async ({ query, coordinates }: { query: string; coordinates: number[] }) => {
|
execute: async ({ query, coordinates }: { query: string; coordinates: number[] }) => {
|
||||||
try {
|
try {
|
||||||
// Forward geocoding with Google Maps API
|
// Forward geocoding with Google Maps API
|
||||||
const googleApiKey = process.env.GOOGLE_MAPS_API_KEY;
|
const googleApiKey = serverEnv.GOOGLE_MAPS_API_KEY;
|
||||||
const googleResponse = await fetch(
|
const googleResponse = await fetch(
|
||||||
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(query)}&key=${googleApiKey}`
|
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(query)}&key=${googleApiKey}`
|
||||||
);
|
);
|
||||||
const googleData = await googleResponse.json();
|
const googleData = await googleResponse.json();
|
||||||
|
|
||||||
// Reverse geocoding with Mapbox
|
// Reverse geocoding with Mapbox
|
||||||
const mapboxToken = process.env.MAPBOX_ACCESS_TOKEN;
|
const mapboxToken = serverEnv.MAPBOX_ACCESS_TOKEN;
|
||||||
const [lat, lng] = coordinates;
|
const [lat, lng] = coordinates;
|
||||||
const mapboxResponse = await fetch(
|
const mapboxResponse = await fetch(
|
||||||
`https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lng}&latitude=${lat}&access_token=${mapboxToken}`
|
`https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lng}&latitude=${lat}&access_token=${mapboxToken}`
|
||||||
@ -805,7 +805,7 @@ export async function POST(req: Request) {
|
|||||||
location?: string;
|
location?: string;
|
||||||
radius?: number;
|
radius?: number;
|
||||||
}) => {
|
}) => {
|
||||||
const mapboxToken = process.env.MAPBOX_ACCESS_TOKEN;
|
const mapboxToken = serverEnv.MAPBOX_ACCESS_TOKEN;
|
||||||
|
|
||||||
let proximity = '';
|
let proximity = '';
|
||||||
if (location) {
|
if (location) {
|
||||||
@ -854,9 +854,9 @@ export async function POST(req: Request) {
|
|||||||
from: z.string().describe("The source language (optional, will be auto-detected if not provided)."),
|
from: z.string().describe("The source language (optional, will be auto-detected if not provided)."),
|
||||||
}),
|
}),
|
||||||
execute: async ({ text, to, from }: { text: string; to: string; from?: string }) => {
|
execute: async ({ text, to, from }: { text: string; to: string; from?: string }) => {
|
||||||
const key = process.env.AZURE_TRANSLATOR_KEY;
|
const key = serverEnv.AZURE_TRANSLATOR_KEY;
|
||||||
const endpoint = "https://api.cognitive.microsofttranslator.com";
|
const endpoint = "https://api.cognitive.microsofttranslator.com";
|
||||||
const location = process.env.AZURE_TRANSLATOR_LOCATION;
|
const location = serverEnv.AZURE_TRANSLATOR_LOCATION;
|
||||||
|
|
||||||
const url = `${endpoint}/translate?api-version=3.0&to=${to}${from ? `&from=${from}` : ''}`;
|
const url = `${endpoint}/translate?api-version=3.0&to=${to}${from ? `&from=${from}` : ''}`;
|
||||||
|
|
||||||
@ -893,14 +893,14 @@ export async function POST(req: Request) {
|
|||||||
type: string;
|
type: string;
|
||||||
radius: number;
|
radius: number;
|
||||||
}) => {
|
}) => {
|
||||||
const apiKey = process.env.TRIPADVISOR_API_KEY;
|
const apiKey = serverEnv.TRIPADVISOR_API_KEY;
|
||||||
let finalLat = latitude;
|
let finalLat = latitude;
|
||||||
let finalLng = longitude;
|
let finalLng = longitude;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try geocoding first
|
// Try geocoding first
|
||||||
const geocodingData = await fetch(
|
const geocodingData = await fetch(
|
||||||
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(location)}&key=${process.env.GOOGLE_MAPS_API_KEY}`
|
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(location)}&key=${serverEnv.GOOGLE_MAPS_API_KEY}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const geocoding = await geocodingData.json();
|
const geocoding = await geocodingData.json();
|
||||||
@ -1007,7 +1007,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
// Get timezone for the location
|
// Get timezone for the location
|
||||||
const tzResponse = await fetch(
|
const tzResponse = await fetch(
|
||||||
`https://maps.googleapis.com/maps/api/timezone/json?location=${details.latitude},${details.longitude}×tamp=${Math.floor(Date.now() / 1000)}&key=${process.env.GOOGLE_MAPS_API_KEY}`
|
`https://maps.googleapis.com/maps/api/timezone/json?location=${details.latitude},${details.longitude}×tamp=${Math.floor(Date.now() / 1000)}&key=${serverEnv.GOOGLE_MAPS_API_KEY}`
|
||||||
);
|
);
|
||||||
const tzData = await tzResponse.json();
|
const tzData = await tzResponse.json();
|
||||||
const timezone = tzData.timeZoneId || 'UTC';
|
const timezone = tzData.timeZoneId || 'UTC';
|
||||||
@ -1131,7 +1131,7 @@ export async function POST(req: Request) {
|
|||||||
execute: async ({ flight_number }: { flight_number: string }) => {
|
execute: async ({ flight_number }: { flight_number: string }) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.aviationstack.com/v1/flights?access_key=${process.env.AVIATION_STACK_API_KEY}&flight_iata=${flight_number}`
|
`https://api.aviationstack.com/v1/flights?access_key=${serverEnv.AVIATION_STACK_API_KEY}&flight_iata=${flight_number}`
|
||||||
);
|
);
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { list, del, ListBlobResult } from '@vercel/blob';
|
import { del, list, ListBlobResult } from '@vercel/blob';
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
export const runtime = 'edge';
|
export const runtime = 'edge';
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
if (req.headers.get('Authorization') !== `Bearer ${process.env.CRON_SECRET}`) {
|
if (req.headers.get('Authorization') !== `Bearer ${serverEnv.CRON_SECRET}`) {
|
||||||
return new NextResponse('Unauthorized', { status: 401 });
|
return new NextResponse('Unauthorized', { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ThemeProvider } from "next-themes"
|
import { clientEnv } from "@/env/client";
|
||||||
import { ReactNode } from "react"
|
import { ThemeProvider } from "next-themes";
|
||||||
import posthog from 'posthog-js'
|
import posthog from 'posthog-js';
|
||||||
import { PostHogProvider } from 'posthog-js/react'
|
import { PostHogProvider } from 'posthog-js/react';
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
|
posthog.init(clientEnv.NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
api_host: clientEnv.NEXT_PUBLIC_POSTHOG_HOST,
|
||||||
person_profiles: 'always',
|
person_profiles: 'always',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import React, { useEffect, useRef, useCallback } from 'react';
|
import { clientEnv } from "@/env/client";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||||
import { cn } from "@/lib/utils";
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
interface Location {
|
interface Location {
|
||||||
lat: number;
|
lat: number;
|
||||||
@ -38,7 +39,7 @@ interface Place {
|
|||||||
timezone?: string;
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || '';
|
mapboxgl.accessToken = clientEnv.NEXT_PUBLIC_MAPBOX_TOKEN || '';
|
||||||
|
|
||||||
interface InteractiveMapProps {
|
interface InteractiveMapProps {
|
||||||
center: Location;
|
center: Location;
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
// /app/components/map-components.tsx
|
// /app/components/map-components.tsx
|
||||||
import React, { useEffect, useRef } from 'react';
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { clientEnv } from "@/env/client";
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || '';
|
mapboxgl.accessToken = clientEnv.NEXT_PUBLIC_MAPBOX_TOKEN || '';
|
||||||
|
|
||||||
interface Location {
|
interface Location {
|
||||||
lat: number;
|
lat: number;
|
||||||
@ -171,4 +172,4 @@ const MapContainer: React.FC<MapContainerProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { MapComponent, MapSkeleton, MapContainer };
|
export { MapComponent, MapContainer, MapSkeleton };
|
||||||
|
|||||||
16
env/client.ts
vendored
Normal file
16
env/client.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// https://env.t3.gg/docs/nextjs#create-your-schema
|
||||||
|
import { createEnv } from '@t3-oss/env-nextjs'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const clientEnv = createEnv({
|
||||||
|
client: {
|
||||||
|
NEXT_PUBLIC_MAPBOX_TOKEN: z.string().min(1),
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1),
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST: z.string().min(1).url(),
|
||||||
|
},
|
||||||
|
runtimeEnv: {
|
||||||
|
NEXT_PUBLIC_MAPBOX_TOKEN: process.env.NEXT_PUBLIC_MAPBOX_TOKEN,
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||||
|
},
|
||||||
|
})
|
||||||
24
env/server.ts
vendored
Normal file
24
env/server.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// https://env.t3.gg/docs/nextjs#create-your-schema
|
||||||
|
import { createEnv } from '@t3-oss/env-nextjs'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const serverEnv = createEnv({
|
||||||
|
server: {
|
||||||
|
ELEVENLABS_API_KEY: z.string().min(1),
|
||||||
|
TAVILY_API_KEY: z.string().min(1),
|
||||||
|
EXA_API_KEY: z.string().min(1),
|
||||||
|
TMDB_API_KEY: z.string().min(1),
|
||||||
|
YT_ENDPOINT: z.string().min(1),
|
||||||
|
FIRECRAWL_API_KEY: z.string().min(1),
|
||||||
|
OPENWEATHER_API_KEY: z.string().min(1),
|
||||||
|
SANDBOX_TEMPLATE_ID: z.string().min(1),
|
||||||
|
GOOGLE_MAPS_API_KEY: z.string().min(1),
|
||||||
|
MAPBOX_ACCESS_TOKEN: z.string().min(1),
|
||||||
|
AZURE_TRANSLATOR_KEY: z.string().min(1),
|
||||||
|
AZURE_TRANSLATOR_LOCATION: z.string().min(1),
|
||||||
|
TRIPADVISOR_API_KEY: z.string().min(1),
|
||||||
|
AVIATION_STACK_API_KEY: z.string().min(1),
|
||||||
|
CRON_SECRET: z.string().min(1),
|
||||||
|
},
|
||||||
|
experimental__runtimeEnv: process.env,
|
||||||
|
})
|
||||||
@ -1,6 +1,15 @@
|
|||||||
|
// https://env.t3.gg/docs/nextjs#validate-schema-on-build-(recommended)
|
||||||
|
import { createJiti } from 'jiti'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
const jiti = createJiti(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// Import env here to validate during build. Using jiti we can import .ts files :)
|
||||||
|
jiti.import('./env/server')
|
||||||
|
jiti.import('./env/client')
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
transpilePackages: ["geist"],
|
transpilePackages: ['geist'],
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -41,45 +50,45 @@ const nextConfig = {
|
|||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com',
|
hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: "**"
|
pathname: '**',
|
||||||
},
|
},
|
||||||
// upload.wikimedia.org
|
// upload.wikimedia.org
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'upload.wikimedia.org',
|
hostname: 'upload.wikimedia.org',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: '**'
|
pathname: '**',
|
||||||
},
|
},
|
||||||
// media.theresanaiforthat.com
|
// media.theresanaiforthat.com
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'media.theresanaiforthat.com',
|
hostname: 'media.theresanaiforthat.com',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: '**'
|
pathname: '**',
|
||||||
},
|
},
|
||||||
// www.uneed.best
|
// www.uneed.best
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'www.uneed.best',
|
hostname: 'www.uneed.best',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: '**'
|
pathname: '**',
|
||||||
},
|
},
|
||||||
// image.tmdb.org
|
// image.tmdb.org
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'image.tmdb.org',
|
hostname: 'image.tmdb.org',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: '/t/p/original/**'
|
pathname: '/t/p/original/**',
|
||||||
},
|
},
|
||||||
// image.tmdb.org
|
// image.tmdb.org
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'image.tmdb.org',
|
hostname: 'image.tmdb.org',
|
||||||
port: '',
|
port: '',
|
||||||
pathname: '/**'
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"@radix-ui/react-switch": "^1.1.1",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.0",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
|
"@t3-oss/env-nextjs": "^0.11.1",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@tavily/core": "^0.0.2",
|
"@tavily/core": "^0.0.2",
|
||||||
"@types/katex": "^0.16.7",
|
"@types/katex": "^0.16.7",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"google-auth-library": "^9.14.1",
|
"google-auth-library": "^9.14.1",
|
||||||
"highlight.js": "^11.10.0",
|
"highlight.js": "^11.10.0",
|
||||||
|
"jiti": "^2.4.2",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"lucide-react": "^0.424.0",
|
"lucide-react": "^0.424.0",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
|
|||||||
8101
pnpm-lock.yaml
8101
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user