Technology Stack
| Layer | Technology | Version/Details |
|---|---|---|
| Backend | Express.js | ^5.2.1 |
| Database | SQLite | better-sqlite3 ^12.6.2 |
| Authentication | JWT | jsonwebtoken ^9.0.2 |
| Frontend | Tailwind CSS | ^3.4.17 |
| Image Processing | Sharp | ^0.34.5 |
| File Upload | Multer | ^2.0.2 |
| Security | express-rate-limit | ^7.5.0 |
| Compression | compression | ^1.8.1 |
| CORS | cors | ^2.8.6 |
| Telegram | Native HTTPS | - |
Purpose and Philosophy
One Star is a complete business website designed for a photography and videography studio. The project provides a comprehensive digital presence for creative professionals, featuring portfolio showcase, service listings, booking management, and an intuitive admin dashboard.
The core philosophy centers on "Elegant Simplicity" - a clean, sophisticated design that puts the visual work front and center while providing powerful backend tools for managing the business. The website uses premium typography (Cinzel, Cormorant Garamond, Bebas Neue) to create an upscale, cinematic feel appropriate for high-end photography services.
Core Design Principles
- Visual-First Approach: The website prioritizes showcasing the photographer's work with minimal distractions. Large imagery, generous whitespace, and elegant typography create an immersive experience.
- Automated Notifications: Telegram bot integration provides instant notifications for new bookings and contact inquiries, enabling rapid response to potential clients.
- Automated Image Processing: All uploaded portfolio images are automatically converted to WebP format using Sharp, ensuring optimal file sizes without quality loss.
- Content Scraping: The system can automatically fetch YouTube thumbnails and social media post previews, eliminating manual image preparation.
- Rate-Limited Security: The API implements rate limiting to prevent abuse while maintaining responsive service for legitimate users.
Architecture Deep Dive
Database Schema
The system uses SQLite with six interconnected tables:
-- Portfolio items for showcasing work
CREATE TABLE portfolio (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
image_url TEXT,
category TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Client bookings with status tracking
CREATE TABLE bookings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT NOT NULL,
service TEXT,
booking_date TEXT,
booking_time TEXT,
message TEXT,
status TEXT DEFAULT 'pending',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Contact form submissions
CREATE TABLE contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT,
phone TEXT NOT NULL,
message TEXT NOT NULL,
status TEXT DEFAULT 'unread',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Service offerings with pricing
CREATE TABLE services (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
subtitle TEXT,
price TEXT,
unit TEXT,
icon TEXT,
features TEXT,
is_featured INTEGER DEFAULT 0,
category TEXT DEFAULT 'main',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- YouTube video gallery
CREATE TABLE videos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
video_url TEXT NOT NULL,
thumbnail_url TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Social media posts (Instagram/Facebook)
CREATE TABLE social_posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT,
post_url TEXT NOT NULL,
thumbnail_url TEXT,
caption TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);API Endpoints
// Portfolio API
app.get('/api/portfolio', ...); // Public: Get all portfolio items
app.post('/api/portfolio', authenticateToken, upload.single('image'), ...); // Protected: Add with WebP conversion
app.delete('/api/portfolio/:id', authenticateToken, ...); // Protected: Delete
// Bookings API
app.get('/api/bookings', authenticateToken, ...); // Protected: List all
app.post('/api/bookings', apiLimiter, ...); // Public (rate-limited): Create booking
app.patch('/api/bookings/:id', authenticateToken, ...); // Protected: Update status
// Contacts API
app.get('/api/contacts', authenticateToken, ...); // Protected: List all
app.post('/api/contacts', apiLimiter, ...); // Public (rate-limited): Submit inquiry
// Services API
app.get('/api/services', ...); // Public: List services
app.post('/api/services', authenticateToken, ...); // Protected: Add service
app.delete('/api/services/:id', authenticateToken, ...); // Protected: Delete
// Videos API
app.get('/api/videos', ...); // Public: List videos
app.post('/api/videos', authenticateToken, ...); // Protected: Add with auto-thumbnail
app.delete('/api/videos/:id', authenticateToken, ...); // Protected: Delete
// Social Posts API
app.get('/api/social_posts', ...); // Public: List posts
app.post('/api/social_posts', authenticateToken, ...); // Protected: Add post
app.delete('/api/social_posts/:id', authenticateToken, ...); // Protected: Delete
// Auth API
app.post('/api/login', loginLimiter, ...); // Rate-limited loginAuthentication System
// JWT Authentication Middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access denied. No token provided.' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid or expired token.' });
}
req.user = user;
next();
});
}
// Login Controller
app.post('/api/login', loginLimiter, (req, res) => {
const { email, password } = req.body;
const adminEmail = process.env.ADMIN_EMAIL;
const adminPass = process.env.ADMIN_PASSWORD;
if (email === adminEmail && password === adminPass) {
const token = jwt.sign(
{ email: adminEmail, role: 'admin' },
JWT_SECRET,
{ expiresIn: '24h' }
);
sendTelegramMessage(`🔓 *ADMIN LOGIN DETECTED*\n📅 *Time:* ${new Date()}`);
res.json({ success: true, token });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});Rate Limiting
// API Rate Limiter - 100 requests per 15 minutes
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: 'Too many requests, please try again later.' }
});
// Login Rate Limiter - 10 attempts per hour
const loginLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 10,
message: { error: 'Too many login attempts, please try again after an hour.' }
});Image Processing with Sharp
// Portfolio upload with automatic WebP conversion
app.post('/api/portfolio', authenticateToken, upload.single('image'), async (req, res) => {
try {
const { title, category } = req.body;
if (!req.file) return res.status(400).json({ error: 'Image is required' });
const inputPath = req.file.path;
const outputFilename = `${Date.now()}.webp`;
const outputPath = path.join('uploads', outputFilename);
// Convert to WebP using sharp
await sharp(inputPath)
.webp({ quality: 80 })
.toFile(outputPath);
// Delete the original uploaded file
fs.unlinkSync(inputPath);
const image_url = `/uploads/${outputFilename}`;
const stmt = db.prepare('INSERT INTO portfolio (title, image_url, category) VALUES (?, ?, ?)');
const result = stmt.run(title, image_url, category);
res.status(201).json({ id: result.lastInsertRowid, title, image_url, category });
} catch (err) {
console.error('Upload Error:', err);
res.status(500).json({ error: 'Failed to upload item' });
}
});Telegram Notifications
// Telegram Bot Integration
function sendTelegramMessage(message) {
if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) {
console.warn('⚠️ Telegram credentials not found in .env');
return;
}
const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
const data = JSON.stringify({
chat_id: TELEGRAM_CHAT_ID,
text: message,
parse_mode: 'Markdown'
});
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
const req = https.request(url, options, (res) => {
res.on('data', () => { });
});
req.on('error', (error) => {
console.error('❌ Telegram Notification Failed:', error);
});
req.write(data);
req.end();
}
// Booking notification
app.post('/api/bookings', apiLimiter, (req, res) => {
const { name, email, phone, service, date, time, message } = req.body;
// ... save to database ...
const telegramMsg = `🚀 *NEW BOOKING*\n\n` +
`👤 *Client:* ${name}\n` +
`📞 *Phone:* ${phone}\n` +
`📧 *Email:* ${email || 'N/A'}\n` +
`📸 *Service:* ${service}\n` +
`📅 *Date:* ${date || 'N/A'}\n` +
`⏰ *Time:* ${time || 'N/A'}\n\n` +
`✍️ *Message:* ${message || 'None'}`;
sendTelegramMessage(telegramMsg);
res.status(201).json({ id: result.lastInsertRowid, message: 'Booking successful' });
});YouTube Thumbnail Auto-Fetch
// Auto-fetch YouTube thumbnail if not provided
app.post('/api/videos', authenticateToken, (req, res) => {
let { title, video_url, thumbnail_url } = req.body;
if (!title || !video_url) return res.status(400).json({ error: 'Title and Video URL are required' });
// Auto-fetch YouTube thumbnail if not provided
if (!thumbnail_url) {
const idMatch = video_url.match(/(?:v=|\/embed\/|\/watch\?v=|\/\d+\/|\/vi\/|youtu\.be\/|shorts\/)([a-zA-Z0-9_-]{11})/);
if (idMatch) {
thumbnail_url = `https://img.youtube.com/vi/${idMatch[1]}/mqdefault.jpg`;
}
}
const stmt = db.prepare('INSERT INTO videos (title, video_url, thumbnail_url) VALUES (?, ?, ?)');
const result = stmt.run(title, video_url, thumbnail_url);
res.status(201).json({ id: result.lastInsertRowid, title, video_url, thumbnail_url });
});Social Media Thumbnail Scraping
// Proxy endpoint to scrape social media thumbnails
app.get('/api/social-proxy', authenticateToken, async (req, res) => {
try {
const { url } = req.query;
if (!url) return res.status(400).json({ error: 'URL is required' });
const response = await fetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X)'
}
});
const html = await response.text();
// Extract og:image
const ogImageMatch = html.match(/<meta[^>]*property="og:image"[^>]*content="([^"]+)"/i) ||
html.match(/<meta[^>]*content="([^"]+)"[^>]*property="og:image"/i);
if (ogImageMatch && ogImageMatch[1]) {
const imageUrl = ogImageMatch[1].replace(/&/g, '&');
return res.json({ thumbnail_url: imageUrl });
}
res.status(404).json({ error: 'Thumbnail not found' });
} catch (err) {
res.status(500).json({ error: 'Failed to fetch thumbnail' });
}
});Health Check Endpoint
app.get('/api/health', (req, res) => {
try {
const dbCheck = db.prepare('SELECT 1').get();
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: '1.2.0',
integrations: {
database: dbCheck ? 'connected' : 'error',
telegram: (TELEGRAM_BOT_TOKEN && TELEGRAM_CHAT_ID) ? 'configured' : 'not_configured'
}
});
} catch (err) {
res.status(503).json({
status: 'unhealthy',
error: err.message
});
}
});Key Features
1. Portfolio Management
- Image upload with automatic WebP conversion (Sharp)
- Category-based organization (Portrait, Wedding, Commercial, Films)
- Automatic thumbnail generation
- Delete functionality with file cleanup
2. Booking System
- Complete client information capture (name, email, phone)
- Service selection
- Date and time selection
- Message/notes field
- Status management (pending, confirmed, cancelled)
- Telegram notifications for new bookings
3. Contact Form
- Name, email, phone, and message capture
- Telegram notifications for new inquiries
- Status tracking (read/unread)
4. Services Management
- Title, subtitle, and pricing display
- Feature list support
- Icon integration (Feather icons)
- Featured/popular marking
- Category support (main/addon)
5. Video Gallery
- YouTube URL support
- Automatic thumbnail fetching from YouTube
- Custom thumbnail override option
- Video management (add/delete)
6. Social Media Integration
- Instagram and Facebook support
- Automatic thumbnail scraping from post URLs
- Caption preservation
- Platform detection
7. Admin Dashboard
- JWT-based authentication
- Calendar view with bookings
- Real-time data management
- Modal-based content editing
Admin Panel Features
The admin panel provides comprehensive management capabilities:
Portfolio Section
- Grid view of all portfolio items
- Add new items with image upload
- Delete functionality with file cleanup
Calendar Section
- Monthly calendar view
- Booking markers on dates
- Today's schedule display
- Upcoming events list
Bookings Section
- Complete booking table
- Status management (confirm/cancel)
- Client details display
- Service and appointment info
Contacts Section
- Message list with contact info
- Read/unread status
- Full message display
Services Section
- Service card grid display
- Featured service highlighting
- Price and feature display
- Add/delete functionality
YouTube & Social Section
- Video grid with thumbnails
- Social post grid
- Platform indicator icons
- Auto-thumbnail fetch button
Frontend Design
Typography
- Cinzel: Headers and branding (elegant serif)
- Cormorant Garamond: Body text (classic serif)
- Bebas Neue: Accent text (bold display)
Color Scheme
- Primary: Black (#000000)
- Secondary: Gold/Yellow (#F59E0B)
- Background: Light gray (#F9FAFB)
- Text: Dark gray (#111827)
Responsive Design
- Mobile-first approach
- Tailwind CSS responsive utilities
- Grid layouts for portfolio and services
- Modal-based admin interface
Deployment
Docker Configuration
# docker-compose.yml
services:
onestar:
build: .
ports:
- "3010:3000"
volumes:
- ./uploads:/app/uploads
- ./database.sqlite:/app/database.sqlite
environment:
- PORT=3000
- JWT_SECRET=your-secret
- [email protected]
- ADMIN_PASSWORD=your-password
- TELEGRAM_BOT_TOKEN=your-bot-token
- TELEGRAM_CHAT_ID=your-chat-idDeployment Script
The project uses an automated rsync-based deployment:
./deploy.sh --autoEnvironment Variables
PORT=3000
JWT_SECRET=your-jwt-secret-key
ADMIN_EMAIL=[email protected]
ADMIN_PASSWORD=secure-admin-password
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
TELEGRAM_CHAT_ID=your-telegram-chat-idSecurity Features
- JWT Authentication: Token-based secure access
- Rate Limiting: Prevents API abuse
- Environment-Separated Credentials: No hardcoded secrets
- File Type Validation: Image-only uploads
- Token Expiration: 24-hour token validity
Build and Run
# Install dependencies
npm install
# Build Tailwind CSS
npm run build:css
# Start server
npm start
# Development with auto-reload
npm run devConclusion
One Star represents a comprehensive business website solution for creative professionals. The project demonstrates sophisticated engineering patterns including:
- Full-stack Express.js application with RESTful API design
- SQLite database with proper schema design
- JWT-based authentication with role management
- Automated image processing pipeline using Sharp
- External service integration (Telegram, YouTube, Social Media)
- Rate-limiting for API security
- Modern Tailwind CSS styling with premium typography
- Complete admin dashboard with real-time management
The system provides everything a photography/videography business needs: showcase their work, list their services, capture bookings, and manage their content through an intuitive admin interface. The Telegram integration ensures never missing a potential client inquiry.
(End of file - 592 lines)
Architecture Feedback
Spotted a potential optimization or antipattern? Let me know.