mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 13:01:09 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
12 KiB
Plan zur Vereinheitlichung der Namenskonventionen
Datum: 15. Januar 2025
Status: In Bearbeitung
1. Analyse der aktuellen Situation ✅
Identifizierte Collections
Nach Analyse des Codes wurden folgende Collections gefunden:
Plural Collections (korrekt):
users✓links✓folders✓clicks✓tags✓cards✓themes✓feature_requests✓feature_votes✓template_ratings✓
Inkonsistente Collections:
link_tags(solltelinks_tagssein für Konsistenz)card_templates(korrekt)user_cards(inkonsistent mitcards)
Feldnamen-Inkonsistenzen
Problem 1: Foreign Key Benennungen
Aktuell inkonsistent:
- In
links:user(sollteuser_idsein) - In
analytics:link(solltelink_idsein) - In
folders: manchmaluser_id, manchmaluser - In
link_tags:link_idundtag_id(korrekt)
Problem 2: Timestamp-Felder
Aktuell:
- Automatisch:
created,updated - Manuell:
clicked_at,expires_at - Inkonsistent: manchmal
_atsuffix, manchmal nicht
Problem 3: Boolean-Felder
Aktuell:
activevsis_activepublicvsis_publicverifiedvsis_verified
2. Mapping-Tabelle für Umbenennungen
Collection-Umbenennungen
| Alt | Neu | Priorität | Breaking |
|---|---|---|---|
link_tags |
links_tags |
Niedrig | Ja |
user_cards |
Behalten oder in cards integrieren |
Mittel | Ja |
Feld-Umbenennungen
Links Collection
| Alt | Neu | Typ | Breaking |
|---|---|---|---|
user |
user_id |
relation | Ja |
active |
is_active |
bool | Ja |
folder |
folder_id |
relation | Ja |
Analytics Collection
| Alt | Neu | Typ | Breaking |
|---|---|---|---|
link |
link_id |
relation | Ja |
ip |
ip_address |
text | Ja |
Folders Collection
| Alt | Neu | Typ | Breaking |
|---|---|---|---|
user |
user_id |
relation | Ja |
public |
is_public |
bool | Ja |
Tags Collection
| Alt | Neu | Typ | Breaking |
|---|---|---|---|
user |
user_id |
relation | Ja |
public |
is_public |
bool | Ja |
3. Implementierungsstrategie
Phase 1: Vorbereitung (Woche 1)
- Backup erstellen
- Feature Flag einführen:
USE_NEW_NAMING_CONVENTION - Compatibility Layer entwickeln
Phase 2: Duale Unterstützung (Woche 2-3)
- Neue Felder parallel zu alten anlegen
- Daten synchron halten
- Code für beide Varianten vorbereiten
Phase 3: Migration (Woche 4)
- Schrittweise Migration der Daten
- Testing auf Staging
- Monitoring einrichten
Phase 4: Cleanup (Woche 5)
- Alte Felder entfernen
- Compatibility Layer entfernen
- Documentation aktualisieren
4. Migration Scripts
4.1 Database Migration Script
// pb_migrations/002_naming_conventions.js
migrate(
(db) => {
// Phase 1: Add new fields parallel to old ones
const collections = [
{ name: 'links', oldField: 'user', newField: 'user_id' },
{ name: 'links', oldField: 'active', newField: 'is_active' },
{ name: 'links', oldField: 'folder', newField: 'folder_id' },
{ name: 'analytics', oldField: 'link', newField: 'link_id' },
{ name: 'analytics', oldField: 'ip', newField: 'ip_address' },
{ name: 'folders', oldField: 'user', newField: 'user_id' },
{ name: 'folders', oldField: 'public', newField: 'is_public' },
{ name: 'tags', oldField: 'user', newField: 'user_id' },
{ name: 'tags', oldField: 'public', newField: 'is_public' },
];
collections.forEach(({ name, oldField, newField }) => {
const collection = $app.dao().findCollectionByNameOrId(name);
if (collection) {
const existingOld = collection.schema.find((f) => f.name === oldField);
const existingNew = collection.schema.find((f) => f.name === newField);
if (existingOld && !existingNew) {
// Clone field with new name
const newFieldSchema = JSON.parse(JSON.stringify(existingOld));
newFieldSchema.name = newField;
collection.schema.addField(new SchemaField(newFieldSchema));
// Copy data
$app.dao().db().newQuery(`UPDATE ${name} SET ${newField} = ${oldField}`).execute();
}
}
});
// Update collection rules with new field names
updateCollectionRules();
},
(db) => {
// Rollback: Remove new fields
const collections = [
{ name: 'links', fields: ['user_id', 'is_active', 'folder_id'] },
{ name: 'analytics', fields: ['link_id', 'ip_address'] },
{ name: 'folders', fields: ['user_id', 'is_public'] },
{ name: 'tags', fields: ['user_id', 'is_public'] },
];
collections.forEach(({ name, fields }) => {
const collection = $app.dao().findCollectionByNameOrId(name);
if (collection) {
fields.forEach((field) => {
collection.schema.removeField(field);
});
$app.dao().saveCollection(collection);
}
});
}
);
4.2 Compatibility Layer (TypeScript)
// src/lib/db/compatibility.ts
export class DBCompatibility {
private useNewNaming: boolean;
constructor() {
this.useNewNaming = import.meta.env.PUBLIC_USE_NEW_NAMING === 'true';
}
// Field name mapper
field(oldName: string): string {
if (!this.useNewNaming) return oldName;
const mapping: Record<string, string> = {
user: 'user_id',
link: 'link_id',
folder: 'folder_id',
active: 'is_active',
public: 'is_public',
ip: 'ip_address',
};
return mapping[oldName] || oldName;
}
// Query builder helper
buildFilter(filters: Record<string, any>): string {
const mapped: Record<string, any> = {};
Object.entries(filters).forEach(([key, value]) => {
mapped[this.field(key)] = value;
});
return Object.entries(mapped)
.map(([k, v]) => `${k}="${v}"`)
.join(' && ');
}
// Data transformer for responses
transformResponse(data: any): any {
if (!this.useNewNaming) return data;
// Map new field names back to old for backward compatibility
const reverseMapping: Record<string, string> = {
user_id: 'user',
link_id: 'link',
folder_id: 'folder',
is_active: 'active',
is_public: 'public',
ip_address: 'ip',
};
const transformed = { ...data };
Object.entries(reverseMapping).forEach(([newName, oldName]) => {
if (newName in transformed) {
transformed[oldName] = transformed[newName];
if (this.useNewNaming) {
delete transformed[newName];
}
}
});
return transformed;
}
}
export const dbCompat = new DBCompatibility();
5. Code Refactoring Plan
5.1 Utility Function für Migration
// src/lib/db/migrate-helpers.ts
import { pb } from '$lib/pocketbase';
import { dbCompat } from './compatibility';
export async function getLinks(userId: string) {
const filter = dbCompat.buildFilter({ user: userId });
const results = await pb.collection('links').getList(1, 100, { filter });
return results.items.map((item) => dbCompat.transformResponse(item));
}
5.2 Schrittweise Migration der Codebasis
- Alle direkten PocketBase-Aufrufe wrappen
- Tests für beide Naming-Conventions schreiben
- Graduelle Aktivierung per Feature Flag
6. Test-Strategie
6.1 Unit Tests
// src/tests/naming-migration.spec.ts
describe('Naming Convention Migration', () => {
describe('Compatibility Layer', () => {
it('should map old field names to new', () => {
expect(dbCompat.field('user')).toBe('user_id');
expect(dbCompat.field('active')).toBe('is_active');
});
it('should build correct filters', () => {
const filter = dbCompat.buildFilter({ user: '123', active: true });
expect(filter).toBe('user_id="123" && is_active="true"');
});
});
describe('Data Migration', () => {
it('should handle both field versions', async () => {
// Test with old fields
// Test with new fields
// Test with both present
});
});
});
6.2 Integration Tests
- Test alle API Endpoints mit beiden Namenskonventionen
- Test Datenintegrität während Migration
- Performance-Tests vor und nach Migration
6.3 E2E Tests
- Vollständiger User-Flow Test
- Link-Erstellung und -Abruf
- Analytics-Tracking
- Admin-Funktionen
7. Rollback-Plan
7.1 Sofortiger Rollback (< 1 Stunde)
# 1. Feature Flag deaktivieren
export PUBLIC_USE_NEW_NAMING=false
# 2. Cache clearen
redis-cli FLUSHALL
# 3. Server neustarten
pm2 restart uload
7.2 Daten-Rollback (< 24 Stunden)
// pb_migrations/002_naming_conventions_rollback.js
migrate((db) => {
// Daten von neuen Feldern zurück zu alten kopieren
const updates = [
'UPDATE links SET user = user_id WHERE user_id IS NOT NULL',
'UPDATE links SET active = is_active WHERE is_active IS NOT NULL',
'UPDATE analytics SET link = link_id WHERE link_id IS NOT NULL',
// ... weitere Updates
];
updates.forEach((sql) => {
$app.dao().db().newQuery(sql).execute();
});
// Neue Felder entfernen
// ... (siehe oben)
});
7.3 Vollständiger Rollback
# 1. Backup wiederherstellen
./backend/pocketbase restore backup_before_migration.zip
# 2. Code auf vorherige Version zurücksetzen
git revert [migration-commit]
# 3. Deploy
npm run build && npm run deploy
8. Monitoring & Validierung
8.1 Metriken zu überwachen
- Response Times vor/nach Migration
- Error Rates
- Database Query Performance
- User Activity Patterns
8.2 Validierungs-Checks
// src/lib/db/validation.ts
export async function validateMigration() {
const checks = {
linksIntegrity: await checkLinksIntegrity(),
analyticsConsistency: await checkAnalyticsConsistency(),
relationsValid: await checkRelationsValid(),
performanceMetrics: await checkPerformance(),
};
return checks;
}
9. Zeitplan
| Woche | Phase | Aktivitäten | Verantwortlich |
|---|---|---|---|
| 1 | Vorbereitung | Backup, Feature Flags, Compatibility Layer | DevOps + Backend |
| 2-3 | Implementierung | Duale Felder, Code-Anpassungen | Backend |
| 4 | Testing | Unit, Integration, E2E Tests | QA + Backend |
| 5 | Migration | Staging-Deploy, Monitoring | DevOps |
| 6 | Production | Schrittweiser Rollout | Team |
| 7 | Cleanup | Alte Felder entfernen | Backend |
10. Risiken & Mitigationen
| Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|---|---|---|---|
| Datenverlust | Niedrig | Hoch | Mehrfache Backups, Test-Migrations |
| Performance-Degradation | Mittel | Mittel | Monitoring, Rollback-Plan |
| Breaking Changes für Clients | Hoch | Mittel | Compatibility Layer, Versioning |
| Inkonsistente Daten | Mittel | Hoch | Validierungs-Checks, Transactions |
11. Erfolgs-Kriterien
- ✅ Alle Tests grün
- ✅ Keine Performance-Verschlechterung (< 5% Unterschied)
- ✅ Keine Datenverluste
- ✅ Erfolgreiche Migration auf Staging
- ✅ < 0.1% Error Rate nach Production Deploy
- ✅ Positive Developer Feedback
12. Nächste Schritte
- Review dieses Plans mit dem Team
- Approval von Tech Lead / CTO
- Setup der Test-Umgebung
- Implementierung des Compatibility Layers
- Start der Phase 1
Dieser Plan wird kontinuierlich aktualisiert. Letzte Änderung: 15. Januar 2025