feat(nextcloud-status): Modul mit Statusabruf, Versionsbewertung und Kachelansicht

- NextcloudInstance mit Zeilenschutz, Migration 20261002150000
- Bewertung (Ampel) als reine Funktion, status.php-Abruf mit Grenzen, endoflife.date-Zwischenspeicher
- API: Liste mit Bewertung, Anlegen und Einzelprüfung nur mit Verwalten
- Modulseite mit Kacheln, Registrierung in Seitenleiste, Marktplatz und Symbolen

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-02 14:49:02 +02:00
parent 7188733f70
commit 5ef7b0c6cc
31 changed files with 2513 additions and 2 deletions
@@ -0,0 +1,29 @@
import 'reflect-metadata';
import { GUARDS_METADATA } from '@nestjs/common/constants';
import { describe, expect, it } from 'vitest';
import { ROLES_KEY } from '../auth/decorators/roles.decorator';
import { MODULE_MANAGE_KEY, MODULE_SLUG_KEY, ModuleGuard } from '../module-registry/module.guard';
import { NextcloudStatusController } from './nextcloud-status.controller';
const proto = NextcloudStatusController.prototype as unknown as Record<string, unknown>;
describe('NextcloudStatusController Metadaten', () => {
it('Klasse traegt Modul-Slug und ModuleGuard', () => {
expect(Reflect.getMetadata(MODULE_SLUG_KEY, NextcloudStatusController)).toBe(
'nextcloud-status',
);
expect(Reflect.getMetadata(GUARDS_METADATA, NextcloudStatusController)).toContain(ModuleGuard);
});
it('list bleibt auf Benutzen-Ebene', () => {
expect(Reflect.getMetadata(MODULE_MANAGE_KEY, proto.list as object)).toBeUndefined();
expect(Reflect.getMetadata(ROLES_KEY, proto.list as object)).toBeUndefined();
});
it.each(['create', 'checkOne'])('%s verlangt Verwalten ohne Rollen-Decorator', (name) => {
const fn = proto[name] as object;
expect(Reflect.getMetadata(MODULE_MANAGE_KEY, fn)).toBe(true);
expect(Reflect.getMetadata(MODULE_SLUG_KEY, fn)).toBe('nextcloud-status');
expect(Reflect.getMetadata(ROLES_KEY, fn)).toBeUndefined();
});
});