🚀 ci: add mana-search and api-gateway to Docker builds

- Add mana-search + SearXNG to docker-compose.macmini.yml
- Add api-gateway dependency on mana-search
- Add CI workflow for building mana-search Docker image
- Add CI workflow for building api-gateway Docker image
This commit is contained in:
Till-JS 2026-01-29 18:34:18 +01:00
parent 4c1beb9534
commit 271496b0fd
55 changed files with 403 additions and 280 deletions

View file

@ -50,6 +50,8 @@ jobs:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
outputs:
mana-core-auth: ${{ steps.changes.outputs.mana-core-auth }}
mana-search: ${{ steps.changes.outputs.mana-search }}
api-gateway: ${{ steps.changes.outputs.api-gateway }}
manacore-web: ${{ steps.changes.outputs.manacore-web }}
chat-backend: ${{ steps.changes.outputs.chat-backend }}
chat-web: ${{ steps.changes.outputs.chat-web }}
@ -84,6 +86,8 @@ jobs:
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.force_build_all }}" == "true" ]; then
echo "Force rebuild all services requested"
echo "mana-core-auth=true" >> $GITHUB_OUTPUT
echo "mana-search=true" >> $GITHUB_OUTPUT
echo "api-gateway=true" >> $GITHUB_OUTPUT
echo "manacore-web=true" >> $GITHUB_OUTPUT
echo "chat-backend=true" >> $GITHUB_OUTPUT
echo "chat-web=true" >> $GITHUB_OUTPUT
@ -122,6 +126,8 @@ jobs:
# workflow_dispatch without force - build all
echo "Workflow dispatch without force_build_all - building all"
echo "mana-core-auth=true" >> $GITHUB_OUTPUT
echo "mana-search=true" >> $GITHUB_OUTPUT
echo "api-gateway=true" >> $GITHUB_OUTPUT
echo "manacore-web=true" >> $GITHUB_OUTPUT
echo "chat-backend=true" >> $GITHUB_OUTPUT
echo "chat-web=true" >> $GITHUB_OUTPUT
@ -181,6 +187,22 @@ jobs:
echo "mana-core-auth=false" >> $GITHUB_OUTPUT
fi
# mana-search: services/mana-search
SEARCH_CHANGED=$(check_pattern "services/mana-search/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$SEARCH_CHANGED" == "true" ]; then
echo "mana-search=true" >> $GITHUB_OUTPUT
else
echo "mana-search=false" >> $GITHUB_OUTPUT
fi
# api-gateway: services/mana-api-gateway + packages/shared-nestjs-auth
GATEWAY_CHANGED=$(check_pattern "services/mana-api-gateway/|packages/shared-nestjs-auth/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$GATEWAY_CHANGED" == "true" ]; then
echo "api-gateway=true" >> $GITHUB_OUTPUT
else
echo "api-gateway=false" >> $GITHUB_OUTPUT
fi
# manacore-web: apps/manacore/apps/web + shared packages
MANACORE_WEB_CHANGED=$(check_pattern "apps/manacore/apps/web/|apps/manacore/packages/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$MANACORE_WEB_CHANGED" == "true" ]; then
@ -355,6 +377,8 @@ jobs:
echo "| Service | Will Build |" >> $GITHUB_STEP_SUMMARY
echo "|---------|------------|" >> $GITHUB_STEP_SUMMARY
echo "| mana-core-auth | ${{ steps.changes.outputs.mana-core-auth }} |" >> $GITHUB_STEP_SUMMARY
echo "| mana-search | ${{ steps.changes.outputs.mana-search }} |" >> $GITHUB_STEP_SUMMARY
echo "| api-gateway | ${{ steps.changes.outputs.api-gateway }} |" >> $GITHUB_STEP_SUMMARY
echo "| manacore-web | ${{ steps.changes.outputs.manacore-web }} |" >> $GITHUB_STEP_SUMMARY
echo "| chat-backend | ${{ steps.changes.outputs.chat-backend }} |" >> $GITHUB_STEP_SUMMARY
echo "| chat-web | ${{ steps.changes.outputs.chat-web }} |" >> $GITHUB_STEP_SUMMARY
@ -443,6 +467,64 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
build-mana-search:
name: Build mana-search
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.mana-search == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/mana-search
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: services/mana-search
file: services/mana-search/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-api-gateway:
name: Build api-gateway
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.api-gateway == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/api-gateway
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: services/mana-api-gateway/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-manacore-web:
name: Build manacore-web
runs-on: ubuntu-latest

View file

@ -23,6 +23,7 @@
"dependencies": {
"@calendar/shared": "workspace:*",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@nestjs/common": "^10.4.15",

View file

@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { MetricsModule } from '@manacore/shared-nestjs-metrics';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { CalendarModule } from './calendar/calendar.module';
import { EventModule } from './event/event.module';
import { EventTagModule } from './event-tag/event-tag.module';
@ -27,7 +27,7 @@ import { NotificationModule } from './notification/notification.module';
excludePaths: ['/health'],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'calendar-backend' }),
EmailModule,
NotificationModule,
CalendarModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'calendar-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -27,6 +27,7 @@
"dependencies": {
"@manacore/shared-errors": "workspace:*",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@nestjs/common": "^10.4.15",

View file

@ -8,7 +8,7 @@ import { TemplateModule } from './template/template.module';
import { SpaceModule } from './space/space.module';
import { DocumentModule } from './document/document.module';
import { ModelModule } from './model/model.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({
imports: [
@ -27,7 +27,7 @@ import { HealthModule } from './health/health.module';
SpaceModule,
DocumentModule,
ModelModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'chat-backend' }),
],
})
export class AppModule {}

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
service: 'chat-backend',
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -20,6 +20,7 @@
"dependencies": {
"@clock/shared": "workspace:*",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@nestjs/common": "^10.4.15",

View file

@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { MetricsModule } from '@manacore/shared-nestjs-metrics';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { AlarmModule } from './alarm/alarm.module';
import { TimerModule } from './timer/timer.module';
import { WorldClockModule } from './world-clock/world-clock.module';
@ -21,7 +21,7 @@ import { PresetModule } from './preset/preset.module';
excludePaths: ['/health'],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'clock-backend' }),
AlarmModule,
TimerModule,
WorldClockModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
service: 'clock-backend',
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@manacore/shared-storage": "workspace:*",

View file

@ -6,7 +6,7 @@ import { ContactModule } from './contact/contact.module';
import { TagModule } from './tag/tag.module';
import { NoteModule } from './note/note.module';
import { ActivityModule } from './activity/activity.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { ImportModule } from './import/import.module';
import { ExportModule } from './export/export.module';
import { GoogleModule } from './google/google.module';
@ -30,7 +30,7 @@ import { NetworkModule } from './network/network.module';
TagModule,
NoteModule,
ActivityModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'contacts-backend' }),
ImportModule,
ExportModule,
GoogleModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'contacts-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -25,6 +25,7 @@
"dependencies": {
"@nutriphi/shared": "workspace:*",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@google/generative-ai": "^0.21.0",
"@nestjs/common": "^10.4.15",

View file

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { MealModule } from './meal/meal.module';
import { GoalsModule } from './goals/goals.module';
import { FavoritesModule } from './favorites/favorites.module';
@ -16,7 +16,7 @@ import { RecommendationsModule } from './recommendations/recommendations.module'
envFilePath: ['.env', '.env.development'],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'nutriphi-backend' }),
MealModule,
GoalsModule,
FavoritesModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'nutriphi-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -22,6 +22,7 @@
"@manacore/nestjs-integration": "workspace:*",
"@manacore/shared-errors": "workspace:*",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-storage": "workspace:*",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",

View file

@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ManaCoreModule } from '@manacore/nestjs-integration';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { ModelModule } from './model/model.module';
import { TagModule } from './tag/tag.module';
import { ImageModule } from './image/image.module';
@ -31,7 +31,7 @@ import { BatchModule } from './batch/batch.module';
inject: [ConfigService],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'picture-backend' }),
ModelModule,
TagModule,
ImageModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
service: 'picture-backend',
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -20,6 +20,7 @@
"dependencies": {
"@google/generative-ai": "^0.21.0",
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@manacore/shared-storage": "workspace:*",
"@nestjs/common": "^10.4.15",

View file

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { PlantModule } from './plant/plant.module';
import { PhotoModule } from './photo/photo.module';
import { AnalysisModule } from './analysis/analysis.module';
@ -14,7 +14,7 @@ import { WateringModule } from './watering/watering.module';
envFilePath: '.env',
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'planta-backend' }),
PlantModule,
PhotoModule,
AnalysisModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'planta-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -17,6 +17,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",

View file

@ -5,7 +5,7 @@ import { DeckModule } from './deck/deck.module';
import { SlideModule } from './slide/slide.module';
import { ThemeModule } from './theme/theme.module';
import { ShareModule } from './share/share.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({
imports: [
@ -18,7 +18,7 @@ import { HealthModule } from './health/health.module';
SlideModule,
ThemeModule,
ShareModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'presi-backend' }),
],
})
export class AppModule {}

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
service: 'presi-backend',
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -18,6 +18,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@nestjs/common": "^10.4.9",
"@nestjs/config": "^3.3.0",

View file

@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MetricsModule } from '@manacore/shared-nestjs-metrics';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { SkillModule } from './skill/skill.module';
import { ActivityModule } from './activity/activity.module';
@ -17,7 +17,7 @@ import { ActivityModule } from './activity/activity.module';
excludePaths: ['/health'],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'skilltree-backend' }),
SkillModule,
ActivityModule,
],

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
timestamp: new Date().toISOString(),
service: 'skilltree-backend',
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-storage": "workspace:*",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",

View file

@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { FileModule } from './file/file.module';
import { FolderModule } from './folder/folder.module';
import { ShareModule } from './share/share.module';
@ -16,7 +16,7 @@ import { StorageModule } from './storage/storage.module';
isGlobal: true,
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'storage-backend', route: 'api/v1/health' }),
StorageModule,
FileModule,
FolderModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('api/v1/health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'storage-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-metrics": "workspace:*",
"@nestjs/common": "^10.4.9",
"@nestjs/config": "^3.3.0",

View file

@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { MetricsModule } from '@manacore/shared-nestjs-metrics';
import { DatabaseModule } from './db/database.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
import { ProjectModule } from './project/project.module';
import { TaskModule } from './task/task.module';
import { LabelModule } from './label/label.module';
@ -23,7 +23,7 @@ import { NetworkModule } from './network/network.module';
excludePaths: ['/health'],
}),
DatabaseModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'todo-backend' }),
ProjectModule,
TaskModule,
LabelModule,

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'todo-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@manacore/shared-nestjs-auth": "workspace:*",
"@manacore/shared-nestjs-health": "workspace:*",
"@manacore/shared-nestjs-setup": "workspace:*",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",

View file

@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './db/database.module';
import { FavoriteModule } from './favorite/favorite.module';
import { ListModule } from './list/list.module';
import { HealthModule } from './health/health.module';
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({
imports: [
@ -14,7 +14,7 @@ import { HealthModule } from './health/health.module';
DatabaseModule,
FavoriteModule,
ListModule,
HealthModule,
HealthModule.forRoot({ serviceName: 'quote-backend' }),
],
})
export class AppModule {}

View file

@ -1,13 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller('health')
export class HealthController {
@Get()
check() {
return {
status: 'ok',
service: 'quote-backend',
timestamp: new Date().toISOString(),
};
}
}

View file

@ -1,7 +0,0 @@
import { Module } from '@nestjs/common';
import { HealthController } from './health.controller';
@Module({
controllers: [HealthController],
})
export class HealthModule {}

View file

@ -114,6 +114,8 @@ services:
depends_on:
mana-core-auth:
condition: service_healthy
mana-search:
condition: service_healthy
postgres:
condition: service_healthy
redis:
@ -139,6 +141,59 @@ services:
retries: 3
start_period: 40s
# ============================================
# Search Service (SearXNG + NestJS)
# ============================================
searxng:
image: searxng/searxng:latest
container_name: mana-searxng
restart: always
volumes:
- ./services/mana-search/searxng/settings.yml:/etc/searxng/settings.yml:ro
- ./services/mana-search/searxng/limiter.toml:/etc/searxng/limiter.toml:ro
environment:
SEARXNG_BASE_URL: http://searxng:8080
SEARXNG_SECRET: ${SEARXNG_SECRET:-change-me-searxng-secret}
# Internal only - no external port mapping
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
mana-search:
image: ghcr.io/memo-2023/mana-search:latest
container_name: mana-search
restart: always
depends_on:
searxng:
condition: service_healthy
redis:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3021
SEARXNG_URL: http://searxng:8080
SEARXNG_TIMEOUT: 15000
SEARXNG_DEFAULT_LANGUAGE: de-DE
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis123}
CACHE_SEARCH_TTL: 3600
CACHE_EXTRACT_TTL: 86400
EXTRACT_TIMEOUT: 10000
EXTRACT_MAX_LENGTH: 50000
ports:
- "3021:3021"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3021/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ============================================
# ManaCore Dashboard
# ============================================

View file

@ -133,14 +133,25 @@ bootstrap();
---
### 1.3 MITTEL: Health Endpoints (170 LOC)
### ~~1.3 MITTEL: Health Endpoints~~ ✅ ERLEDIGT (~300 LOC gespart)
**Problem:** 13 Backends haben identische Health-Controller.
**Status:** `@manacore/shared-nestjs-health` Package erstellt und 12 Backends migriert (29.01.2026)
**Empfehlung:** Erstelle `@manacore/shared-nestjs-health`
**Erstelltes Package:** `packages/shared-nestjs-health/`
- `HealthModule.forRoot({ serviceName, version?, includeUptime?, route? })`
- Dynamischer Controller mit konfigurierbarer Route (default: 'health')
- Einheitliche HealthCheckResponse mit timestamp
**Migrierte Backends (12 von 13):**
- ✅ calendar, chat, clock, contacts, nutriphi, picture, planta, presi, skilltree, storage, todo, zitare
- ⏭️ questions (übersprungen - hat erweiterten DB-Health-Check)
**Besonderheiten:**
- storage: Custom route `api/v1/health`
- zitare: serviceName `quote-backend`
**Vorher (14 LOC pro Backend):**
```typescript
// Vorher (14 LOC pro Backend)
@Controller('health')
export class HealthController {
@Get()
@ -148,12 +159,16 @@ export class HealthController {
return { status: 'ok', timestamp: new Date().toISOString(), service: 'chat' };
}
}
// Nachher (1 LOC)
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({ imports: [HealthModule.forRoot('chat-backend')] })
```
**Nachher (1 LOC):**
```typescript
import { HealthModule } from '@manacore/shared-nestjs-health';
@Module({ imports: [HealthModule.forRoot({ serviceName: 'chat-backend' })] })
```
**Einsparung:** 12 Backends × 26 LOC (Controller + Module) = ~312 LOC gelöscht
---
## 2. Frontend Stores (Svelte 5)
@ -470,7 +485,7 @@ export default createDrizzleConfig({ dbName: 'chat' });
| Aufgabe | LOC | Aufwand | Status |
|---------|-----|---------|--------|
| ~~`@manacore/shared-nestjs-setup` erstellen~~ | ~~1.800~~**280** | ~~Mittel~~ | ✅ 8 Backends migriert |
| `@manacore/shared-nestjs-health` erstellen | 170 | Niedrig | Offen |
| ~~`@manacore/shared-nestjs-health` erstellen~~ | ~~170~~**312** | ~~Niedrig~~ | ✅ 12 Backends migriert |
| ~~Drizzle Config Factory erstellen~~ | ~~200~~**160** | ~~Niedrig~~ | ✅ Erledigt |
### ~~Phase 4: Skeleton Refactoring~~ ✅ ANALYSIERT

View file

@ -0,0 +1,29 @@
{
"name": "@manacore/shared-nestjs-health",
"version": "1.0.0",
"description": "Shared NestJS health check module for ManaCore backends",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"scripts": {
"build": "tsc",
"clean": "rm -rf dist",
"type-check": "tsc --noEmit"
},
"files": [
"dist"
],
"peerDependencies": {
"@nestjs/common": "^10.0.0 || ^11.0.0"
},
"devDependencies": {
"@nestjs/common": "^10.0.0",
"@types/node": "^22.10.2",
"typescript": "^5.0.0"
}
}

View file

@ -0,0 +1,114 @@
import { Controller, DynamicModule, Get, Module } from '@nestjs/common';
/**
* Health check response
*/
export interface HealthCheckResponse {
status: 'ok' | 'error';
service: string;
timestamp: string;
version?: string;
uptime?: number;
}
/**
* Options for configuring the health module
*/
export interface HealthModuleOptions {
/**
* Service name to include in health response
* @example 'chat-backend'
*/
serviceName: string;
/**
* Optional version string
* @example '1.0.0'
*/
version?: string;
/**
* Include uptime in response (default: false)
*/
includeUptime?: boolean;
/**
* Custom route path (default: 'health')
*/
route?: string;
}
/**
* Create a health controller with the given options
*/
function createHealthController(options: HealthModuleOptions) {
const route = options.route || 'health';
@Controller(route)
class HealthController {
@Get()
check(): HealthCheckResponse {
const response: HealthCheckResponse = {
status: 'ok',
service: options.serviceName,
timestamp: new Date().toISOString(),
};
if (options.version) {
response.version = options.version;
}
if (options.includeUptime) {
response.uptime = process.uptime();
}
return response;
}
}
return HealthController;
}
/**
* Shared health check module for NestJS backends
*
* Provides a simple health endpoint that returns:
* - status: 'ok'
* - service: configured service name
* - timestamp: ISO timestamp
* - version: (optional) service version
* - uptime: (optional) process uptime in seconds
*
* @example
* ```typescript
* // Basic usage
* @Module({
* imports: [HealthModule.forRoot({ serviceName: 'chat-backend' })],
* })
* export class AppModule {}
*
* // With all options
* @Module({
* imports: [
* HealthModule.forRoot({
* serviceName: 'chat-backend',
* version: '1.0.0',
* includeUptime: true,
* route: 'health', // default
* }),
* ],
* })
* export class AppModule {}
* ```
*/
@Module({})
export class HealthModule {
static forRoot(options: HealthModuleOptions): DynamicModule {
const HealthController = createHealthController(options);
return {
module: HealthModule,
controllers: [HealthController],
};
}
}

View file

@ -0,0 +1,24 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

46
pnpm-lock.yaml generated
View file

@ -68,6 +68,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -388,6 +391,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -730,6 +736,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -1000,6 +1009,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -2210,6 +2222,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-setup':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-setup
@ -2517,6 +2532,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-storage':
specifier: workspace:*
version: link:../../../../packages/shared-storage
@ -3030,6 +3048,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-setup':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-setup
@ -3233,6 +3254,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-setup':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-setup
@ -3715,6 +3739,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -3878,6 +3905,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-storage':
specifier: workspace:*
version: link:../../../../packages/shared-storage
@ -4075,6 +4105,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-metrics':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-metrics
@ -4337,6 +4370,9 @@ importers:
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
'@manacore/shared-nestjs-health':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-health
'@manacore/shared-nestjs-setup':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-setup
@ -4866,14 +4902,10 @@ importers:
version: 5.9.3
packages/shared-nestjs-health:
dependencies:
'@nestjs/common':
specifier: ^10.0.0 || ^11.0.0
version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
devDependencies:
'@manacore/shared-tsconfig':
specifier: workspace:*
version: link:../shared-tsconfig
'@nestjs/common':
specifier: ^10.0.0
version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@types/node':
specifier: ^22.10.2
version: 22.19.1