from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static from django.views.static import serve from django.views.generic import TemplateView from django.http import FileResponse, Http404 from links.views import LinkDetailView, LinkUpdateView, CustomLinkView from links.file_views import PublicFileView, import_image_view from django.urls import path, include, re_path from django.conf.urls.i18n import i18n_patterns import os def _serve_static_file(filename, content_type): """Serve a file directly from the committed static/ directory.""" def view(request): path = os.path.join(settings.BASE_DIR, 'static', filename) try: return FileResponse(open(path, 'rb'), content_type=content_type) except FileNotFoundError: raise Http404 return view urlpatterns = [ # LLM-friendly discovery endpoints — must be FIRST before any catch-all routes path('llms.txt', _serve_static_file('llms.txt', 'text/plain; charset=utf-8'), name='llms-txt'), path('.well-known/ai-plugin.json', _serve_static_file('.well-known/ai-plugin.json', 'application/json'), name='ai-plugin-json'), path('admin/', admin.site.urls), # Add API URLs before locale URLs path('api/', include('links.api_urls')), # New line for API routes path('api/invest/', include('invest.api_urls')), path('api/jbot/', include('jbot.api_urls')), # JBOT API endpoints # Media files path('media/', serve, { 'document_root': settings.MEDIA_ROOT, }), # Music files path('music/', serve, { 'document_root': settings.MUSIC_ROOT, }), path('i18n/', include('django.conf.urls.i18n')), path('search/', include('links.search_urls')), path('link//', LinkDetailView.as_view(), name='link_detail'), path('link//edit/', LinkUpdateView.as_view(), name='link_update'), path('custom//', CustomLinkView.as_view(), name='custom_link'), path('custom//edit/', LinkUpdateView.as_view(), name='custom_link_update'), path('invest/', include('invest.urls')), # Include netscan and files BEFORE links.urls to prevent the alias catch-all from intercepting them path('ui/netscan/', include('netscan.urls')), path('ui/nginxmon/', include('nginxmon.urls')), path('ui/routermon/', include('routermon.urls')), path('ui/pricemon/', include('pricemon.urls')), path('ui/files/', include('links.file_urls')), # JBOT AI Robot Face SPA path('jbot/', include('jbot.urls')), # Import external image by URL — /import/images/ (also plural alias) path('import/images/', import_image_view, name='import-image'), path('imports/images/', import_image_view, name='imports-image'), # Public file access — /public/files/{uuid}-{filename} re_path( r'^public/files/(?P[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-(?P.+)$', PublicFileView.as_view(), name='public-file', ), # Include main app URLs with locale path('', include('links.urls')), ] if settings.DEBUG: urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)