hugex-gh / app /routes /status.tsx
drbh
fix: make setup more strict
e30ea26
import { json } from "@remix-run/node";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
export async function loader({ request }: LoaderFunctionArgs) {
// Check environment variables
const envCheck = {
GITHUB_APP_ID: {
value: process.env.GITHUB_APP_ID || null,
required: true,
example: "your-app-id",
},
GITHUB_APP_NAME: {
value: process.env.GITHUB_APP_NAME || null,
required: true,
example: "your-app-name",
},
GITHUB_APP_PRIVATE_KEY: {
value: process.env.GITHUB_APP_PRIVATE_KEY ? "βœ“ Set" : null,
required: true,
example: "-----BEGIN RSA PRIVATE KEY-----...",
},
GITHUB_APP_CLIENT_ID: {
value: process.env.GITHUB_APP_CLIENT_ID || null,
required: true,
example: "your-client-id",
},
GITHUB_APP_CLIENT_SECRET: {
value: process.env.GITHUB_APP_CLIENT_SECRET ? "βœ“ Set" : null,
required: true,
example: "your-client-secret",
},
GITHUB_WEBHOOK_SECRET: {
value: process.env.GITHUB_WEBHOOK_SECRET ? "βœ“ Set" : null,
required: false,
example: "your-webhook-secret",
},
GITHUB_CALLBACK_URL: {
value: process.env.GITHUB_CALLBACK_URL || null,
required: true,
example: "http://localhost:3000/auth/github/callback",
},
SESSION_SECRET: {
value: process.env.SESSION_SECRET ? "βœ“ Set" : null,
required: true,
example: "your-session-secret",
},
};
const missing = Object.entries(envCheck).filter(
([key, config]) => config.required && !config.value
);
const isReady = missing.length === 0;
return json({
envCheck,
missing,
isReady,
nodeEnv: process.env.NODE_ENV || "development",
});
}
export default function Status() {
const { envCheck, missing, isReady, nodeEnv } = useLoaderData<typeof loader>();
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-4xl mx-auto px-4">
<div className="bg-white rounded-lg shadow-md p-6">
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold text-gray-900">
Environment Status
</h1>
<Link
to="/"
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
>
← Back to Home
</Link>
</div>
{/* Status Overview */}
<div className="mb-8">
<div className={`p-4 rounded-lg border ${
isReady
? 'bg-green-50 border-green-200'
: 'bg-red-50 border-red-200'
}`}>
<div className="flex items-center">
<div className="flex-shrink-0">
{isReady ? (
<svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
) : (
<svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
)}
</div>
<div className="ml-3">
<h3 className={`text-sm font-medium ${
isReady ? 'text-green-800' : 'text-red-800'
}`}>
{isReady ? 'βœ… Ready to go!' : '⚠️ Configuration needed'}
</h3>
<div className={`mt-2 text-sm ${
isReady ? 'text-green-700' : 'text-red-700'
}`}>
{isReady ? (
<p>All required environment variables are configured.</p>
) : (
<p>
{missing.length} required environment variable{missing.length > 1 ? 's' : ''} missing.
</p>
)}
</div>
</div>
</div>
</div>
</div>
{/* Environment Variables Table */}
<div className="mb-8">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Environment Variables
</h2>
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table className="min-w-full divide-y divide-gray-300">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Variable
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Required
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Example
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{Object.entries(envCheck).map(([key, config]) => (
<tr key={key}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{key}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{config.value ? (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
{config.value}
</span>
) : (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
Not set
</span>
)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{config.required ? 'βœ… Yes' : 'βšͺ Optional'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono">
{config.example}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* Missing Variables */}
{missing.length > 0 && (
<div className="mb-8">
<h2 className="text-lg font-semibold text-gray-900 mb-4">
Missing Configuration
</h2>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800">
Add these to your .env file:
</h3>
<div className="mt-2 text-sm text-yellow-700">
<div className="bg-gray-900 text-green-400 p-3 rounded font-mono text-xs overflow-x-auto">
{missing.map(([key, config]) => (
<div key={key}>
{key}={config.example}
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
)}
{/* Quick Actions */}
<div className="bg-gray-50 rounded-lg p-4">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Quick Actions
</h3>
<div className="grid md:grid-cols-2 gap-4">
<div>
<h4 className="font-medium text-gray-900 mb-2">Generate Secrets</h4>
<p className="text-sm text-gray-600 mb-3">
Generate random secrets for SESSION_SECRET and GITHUB_WEBHOOK_SECRET:
</p>
<code className="text-xs bg-gray-800 text-green-400 p-2 rounded block">
npm run secrets
</code>
</div>
<div>
<h4 className="font-medium text-gray-900 mb-2">Environment</h4>
<p className="text-sm text-gray-600 mb-2">
Current environment: <span className="font-mono">{nodeEnv}</span>
</p>
<p className="text-sm text-gray-600">
{nodeEnv === 'development' ? (
<>βœ… Development mode - using .env file</>
) : (
<>πŸš€ Production mode - using system environment</>
)}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}