A production-ready web application for sharing secrets (text, images, videos, files) that self-destruct after being viewed once.
cd /Users/vrushabhdadabaravkar/onetime
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env file with your configuration
python -m uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000
Open your browser to: http://localhost:8000
onetime/
├── backend/
│ ├── main.py # FastAPI application and routes
│ ├── models.py # SQLAlchemy database models
│ ├── database.py # Database configuration
│ ├── schemas.py # Pydantic schemas for validation
│ ├── security.py # Security utilities (hashing, sanitization)
│ └── cleanup.py # Background cleanup task
├── frontend/
│ ├── index.html # Home page
│ ├── create.html # Create secret page
│ ├── view.html # View secret page
│ ├── expired.html # Expired/already viewed page
│ ├── privacy.html # Privacy policy
│ ├── terms.html # Terms of service
│ └── styles.css # Custom CSS
├── uploads/ # File storage directory
├── requirements.txt # Python dependencies
├── .env.example # Environment variables template
└── README.md # This file
Edit the .env file to configure:
DATABASE_URL: Database connection string (default: SQLite)SECRET_KEY: Secret key for security featuresMAX_FILE_SIZE_FREE: Max file size for free users (bytes)MAX_FILE_SIZE_PREMIUM: Max file size for premium users (bytes)UPLOAD_DIR: Directory for file uploadsRATE_LIMIT_PER_MINUTE: Rate limit per IP addressALLOWED_ORIGINS: CORS allowed originsPOST /api/secrets
Content-Type: multipart/form-data
Fields:
- content_type: "text" | "image" | "video" | "file"
- content: string (for text secrets)
- file: File (for file-based secrets)
- password: string (optional, premium)
- expiry_hours: number (optional, premium)
- is_premium: boolean
Response:
{
"id": "uuid",
"url": "http://localhost:8000/view/uuid",
"expires_at": "2024-12-15T12:00:00",
"has_password": false,
"content_type": "text"
}
POST /api/secrets/{secret_id}/verify
Content-Type: application/json
Body:
{
"password": "string"
}
Response:
{
"verified": true
}
GET /api/secrets/{secret_id}?password=optional
Response:
{
"content_type": "text",
"content": "secret message",
"file_name": null,
"mime_type": null,
"download_url": null
}
The one-time view mechanism works as follows:
view_count = 0view_count >= 1 or expiry_time exceededview_count is incrementedA background task runs every 5 minutes to:
# Update DATABASE_URL in .env
DATABASE_URL=postgresql://user:password@localhost/onetimeview
# Generate secure random key
python -c "import secrets; print(secrets.token_urlsafe(32))"
# Add to .env
# Use reverse proxy (nginx) or hosting platform
# Ensure all traffic uses HTTPS
gunicorn backend.main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
CREATE TABLE secrets (
id VARCHAR(36) PRIMARY KEY,
content_type VARCHAR(20) NOT NULL,
content TEXT,
file_path VARCHAR(255),
file_name VARCHAR(255),
mime_type VARCHAR(100),
password_hash VARCHAR(100),
expiry_time DATETIME,
view_count INTEGER DEFAULT 0,
max_views INTEGER DEFAULT 1,
is_premium BOOLEAN DEFAULT FALSE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
The application is optimized for:
Test the application:
# Start the server
python -m uvicorn backend.main:app --reload
# Test in browser
# 1. Create a text secret at http://localhost:8000/create
# 2. Copy the generated link
# 3. Open link - you should see the warning
# 4. View the secret - content should display
# 5. Try opening link again - should redirect to /expired
This project is provided as-is for educational and commercial use.
For questions or issues:
Potential improvements: