Init: RoggioApp Architecture, Prisma Schema, API MVP

This commit is contained in:
Clara Zetkin
2026-04-26 19:42:42 +02:00
commit 193b29e8a9
5256 changed files with 1446953 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
console.log('🧹 Lösche alte Daten für sauberen Testlauf...')
await prisma.event.deleteMany()
await prisma.groupMembership.deleteMany()
await prisma.group.deleteMany()
await prisma.person.deleteMany()
await prisma.unit.deleteMany()
await prisma.vaultData.deleteMany()
console.log('🏗️ Baue den Unit-Graphen (Grundstück -> Haus -> Wohnung -> Räume -> Inventar)...')
// 1. Grundstück
const campus = await prisma.unit.create({
data: {
name: 'Campus Hauptgelände',
traits: { type: 'campus' }
}
})
// 2. Haus A
const houseA = await prisma.unit.create({
data: {
name: 'Haus A',
parentId: campus.id,
traits: { type: 'building' }
}
})
// 3. Wohnung Sonnenaufgang (Rental Unit)
const apartment = await prisma.unit.create({
data: {
name: 'Wohnung Sonnenaufgang (1. Stock)',
parentId: houseA.id,
traits: {
type: 'apartment',
is_rentable: true,
base_price: 85.00
}
}
})
// 4. Räume der Wohnung
const kitchen = await prisma.unit.create({
data: { name: 'Küche', parentId: apartment.id, traits: { type: 'room', area_sqm: 12.0 } }
})
const bedroom1 = await prisma.unit.create({
data: { name: 'Schlafzimmer Groß', parentId: apartment.id, traits: { type: 'room', area_sqm: 18.5 } }
})
// 5. Inventar als Units (Sub-Units der Räume)
await prisma.unit.create({
data: { name: 'Doppelbett', parentId: bedroom1.id, traits: { type: 'inventory', inventory_category: 'bed', sleep_capacity: 2 } }
})
// 6. Test: Gasflaschen im Butanlager (außerhalb der Wohnung, direkt im Haus)
const storage = await prisma.unit.create({
data: { name: 'Butanlager', parentId: houseA.id, traits: { type: 'room', needs_maintenance: true } }
})
await prisma.unit.create({
data: { name: '5kg Butangas Vorrat', parentId: storage.id, traits: { type: 'stock', soll: 10, ist: 7, alert_limit: 3 } }
})
console.log('👤 Erstelle Person (Sev) und simuliere Vault...')
// Vault Data Simulation
const secureVaultEntry = await prisma.vaultData.create({
data: {
entityType: 'Person',
entityId: 'pending', // Wird nach Person-Erstellung aktualisiert
secureData: JSON.stringify({ passport: 'X123456789', iban: 'DE1234...' })
}
})
const sev = await prisma.person.create({
data: {
firstName: 'Sev',
lastName: 'Atzinger',
email: 'sev@example.com',
traits: {
pronouns: 'er/ihm',
vault_id: secureVaultEntry.id
}
}
})
await prisma.vaultData.update({ where: { id: secureVaultEntry.id }, data: { entityId: sev.id } })
console.log('📅 Simuliere Event: Sev bucht die Wohnung Sonnenaufgang...')
await prisma.event.create({
data: {
type: 'RENTAL',
status: 'CONFIRMED',
startTime: new Date('2026-08-14T15:00:00Z'),
endTime: new Date('2026-08-21T10:00:00Z'),
targetUnitId: apartment.id,
targetPersonId: sev.id,
payload: { guests: 2, total_price: 595.00 }
}
})
console.log('\n--- 🔍 TEST: DYNAMISCHE ABFRAGE DER WOHNUNG ---')
// Wir holen die Wohnung und generieren die Kapazität aus den Kindern (Tiefen-Abfrage)
const apartmentWithChildren = await prisma.unit.findUnique({
where: { id: apartment.id },
include: {
children: {
include: { children: true } // Wir holen Räume UND deren Inventar
}
}
})
let totalArea = 0;
let totalSleepCapacity = 0;
apartmentWithChildren?.children.forEach(room => {
if (room.traits && typeof room.traits === 'object' && 'area_sqm' in room.traits) {
totalArea += (room.traits as any).area_sqm;
}
// Check Inventar im Raum
room.children.forEach(inventory => {
if (inventory.traits && typeof inventory.traits === 'object' && 'sleep_capacity' in inventory.traits) {
totalSleepCapacity += (inventory.traits as any).sleep_capacity;
}
})
})
console.log(`Wohnung: ${apartmentWithChildren?.name}`)
console.log(`Gesamtfläche: ${totalArea} qm (Berechnet aus den Räumen)`)
console.log(`Schlafplätze: ${totalSleepCapacity} (Berechnet aus den Betten)`)
console.log('------------------------------------------------\n')
}
main()
.catch(e => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
+78
View File
@@ -0,0 +1,78 @@
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { PrismaClient } from '@prisma/client'
const app = new Hono()
const prisma = new PrismaClient()
app.get('/', (c) => {
return c.html(`
<html>
<head>
<title>BookMe - Roggio API</title>
<style>
body { font-family: monospace; background: #282a36; color: #f8f8f2; padding: 2rem; }
h1 { color: #50fa7b; }
.unit { border: 1px solid #6272a4; padding: 1rem; margin-bottom: 1rem; border-radius: 8px; }
</style>
</head>
<body>
<h1>BookMe API (RoggioApp MVP)</h1>
<p>Willkommen an der API-Schnittstelle. Test-Endpunkte:</p>
<ul>
<li><a href="/api/apartments" style="color: #ff79c6;">/api/apartments</a> - Zeigt alle mietbaren Einheiten (dynamisch berechnet)</li>
</ul>
</body>
</html>
`)
})
app.get('/api/apartments', async (c) => {
const units = await prisma.unit.findMany({
include: {
children: {
include: { children: true }
}
}
})
const apartments = units.filter(u => {
const traits = u.traits as any
return traits && traits.is_rentable === true
}).map(apartment => {
let totalArea = 0;
let totalSleepCapacity = 0;
apartment.children.forEach(room => {
const rTraits = room.traits as any;
if (rTraits?.area_sqm) totalArea += rTraits.area_sqm;
room.children.forEach(inv => {
const iTraits = inv.traits as any;
if (iTraits?.sleep_capacity) totalSleepCapacity += iTraits.sleep_capacity;
});
});
return {
id: apartment.id,
name: apartment.name,
base_price: (apartment.traits as any).base_price,
calculated_stats: {
area_sqm: totalArea,
sleep_capacity: totalSleepCapacity
},
rooms: apartment.children.map(r => r.name)
}
})
return c.json(apartments)
})
const port = 3000
console.log(`Server is running on port ${port}`)
serve({
fetch: app.fetch,
port,
hostname: '0.0.0.0'
})