feat(mana-media): add centralized media storage with NutriPhi integration

- Implement mana-media service with PostgreSQL/Drizzle ORM persistence
- Add content-addressable storage (SHA-256) for automatic deduplication
- Add Matrix MXC URL import endpoint to copy images from Matrix
- Create @manacore/media-client package for service consumption
- Integrate mana-media into NutriPhi bot for persistent image storage
- Update pnpm-workspace.yaml to include nested service packages
- Add mana-media to docker-compose with port 3015

Images sent to NutriPhi bot are now stored in mana-media after analysis,
providing persistent storage with deduplication across all apps.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-02 17:30:14 +01:00
parent 171cf7a854
commit d4663b5643
31 changed files with 2114 additions and 4419 deletions

View file

@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { MatrixService } from './matrix.service';
import { NutriPhiModule } from '../nutriphi/nutriphi.module';
import { SessionModule, TranscriptionModule, CreditModule } from '@manacore/bot-services';
import { MediaModule } from '../media/media.module';
@Module({
imports: [
@ -9,6 +10,7 @@ import { SessionModule, TranscriptionModule, CreditModule } from '@manacore/bot-
SessionModule.forRoot({ storageMode: 'redis' }),
TranscriptionModule.forRoot(),
CreditModule.forRoot(),
MediaModule,
],
providers: [MatrixService],
exports: [MatrixService],

View file

@ -14,6 +14,7 @@ import {
WeeklyStats,
} from '../nutriphi/nutriphi.service';
import { SessionService, TranscriptionService, CreditService } from '@manacore/bot-services';
import { MediaService } from '../media/media.service';
import { HELP_MESSAGE, MEAL_TYPE_LABELS } from '../config/configuration';
const PHOTO_ANALYSIS_CREDITS = 3;
@ -36,7 +37,8 @@ export class MatrixService extends BaseMatrixService {
private nutriphiService: NutriPhiService,
private sessionService: SessionService,
private transcriptionService: TranscriptionService,
private creditService: CreditService
private creditService: CreditService,
private mediaService: MediaService
) {
super(configService);
}
@ -114,6 +116,19 @@ Sag "hilfe" fur alle Befehle!`;
const response = this.formatAnalysisResult(result);
await this.sendMessage(roomId, response);
// Store image in mana-media for persistent storage (non-blocking)
// Use Matrix sender ID as user identifier
this.mediaService
.storeFromMatrix(mxcUrl, sender)
.then((mediaResult) => {
if (mediaResult) {
this.logger.log(`Image stored in mana-media: ${mediaResult.id}`);
}
})
.catch((error) => {
this.logger.warn(`Failed to store image in mana-media: ${error}`);
});
} catch (error) {
await this.client.setTyping(roomId, false);
const errorMsg = error instanceof Error ? error.message : 'Unbekannter Fehler';