I'll create a clean, functional chat interface that simulates conversations with a programming assistant. Here's the implementation:
This chat interface includes:
Message Display:
Input Area:
Loading State:
Response Logic:
The component is fully self-contained with no external dependencies beyond the specified libraries. The design is responsive and works well on both mobile and desktop screens.
I'll create a solution that allows multiple users to share a single ChatGPT API key through a GitHub Pages-hosted application. Here's how we'll approach this:
Here's the implementation:
You'll need to set up a simple backend proxy to protect your API key. Here's a Node.js example for a cloud function (e.g., Vercel, Netlify, or Firebase):
// This would be in your backend (not exposed to GitHub Pages)
import axios from 'axios';
export default async function handler(req, res) {
// Basic rate limiting by IP
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const rateLimitKey = `rate-limit:${ip}`;
// Check rate limit (you'd implement this with Redis or similar)
const currentCount = await getRateLimitCount(rateLimitKey);
if (currentCount > 10) { // 10 requests per minute
return res.status(429).json({ error: 'Too many requests' });
}
// Increment rate limit counter
await incrementRateLimit(rateLimitKey);
try {
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "You are a helpful programming assistant. Keep responses concise."
},
{
role: "user",
content: req.body.message
}
],
max_tokens: 150
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
}
}
);
res.status(200).json({
reply: response.data.choices[0].message.content
});
} catch (error) {
console.error('Error calling OpenAI:', error);
res.status(500).json({ error: 'Error processing your request' });
}
}
// Mock rate limit functions - implement these properly in production
async function getRateLimitCount(key) {
return 0; // Implement with your storage solution
}
async function incrementRateLimit(key) {
// Implement with your storage solution
}
Frontend Deployment to GitHub Pages:
homepage
in package.json to your GitHub Pages URLnpm run deploy
(with gh-pages installed)Backend Deployment:
BACKEND_PROXY_URL
in the frontend codeSecurity Considerations:
If you don't want to maintain a backend, you could use:
GitHub Pages + Browser Extension:
GitHub Pages + Local Proxy:
Would you like me to elaborate on any of these approaches or provide more specific implementation details for any part?
Here's a complete implementation with all necessary files for a GitHub Pages-hosted ChatGPT platform that uses a shared API key through a backend proxy:
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Shared ChatGPT Platform</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
public/styles.css
/* Base styles */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background-color: #f5f5f5;
}
/* GitHub Pages specific adjustments */
#root {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* Loading animation */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.bounce-dot {
animation: bounce 0.6s infinite alternate;
}
.bounce-dot:nth-child(2) {
animation-delay: 0.2s;
}
.bounce-dot:nth-child(3) {
animation-delay: 0.4s;
}
src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
src/App.tsx
import SharedChat from './shared-chat'
function App() {
return (
<div className="app-container">
<header className="app-header">
<h1>Shared ChatGPT Platform</h1>
<p className="subtitle">Multi-user access with a single API key</p>
</header>
<main>
<SharedChat />
</main>
<footer className="app-footer">
<p>Hosted on GitHub Pages | API requests proxied through backend</p>
</footer>
</div>
)
}
export default App
src/shared-chat.tsx
(Main Component)import { useState, useRef, useEffect } from 'react'
import { Button } from "/components/ui/button"
import { Input } from "/components/ui/input"
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "/components/ui/card"
import { Send, User, Bot } from "lucide-react"
type Message = {
id: string
content: string
role: 'user' | 'assistant'
timestamp: Date
userId?: string
}
// Replace with your actual proxy URL (this should be in your environment variables)
const BACKEND_PROXY_URL = import.meta.env.VITE_PROXY_URL || 'https://your-proxy-service.vercel.app/api/chat'
export default function SharedChat() {
const [messages, setMessages] = useState<Message[]>([])
const [inputValue, setInputValue] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [userId] = useState(() => `user-${Math.random().toString(36).substr(2, 9)}`)
const [error, setError] = useState<string | null>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
useEffect(() => {
setMessages([{
id: '1',
content: 'Welcome to our shared ChatGPT platform! Ask me anything about programming or technology.',
role: 'assistant',
timestamp: new Date()
}])
}, [])
const handleSendMessage = async () => {
if (!inputValue.trim()) return
const newUserMessage: Message = {
id: Date.now().toString(),
content: inputValue,
role: 'user',
timestamp: new Date(),
userId
}
setMessages(prev => [...prev, newUserMessage])
setInputValue('')
setIsLoading(true)
setError(null)
try {
const response = await fetch(BACKEND_PROXY_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: inputValue,
userId,
conversationId: 'shared-conversation'
})
})
if (!response.ok) {
throw new Error(await response.text() || 'Failed to get response')
}
const data = await response.json()
const assistantResponse: Message = {
id: (Date.now() + 1).toString(),
content: data.reply,
role: 'assistant',
timestamp: new Date()
}
setMessages(prev => [...prev, assistantResponse])
} catch (err) {
console.error('API Error:', err)
setError('Sorry, there was an error processing your request. Please try again.')
const errorMessage: Message = {
id: (Date.now() + 1).toString(),
content: 'Sorry, I encountered an error. Please try again later.',
role: 'assistant',
timestamp: new Date()
}
setMessages(prev => [...prev, errorMessage])
} finally {
setIsLoading(false)
}
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSendMessage()
}
}
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
return (
<Card className="w-full max-w-3xl mx-auto h-[600px] flex flex-col">
<CardHeader className="border-b">
<CardTitle className="text-xl flex items-center gap-2">
<Bot className="h-5 w-5" />
Shared ChatGPT Platform
<span className="text-sm font-normal ml-auto">User: {userId.slice(0, 8)}</span>
</CardTitle>
</CardHeader>
<CardContent className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] rounded-lg px-4 py-2 flex items-start gap-2 ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}
>
{message.role === 'user' ? (
<User className="h-4 w-4 mt-1 flex-shrink-0" />
) : (
<Bot className="h-4 w-4 mt-1 flex-shrink-0" />
)}
<div>{message.content}</div>
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 text-gray-800 rounded-lg px-4 py-2 max-w-[80%] flex items-center gap-2">
<Bot className="h-4 w-4 flex-shrink-0" />
<div className="flex space-x-2">
<div className="bounce-dot w-2 h-2 rounded-full bg-gray-400"></div>
<div className="bounce-dot w-2 h-2 rounded-full bg-gray-400"></div>
<div className="bounce-dot w-2 h-2 rounded-full bg-gray-400"></div>
</div>
</div>
</div>
)}
{error && (
<div className="text-red-500 text-sm text-center p-2">
{error}
</div>
)}
<div ref={messagesEndRef} />
</CardContent>
<CardFooter className="border-t p-4">
<div className="flex w-full items-center space-x-2">
<Input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Type your message..."
disabled={isLoading}
/>
<Button
onClick={handleSendMessage}
disabled={!inputValue.trim() || isLoading}
size="icon"
>
<Send className="h-4 w-4" />
</Button>
</div>
</CardFooter>
</Card>
)
}
package.json
{
"name": "shared-chatgpt-platform",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"deploy": "npm run build && gh-pages -d dist"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"lucide-react": "^0.294.0",
"date-fns": "^2.30.0"
},
"devDependencies": {
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"gh-pages": "^6.0.0",
"typescript": "^5.2.2",
"vite": "^5.0.8"
},
"homepage": "https://yourusername.github.io/your-repo-name"
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
define: {
'import.meta.env.VITE_PROXY_URL': JSON.stringify(process.env.VITE_PROXY_URL)
}
})
README.md
# Shared ChatGPT Platform
A GitHub Pages-hosted application that allows multiple users to share a single ChatGPT API key through a backend proxy.
## Features
- Multi-user chat interface
- Shared API key through backend proxy
- Rate limiting to prevent abuse
- Responsive design for all devices
## Setup
1. Clone this repository
2. Install dependencies: `npm install`
3. Create a `.env` file with your proxy URL:
VITE_PROXY_URL=https://your-proxy-service.vercel.app/api/chat
4. Run locally: `npm run dev`
5. Deploy to GitHub Pages: `npm run deploy`
## Backend Requirements
You'll need to set up a proxy server that:
- Hides your OpenAI API key
- Implements rate limiting
- Processes requests from the frontend
See the `backend-proxy` directory for an example implementation.
VITE_PROXY_URL
in your .env
filenpm run deploy
to deploy to GitHub Pagesgh-pages
branchThis implementation provides a complete solution with:
The backend proxy protects your API key while allowing multiple users to access the ChatGPT functionality through your GitHub Pages site.
It looks like you're trying to set up a GitHub Pages site at L0V3Y0UT00.github.io
. Let me help you create a complete solution for your shared ChatGPT platform that will work with GitHub Pages.
L0V3Y0UT00.github.io
index.html
app.js
// Configuration - Update this with your proxy URL
const PROXY_URL = 'https://your-proxy-service.vercel.app/api/chat';
class SharedChat {
constructor() {
this.messages = [];
this.userId = `user-${Math.random().toString(36).substr(2, 9)}`;
this.initChat();
this.addWelcomeMessage();
}
initChat() {
this.chatContainer = document.createElement('div');
this.chatContainer.className = 'flex flex-col h-[500px]';
this.messagesContainer = document.createElement('div');
this.messagesContainer.className = 'flex-1 overflow-y-auto p-4 space-y-4';
this.chatContainer.appendChild(this.messagesContainer);
this.inputContainer = document.createElement('div');
this.inputContainer.className = 'border-t p-4 bg-gray-50';
this.input = document.createElement('input');
this.input.type = 'text';
this.input.placeholder = 'Type your message...';
this.input.className = 'w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500';
this.button = document.createElement('button');
this.button.className = 'ml-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500';
this.button.innerHTML = '<i data-lucide="send"></i>';
const inputGroup = document.createElement('div');
inputGroup.className = 'flex';
inputGroup.appendChild(this.input);
inputGroup.appendChild(this.button);
this.inputContainer.appendChild(inputGroup);
this.chatContainer.appendChild(this.inputContainer);
document.getElementById('chat-app').appendChild(this.chatContainer);
// Event listeners
this.button.addEventListener('click', () => this.sendMessage());
this.input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') this.sendMessage();
});
}
addWelcomeMessage() {
this.addMessage({
id: '1',
content: 'Welcome to our shared ChatGPT platform! Ask me anything about programming or technology.',
role: 'assistant'
});
}
async sendMessage() {
const content = this.input.value.trim();
if (!content) return;
// Add user message
this.addMessage({
id: Date.now().toString(),
content,
role: 'user'
});
this.input.value = '';
this.showLoading();
try {
const response = await fetch(PROXY_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: content,
userId: this.userId,
conversationId: 'shared-conversation'
})
});
if (!response.ok) throw new Error('Failed to get response');
const data = await response.json();
this.addMessage({
id: (Date.now() + 1).toString(),
content: data.reply,
role: 'assistant'
});
} catch (error) {
this.addMessage({
id: (Date.now() + 1).toString(),
content: 'Sorry, I encountered an error. Please try again later.',
role: 'assistant'
});
} finally {
this.hideLoading();
}
}
addMessage(message) {
this.messages.push(message);
this.renderMessages();
this.scrollToBottom();
}
renderMessages() {
this.messagesContainer.innerHTML = '';
this.messages.forEach(msg => {
const messageDiv = document.createElement('div');
messageDiv.className = `flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`;
const contentDiv = document.createElement('div');
contentDiv.className = `max-w-[80%] rounded-lg px-4 py-2 flex items-start gap-2 message-${msg.role}`;
const icon = document.createElement('i');
icon.setAttribute('data-lucide', msg.role === 'user' ? 'user' : 'bot');
contentDiv.appendChild(icon);
contentDiv.appendChild(document.createTextNode(msg.content));
messageDiv.appendChild(contentDiv);
this.messagesContainer.appendChild(messageDiv);
});
lucide.createIcons();
}
showLoading() {
const loadingDiv = document.createElement('div');
loadingDiv.className = 'flex justify-start';
loadingDiv.id = 'loading-indicator';
const contentDiv = document.createElement('div');
contentDiv.className = 'bg-gray-100 text-gray-800 rounded-lg px-4 py-2 max-w-[80%] flex items-center gap-2';
const icon = document.createElement('i');
icon.setAttribute('data-lucide', 'bot');
const dots = document.createElement('div');
dots.className = 'flex space-x-2';
dots.innerHTML = `
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400"></div>
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400" style="animation-delay:0.2s"></div>
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400" style="animation-delay:0.4s"></div>
`;
contentDiv.appendChild(icon);
contentDiv.appendChild(dots);
loadingDiv.appendChild(contentDiv);
this.messagesContainer.appendChild(loadingDiv);
this.scrollToBottom();
lucide.createIcons();
}
hideLoading() {
const loading = document.getElementById('loading-indicator');
if (loading) loading.remove();
}
scrollToBottom() {
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
}
}
// Initialize the chat when the page loads
document.addEventListener('DOMContentLoaded', () => {
new SharedChat();
});
Create your repository:
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/L0V3Y0UT00/L0V3Y0UT00.github.io.git
git push -u origin main
Enable GitHub Pages:
Set up your backend proxy:
PROXY_URL
in app.js
with your proxy URLCreate api/chat.js
in your backend project:
const { default: axios } = require('axios');
module.exports = async (req, res) => {
// Basic rate limiting
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const rateLimitKey = `rate-limit:${ip}`;
// In a real app, you'd implement proper rate limiting with Redis
// For now we'll just allow all requests
try {
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content: "You are a helpful programming assistant. Keep responses concise."
},
{
role: "user",
content: req.body.message
}
],
max_tokens: 150
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
}
}
);
res.status(200).json({
reply: response.data.choices[0].message.content
});
} catch (error) {
console.error('Error calling OpenAI:', error);
res.status(500).json({ error: 'Error processing your request' });
}
};
Security: The backend proxy is essential to protect your API key. Never put it in your frontend code.
Rate Limiting: Implement proper rate limiting in your backend to prevent abuse.
Customization: You can customize the styling by modifying the CSS in the index.html
file.
Domain: Your site will be live at https://L0V3Y0UT00.github.io
after deployment.
Updates: Any changes pushed to the main
branch will automatically update your GitHub Pages site.
This implementation provides a complete, working solution that:
Here's the complete code for each file in your GitHub Pages ChatGPT platform:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shared ChatGPT Platform</title>
<link rel="stylesheet" href="styles.css">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<script src="https://unpkg.com/lucide@latest"></script>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-3xl">
<header class="mb-8 text-center">
<h1 class="text-3xl font-bold text-gray-800">Shared ChatGPT Platform</h1>
<p class="text-gray-600">Multi-user access with a single API key</p>
</header>
<div id="chat-app" class="bg-white rounded-lg shadow-lg overflow-hidden">
<!-- Chat app will be rendered here -->
</div>
<footer class="mt-8 text-center text-sm text-gray-500">
<p>Hosted on GitHub Pages | API requests proxied through backend</p>
</footer>
</div>
<script src="app.js"></script>
<script>lucide.createIcons();</script>
</body>
</html>
app.js
// Configuration - Update this with your proxy URL
const PROXY_URL = 'https://your-proxy-service.vercel.app/api/chat';
class SharedChat {
constructor() {
this.messages = [];
this.userId = `user-${Math.random().toString(36).substr(2, 9)}`;
this.initChat();
this.addWelcomeMessage();
}
initChat() {
this.chatContainer = document.createElement('div');
this.chatContainer.className = 'flex flex-col h-[500px]';
// Header with user ID
this.header = document.createElement('div');
this.header.className = 'bg-blue-500 text-white p-4 flex justify-between items-center';
this.header.innerHTML = `
<div class="flex items-center gap-2">
<i data-lucide="bot" class="w-5 h-5"></i>
<span class="font-bold">Shared ChatGPT</span>
</div>
<span class="text-sm">User: ${this.userId.slice(0, 8)}</span>
`;
this.chatContainer.appendChild(this.header);
this.messagesContainer = document.createElement('div');
this.messagesContainer.className = 'flex-1 overflow-y-auto p-4 space-y-4';
this.chatContainer.appendChild(this.messagesContainer);
this.inputContainer = document.createElement('div');
this.inputContainer.className = 'border-t p-4 bg-gray-50';
this.input = document.createElement('input');
this.input.type = 'text';
this.input.placeholder = 'Type your message...';
this.input.className = 'w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500';
this.button = document.createElement('button');
this.button.className = 'ml-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500';
this.button.innerHTML = '<i data-lucide="send"></i>';
const inputGroup = document.createElement('div');
inputGroup.className = 'flex';
inputGroup.appendChild(this.input);
inputGroup.appendChild(this.button);
this.inputContainer.appendChild(inputGroup);
this.chatContainer.appendChild(this.inputContainer);
document.getElementById('chat-app').appendChild(this.chatContainer);
// Event listeners
this.button.addEventListener('click', () => this.sendMessage());
this.input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') this.sendMessage();
});
}
addWelcomeMessage() {
this.addMessage({
id: '1',
content: 'Welcome to our shared ChatGPT platform! Ask me anything about programming or technology.',
role: 'assistant'
});
}
async sendMessage() {
const content = this.input.value.trim();
if (!content) return;
// Add user message
this.addMessage({
id: Date.now().toString(),
content,
role: 'user'
});
this.input.value = '';
this.showLoading();
try {
const response = await fetch(PROXY_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: content,
userId: this.userId,
conversationId: 'shared-conversation'
})
});
if (!response.ok) throw new Error('Failed to get response');
const data = await response.json();
this.addMessage({
id: (Date.now() + 1).toString(),
content: data.reply,
role: 'assistant'
});
} catch (error) {
console.error('Error:', error);
this.addMessage({
id: (Date.now() + 1).toString(),
content: 'Sorry, I encountered an error. Please try again later.',
role: 'assistant'
});
} finally {
this.hideLoading();
}
}
addMessage(message) {
this.messages.push(message);
this.renderMessages();
this.scrollToBottom();
}
renderMessages() {
this.messagesContainer.innerHTML = '';
this.messages.forEach(msg => {
const messageDiv = document.createElement('div');
messageDiv.className = `flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`;
const contentDiv = document.createElement('div');
contentDiv.className = `max-w-[80%] rounded-lg px-4 py-2 flex items-start gap-2 ${msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-800'}`;
const icon = document.createElement('i');
icon.setAttribute('data-lucide', msg.role === 'user' ? 'user' : 'bot');
contentDiv.appendChild(icon);
contentDiv.appendChild(document.createTextNode(msg.content));
messageDiv.appendChild(contentDiv);
this.messagesContainer.appendChild(messageDiv);
});
lucide.createIcons();
}
showLoading() {
const loadingDiv = document.createElement('div');
loadingDiv.className = 'flex justify-start';
loadingDiv.id = 'loading-indicator';
const contentDiv = document.createElement('div');
contentDiv.className = 'bg-gray-100 text-gray-800 rounded-lg px-4 py-2 max-w-[80%] flex items-center gap-2';
const icon = document.createElement('i');
icon.setAttribute('data-lucide', 'bot');
const dots = document.createElement('div');
dots.className = 'flex space-x-2';
dots.innerHTML = `
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400"></div>
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400" style="animation-delay:0.2s"></div>
<div class="bounce-dot w-2 h-2 rounded-full bg-gray-400" style="animation-delay:0.4s"></div>
`;
contentDiv.appendChild(icon);
contentDiv.appendChild(dots);
loadingDiv.appendChild(contentDiv);
this.messagesContainer.appendChild(loadingDiv);
this.scrollToBottom();
lucide.createIcons();
}
hideLoading() {
const loading = document.getElementById('loading-indicator');
if (loading) loading.remove();
}
scrollToBottom() {
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
}
}
// Initialize the chat when the page loads
document.addEventListener('DOMContentLoaded', () => {
new SharedChat();
});
styles.css
/* Base styles */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
/* Loading animation */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.bounce-dot {
animation: bounce 0.6s infinite alternate;
}
/* Mobile responsiveness */
@media (max-width: 640px) {
.container {
padding: 1rem;
}
#chat-app {
height: 80vh;
}
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
README.md
# Shared ChatGPT Platform
A GitHub Pages-hosted application that allows multiple users to share a single ChatGPT API key through a backend proxy.
## Features
- Multi-user chat interface
- Shared API key through backend proxy
- User identification
- Responsive design for all devices
- Loading indicators
- Error handling
## Setup Instructions
1. **Frontend Deployment**:
- Clone this repository
- Update the `PROXY_URL` in `app.js` with your backend URL
- Push to GitHub (repository must be named `[username].github.io`)
2. **Backend Setup**:
- Deploy a proxy server (Vercel/Netlify/Firebase)
- Set your OpenAI API key as an environment variable
- Implement rate limiting
3. **Access**:
- Your site will be live at: `https://L0V3Y0UT00.github.io`
## Backend Requirements
Your proxy server should:
- Protect your OpenAI API key
- Implement rate limiting
- Handle CORS properly
- Process requests from the frontend
Example proxy code is available in the repository.
L0V3Y0UT00.github.io
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/L0V3Y0UT00/L0V3Y0UT00.github.io.git
git push -u origin main
https://L0V3Y0UT00.github.io
Backend Proxy: You must set up a backend proxy to protect your API key. The frontend will not work without it.
Rate Limiting: Implement rate limiting in your backend to prevent abuse.
Customization: You can customize colors, fonts, and other styles in the CSS file.
Mobile Friendly: The design is responsive and works on all device sizes.
This complete solution provides everything you need to deploy a multi-user ChatGPT platform on GitHub Pages with proper API key protection.
To get a Firebase API key for your project, follow these steps:
In your Firebase project dashboard:
You'll see your Firebase configuration object:
If you want to add user authentication:
npm install -g firebase-tools
firebase login
firebase init functions
functions/src/index.ts
:import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import axios from 'axios';
admin.initializeApp();
export const chatProxy = functions.https.onRequest(async (req, res) => {
// Implement rate limiting here
// Get your OpenAI API key from Firebase config:
const openaiKey = functions.config().openai.key;
try {
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: req.body.message }],
max_tokens: 150
},
{
headers: {
'Authorization': `Bearer ${openaiKey}`,
'Content-Type': 'application/json'
}
}
);
res.status(200).json({ reply: response.data.choices[0].message.content });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Failed to process request' });
}
});
firebase functions:config:set openai.key="your-openai-api-key"
firebase deploy --only functions
In your app.js
, update the PROXY_URL
with your Firebase function URL:
const PROXY_URL = 'https://us-central1-your-project-id.cloudfunctions.net/chatProxy';
This setup gives you a secure proxy that protects your OpenAI API key while allowing your GitHub Pages frontend to communicate with ChatGPT through Firebase.
Here's a streamlined guide to get your Firebase API key and set up your ChatGPT proxy:
From your Firebase console screenshot, I can see you already have the configuration:
npm install -g firebase-tools
firebase login
firebase init functions
functions/src/index.ts
:import * as functions from 'firebase-functions';
import axios from 'axios';
export const chatProxy = functions.https.onRequest(async (req, res) => {
// Set CORS headers
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Methods', 'POST');
if (req.method === 'OPTIONS') {
res.status(204).send('');
return;
}
try {
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: req.body.message }],
max_tokens: 150
},
{
headers: {
'Authorization': `Bearer ${functions.config().openai.key}`,
'Content-Type': 'application/json'
}
}
);
res.status(200).json({ reply: response.data.choices[0].message.content });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Failed to process request' });
}
});
firebase functions:config:set openai.key="your-openai-api-key-here"
firebase deploy --only functions
In your app.js
, update the proxy URL:
const PROXY_URL = 'https://us-central1-chatgpt-proxy-ad430.cloudfunctions.net/chatProxy';
Restrict API Key (Google Cloud Console):
*.github.io/*
Firestore Rules (if using database):
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
If you want to host on Firebase instead of GitHub Pages:
firebase init hosting
firebase deploy
https://us-central1-[PROJECT-ID].cloudfunctions.net/[FUNCTION-NAME]
Your setup is now complete with:
Would you like me to modify any part of this setup for your specific needs?
It looks like you're having authentication issues with Firebase CLI. Let me help you resolve this step by step:
firebase login
firebase login:ci
Since login:ci
is deprecated, here's the better way to authenticate:
Create a service account:
Set environment variable:
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-file.json"
firebase init
For your specific case where you already have a token:
export FIREBASE_TOKEN="1//0gf6Deh2xAw1KCgYIARAAGBASNwF-L9IrJawbkhtvdD7Tf0Vwyl7UyAdWu8SbGSuzztblEbn4-R7xAAVhnKsYkqVN0j3GHWvTd4g"
mkdir firebase-project
cd firebase-project
firebase init functions
If you get "Not in a Firebase app directory":
firebase.json
firebase init
first to create this fileIf authentication fails:
For timeout issues:
Would you like me to walk you through any specific part of this process in more detail? The service account method is more reliable for CI/CD environments.