dodanie hasla i poprawki
This commit is contained in:
185
App.tsx
185
App.tsx
@@ -7,10 +7,31 @@ import StepEventType from './components/StepEventType';
|
|||||||
import StepToneGoal from './components/StepToneGoal';
|
import StepToneGoal from './components/StepToneGoal';
|
||||||
import StepDetails from './components/StepDetails';
|
import StepDetails from './components/StepDetails';
|
||||||
import StepResult from './components/StepResult';
|
import StepResult from './components/StepResult';
|
||||||
import { ChevronLeft, ExternalLink, Sparkles, User } from 'lucide-react';
|
import { ChevronLeft, ExternalLink, Sparkles, User, Lock, ArrowRight, AlertCircle, Bug } from 'lucide-react';
|
||||||
import { generateStoryContent } from './services/geminiService';
|
import { generateStoryContent } from './services/geminiService';
|
||||||
|
|
||||||
const STORAGE_KEY = 'gpx-storyteller-state-v6'; // Incremented version for structure change
|
const STORAGE_KEY = 'gpx-storyteller-state-v6'; // Incremented version for structure change
|
||||||
|
const AUTH_KEY = 'promptstory-auth-token';
|
||||||
|
|
||||||
|
// --- PASSWORD CONFIGURATION ---
|
||||||
|
|
||||||
|
// 1. Try fetching from Vite standard (import.meta.env)
|
||||||
|
// @ts-ignore
|
||||||
|
const VITE_ENV_PASS = import.meta.env && import.meta.env.VITE_APP_PASSWORD ? import.meta.env.VITE_APP_PASSWORD : '';
|
||||||
|
|
||||||
|
// 2. Try fetching from Process env (sometimes used in other build tools)
|
||||||
|
// @ts-ignore
|
||||||
|
const PROCESS_ENV_PASS = (typeof process !== 'undefined' && process.env && process.env.VITE_APP_PASSWORD) ? process.env.VITE_APP_PASSWORD : '';
|
||||||
|
|
||||||
|
// 3. Fallback (Hardcoded safety net)
|
||||||
|
// W tym środowisku (AI Studio/Web Preview) restart serwera jest trudny.
|
||||||
|
// Ustawiamy hasło "na sztywno" jako zapas, żeby działało od razu.
|
||||||
|
const FALLBACK_PASS = 'Prometeusz';
|
||||||
|
|
||||||
|
// Decision Logic
|
||||||
|
const ENV_PASSWORD = VITE_ENV_PASS || PROCESS_ENV_PASS;
|
||||||
|
const APP_PASSWORD = ENV_PASSWORD || FALLBACK_PASS;
|
||||||
|
const IS_USING_FALLBACK = !ENV_PASSWORD;
|
||||||
|
|
||||||
const INITIAL_STATE: WizardState = {
|
const INITIAL_STATE: WizardState = {
|
||||||
step: Step.CONTEXT,
|
step: Step.CONTEXT,
|
||||||
@@ -38,7 +59,131 @@ const INITIAL_STATE: WizardState = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- LOGIN SCREEN COMPONENT ---
|
||||||
|
const LoginScreen: React.FC<{ onLogin: (success: boolean) => void }> = ({ onLogin }) => {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [showDebug, setShowDebug] = useState(false);
|
||||||
|
|
||||||
|
// DEBUGGING: Log to console on mount
|
||||||
|
useEffect(() => {
|
||||||
|
console.group("--- DEBUG PASSWORD SYSTEM ---");
|
||||||
|
console.log("1. Detected VITE_ENV:", VITE_ENV_PASS ? "****" : "(empty)");
|
||||||
|
console.log("2. Detected PROCESS_ENV:", PROCESS_ENV_PASS ? "****" : "(empty)");
|
||||||
|
console.log("3. Final Password Source:", IS_USING_FALLBACK ? "FALLBACK (Hardcoded)" : ".ENV FILE");
|
||||||
|
console.log("4. Active Password Length:", APP_PASSWORD.length);
|
||||||
|
console.groupEnd();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Trim input to avoid accidental spaces from copy-paste
|
||||||
|
if (input.trim() === APP_PASSWORD) {
|
||||||
|
onLogin(true);
|
||||||
|
} else {
|
||||||
|
console.log(`Failed Login. Input: "${input}" vs Expected: "${APP_PASSWORD}"`);
|
||||||
|
setError(true);
|
||||||
|
setInput('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-4">
|
||||||
|
<div className="bg-white p-8 rounded-xl shadow-lg border border-gray-100 max-w-md w-full text-center space-y-6 animate-fade-in relative">
|
||||||
|
|
||||||
|
{/* Debug Toggle (Hidden in corner) */}
|
||||||
|
<button
|
||||||
|
onClick={() => setShowDebug(!showDebug)}
|
||||||
|
className="absolute top-2 right-2 text-gray-200 hover:text-gray-400 p-2"
|
||||||
|
title="Pokaż informacje debugowania"
|
||||||
|
>
|
||||||
|
<Bug size={16} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="flex justify-center mb-2">
|
||||||
|
<div className="bg-[#EA4420]/10 p-4 rounded-full text-[#EA4420]">
|
||||||
|
<Lock size={32} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900">Dostęp Chroniony</h2>
|
||||||
|
<p className="text-gray-500 mt-2">Wprowadź hasło, aby uzyskać dostęp do kreatora PromptStory.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInput(e.target.value);
|
||||||
|
setError(false);
|
||||||
|
}}
|
||||||
|
placeholder="Hasło..."
|
||||||
|
className={`w-full p-4 border rounded-lg outline-none transition-all font-medium text-center tracking-widest ${
|
||||||
|
error
|
||||||
|
? 'border-red-300 bg-red-50 focus:border-red-500'
|
||||||
|
: 'border-gray-200 focus:border-[#EA4420] focus:ring-1 focus:ring-[#EA4420]'
|
||||||
|
}`}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="flex items-center justify-center gap-2 text-red-500 text-sm font-medium animate-pulse">
|
||||||
|
<AlertCircle size={16} />
|
||||||
|
<span>Nieprawidłowe hasło</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-[#EA4420] text-white py-4 rounded-lg font-bold hover:bg-[#d63b1a] transition-colors flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<span>Odblokuj</span>
|
||||||
|
<ArrowRight size={20} />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* DEBUG PANEL */}
|
||||||
|
{showDebug && (
|
||||||
|
<div className="bg-gray-800 text-left text-green-400 p-4 rounded-md text-xs font-mono mt-4 overflow-hidden">
|
||||||
|
<p className="font-bold border-b border-gray-600 mb-2 pb-1 text-white">DEBUG STATUS:</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-x-4 gap-y-1 mt-2">
|
||||||
|
<span className="text-gray-400">SOURCE:</span>
|
||||||
|
<span className={IS_USING_FALLBACK ? "text-yellow-400 font-bold" : "text-green-400 font-bold"}>
|
||||||
|
{IS_USING_FALLBACK ? "FALLBACK (Code)" : ".ENV FILE"}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="text-gray-400">ACTIVE PASS:</span>
|
||||||
|
<span>{APP_PASSWORD.substring(0, 4)}... (len: {APP_PASSWORD.length})</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{IS_USING_FALLBACK && (
|
||||||
|
<div className="mt-3 text-yellow-500 border-t border-gray-600 pt-2">
|
||||||
|
<p>System używa hasła awaryjnego zdefiniowanego w kodzie, ponieważ nie może odświeżyć pliku .env bez restartu.</p>
|
||||||
|
<p className="mt-1 font-bold text-white">Twoje hasło powinno teraz działać.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="text-xs text-gray-300 pt-4">
|
||||||
|
PromptStory v1.0 • Private Instance
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
|
// Auth State
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
const [isAuthChecking, setIsAuthChecking] = useState(true);
|
||||||
|
|
||||||
|
// App State
|
||||||
const [data, setData] = useState<WizardState>(INITIAL_STATE);
|
const [data, setData] = useState<WizardState>(INITIAL_STATE);
|
||||||
const [generatedContent, setGeneratedContent] = useState<GeneratedContent | null>(null);
|
const [generatedContent, setGeneratedContent] = useState<GeneratedContent | null>(null);
|
||||||
const [isGenerating, setIsGenerating] = useState(false);
|
const [isGenerating, setIsGenerating] = useState(false);
|
||||||
@@ -49,7 +194,29 @@ const App: React.FC = () => {
|
|||||||
const [logoError, setLogoError] = useState(false);
|
const [logoError, setLogoError] = useState(false);
|
||||||
const [avatarError, setAvatarError] = useState(false);
|
const [avatarError, setAvatarError] = useState(false);
|
||||||
|
|
||||||
// Persistence Logic
|
// 1. Check Auth on Mount
|
||||||
|
useEffect(() => {
|
||||||
|
const storedAuth = localStorage.getItem(AUTH_KEY);
|
||||||
|
if (storedAuth === 'true') {
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
}
|
||||||
|
setIsAuthChecking(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleLogin = (success: boolean) => {
|
||||||
|
if (success) {
|
||||||
|
localStorage.setItem(AUTH_KEY, 'true');
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
localStorage.removeItem(AUTH_KEY);
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
resetApp();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Load Data Persistence Logic
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const saved = localStorage.getItem(STORAGE_KEY);
|
const saved = localStorage.getItem(STORAGE_KEY);
|
||||||
if (saved) {
|
if (saved) {
|
||||||
@@ -154,7 +321,13 @@ const App: React.FC = () => {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isLoaded) return null;
|
// --- RENDER LOGIC ---
|
||||||
|
|
||||||
|
if (isAuthChecking || !isLoaded) return null; // Loading state
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return <LoginScreen onLogin={handleLogin} />;
|
||||||
|
}
|
||||||
|
|
||||||
// Step Labels for Progress Bar (Order Changed)
|
// Step Labels for Progress Bar (Order Changed)
|
||||||
const stepsLabels = ['Kontekst', 'Typ', 'Platforma', 'Vibe & Cel', 'Szczegóły'];
|
const stepsLabels = ['Kontekst', 'Typ', 'Platforma', 'Vibe & Cel', 'Szczegóły'];
|
||||||
@@ -179,9 +352,15 @@ const App: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<h1 className="text-xl font-bold tracking-tight text-gray-900">PromptStory</h1>
|
<h1 className="text-xl font-bold tracking-tight text-gray-900">PromptStory</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
<button onClick={resetApp} className="text-xs font-medium text-gray-400 hover:text-[#EA4420] transition-colors uppercase tracking-wide">
|
<button onClick={resetApp} className="text-xs font-medium text-gray-400 hover:text-[#EA4420] transition-colors uppercase tracking-wide">
|
||||||
Resetuj
|
Resetuj
|
||||||
</button>
|
</button>
|
||||||
|
<button onClick={handleLogout} className="text-xs font-medium text-gray-400 hover:text-red-500 transition-colors uppercase tracking-wide">
|
||||||
|
Wyloguj
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user