Spaces:
Running
Running
# Browser-Only Version (No User Authentication) | |
# Use Python 3.11 slim image for better compatibility | |
FROM python:3.11-slim | |
# Set environment variables | |
ENV PYTHONUNBUFFERED=1 | |
ENV DEBIAN_FRONTEND=noninteractive | |
ENV PORT=7860 | |
# Install system dependencies | |
RUN apt-get update && apt-get install -y \ | |
git \ | |
libmagic1 \ | |
libmagic-dev \ | |
file \ | |
gcc \ | |
g++ \ | |
libc6-dev \ | |
libffi-dev \ | |
libjpeg-dev \ | |
libpng-dev \ | |
libtiff-dev \ | |
libwebp-dev \ | |
zlib1g-dev \ | |
&& rm -rf /var/lib/apt/lists/* | |
# Set work directory | |
WORKDIR /app | |
# Clone the repository | |
RUN git clone https://github.com/wjbmattingly/VLAMy.git . | |
# Install Python dependencies | |
RUN pip install --no-cache-dir -r requirements.txt | |
# Create necessary directories | |
RUN mkdir -p /app/media/imports /app/staticfiles /app/logs | |
# Create custom settings for no-auth mode | |
RUN echo 'from .settings import *\n\ | |
\n\ | |
# Disable authentication requirements but keep auth apps for model dependencies\n\ | |
REST_FRAMEWORK = {\n\ | |
"DEFAULT_AUTHENTICATION_CLASSES": [],\n\ | |
"DEFAULT_PERMISSION_CLASSES": [\n\ | |
"rest_framework.permissions.AllowAny",\n\ | |
],\n\ | |
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",\n\ | |
"PAGE_SIZE": 20,\n\ | |
}\n\ | |
\n\ | |
# Use in-memory database for simplicity\n\ | |
DATABASES = {\n\ | |
"default": {\n\ | |
"ENGINE": "django.db.backends.sqlite3",\n\ | |
"NAME": ":memory:",\n\ | |
}\n\ | |
}\n\ | |
\n\ | |
# Enhanced cache settings for browser-only mode\n\ | |
CACHES = {\n\ | |
"default": {\n\ | |
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",\n\ | |
"LOCATION": "vlamy-cache",\n\ | |
"TIMEOUT": 86400, # 24 hours\n\ | |
"OPTIONS": {\n\ | |
"MAX_ENTRIES": 1000,\n\ | |
}\n\ | |
}\n\ | |
}\n\ | |
\n\ | |
# Keep auth middleware but disable auth requirements\n\ | |
MIDDLEWARE = [\n\ | |
"corsheaders.middleware.CorsMiddleware",\n\ | |
"django.middleware.security.SecurityMiddleware",\n\ | |
"django.contrib.sessions.middleware.SessionMiddleware",\n\ | |
"django.middleware.common.CommonMiddleware",\n\ | |
"django.middleware.csrf.CsrfViewMiddleware",\n\ | |
"django.contrib.auth.middleware.AuthenticationMiddleware",\n\ | |
"django.contrib.messages.middleware.MessageMiddleware",\n\ | |
# Removed XFrameOptionsMiddleware to allow iframe embedding\n\ | |
]\n\ | |
\n\ | |
# Keep all apps installed to avoid model dependency issues\n\ | |
INSTALLED_APPS = [\n\ | |
"django.contrib.admin",\n\ | |
"django.contrib.auth",\n\ | |
"django.contrib.contenttypes",\n\ | |
"django.contrib.sessions",\n\ | |
"django.contrib.messages",\n\ | |
"django.contrib.staticfiles",\n\ | |
"rest_framework",\n\ | |
"rest_framework.authtoken",\n\ | |
"corsheaders",\n\ | |
"ocr_app",\n\ | |
]\n\ | |
\n\ | |
# Allow all CORS origins for browser-only mode\n\ | |
CORS_ALLOW_ALL_ORIGINS = True\n\ | |
CORS_ALLOW_CREDENTIALS = False\n\ | |
\n\ | |
# Static files configuration for no-auth mode\n\ | |
import os\n\ | |
STATIC_URL = "/static/"\n\ | |
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")\n\ | |
STATICFILES_DIRS = [\n\ | |
os.path.join(BASE_DIR, "static"),\n\ | |
]\n\ | |
\n\ | |
# Media files configuration\n\ | |
MEDIA_URL = "/media/"\n\ | |
MEDIA_ROOT = os.path.join(BASE_DIR, "media")\n\ | |
\n\ | |
# Enable static file serving in development\n\ | |
DEBUG = True\n\ | |
\n\ | |
# Session settings for browser storage\n\ | |
SESSION_ENGINE = "django.contrib.sessions.backends.cache"\n\ | |
SESSION_CACHE_ALIAS = "default"\n\ | |
SESSION_COOKIE_AGE = 86400 # 24 hours\n\ | |
\n\ | |
# Disable file logging to avoid permission issues\n\ | |
LOGGING = {\n\ | |
"version": 1,\n\ | |
"disable_existing_loggers": False,\n\ | |
"handlers": {\n\ | |
"console": {\n\ | |
"level": "INFO",\n\ | |
"class": "logging.StreamHandler",\n\ | |
},\n\ | |
},\n\ | |
"loggers": {\n\ | |
"ocr_app": {\n\ | |
"handlers": ["console"],\n\ | |
"level": "INFO",\n\ | |
"propagate": True,\n\ | |
},\n\ | |
},\n\ | |
}\n\ | |
\n\ | |
# Create anonymous user middleware reference\n\ | |
MIDDLEWARE.insert(0, "vlamy_ocr.middleware.AnonymousUserMiddleware")\n\ | |
\n\ | |
# Settings for HuggingFace Spaces iframe embedding\n\ | |
X_FRAME_OPTIONS = "ALLOWALL" # Allow iframe embedding\n\ | |
SECURE_CROSS_ORIGIN_OPENER_POLICY = None\n\ | |
SECURE_REFERRER_POLICY = "same-origin"\n\ | |
\n\ | |
# Additional security settings for iframe embedding\n\ | |
SECURE_CONTENT_TYPE_NOSNIFF = False\n\ | |
SECURE_BROWSER_XSS_FILTER = False\n\ | |
\n\ | |
# Allow HuggingFace Spaces in CSP\n\ | |
CSP_FRAME_ANCESTORS = ["*"]\n\ | |
CSP_DEFAULT_SRC = ["*"]\n\ | |
\n\ | |
# Disable Django security warnings for iframe embedding\n\ | |
import warnings\n\ | |
warnings.filterwarnings("ignore", module="django.security")\n\ | |
\n\ | |
' > /app/vlamy_ocr/settings_no_auth.py | |
# Create a custom URL configuration for no-auth mode | |
RUN echo 'from django.urls import path, include\n\ | |
from django.conf import settings\n\ | |
from django.conf.urls.static import static\n\ | |
from django.views.generic import TemplateView\n\ | |
from django.contrib.staticfiles.urls import staticfiles_urlpatterns\n\ | |
import os\n\ | |
\n\ | |
urlpatterns = [\n\ | |
# API endpoints (no auth required - permissions handled by settings)\n\ | |
path("api/", include("ocr_app.urls")),\n\ | |
\n\ | |
# Frontend routes (single-page application)\n\ | |
path("", TemplateView.as_view(template_name="index.html"), name="home"),\n\ | |
path("app/", TemplateView.as_view(template_name="index.html"), name="app"),\n\ | |
]\n\ | |
\n\ | |
# Always serve static and media files (essential for no-auth mode)\n\ | |
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)\n\ | |
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)\n\ | |
urlpatterns += staticfiles_urlpatterns()\n\ | |
\n\ | |
# Debug info\n\ | |
print(f"Static URL: {settings.STATIC_URL}")\n\ | |
print(f"Static Root: {settings.STATIC_ROOT}")\n\ | |
static_dirs = getattr(settings, "STATICFILES_DIRS", [])\n\ | |
print(f"Static Dirs: {static_dirs}")\n\ | |
' > /app/vlamy_ocr/urls_no_auth.py | |
# Ensure static directories exist and collect static files | |
RUN mkdir -p /app/static /app/staticfiles && \ | |
DJANGO_SETTINGS_MODULE=vlamy_ocr.settings_no_auth python manage.py collectstatic --noinput --verbosity=2 | |
# Replace the main urls.py with no-auth version at build time | |
RUN cp /app/vlamy_ocr/urls_no_auth.py /app/vlamy_ocr/urls.py | |
# Create anonymous user middleware for no-auth mode | |
RUN echo 'import os\n\ | |
from django.contrib.auth.models import User, AnonymousUser\n\ | |
from django.core.management import execute_from_command_line\n\ | |
from django.db import connection\n\ | |
from django.apps import apps\n\ | |
\n\ | |
class AnonymousUserMiddleware:\n\ | |
"""Middleware that automatically handles migrations and creates an anonymous user for all requests"""\n\ | |
\n\ | |
def __init__(self, get_response):\n\ | |
self.get_response = get_response\n\ | |
self._anonymous_user = None\n\ | |
self._migrations_applied = False\n\ | |
self._setup_complete = False\n\ | |
\n\ | |
def _ensure_migrations_and_user(self):\n\ | |
"""Ensure migrations are applied and anonymous user exists"""\n\ | |
if self._setup_complete:\n\ | |
return\n\ | |
\n\ | |
try:\n\ | |
# Check if migrations need to be applied\n\ | |
if not self._migrations_applied:\n\ | |
print("Applying database migrations...")\n\ | |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "vlamy_ocr.settings_no_auth")\n\ | |
execute_from_command_line(["manage.py", "migrate", "--noinput"])\n\ | |
self._migrations_applied = True\n\ | |
print("Migrations applied successfully")\n\ | |
\n\ | |
# Create anonymous user\n\ | |
if not self._anonymous_user:\n\ | |
self._anonymous_user, created = User.objects.get_or_create(\n\ | |
username="anonymous",\n\ | |
defaults={\n\ | |
"email": "[email protected]",\n\ | |
"first_name": "Anonymous",\n\ | |
"last_name": "User"\n\ | |
}\n\ | |
)\n\ | |
\n\ | |
# Ensure user profile exists and is approved\n\ | |
if hasattr(self._anonymous_user, "profile"):\n\ | |
if not self._anonymous_user.profile.is_approved:\n\ | |
self._anonymous_user.profile.is_approved = True\n\ | |
self._anonymous_user.profile.save()\n\ | |
else:\n\ | |
from ocr_app.models import UserProfile\n\ | |
profile, profile_created = UserProfile.objects.get_or_create(\n\ | |
user=self._anonymous_user,\n\ | |
defaults={"is_approved": True}\n\ | |
)\n\ | |
\n\ | |
print(f"Anonymous user ready: created={created}, user_id={self._anonymous_user.id}")\n\ | |
\n\ | |
self._setup_complete = True\n\ | |
\n\ | |
except Exception as e:\n\ | |
print(f"Error in middleware setup: {e}")\n\ | |
# Fallback to Django AnonymousUser\n\ | |
self._anonymous_user = AnonymousUser()\n\ | |
\n\ | |
def __call__(self, request):\n\ | |
# Ensure setup is complete\n\ | |
self._ensure_migrations_and_user()\n\ | |
\n\ | |
# Always assign the anonymous user to the request\n\ | |
request.user = self._anonymous_user\n\ | |
\n\ | |
response = self.get_response(request)\n\ | |
return response\n\ | |
' > /app/vlamy_ocr/middleware.py | |
# Create a startup script for no-auth mode | |
RUN echo '#!/bin/bash\n\ | |
\n\ | |
# Set the custom settings module\n\ | |
export DJANGO_SETTINGS_MODULE=vlamy_ocr.settings_no_auth\n\ | |
\n\ | |
echo "Starting VLAMy in browser-only mode (no authentication required)"\n\ | |
echo "Anonymous user will be created automatically on first request"\n\ | |
\n\ | |
# Start the Django development server (migrations will run automatically)\n\ | |
python manage.py runserver 0.0.0.0:$PORT\n\ | |
' > /app/start.sh | |
# Set proper permissions for all files and ensure startup script is executable | |
RUN chmod -R 755 /app && \ | |
chmod +x /app/start.sh && \ | |
chown -R root:root /app | |
# Environment variables for production (no-auth mode) | |
ENV DEBUG=True | |
ENV SECRET_KEY="browser-only-key-no-sensitive-data" | |
ENV ALLOWED_HOSTS="*" | |
ENV DJANGO_SETTINGS_MODULE="vlamy_ocr.settings_no_auth" | |
# Expose port | |
EXPOSE 7860 | |
# Start the application | |
CMD ["/app/start.sh"] |