added: new button link to Matrix/Element downloads in "Clients" added: new group "Entwicklung" with link buttons to git.jprise.info and KNIME download added: new group "Administration" with link button to oauth.jprise.info (Zitadel) |
||
|---|---|---|
| deploy | ||
| JPRISE | ||
| .gitignore | ||
| CLAUDE.md | ||
| CLAUDE.SoulfulShack.md | ||
| JPRISE.sln | ||
| README.md | ||
JPRISE Landing Page
Blazor-Server-App, die die statische index.html auf jprise.info ersetzt und vor den Inhalt einen OIDC-Login (Zitadel
auf oauth.jprise.info) schaltet.
Stack
- .NET 10 / ASP.NET Core, Blazor Web App mit Server-Interaktivität
- OIDC via
Microsoft.AspNetCore.Authentication.OpenIdConnect(Authorization Code + PKCE,client_secret_basic) - Serilog (Console + tägliche rotierende Datei in
Logs/jprise-YYYYMMDD.log) - Deployment: Windows IIS (In-Process) oder Linux/Kestrel, identischer Publish-Output
Landing-Page-Inhalt
Die Startseite (Components/Pages/Home.razor) besteht aus fünf Kachel-Sektionen. Externe Links öffnen in einem neuen
Tab (target="_blank" rel="noopener noreferrer"), interne Seiten bleiben im selben Tab.
| Sektion | Kachel | Ziel | CTA |
|---|---|---|---|
| Dienste | ownCloud | https://ocis.jprise.info |
ÖFFNEN |
| Galène | https://galene.jprise.info |
BETRETEN | |
| Matrix-Chat | https://chat.jprise.info |
BETRETEN | |
| Clients | RustDesk | https://rustdesk.com/ |
HERUNTERLADEN |
| Syncthing | https://syncthing.net/downloads/ |
HERUNTERLADEN | |
| KeePassXC | https://keepassxc.org/download/ |
HERUNTERLADEN | |
| Element | https://element.io/download |
HERUNTERLADEN | |
| Infrastruktur | jprise.de | /infrastruktur/jprise.de |
ANZEIGEN |
| jprise.info | /infrastruktur/jprise.info |
ANZEIGEN | |
| Anwendungen | /anwendungen |
VERWALTEN | |
| Entwicklung | Forgejo | https://git.jprise.info |
ÖFFNEN |
| KNIME | https://www.knime.com/downloads |
HERUNTERLADEN | |
| Administration | Zitadel | https://oauth.jprise.info |
ÖFFNEN |
Es gibt keinen .hero-Block mehr; den Abstand zur fixierten Kopfzeile (Wordmark + User-Chip) trägt die erste Sektion
über main > .section:first-of-type in wwwroot/app.css. Das Fade-in-Stagger der Kacheln ist auf
.service:nth-of-type(5) begrenzt — eine Sektion mit mehr als fünf Kacheln ließe die zusätzlichen ohne Verzögerung
einblenden. Die einzelne Administration-Kachel läuft (Grid repeat(auto-fit, minmax(260px, 1fr))) über die volle
Breite; bei Bedarf zentriert ein .services--single-Modifier sie auf normale Kachelbreite.
Erstmaliges Setup (Dev)
1. Client-Secret in user-secrets ablegen
Niemals in appsettings.json einchecken.
cd JPRISE
dotnet user-secrets set "Oidc:ClientSecret" "<SECRET-AUS-ZITADEL>"
Speicherort macOS: ~/.microsoft/usersecrets/1e65e766-ff77-4dd6-a525-0834bed98b8b/secrets.json.
2. Lokal starten
dotnet run --launch-profile https
- HTTPS auf
https://localhost:5001, HTTP aufhttp://localhost:5000. - Beim ersten Aufruf wird automatisch zur Zitadel-Anmeldung umgeleitet.
- Nach erfolgreichem Login Redirect zurück nach
/signin-oidc→ Cookie wird gesetzt → Index-Seite ist sichtbar.
3. Logout testen
Button „Abmelden" oben links → POST /logout → SignOut Cookie + Redirect an Zitadel end_session_endpoint →
Rück-Redirect nach /signout-callback-oidc → /.
Zitadel-Konfiguration (Referenz)
| Feld | Wert |
|---|---|
| Authority / Issuer | https://oauth.jprise.info |
| Discovery | https://oauth.jprise.info/.well-known/openid-configuration |
| Project / App | JPRISE / LandingPage |
| Client-ID | 374059785240182787 |
| Client-Type | Web (Confidential) |
| Response Type | Code |
| Grant Type | Authorization Code |
| Auth Method | Basic (client_secret_basic) |
| Scopes | openid profile email |
Redirect-URIs (müssen in Zitadel hinterlegt sein)
Login-Callback:
https://jprise.info/signin-oidchttps://localhost:5001/signin-oidchttp://localhost:5000/signin-oidc
Post-Logout-Redirect:
https://jprise.info/signout-callback-oidchttps://localhost:5001/signout-callback-oidchttp://localhost:5000/signout-callback-oidc
Build & Publish
Windows-Ziel (IIS auf vmd157055)
dotnet publish JPRISE/JPRISE.csproj \
-c Release \
-r win-x64 \
--self-contained false \
-o publish/win
Output: publish/win/ mit JPRISE.dll, web.config, wwwroot/, Konfigs.
Stolperstein (Publish von macOS aus):
dotnet publishräumt das Zielverzeichnis nicht selbst auf — alte Artefakte (umbenannte DLLs, entferntewwwroot-Dateien) bleiben liegen und können den Eindruck erwecken, der Build habe „nichts geändert". Wer das Verzeichnis vorher mitrm -rf publish/winlöscht, läuft auf macOS → win-x64 aber in einen zweiten Fehler: dieTransformWebConfig-Task bricht mitCould not find a part of the path '.../publish/win/web.config'ab, weil der Elternpfad zum Schreibzeitpunkt fehlt. Lösung — Verzeichnis nach dem Löschen wieder anlegen:rm -rf publish/win && mkdir -p publish/win dotnet publish JPRISE/JPRISE.csproj -c Release -r win-x64 --self-contained false -o publish/winRobuster im csproj verankert (greift aus Terminal und Rider): ein
BeforeTargets="PrepareForPublish"-Target, dasRemoveDir+MakeDirauf$(PublishDir)ausführt. NichtBeforeTargets="Publish"verwenden — das löscht das Verzeichnis zu spät und triggert exakt denTransformWebConfig-Fehler. Rider selbst hat keinen „Clean before publish"- Haken, der das Output-Verzeichnis leert (Build → Cleanputzt nurbin/obj).
Linux/Kestrel-Ziel
dotnet publish JPRISE/JPRISE.csproj \
-c Release \
-r linux-x64 \
--self-contained false \
-o publish/linux
Auf dem Linux-Host genügt dotnet JPRISE.dll hinter einem Reverse Proxy (oder Kestrel direkt mit ASPNETCORE_URLS).
Deployment nach jprise.info (IIS)
-
Client-Secret als System-Environment-Variable auf dem Windows-Server setzen. Doppel-Underscore (
__) = ASP.NET-Core-Konfigurationspfad-Separator, mappt aufOidc:ClientSecret.Variante A — PowerShell (als Administrator):
[System.Environment]::SetEnvironmentVariable( "Oidc__ClientSecret", "<SECRET-AUS-ZITADEL>", [System.EnvironmentVariableTarget]::Machine)Variante B — cmd (elevated):
setx Oidc__ClientSecret "<SECRET-AUS-ZITADEL>" /MVariante C — GUI:
sysdm.cpl→ Erweitert → Umgebungsvariablen → Systemvariablen → Neu.IIS sieht neue System-Env-Vars erst nach einem Neustart des Application Pools (oder
iisreset):Import-Module WebAdministration Restart-WebAppPool -Name "jprise.info" # oder hart: iisresetDie
WebAdministration-Cmdlets (Get-Website,Restart-WebAppPool, …) undappcmdlesen die zentraleapplicationHost.configund brauchen eine als Administrator gestartete Shell — sonst „Die Konfigurationsdatei kann aufgrund unzureichender Berechtigungen nicht gelesen werden". In PowerShell 7 ist das Modul zusätzlich explizit zu laden:Import-Module WebAdministration -UseWindowsPowerShell.appcmdliegt nicht im PATH und wird inpwshmit vollem Pfad aufgerufen:& "$env:windir\system32\inetsrv\appcmd.exe" list sites.Prüfen, dass die Variable gesetzt ist (neue Shell aufmachen):
[System.Environment]::GetEnvironmentVariable("Oidc__ClientSecret", "Machine") -
App-Pool stoppen in IIS-Manager (Site
jprise.info→ Stopp). -
SFTP-Upload des
publish/win/-Inhalts nachC:\inetpub\jprise.info\(gleicher Pfad wie SoulfulShack). Mountainduck-Mount ist verfügbar unter:/Users/edmond/Library/CloudStorage/MountainDuck-vmd157055.contaboserver.net–SFTP/C/inetpub/jprise.info/ -
App-Pool starten. Erster Request triggert OIDC-Discovery → bei Erfolg wird zur Anmeldung weitergeleitet.
-
Logs prüfen:
C:\inetpub\jprise.info\Logs\jprise-YYYYMMDD.log(Serilog rolling file).
Alternative zum Pool-Stopp/-Start (Schritte 2 + 4): Eine
app_offline.htmins Content-RootC:\inetpub\jprise.info\legen — das ASP.NET Core Module fährt die App beim Erscheinen der Datei kontrolliert herunter (vermeidet DLL-File-Locks während des Uploads) und beim Löschen wieder hoch. Das braucht keine IIS-Rechte, nur Schreibzugriff auf den Ordner (über den MountainDuck-Mount möglich) — also weder elevated Shell noch RDP:"<h1>Wartung</h1>" | Set-Content "C:\inetpub\jprise.info\app_offline.htm" # … publish/win/ hochladen … Remove-Item "C:\inetpub\jprise.info\app_offline.htm"
Troubleshooting
| Symptom | Ursache | Lösung |
|---|---|---|
unauthorized_client von Zitadel |
Client-Secret fehlt/falsch | dotnet user-secrets list bzw. IIS-Env-Var prüfen |
| Endlos-Loop nach Login | Cookie wird nicht akzeptiert (SameSite/Secure) | HTTPS verwenden; Cookie SameSite=Lax, Secure=Always ist gesetzt |
redirect_uri_mismatch |
URI nicht in Zitadel hinterlegt | exakte URL (Schema + Host + Port + Pfad) eintragen |
invalid_request / „requested redirect_uri is missing" |
lokale Callback-URL (localhost:5001) nicht in der LandingPage-Client-Config |
lokale Redirect-URIs in Zitadel nachtragen (siehe Abschnitt „Redirect-URIs") |
| 502.5 / Worker startet nicht | Hosting Bundle Version mismatch | dotnet --info auf Server prüfen, ≥ 10.0.8 erforderlich |
id_token fehlt beim Logout |
SaveTokens nicht aktiv |
bereits true gesetzt — Cookie nach Re-Login prüfen |
Anwendungs-Verwaltung (/anwendungen)
Authentifizierte CRUD-Seite für das Firmen-Anwendungsverzeichnis (DevExpress DxGrid, SQLite-Backend).
- Spalten: Typ, Anwendung, Hersteller, Link, Ansprechpartner, Melder, Administrator, Administrator (Stellvertreter).
- Export: Über die Grid-Toolbar (Button „Export") in vier Formaten — XLSX, XLS, CSV, PDF. Der Download wird direkt im
Browser ausgelöst. CSV exportiert mit
EncodeExecutableContent = true(Schutz vor CSV-Injection, da Admin-Namen frei eingegeben werden). - Anlegen/Bearbeiten/Löschen über das Popup-Edit-Formular bzw. die Command-Spalte.
Schema-Änderung (neue Spalte) — Ablauf
Die Anwendung ruft beim Start db.Database.Migrate() auf; Migrationen werden also automatisch und idempotent beim ersten
Request nach dem Deploy angewendet. Für eine neue Spalte:
- Property in
Data/Anwendung.csergänzen (z. B.public string Administrator { get; set; } = string.Empty;— der Default sorgt für leeren String stattNULL). - Migration generieren:
dotnet ef migrations add AddAdministratorColumns --project JPRISE/JPRISE.csproj --output-dir Data/Migrations - Generierte Migration prüfen — für SQLite mit bereits befüllter Tabelle muss jede
AddColumn-Operationnullable: false, defaultValue: ""haben, sonst schlägt die Migration auf den Bestandszeilen fehl. - Vor dem Deploy das rohe SQL gegenlesen (ändert nichts, gibt nur aus):
dotnet ef migrations script --project JPRISE/JPRISE.csproj --idempotent - Spalte an den UI-Stellen pflegen (Grid + Edit-Formular) — siehe
CLAUDE.md, Abschnitt „Anwendungs-Verwaltung", für die drei Code-Orte, die zusammen gepflegt werden müssen.
Backup vor dem ersten echten Migrate:
Migrate()ändert die Live-jprise.dbin-place. Dadeploy/deploy.shohne--deletersynct, bleibt die DB zwar erhalten — aber zieh dir vor dem Deploy einmal diejprise.dbüber den MountainDuck-Mount, bevor eine Migration sie anfasst.
Architektur-Hinweise für spätere Erweiterungen
- User-spezifische Sub-Pages: Eine neue Razor-Page unter
Components/Pages/anlegen; die globaleFallbackPolicy = RequireAuthenticatedUserschützt sie automatisch. Für rollenbasierten Zugriff inProgram.cszusätzlicheAddPolicy()-Calls plus[Authorize(Policy = "...")]auf der Komponente. - Claims-Mapping: Aktuell sind
name,email,preferred_usernameals Display-Name-Fallback verdrahtet (sieheMainLayout.razorDisplayName). Wenn Zitadel zusätzliche Claims liefert (z. B.role, Custom-Claims), reicht es,MapInboundClaims=false+ den vorhandenenTokenValidationParameterszu erweitern. - Backchannel-Logout: Zitadel unterstützt es; falls benötigt, kann in
Program.csein zusätzlicherMapPost("/backchannel-logout")ergänzt werden.