Migrate omsorgapp to browser SPA, add Docker/CI build setup
Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Failing after 4s
Docker-Images bauen und veröffentlichen / build (VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Failing after 4s
Docker-Images bauen und veröffentlichen / build (VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 3s
- omsorgapp: drop Electron, run as a plain Vite/React browser app; refresh token moves to an HttpOnly cookie (omsorgCore), CORS added for the new browser origin, document download/preview switched to Blob-based browser APIs. - Add Dockerfiles for omsorgCore, omsorgapp, and omsorgWeb, a docker-compose.yml wiring Postgres/MySQL/all three apps together, and a Gitea Actions workflow that builds and pushes images to the repo's container registry on push to main and on version tags. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e9e96a57dc
commit
598dfcd38a
@@ -132,6 +132,21 @@ function current_role(): string {
|
||||
return $_SESSION['omsorgcore_profile']['role'] ?? '';
|
||||
}
|
||||
|
||||
// Serverseitig ist jeder Endpunkt ohnehin über [RequirePermission] gegated (siehe
|
||||
// omsorgCore/CLAUDE.md, "Rechtesystem") - das hier ist nur die UI-Seite davon (Tab/Formular
|
||||
// ausblenden statt nur eine 403-Antwort abzuwarten), analog zu hasPermission() in
|
||||
// omsorgapp/src/app/AuthContext.jsx. $_SESSION['omsorgcore_profile']['permissions'] kommt aus
|
||||
// GET /api/auth/me (PermissionDto[] { module, action, scope }, siehe MeResponse in omsorgCore).
|
||||
function has_permission(string $module, string $action): bool {
|
||||
$permissions = $_SESSION['omsorgcore_profile']['permissions'] ?? [];
|
||||
foreach ($permissions as $permission) {
|
||||
if (($permission['module'] ?? null) === $module && ($permission['action'] ?? null) === $action) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function e(string $s): string {
|
||||
return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
@@ -35,6 +35,16 @@ function layout_start(string $title, string $currentPage = 'dashboard'): void {
|
||||
<a href="dashboard.php" class="<?= $currentPage === 'dashboard' ? 'active' : '' ?>">
|
||||
<span class="nav-icon">⊞</span><span class="nav-label"> Dashboard</span>
|
||||
</a>
|
||||
<?php if (has_permission('Absences', 'View')): ?>
|
||||
<a href="urlaubsantrag.php" class="<?= $currentPage === 'urlaubsantrag' ? 'active' : '' ?>">
|
||||
<span class="nav-icon">🗓</span><span class="nav-label"> Urlaub & Abwesenheit</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (has_permission('TimeEntries', 'View')): ?>
|
||||
<a href="stundenerfassung.php" class="<?= $currentPage === 'stundenerfassung' ? 'active' : '' ?>">
|
||||
<span class="nav-icon">⏱</span><span class="nav-label"> Zeiterfassung</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<a href="settings.php" class="<?= $currentPage === 'settings' ? 'active' : '' ?>">
|
||||
<span class="nav-icon">⚙</span><span class="nav-label"> Einstellungen</span>
|
||||
</a>
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// analog zu httpClient.cjs/authClient.cjs in omsorgapp. Alle Aufrufer bekommen ein einheitliches
|
||||
// Ergebnis-Array: ['ok' => bool, 'status' => int, 'data' => array|null].
|
||||
//
|
||||
// Nur Auth-Endpunkte - Connect verwaltet keine anderen Nutzer/Rollen/Mitarbeiter (das bleibt
|
||||
// OMSORG Desktop vorbehalten), deshalb fehlen hier bewusst omsorgcore_users_*/roles_*/employees_*.
|
||||
// Auth-Endpunkte plus die für den Außendienst freigegebenen Absences-/ValueLists-Endpunkte -
|
||||
// Connect verwaltet weiterhin keine anderen Nutzer/Rollen/Mitarbeiter (das bleibt OMSORG Desktop
|
||||
// vorbehalten), deshalb fehlen hier bewusst omsorgcore_users_*/roles_*/employees_*.
|
||||
//
|
||||
// Transport läuft über den generierten Client in ../api-client-php/ (openapi-generator, siehe
|
||||
// ../api-client-php/ANLEITUNG.md) statt über rohes cURL.
|
||||
@@ -110,3 +111,150 @@ function omsorgcore_password_policy(array $config): array
|
||||
return omsorgcore_call($config, fn($cfg) => (new \OmsorgCoreClient\Api\AuthApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiAuthPasswordPolicyGetWithHttpInfo());
|
||||
}
|
||||
|
||||
// --- ValueLists (nur lesend - Dropdown-Optionen, z. B. "AbsenceType") ---
|
||||
|
||||
function omsorgcore_value_list_items(array $config, string $accessToken, string $key): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $key) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\ValueListsApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiValueListsKeyItemsGetWithHttpInfo($key);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Absences (Urlaub/Krankmeldung/Sonstige, FR-CON-1) ---
|
||||
// Own-Scope: die EmployeeId wird serverseitig aus dem JWT aufgelöst (siehe AbsenceService in
|
||||
// omsorgCore), Connect schickt hier bewusst nie eine employeeId mit.
|
||||
|
||||
function omsorgcore_absences_list(array $config, string $accessToken, ?string $status = null): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $status) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiAbsencesGetWithHttpInfo($status);
|
||||
});
|
||||
}
|
||||
|
||||
function omsorgcore_absences_create(array $config, string $accessToken, array $payload): array
|
||||
{
|
||||
$createAbsenceRequest = new \OmsorgCoreClient\Model\CreateAbsenceRequest([
|
||||
'type' => $payload['type'],
|
||||
'start_date' => new \DateTime($payload['startDate']),
|
||||
'end_date' => new \DateTime($payload['endDate']),
|
||||
'reason' => $payload['reason'] !== '' ? $payload['reason'] : null,
|
||||
'substitute' => $payload['substitute'] !== '' ? $payload['substitute'] : null,
|
||||
'note' => $payload['note'] !== '' ? $payload['note'] : null,
|
||||
]);
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $createAbsenceRequest) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiAbsencesPostWithHttpInfo($createAbsenceRequest);
|
||||
});
|
||||
}
|
||||
|
||||
// Nur solange der Antrag noch "Eingereicht" ist (serverseitig erzwungen, siehe
|
||||
// AbsenceService.UpdateAsync in omsorgCore) - Genehmigt/Abgelehnt sind über diesen Endpoint
|
||||
// bewusst nicht mehr änderbar.
|
||||
function omsorgcore_absences_update(array $config, string $accessToken, string $id, array $payload): array
|
||||
{
|
||||
$updateAbsenceRequest = new \OmsorgCoreClient\Model\UpdateAbsenceRequest([
|
||||
'type' => $payload['type'],
|
||||
'start_date' => new \DateTime($payload['startDate']),
|
||||
'end_date' => new \DateTime($payload['endDate']),
|
||||
'reason' => $payload['reason'] !== '' ? $payload['reason'] : null,
|
||||
'substitute' => $payload['substitute'] !== '' ? $payload['substitute'] : null,
|
||||
'note' => $payload['note'] !== '' ? $payload['note'] : null,
|
||||
]);
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $id, $updateAbsenceRequest) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\AbsencesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiAbsencesIdPutWithHttpInfo($id, $updateAbsenceRequest);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Orders (nur lesend - Auftrags-Dropdown fürs Zeiterfassungsformular) ---
|
||||
// Order hat noch keine Mitarbeiter-Zuweisung (FR-EM-3 offen), daher zeigt das Dropdown alle
|
||||
// aktiven Aufträge - siehe omsorgCore/CLAUDE.md, Abschnitt "Zeiterfassung", dokumentierte Einschränkung.
|
||||
|
||||
function omsorgcore_orders_list(array $config, string $accessToken): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\OrdersApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiOrdersGetWithHttpInfo(null, null, null, null, null, null, 1, 200);
|
||||
});
|
||||
}
|
||||
|
||||
// --- TimeEntries (Zeiterfassung pro Schicht, FR-ZE-1/FR-ZE-2) ---
|
||||
// Own-Scope: die EmployeeId wird serverseitig aus dem JWT aufgelöst (siehe TimeEntryService in
|
||||
// omsorgCore), Connect schickt hier bewusst nie eine employeeId mit.
|
||||
|
||||
function omsorgcore_time_entries_list(array $config, string $accessToken, ?string $statusId = null): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $statusId) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiTimeEntriesGetWithHttpInfo($statusId);
|
||||
});
|
||||
}
|
||||
|
||||
function omsorgcore_time_entries_create(array $config, string $accessToken, array $payload): array
|
||||
{
|
||||
$createTimeEntryRequest = new \OmsorgCoreClient\Model\CreateTimeEntryRequest([
|
||||
'order_id' => $payload['orderId'],
|
||||
'date' => new \DateTime($payload['date']),
|
||||
'start' => $payload['start'],
|
||||
'end' => $payload['end'],
|
||||
'break_duration' => $payload['breakDuration'],
|
||||
'night_hours' => $payload['nightHours'],
|
||||
'saturday_hours' => $payload['saturdayHours'],
|
||||
'sunday_hours' => $payload['sundayHours'],
|
||||
'holiday_hours' => $payload['holidayHours'],
|
||||
]);
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $createTimeEntryRequest) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiTimeEntriesPostWithHttpInfo($createTimeEntryRequest);
|
||||
});
|
||||
}
|
||||
|
||||
// Nur solange status.isEditableByOwner (serverseitig erzwungen, siehe TimeEntryService.UpdateAsync
|
||||
// in omsorgCore) - kein statusId-Feld hier, Statuswechsel laufen ausschließlich über submit/decision.
|
||||
function omsorgcore_time_entries_update(array $config, string $accessToken, string $id, array $payload): array
|
||||
{
|
||||
$updateTimeEntryRequest = new \OmsorgCoreClient\Model\UpdateTimeEntryRequest([
|
||||
'order_id' => $payload['orderId'],
|
||||
'date' => new \DateTime($payload['date']),
|
||||
'start' => $payload['start'],
|
||||
'end' => $payload['end'],
|
||||
'break_duration' => $payload['breakDuration'],
|
||||
'night_hours' => $payload['nightHours'],
|
||||
'saturday_hours' => $payload['saturdayHours'],
|
||||
'sunday_hours' => $payload['sundayHours'],
|
||||
'holiday_hours' => $payload['holidayHours'],
|
||||
]);
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $id, $updateTimeEntryRequest) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiTimeEntriesIdPutWithHttpInfo($id, $updateTimeEntryRequest);
|
||||
});
|
||||
}
|
||||
|
||||
function omsorgcore_value_list_transitions(array $config, string $accessToken, string $key): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $key) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\ValueListsApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiValueListsKeyTransitionsGetWithHttpInfo($key);
|
||||
});
|
||||
}
|
||||
|
||||
function omsorgcore_time_entries_submit(array $config, string $accessToken, string $id): array
|
||||
{
|
||||
return omsorgcore_call($config, function ($cfg) use ($accessToken, $id) {
|
||||
$cfg->setAccessToken($accessToken);
|
||||
return (new \OmsorgCoreClient\Api\TimeEntriesApi(new \GuzzleHttp\Client(), $cfg))
|
||||
->apiTimeEntriesIdSubmitPostWithHttpInfo($id);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user