Implement backend API and database services in Docker setup

- Added a new `api` service for the NestJS backend, including health checks and dependencies on PostgreSQL, Redis, and MinIO.
- Introduced PostgreSQL and Redis services with health checks and configurations for data persistence.
- Added MinIO for S3-compatible object storage and a one-shot service to initialize required buckets.
- Updated the Nginx configuration to proxy requests to the new backend API and MinIO storage.
- Enhanced the Dockerfile to support the new API environment variables and configurations.
- Updated the `package.json` and `package-lock.json` to include new dependencies for QR code generation and other utilities.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dorian
2026-02-12 20:14:39 +00:00
parent f19fd6feef
commit cdd24a5def
478 changed files with 55355 additions and 529 deletions

View File

@@ -220,16 +220,32 @@
</svg>
<span>My Library</span>
</button>
<!-- Content Source Toggle -->
<button @click="handleSourceToggle" class="profile-menu-item flex items-center gap-3 px-4 py-2.5 w-full text-left">
<!-- Backstage (filmmaker only) -->
<button v-if="isFilmmakerUser" @click="navigateTo('/backstage')" class="profile-menu-item flex items-center gap-3 px-4 py-2.5 w-full text-left">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4V20M17 4V20M3 8H7M17 8H21M3 12H21M3 16H7M17 16H21M4 20H20C20.5523 20 21 19.5523 21 19V5C21 4.44772 20.5523 4 20 4H4C3.44772 4 3 4.44772 3 5V19C3 19.5523 3.44772 20 4 20Z" />
</svg>
<span class="flex-1">{{ contentSourceStore.activeSource === 'indeehub' ? 'IndeeHub Films' : 'TopDoc Films' }}</span>
<span class="text-[10px] text-white/40 uppercase tracking-wider">Switch</span>
<span>Backstage</span>
</button>
<!-- Content Source Selector -->
<div class="px-4 py-2">
<p class="text-[10px] text-white/40 uppercase tracking-wider mb-2">Content Source</p>
<div class="flex flex-col gap-1">
<button
v-for="source in contentSourceStore.availableSources"
:key="source.id"
@click="handleSourceSelect(source.id)"
class="profile-menu-item flex items-center gap-3 px-3 py-2 w-full text-left rounded-lg transition-all"
:class="contentSourceStore.activeSource === source.id ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white'"
>
<span class="w-2 h-2 rounded-full" :class="contentSourceStore.activeSource === source.id ? 'bg-[#F7931A]' : 'bg-white/20'"></span>
<span class="flex-1 text-sm">{{ source.label }}</span>
<span v-if="contentSourceStore.activeSource === source.id" class="text-[10px] text-[#F7931A] uppercase tracking-wider">Active</span>
</button>
</div>
</div>
<div class="border-t border-white/10 my-1"></div>
<button @click="handleLogout" class="profile-menu-item flex items-center gap-3 px-4 py-2.5 text-red-400 w-full text-left">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -311,7 +327,7 @@ const emit = defineEmits<Emits>()
const router = useRouter()
const route = useRoute()
const { user, isAuthenticated, loginWithNostr: appLoginWithNostr, logout: appLogout } = useAuth()
const { user, isAuthenticated, isFilmmaker: isFilmmakerComputed, loginWithNostr: appLoginWithNostr, logout: appLogout } = useAuth()
const {
isLoggedIn: nostrLoggedIn,
activePubkey: nostrActivePubkey,
@@ -332,13 +348,16 @@ const {
const contentSourceStore = useContentSourceStore()
const contentStore = useContentStore()
const isFilmmakerUser = isFilmmakerComputed
/** Toggle between IndeeHub and TopDocFilms catalogs, then reload content */
function handleSourceToggle() {
contentSourceStore.toggle()
/** Switch content source and reload */
function handleSourceSelect(sourceId: string) {
contentSourceStore.setSource(sourceId as any)
contentStore.fetchContent()
}
// Source toggle is now handled by handleSourceSelect above
const dropdownOpen = ref(false)
const personaMenuOpen = ref(false)
const algosMenuOpen = ref(false)
@@ -352,6 +371,10 @@ const searchWrapperRef = ref<HTMLElement | null>(null)
/** All films from the active content source for searching */
const allContent = computed<Content[]>(() => {
// When using API source, search from the content store's loaded data
if (contentSourceStore.activeSource === 'indeehub-api') {
return Object.values(contentStore.contentRows).flat()
}
return contentSourceStore.activeSource === 'topdocfilms'
? topDocFilms
: indeeHubFilms