Beta and env changes

This commit is contained in:
jackiettran
2025-07-31 22:06:03 -04:00
parent 7c6c120969
commit 8a02304da8
6 changed files with 215 additions and 39 deletions

View File

@@ -0,0 +1,21 @@
const verifyBetaPassword = (req, res, next) => {
const betaPassword = req.headers['x-beta-password'];
const configuredPassword = process.env.BETA_PASSWORD;
if (!configuredPassword) {
console.error('BETA_PASSWORD environment variable is not set');
return res.status(500).json({ error: 'Beta password not configured on server' });
}
if (!betaPassword) {
return res.status(401).json({ error: 'Beta password required' });
}
if (betaPassword !== configuredPassword) {
return res.status(403).json({ error: 'Invalid beta password' });
}
next();
};
module.exports = { verifyBetaPassword };

10
backend/routes/beta.js Normal file
View File

@@ -0,0 +1,10 @@
const express = require('express');
const router = express.Router();
const { verifyBetaPassword } = require('../middleware/betaAuth');
// Beta verification endpoint
router.get('/verify', verifyBetaPassword, (req, res) => {
res.json({ success: true, message: 'Beta access granted' });
});
module.exports = router;

View File

@@ -1,50 +1,57 @@
// Load environment-specific config // Load environment-specific config
const env = process.env.NODE_ENV; const env = process.env.NODE_ENV || "dev";
const envFile = `.env.${env}`; const envFile = `.env.${env}`;
require('dotenv').config({ require("dotenv").config({
path: process.env.DOTENV_CONFIG_PATH path: envFile,
}); });
const express = require('express'); const express = require("express");
const cors = require('cors'); const cors = require("cors");
const bodyParser = require('body-parser'); const bodyParser = require("body-parser");
const path = require('path'); const path = require("path");
const { sequelize } = require('./models'); // Import from models/index.js to ensure associations are loaded const { sequelize } = require("./models"); // Import from models/index.js to ensure associations are loaded
const authRoutes = require('./routes/auth'); const authRoutes = require("./routes/auth");
const phoneAuthRoutes = require('./routes/phone-auth'); const phoneAuthRoutes = require("./routes/phone-auth");
const userRoutes = require('./routes/users'); const userRoutes = require("./routes/users");
const itemRoutes = require('./routes/items'); const itemRoutes = require("./routes/items");
const rentalRoutes = require('./routes/rentals'); const rentalRoutes = require("./routes/rentals");
const messageRoutes = require('./routes/messages'); const messageRoutes = require("./routes/messages");
const betaRoutes = require("./routes/beta");
const app = express(); const app = express();
app.use(cors()); app.use(cors());
app.use(bodyParser.json({ limit: '5mb' })); app.use(bodyParser.json({ limit: "5mb" }));
app.use(bodyParser.urlencoded({ extended: true, limit: '5mb' })); app.use(bodyParser.urlencoded({ extended: true, limit: "5mb" }));
// Serve static files from uploads directory // Serve static files from uploads directory
app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); app.use("/uploads", express.static(path.join(__dirname, "uploads")));
app.use('/api/auth', authRoutes); // Beta verification route (doesn't require auth)
app.use('/api/auth/phone', phoneAuthRoutes); app.use("/api/beta", betaRoutes);
app.use('/api/users', userRoutes);
app.use('/api/items', itemRoutes);
app.use('/api/rentals', rentalRoutes);
app.use('/api/messages', messageRoutes);
app.get('/', (req, res) => { app.use("/api/auth", authRoutes);
res.json({ message: 'CommunityRentals.App API is running!' }); app.use("/api/auth/phone", phoneAuthRoutes);
app.use("/api/users", userRoutes);
app.use("/api/items", itemRoutes);
app.use("/api/rentals", rentalRoutes);
app.use("/api/messages", messageRoutes);
app.get("/", (req, res) => {
res.json({ message: "CommunityRentals.App API is running!" });
}); });
const PORT = process.env.PORT || 5000; const PORT = process.env.PORT || 5000;
sequelize.sync({ alter: true }).then(() => { sequelize
console.log('Database synced'); .sync({ alter: true })
app.listen(PORT, () => { .then(() => {
console.log(`Server is running on port ${PORT}`); console.log("Database synced");
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
})
.catch((err) => {
console.error("Unable to sync database:", err);
}); });
}).catch(err => {
console.error('Unable to sync database:', err);
});

View File

@@ -1,3 +1,3 @@
# https://www.robotstxt.org/robotstxt.html # https://www.robotstxt.org/robotstxt.html
User-agent: * User-agent: *
Disallow: Disallow: /

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './contexts/AuthContext'; import { AuthProvider } from './contexts/AuthContext';
import BetaPasswordProtection from './components/BetaPasswordProtection';
import Navbar from './components/Navbar'; import Navbar from './components/Navbar';
import Footer from './components/Footer'; import Footer from './components/Footer';
import Home from './pages/Home'; import Home from './pages/Home';
@@ -22,12 +23,13 @@ import './App.css';
function App() { function App() {
return ( return (
<AuthProvider> <BetaPasswordProtection>
<Router> <AuthProvider>
<div className="d-flex flex-column min-vh-100"> <Router>
<Navbar /> <div className="d-flex flex-column min-vh-100">
<main className="flex-grow-1"> <Navbar />
<Routes> <main className="flex-grow-1">
<Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} /> <Route path="/register" element={<Register />} />
@@ -104,6 +106,7 @@ function App() {
</div> </div>
</Router> </Router>
</AuthProvider> </AuthProvider>
</BetaPasswordProtection>
); );
} }

View File

@@ -0,0 +1,135 @@
import React, { useState, useEffect } from "react";
interface BetaPasswordProtectionProps {
children: React.ReactNode;
}
const BetaPasswordProtection: React.FC<BetaPasswordProtectionProps> = ({
children,
}) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
useEffect(() => {
// Check if user already has valid beta access
const betaToken = localStorage.getItem("betaAccess");
if (betaToken) {
// Verify the stored token is still valid
verifyBetaAccess(betaToken);
} else {
setLoading(false);
}
}, []);
const verifyBetaAccess = async (token: string) => {
try {
const response = await fetch(
`${process.env.REACT_APP_API_URL}/beta/verify`,
{
headers: {
"X-Beta-Password": token,
},
}
);
if (response.ok) {
setIsAuthenticated(true);
} else {
localStorage.removeItem("betaAccess");
}
} catch (error) {
localStorage.removeItem("betaAccess");
} finally {
setLoading(false);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (!password) {
setError("Please enter a password");
return;
}
try {
const response = await fetch(
`${process.env.REACT_APP_API_URL}/beta/verify`,
{
headers: {
"X-Beta-Password": password,
},
}
);
if (response.ok) {
localStorage.setItem("betaAccess", password);
setIsAuthenticated(true);
} else {
setError("Invalid beta password");
}
} catch (error) {
setError("Failed to verify beta password");
}
};
if (loading) {
return (
<div className="min-vh-100 d-flex align-items-center justify-content-center">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
);
}
if (!isAuthenticated) {
return (
<div className="min-vh-100 d-flex align-items-center justify-content-center bg-light">
<div
className="card shadow"
style={{ maxWidth: "400px", width: "100%" }}
>
<div className="card-body p-5">
<h2 className="text-center mb-4">Beta Access Required</h2>
<p className="text-muted text-center mb-4">
This site is currently in beta testing. Please enter the beta
password to continue.
</p>
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="betaPassword" className="form-label">
Beta Password
</label>
<input
type="password"
className="form-control"
id="betaPassword"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter beta password"
autoFocus
/>
</div>
{error && (
<div className="alert alert-danger" role="alert">
{error}
</div>
)}
<button type="submit" className="btn btn-primary w-100">
Access Beta
</button>
</form>
</div>
</div>
</div>
);
}
return <>{children}</>;
};
export default BetaPasswordProtection;