Änderungsprotokoll¶
Änderungen, die in Apps auf Basis von Coworkee Handlungsbedarf auslösen. Neueste zuerst.
Unveröffentlicht¶
Hinzugefügt¶
- OData:
Coworkee:OData:CaseInsensitive(CoworkeeODataOptions.CaseInsensitive, Standardtrue) legt fest, ob Namen in OData-URIs unabhängig von der Schreibweise passen ($filter=name eq 'x'); bisher war das immer an. Siehe OData. - OData:
AddODataEntity<T>(..., o => o.Key(c => c.IsoCode))registriert ein Set mit einer anderen Schlüsseleigenschaft beliebigen primitiven Typs; Einzelabfragen sind/odata/Countries('DE'). Datensatz-Anhänge, Social, Workflows, Entity-Historie und Webhook-Entity-Ereignisse weisen Entities ohne Guid-Idbei der Registrierung ab; Client-Entities überspringen solche Sets. Siehe OData. - OData: berechnete Eigenschaften (
ODataComputedProperty) lassen sich auswählen ($select=ReferenceNumberDisplay) und kommen dann mit ihrem Wert zurück; ein Ausdruck über die eigenen Spalten der Zeile funktioniert auch auf Sets externer Datenbanken. Siehe OData. - Die tägliche Benachrichtigungs-Zusammenfassung lässt Benachrichtigungen aus, die schon als eigene Mail hinausgingen.
- Benachrichtigungen: Typen mit Kategorien und Einstellungen pro Benutzer (
AddNotificationTypes, Umfang Off/All/Followed, in der App und per E-Mail,cw.NotificationPreferences), Entitäten verfolgen (Followable<T>,cw.NotificationFollows,CoworkeeFollowButton),INotifier.PublishAsync(NotificationMessage)für Empfänger, Verfolger und ein Publikum, E-Mails pro Benachrichtigung (VorlageNotifications.Notification) mit Verzögerung und Entprellung (cw.PendingNotificationMails, Jobcoworkee-notification-mails), Erledigt-Status, Zahlen pro Kategorie und Suche im Eingang (Reiter Eingang, Einstellungen, Gefolgt auf/notifications), neueICoworkeeApi-Member. Apps brauchen eine Migration. Siehe Realtime. - Kommentare:
Status(None,Open,Closed) und App-Properties,PUT /api/v1/comments/{id}/statusmit der neuen BerechtigungSocial.Comments.SetStatus,SocialOptions.ValidateComments(eine Prüfung vor jeder Änderung), das Domain-EventCommentChanged(nach dem Commit).CoworkeeCommentszeigt den Status, bietet ein Status-Menü und nimmtNewStatusfür neue Kommentare;ISocialApi.SetCommentStatusAsyncundAddCommentAsync(..., CommentRequest).Commentist jetzt ein Aggregate Root; Apps brauchen eine Migration. Siehe Social. - Exporte:
GET /api/v1/data/{set}/export/streamstreamt Fortschrittszeilen (NDJSON) und endet mit einem einmaligen anonymen Download-Link (fünf Minuten); ein Client, der vorher geht, bekommt die Datei per Mail.POST /api/v1/data/{set}/export/mailmailt einen Export an den Nutzer und weitere Empfänger. Geplante Exporte (cw.ExportSchedules, BerechtigungDataTransfer.ExportSchedules, Jobcoworkee-export-schedules): Export planen im Export-Menü der Tabelle, Seite Geplante Exporte,IDataTransferApi-Member, Mail-VorlageData.Export,IExportMailer.ReportSchedulehatEveryundWeek(monatlich am letzten Freitag), auch für Berichtsabos (neue Spalten). Apps brauchen eine Migration. Siehe Import und Export. - Temporäre Dateilinks:
POST /api/v1/files/{id}/tokenundPOST /api/v1/attachments/{type}/{id}/{slot}/{attachmentId}/tokenliefern ein Token, das die Datei fünf Minuten lang ohne Anmeldung öffnet (/api/v1/files/token/{token}/{name}, inline oder?download=true); Temporären Link kopieren auf der Seite Dateien,IFilesApi.CreateTokenAsync,IAttachmentsApi.CreateTokenAsync.IAccessTokens(Tabellecw.AccessTokens, einmalig oder bis zum Ablauf, nur Hashes gespeichert) für eigene anonyme Links. Apps brauchen eine Migration. Siehe Dateien. CodeEditor.Highlight: ein regulärer Ausdruck, dessen Treffer hervorgehoben werden; der Mail-Inhaltseditor hebt{{ ... }}hervor.- Mail-Inhaltseditor: Platzhalter sind Chips mit ihrem Namen, die visuelle Werkzeugleiste hat die Mail-Formatierungen und Platzhalter einfügen, Strg+Leertaste öffnet die Platzhalterliste in beiden Modi,
{{wirkt im Visuell-Modus wie{. Siehe Mailing.
Geändert¶
CodeCompletion.InsertTextist jetzt der ganze Text samt Auslöser ({{ name }}stattname }}): Die Auswahl ersetzt, was vomCompletionTriggergetippt ist, und die schließenden Klammern, die Monaco ergänzt hat, und Strg+Leertaste fügt überall einen vollständigen Text ein. Neues FlagSnippetfür Monaco-Snippets ({{ if $1 }}). Eigene Vorschläge fürCodeEditorbrauchen den Auslöser im Text.- Mail-Inhaltseditor: Inhalte, deren Markup der visuelle Editor ändern würde (andere Styles als
color,background-colorundtext-align, Tags über Textformatierung hinaus, Kommentare), öffnen sich im Quelltext, eine gestaltete Schaltfläche geht beim Speichern also nicht mehr verloren. Die mitgelieferten Vorlagen mit Schaltfläche öffnen sich im Quelltext.
Behoben¶
- Der visuelle Mail-Editor zeigte eine leere Werkzeugleiste:
RichTextEditorübergabTools="null", was MudExRichTextEditor 9.5.5 als „keine Werkzeuge“ liest. Er übergibtToolsjetzt nur, wenn gesetzt. - Quelltext-Modus des Mail-Editors:
{{tippen und einen Platzhalter wählen ergab{{ name }}}}, weil Monaco die Klammern schon geschlossen hatte. - Sicherheit, OData: Zeilenfilter und Set-Berechtigungen gelten jetzt auch für Navigationen.
$expandzeigt nur die Zeilen, die das Ziel-Set dem Benutzer auflistet (eine ausgeblendete einzelne Navigation istnull),$filtermitany/all/$countund$orderbyüber eine Navigation sehen nur sichtbare Zeilen, auch in Exporten,query_data, Facettenfiltern,ODataEntityAccess.FilteredAsyncund der globalen Suche.$expandin ein Set ohne dessen Berechtigung antwortet403, in einen Typ, dessenIODataEntityFilter<T>mehr alsWheretut (OrderBy,Take, eine neue Abfrage),400. Solche Filter sollten einfacheWhere-Aufrufe werden, wenn ihr Typ über Navigationen erreicht wird.ODataNavigationScopewendet die Regeln auf eigene Abfragen an. Siehe OData. - Sicherheit, Berichte: Felder über eine Navigation (
Customer/Name) gruppieren und aggregieren nur über die Zeilen, die das Ziel-Set dem Benutzer zeigt; ein ausgeblendetes Ziel zählt als leer. Bisher galt nur die Berechtigung des Sets. Tabellen-Widgets und Abo-Tabellen lesen sie genauso. - OData:
HideundHideUnlesswirken für Member, die eine Basisklasse deklariert, auch wenn das EDM den Basistyp zwischen Sets teilt (eine Navigation zeigt auf die Basis): der Member bleibt im Modell und wird nur auf dem ausblendenden Set abgewiesen (403), mit einer Warnung beim Start. Geschützte Eigenschaften werden nach dem eigenen Typ der Zeile geprüft, so dass einHideUnlessauf einer geerbten Eigenschaft in Listen und expandierten Zeilen hält. Siehe OData. - OData-Antworten mit
400tragen ODatas eigene Meldung indetail; andere Abfragefehler (EF kann die Abfrage nicht übersetzen, was Zeilenfilter zitieren kann) kommen ohne Detail und werden protokolliert. - Sicherheit, OData: die Prüfungen geschützter Eigenschaften, der
$expand-Berechtigungen und der Navigationsfilter lesen die Optionen so, wie OData sie parst; ein Schlüssel mit Leerzeichen (?+$filter=,%20$expand=) umging sie alle. Parameter-Aliase (@a=Size) und$applyüber Navigationen antworten400.[AutoExpand]-Navigationen folgen ihrem Ziel (ein ausgeblendetes oder verbotenes Ziel istnulloder leer). - Sicherheit, OData: Facetten zählen auf den eingeschränkten Zeilen (ein
$filterüber eine Navigation zählte ausgeblendete Zeilen mit), und eine Facettenbeschriftung über eine Navigation (LabelPath = "it.Customer.Name") zeigt den Schlüssel für Zeilen, die der Benutzer nicht sehen darf. - Sicherheit, OData: berechnete Eigenschaften mit
Permissionwerden auch hinter Navigationen abgewiesen ($expand=Bins($select=Volume),$filter=Featured/Volume gt 0) und in expandierten Zeilen weggelassen;Hideauf einer Navigation einer geteilten Basis hält auf Zeilen, die über die Basis erreicht werden. - OData: zwei gefilterte Referenzebenen (
Invoice/Shipment/Customer) und eine Sammlung unter einer ausgeblendeten Referenz lassen sich übersetzen; ein Zeilenfilter, der auf der In-Memory-Abfrage scheitert, aus der der Scope ihn liest, blendet nur seinen eigenen Typ aus, und geprüft werden nur die Navigationsziele, die eine Abfrage erreicht.ODataNavigationScope.For(services, type)mitApplyAsyncersetztForAsyncundApply; Berichte prüfen jedes Ziel einmal pro Abfrage. - OData:
/odata/<Set>/$countantwortete500; die Seitengröße des Modells ([Page]) gilt auf Entity-Sets wieder. - Globale Suche: Felder über eine Navigation (
t => t.Bin!.Name) werden in der Abfrage mit den Regeln der Navigation gelesen; ein ausgeblendetes Ziel lässt das Feld leer. Bisher las der Titel sie aus der geladenen Zeile, in der Navigationen nicht geladen sind. - Berichte auf SQLite gruppieren
DateTimeOffset-Felder eines Coworkee-DbContext nach Zeiträumen (Jahr, Quartal, Monat, Woche, Tag, in UTC); bisher wurden sie abgelehnt. Der DbContext speichert sie als Zahlen; die Zeiträume werden daraus berechnet. - Sicherheit, OData: die Prüfungen geschützter, ausgeblendeter und berechneter Eigenschaften laufen auf der gebundenen Abfrage statt auf ihrem Text:
Featured/ Size(ein Leerzeichen nach/) und$compute=Owner/ Code as Cumgingen sie, und Literale mit einem Eigenschaftsnamen ('a/Note') wurden abgewiesen. Export, Berichtsfilter, Facettenfilter undquery_datanutzen dieselbe Prüfung.$search(Listen und Exporte) sucht nur in Eigenschaften, die der Benutzer lesen darf. Facetten, deren Wert über eine Navigation gelesen wird, fallen weg.$applyüber Navigationen wird nur zu Zeilen mit Zeilenfiltern oder Berechtigungen abgewiesen. - Sicherheit, OData: Aggregate über Entity-Sets in
$apply(aggregate(Employees(Salary with max as M))) lasen geschützte Eigenschaften an den Prüfungen vorbei; jetzt wird jeder Member geprüft, den ein geparster Knoten hält, und eine Abfrage, die die Prüfung nicht parsen kann, antwortet400.ISearchable-Suchfelder über eine Navigation folgen den Regeln des Ziels. Facetten über eine Navigation fallen nur weg, wenn das Ziel einschränkt oder der Wert gesperrt ist; Schlüssel und Ziele ohne Regeln bleiben. Ein Member einer geteilten Basis, der auf einem Set ausgeblendet ist, bleibt auf den anderen lesbar. - Sicherheit, OData:
ISearchable-Suchfelder werden an jedem Member geprüft, den sie lesen (Verkettungen, Methodenaufrufe), und über Navigationen zu gefilterten Zielen weggelassen; eine Facette über den Schlüssel des Ziels braucht den eigenen, gemappten und lesbaren Fremdschlüssel der Zeile auf den EF-Schlüssel, sonst folgt sie den Regeln des Ziels; Facetten über eine Sammlung werden beim Start abgewiesen; ein Abfrageknoten, den die Prüfung nicht lesen kann, antwortet400. Berichte auf SQLite weisen Zeiträume desDateTimeOffseteines externen Kontexts sauber ab. AddODataImportmit Id (Aktualisieren, Upsert) weist ein Set, dessen Schlüssel keine Guid ist, bei der Registrierung ab und aktualisiert Zeilen von Sets mit einem Guid-Schlüssel außerId.AddExtendedAttributesweist eine Entity ohne Guid-Idab.- Sicherheit, OData: Abfrageoptionen ohne
$(filter=,orderby=) werden nicht mehr gelesen; sie umgingen die Prüfung geschützter Eigenschaften. Clients senden$filter.
Sicherheit¶
- Die Exportgrenze pro Benutzer gilt für gestreamte, Hintergrund- und gemailte Exporte und wird unter einer Sperre pro Benutzer geprüft. Liegengebliebene Exporte zählen ab
StartedAt(neue Spalte voncw.DataTransferJobs, derenStatusjetzt ein Concurrency-Token ist) plus einem Puffer; ein Export-Job führt nur wartende Jobs aus und behält einen Fehlschlag, den die Aufräumung inzwischen schrieb. Benachrichtigungsmails gehen nur an aktive Adressen; verzögerte behalten den neuesten Text, wenn die Outbox eine ältere wiederholt. Apps brauchen eine Migration. - Anonyme Token-Links (temporäre Datei- und Anhang-Links, Export-Downloads) sind pro Adresse in der Rate begrenzt (Policy
tokens, 120 pro Minute), und ihre Tokens sind in den Logs von ASP.NET Core, HttpClient und YARP (samt Scopes) und in den Activities ein- und ausgehender sowie weitergeleiteter Anfragen maskiert (***), in API und BFF;Coworkee.Core.Diagnostics.SecretPathsfür eigene Hosts und Logs. Coworkee.Bff verweist jetzt auf Coworkee.Core. Abgelaufene Tokens löscht das Aufbewahrungszielaccess-tokens. - Exporte: gestreamte Exporte haben eine Grenze pro Benutzer (
Coworkee:OData:MaxConcurrentExports, 429), übergeben Exporte überBackgroundExportRowsan den Export-Job und brechen nachCoworkee:OData:ExportTimeoutab; liegengebliebene laufende Exporte schlagen fehl. Geplante Exporte senden nichts mehr, sobald der Besitzer deaktiviert ist oderDataTransfer.ExportSchedulesoder die Berechtigung des Sets verloren hat, und Exporte ausgewählter Gruppen schlagen fehl, wenn keine davon mehr zugänglich ist, statt alle zu listen. - Benachrichtigungen erreichen Verfolger nur, solange sie die Entität noch sehen (jeder Verfolger wird gegen das
Followable<TEntity>-Set geprüft); Verfolger vonFollowable(string)-Typen nur über einAudience. Wartende Benachrichtigungsmails werden über die Outbox geschrieben, nachdem der sendende Befehl festgeschrieben ist, sind pro Benutzer, Typ und Entität eindeutig, werden nach Version übernommen und lassen den Befehl nie scheitern. Der Jobcoworkee-stale-exportslässt Exporte scheitern, die länger alsExportTimeouthängen; ohne Retention-Modul werden abgelaufene Zugriffstokens gelöscht, sobald ein anderes Token eingelöst wird. - Kommentare: Einen Status zu ändern, auch als Autor oder beim Wiederöffnen, braucht
Social.Comments.SetStatus; Autoren geben einen Status nur beim Schreiben an.
0.0.1.41 (2026-10-27)¶
Hinzugefügt¶
- Gruppenbaum und Geltungsbereich: Gruppen haben eine übergeordnete Gruppe und einen
Code-Pfad (anlegen unter einer übergeordneten, verschieben mitPUT /api/v1/identity/groups/{id}/parent, Löschen entfernt den Teilbaum), Mitglieder lassen sich hinzufügen, entfernen und verschieben,IGroupScope(Coworkee.Application.Groups) liefert Filtern die zugänglichen, ausgewählten und aktiven Gruppen,<GroupScopePicker />lässt Benutzer Listen auf einzelne Gruppen einschränken,AddGroupDetailTabergänzt Reiter auf der Gruppenseite, Gruppenimport über den Pfad der übergeordneten Gruppe. Neue BerechtigungenIdentity.Groups.ManageMembers(inManageenthalten) undIdentity.Groups.ScopeAll; siehe Identität. - Externe Datenbanken: mit
AddCoworkeeExternalDbContext<TContext>()lesen OData-Sets, Zeilenfilter, Suche, Export, Berichte und Client-Entities Entities eines zweiten, einfachenDbContext, und die Unit of Work speichert ihn; Client-Entities nehmenChangedAt-Datumsspalten für Entities ohneIAuditable. Siehe Persistenz. - Users-OData-Set:
Groups-Mitgliedschaften, FilterGroups/any(m: m/GroupId eq {id}). - Gruppenseite: Schaltflächen für Import (
Groups.Manage) und Export. Das SetGroupshat ein berechnetesParentPath(„Kunde A/Hamburg“), und der Export schreibtName,DescriptionundParentPathin Baumreihenfolge, so dass sich die Datei wieder importieren lässt, auch in einen anderen Mandanten. - Berechnete OData-Eigenschaften mit
Exported = truesind Spalten des Exports;ComputedQueryRoot(aus Workflows verschoben) undODataComputedProperty.Inlinehelfen, Unterabfragen darin zu schreiben. - Als anderer Benutzer anmelden: Berechtigung
Identity.Users.Impersonate, Anmelden als in der Benutzerliste und auf der Benutzerseite, Zurück zu meinem Konto im Benutzermenü.POST /api/v1/identity/users/{id}/impersonationstellt ein Ticket für eine Minute aus,/bff/impersonateund/bff/stop-impersonationwechseln die Anmeldung über die Auth-Server-Seiten/Account/Impersonateund/Account/StopImpersonation. Tokens tragenimpersonatorundimpersonator_name,/bff/userliefertImpersonatorIdundImpersonatorName.[NotWhileImpersonating]lehnt einen Request während einer Übernahme ab; siehe Identität. AuthClientOptions.AllowImpersonation(Coworkee:Auth:Clients:<n>:AllowImpersonation, standardmäßig aus): Nur solche Clients bedienen jemanden, der als anderer Benutzer angemeldet ist, die anderen antworten mitaccess_denied.AddWebsetzt es für den Web-Client; setze es für eigene BFF-Clients, die Anmelden als unterstützen sollen.AuthServerOptions.MaxImpersonationDuration(Coworkee:Auth:MaxImpersonationDuration, 8 Stunden): Eine Übernahme endet bei der nächsten Autorisierung oder dem nächsten Refresh danach. Sie endet auch, wenn sich der Aufrufer überall abmeldet, sein Passwort ändert, gesperrt wird oder die Regeln nicht mehr gelten.AuditQuery.ImpersonatorId(GET /api/v1/audit?impersonatorId=...);ImpersonatedUser,JobEnvelopeundIntegrationMessagehaben eine optionaleImpersonatorId.
Geändert¶
- Apps brauchen eine Migration (
dotnet ef migrations add GroupTree):cw.GroupsbekommtParentIdundCode, der Name ist pro übergeordneter Gruppe statt pro Mandant eindeutig, neue Tabellecw.GroupSelections. Der Migrationshost vergibt den vorhandenen Gruppen ihre Codes. SetGroupMembersbrauchtIdentity.Groups.ManageMembersstattManage; Rollen mitManagefunktionieren weiter, es enthält die neue Berechtigung.ICoworkeeApihat neue Gruppen-Mitglieder (GetGroupChildrenAsync,SearchGroupTreeAsync,MoveGroupAsync,AddGroupMembersAsync,RemoveGroupMembersAsync,MoveGroupMembersAsync,GetMyGroupsAsync,SelectGroupsAsync): eigene Implementierungen und Test-Fakes kompilieren erst wieder, wenn sie sie ergänzen.PUT /api/v1/identity/groups/{id}mit einer anderenParentIdals der aktuellen übergeordneten Gruppe antwortet mit 400; verschoben wird eine Gruppe mitPUT /api/v1/identity/groups/{id}/parent. FehltParentId, bleibt die übergeordnete Gruppe.IUnitOfWorkliefert eine Hülle, die die externen Contexts und dann den Context der App speichert, nicht mehr den DbContext selbst; Code, der es in einen DbContext umgewandelt hat, lässt sich stattdessen den Context geben.- Aspire 13.6.4 (App-Host-SDK,
Aspire.Hosting.*,Aspire.Hosting.Testing), CommunityToolkit-Aspire-Pakete 13.6.1, Nextended 10.1.48;coworkee updatehebt die festgelegten Pakete an. - Apps brauchen eine Migration (
dotnet ef migrations add Impersonation):cw.AuditEntriesundcw.OutboxMessagesbekommen die SpalteImpersonatorId(nullable). ICoworkeeApihat das neue MitgliedStartImpersonationAsync,ICurrentUserdas StandardmitgliedImpersonatorId(null): Eigene Implementierungen vonICoworkeeApiund Test-Fakes kompilieren erst wieder, wenn sie es ergänzen; eigeneICurrentUser-Implementierungen, die Claims lesen, sollten den Claimimpersonatorliefern.- Ist jemand als anderer Benutzer angemeldet, antworten die Konto- und die Zustimmungsseite des Auth-Servers mit 403, und das Ändern der eigenen E-Mail-Adresse, das Anlegen von API-Schlüsseln, der Export der persönlichen Daten, das Löschen des eigenen Kontos, die Wahl der aktiven Rolle sowie das Anlegen und Ändern von Webhooks und Bericht-Abonnements werden abgelehnt (
auth.impersonating). IsLocalPathdes BFF (/bff/login,/bff/impersonate,/bff/stop-impersonation) lehnt Rücksprungadressen mit Steuerzeichen oder Backslashes ab.[Realtime]sendet weiterhin an alle mit der Ansichts-Berechtigung; sind Gruppen Kunden in einem Mandanten, sehen Clients anderer Einheiten Datensatz-IDs und geänderte Eigenschaftsnamen. Themen pro Gruppe folgen.
0.0.1.40 (2026-10-25)¶
Behoben¶
- Kibana: Die Anmeldung endete mit 500 auf
/oauth2/callback(„id token issued by a different provider"); oauth2-proxy erwartet den Issuer mit dem abschließenden Schrägstrich, den der Auth-Server inissschreibt.
0.0.1.39 (2026-10-25)¶
Hinzugefügt¶
- Open WebUI:
app.AddOpenWebUI(ollama)fügt einen Chat über die Modelle von Ollama hinzu, mit Anmeldung am Auth-Server wie die anderen Tools; Template-OptionopenWebUI(--open-web-ui, mit--ai-provider ollama), eine Karte im Studio und Teil der Vorlage Everything; Deploy-Toolopenwebui.
Geändert¶
- Grafana, WebDataStudio und AspireUI melden sich am Auth-Server der App an, mit oder ohne Keycloak, wie der Proxy von Kibana: je ein Client
{app}-{tool}, nur Administratoren der Systemorganisation, als Admins des Tools. Die lokale Administrator-Anmeldung bleibt. Clients im Keycloak-Realm bekommen sie nicht mehr. - Auth-Server: Benutzerinfo-Endpunkt
/connect/userinfo(sub, email, name, role, tenant).Coworkee:Auth:DynamicBackchannelnennt An- und Abmeldung nach dem Issuer und Token, Benutzerinfo und Schlüssel nach der gefragten Adresse; so erreichen Tools in Containern ihn über das Container-Netz, der Browser über die Host-Adresse. Der App-Host setzt es mit dem Issuer in einem lokalen Lauf. - Grafana 13.2.4 (Nextended 10.1.47); Datenquellen und Volumes von 11.5 funktionieren weiter. Handlungsbedarf: Ein App-Host, der
Nextended.Aspire.Hosting.AspireUIpinnt (OptionaspireUI), hebt den Pin auf 10.1.47, sonst scheitert das Restore mit NU1605. - Studio: Karten von Optionen, die gerade keine Rolle spielen, bleiben sichtbar, deaktiviert, mit dem Grund als Tooltip; Kibana steht direkt nach der Suche und gehört zur Vorlage Everything.
Behoben¶
- Auth-Server: Clients mit
response_mode=form_post(der OpenID-Connect-Handler von ASP.NET Core, WebDataStudio) bekamen nach der Anmeldung eine leere Seite; die Content Security Policy blockierte das Script, das die Antwort weiterschickt. Sie erlaubt genau dieses Script jetzt über seinen Hash. - Kibana: Die Anmeldung eines lokalen Laufs schickt den Browser an die Host-Adressen von Auth-Server und Proxy; sie ging an
aspire.dev.internal, das nur Container kennen. Das Dashboard verlinktkibana-servernicht mehr, darüber kam jeder ohne Proxy herein. - AspireUI: Ein lokaler Lauf gibt ihm wie ein Deployment das Volume
{app}-aspireui-data. Über das geteilteaspireui-data-aspireuisetzte die zuletzt gestartete App die Keycloak-Anmeldung aller anderen (404 auf/realms/<andere App>/.well-known/openid-configuration). Das alte Volume bleibt; löschen mitdocker volume rm aspireui-data-aspireui, sobald es nichts mehr nutzt.
0.0.1.38 (2026-10-25)¶
Neu¶
- Themes: sechs neue eingebaute Themes, 21 insgesamt, die sich auch darin unterscheiden, wo das Logo sitzt: GitHub (dunkler Header mit Logo, kein Logo im Menü), LinkHub (Pillen-Schaltflächen per eigenem CSS, Logo nur im Menü), Terminal (gar kein Logo), Slate (dunkle Seitenleiste mit Logo, schlichter Header), Ember und Mint (Logo nur im Header). Eingebaute Themes können jetzt eigenes CSS mitbringen, App-Themes übernehmen es von ihrer Basis. CLI/Template
--theme, Studio-Karten. Siehe Eingebaute Themes. - Kibana:
options.UseKibana()stellt Kibana neben den Elasticsearch-Container (Template- und CLI-Optionkibana, Studio-Karte Kibana, Deploy-Toolkibana). Davor meldet oauth2-proxy am Auth-Server der App an, mit Keycloak, wenn es an ist; Kibanas eigenes Single Sign-on bräuchte eine kostenpflichtige Elastic-Lizenz. Neu istAuthClientOptions.SystemAdministratorsOnly: Bei so einem Client lässt der Auth-Server nur Administratoren der System-Organisation herein. Siehe Kibana. - Backups: ein heruntergeladenes Zip wieder hochladen (Backup hochladen auf
/admin/backupsund in der AirWorks-Backup-App,POST /api/v1/backups/upload?name=,UploadBackupCommand); es steht in der Liste wie ein hier erstelltes Backup und wird genauso wiederhergestellt. Siehe Datenbank-Backups.
Korrekturen¶
- Berichtsliste: Favoritenstern und Menü einer Berichtskachel stehen auf einer Linie.
0.0.1.37 (2026-10-20)¶
Breaking Changes¶
- Migration nötig in Apps mit
Coworkee.Reporting(das Template hat es standardmäßig): die neuen Tabellencw.ReportSubscriptions(Berichtsabos) undcw.OutgoingMailAttachments(Mailing, Dateien eingereihter Mails).CoworkeeReportingModulehängt jetzt vonCoworkeeMailingModuleab. DateRangesist vonCoworkee.Client.Blazor.Reporting.StatenachCoworkee.Contracts.Reportinggezogen (der Server nutzt es auch); wo es direkt aufgerufen wird, dasusingergänzen.- MudBlazor.Extensions 9.11.6: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.6.
Neu¶
- Berichte: Abos per Mail. Per Mail abonnieren an einem Bericht schickt ihn täglich, wöchentlich oder monatlich zu einer Uhrzeit einer Zeitzone, auf Wunsch mit eigenem Zeitraum und eigenen Facetten; die Mail (
Reports.Subscription, alle Sprachen) in der Sprache des Abonnenten (sonst der Standardsprache der Organisation) zeigt Kennzahlen mit ihren Schwellenwert-Farben und die ersten Zeilen der übrigen Widgets (bei Zeitreihen die neuesten Zeiträume), eine Excel-Arbeitsmappe enthält alle Zeilen, ein Link beendet das Abo. Der Jobcoworkee-report-subscriptionsführt jedes als der Abonnent aus, seine Berechtigungen und Zeilenfilter gelten. Berichte › Abos listet sie; die neue BerechtigungReports.Subscriptionszeigt die der Organisation. Mailing: Anhänge (MailAttachment,IMailSender.QueueWithAttachmentsAsync,MailEnvelope.Files) für alle vier Anbieter; eigeneIMailTransports, dieFilesnicht beachten, funktionieren weiter ohne sie. Siehe Abos per Mail. - Berichte: Berichte mit KI erstellen und ändern. Mit KI erstellen in der Berichtsliste und KI bitten, diesen Bericht zu ändern im Designer schicken einen Prompt an
POST /api/v1/reports/generate(GenerateReportDefinitionQuery,Reports.Manage, braucht ein KI-Modell): der Server baut die Anweisungen aus den Quellen, die der Benutzer lesen darf, prüft die Antwort wie beim Speichern und fragt mit den Fehlern erneut (höchstens zweimal); die Definition öffnet sich ungespeichert im Designer (reports/newfür einen neuen, Rückgängig für eine Änderung). Neues Assistenten-/MCP-Werkzeuggenerate_report;create_reportundupdate_reportwerden dem Assistenten jetzt angeboten (sie laufen nach der Bestätigung des Benutzers). Siehe Einen Bericht mit KI erstellen. - Berichte: Schwellenwerte.
WidgetOptions.Thresholds(ThresholdStop: Startwert, Theme-Farbesuccess/warning/error/infooder#rrggbb, Bezeichnung) färben Werte und Tachos von Kennzahlen, Pivot-Zellen, die TabellenspalteThresholdColumnund mitColorBarsByThresholdBalken; jedes Band zeigt zusätzlich ein Symbol und seine Bezeichnung. Der Designer hat einen Bänder-Editor mit Bänder aus dem Ziel. Siehe Schwellenwerte. - Berichte: Umschalter für Betrachter.
WidgetOptions.Metrics(MetricView: Bezeichnung, Aggregate, Zahlenformat, zusätzlicher Filter) undWidgetOptions.Buckets(BucketView: Bezeichnung, Gruppierungsfeld, Datumsstufe, Datumsfeld) bieten Alternativen, die Betrachter im Kopf von Kennzahlen, Diagrammen, Pivots und Tabellen wählen; der Bericht bleibt unverändert, Exporte nutzen die Wahl, und die Wahl bleibt je Benutzer in der neuen BenutzereinstellungReports.Viewserhalten. Beim Speichern wird jede Alternative geprüft. Siehe Umschalter für Betrachter. - AirWorks: Vollbild-App-Starter.
Coworkee:AirWorks:Launcherdes AirWorks-Hosts (menu, Standard, oderoverlay) erreicht den Desktop alslaunchervon/bff/user; das Template schreibt es in dieappsettings.jsondes Hosts (OptionairworksLauncher, CLI--airworks-launcher, Studio-Karte AirWorks-App-Starter). Der Starter listet die Apps der Administration unter Administration und andere Apps unter ihrerlauncherGroup(neu anCoworkeeApp,entityAppundembedApp, das auchshortcutnimmt;groupbleibt der Bereich der Administration), mit Kontextmenü (Öffnen, Zum Desktop hinzufügen) und Fokusfalle. Bestehende AirWorks-Clients setzen@fgilde/airworksinClientApp/package.jsonauf Commit 9657ff3 (AirWorks 0.6.0), um ihn zu nutzen. Siehe App-Starter.
0.0.1.36 (2026-10-20)¶
Breaking Changes¶
- MudBlazor.Extensions 9.11.5: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.5.
Korrekturen¶
- Navigation: der hinter den Filter-Chips getippte Filtertext bleibt beim Öffnen einer Seite im Filterfeld stehen; bisher wurde das Feld geleert, während der Filter weiter galt. Die Korrektur kommt mit MudBlazor.Extensions 9.11.5.
0.0.1.35 (2026-10-20)¶
Breaking Changes¶
- Anhänge an Datensätzen:
IRecordAttachments.PutAsync,DeleteAsyncundDeleteAllAsyncspeichern nicht mehr selbst, sie gehören zur Unit of Work des Aufrufers (die Pipeline speichert nach einem Command, anderer Code ruftIUnitOfWork.SaveChangesAsync). Blobs zu nie committeten Zeilen werden am Ende des Scopes gelöscht, Blobs entfernter Zeilen nach dem Commit. App-Code, der sie außerhalb eines Commands aufrief, speichert jetzt selbst; lesende Aufrufe (OpenAsync,ListAsync) sehen nur gespeicherte Zeilen. Die HTTP-Endpunkte speichern wie bisher. Siehe Anhänge an Datensätzen. - Migration nötig in Apps mit
Coworkee.Social: die neue Tabellecw.Reactions(Emoji-Reaktionen). Error.Validation(field, message).Messageist die übergebene Meldung statt One or more validation errors occurred.; ebenso dertitleder 400-Problem-Details.code(validation) underrorsbleiben gleich; die Überladung mit dem Dictionary behält den allgemeinen Text.- Migration nötig in Apps mit
Coworkee.Workflows: die neue SpalteRequestedByvoncw.WorkflowApprovals(wer den Übergang in die Genehmigung ausgelöst hat). Die Eskalation einer Genehmigung mitNotByRequester()nimmt die antragstellende Person nicht mehr als Genehmigende auf, wenn sie in der Eskalationsrolle ist; vor der Migration gestartete Genehmigungen haben keine gespeicherte antragstellende Person und werden nicht gefiltert.
Korrekturen¶
coworkee entity add/module add: generierte Listenseiten zeigen bool-Spalten als übersetztes Ja/Nein, Enum-Spalten mit ihrer übersetzten Bezeichnung (EnumLabels),DateTimeOffset-Spalten in Ortszeit undDateOnly-Spalten als kurzes Datum in der Kultur des Benutzers, statt True, Membernamen und+00:00-Offsets. Bestehende generierte Seiten können dieselbenCellTemplates übernehmen.- Lokalisierung: 57 Texte der Bibliothek, die in jeder Sprache englisch erschienen, gibt es jetzt in allen 22 Sprachen: der Einrichtungsassistent (Beschriftungen, Schritte, Hinweise, Meldungen), die Beschreibungen der Seiten Berichte, Chat und Berichtsdaten, Noch keine Berichte, Chat mit {0}, die Berechtigungsmatrix und die Ressourcenberechtigungen (Berechtigungen durchsuchen, Benutzer oder Gruppe, ...), Versionsverlauf, Audit-Zeitleiste, Theme-Auswahl, Meine Einstellungen und die Schalter der Navigation. Der Test der literalen Schlüssel prüft auch
l[...], beide Texte vonL[a ? "…" : "…"]und die Texte, die anPageHeaderundCoworkeeDataTablegehen. - Reporting: Berichtsnamen und -beschreibungen, Widget-Titel und Wertbeschriftungen zeigen ihre Übersetzung, wenn die App einen Text dafür hat (geseedete Berichte lassen sich lokalisieren; der gespeicherte Text bleibt der Schlüssel,
CoworkeeLocalizer.Stored); ein Widget aus einer Vorlage speichert den Schlüssel der Vorlage statt des übersetzten Titels. Die Summe im Donut und die Ziellinie sind übersetzt, und Kategorien der x-Achse drehen sich um 45°, statt abgeschnitten zu werden ("2…", "Back…"). Siehe Reporting. CoworkeeDataTable: Spaltenköpfe behalten ihre Beschriftung auf einer Zeile, eine breite Tabelle scrollt also seitlich, statt ihre letzten Spalten ("Kostenstell", "Stat") unter die fixierte Aktionsspalte zu quetschen.- Layout: der App-Titel in der App-Leiste bleibt einzeilig und endet auf schmalen Bildschirmen mit Auslassungspunkten, statt umzubrechen.
CoworkeeWorkflow: das Fälligkeitsdatum einer Genehmigung behält seinen Abstand zum Wartetext ("…Personen. Fällig 25.10.").- Aspire:
AddMigrationsverdrahtet die Module, die das Migrationsprojekt referenziert, wie bei jedem Dienst (Speicher, Mail, Redis, Suche, Übersetzung, Einträge inapp.Modules); Dateien eines Seeds landeten vorher im lokalen Ordner des Migrations-Hosts und öffneten sich in der App mit 404. App-Hosts, dieCoworkeeModules.Storagevon Hand in die Migrationen verdrahtet haben, können diese Zeile streichen. - Dialoge: ein Seitenblatt oder Dialog der Bibliothek, geöffnet aus einem Dialog, den die App mit dem einfachen MudBlazor-
ShowAsynczeigt (etwa die Dateivorschau vonRecordAttachment), blieb unsichtbar; MudBlazor.Extensions wandte seine Optionen auf den äußeren Dialog an. Das Layout markiert jetzt die schon offenen Dialoge, wenn ein neuer hinzukommt. - Reporting: Diagramme über eine berechnete Quelle (
AddReportSource) mit mehreren Zeilen pro Kategorie und Serie schlagen nicht mehr fehl (die Gruppierung im Speicher machte aus jeder Zeile eine eigene Gruppe, das Diagramm warf dann wegen doppelter Schlüssel); Diagrammserien addieren doppelte Kategorien. - Datensatz-Anhänge: Mit
CreateScope()erzeugte Scopes (Startcode, das Elsa-Modul, ältere Dienste) lassen sich mit eingeschaltetem Anhangsmodul wieder freigeben (AttachmentBlobsist auchIDisposable). - Sicherheit, Social: Kommentare, Tags, Bewertungen, Reaktionen und ihre Echtzeit-Topics eines mit Entitätsklasse registrierten Typs (
Comments<T>, ...) verlangen jetzt, dass der Benutzer die Zeile über das OData-Set des Typs sieht (Berechtigung des Sets, Organisation, Soft Delete, ZeilenfilterIODataEntityFilter<T>); vorher reichten die Berechtigung und eine vorhandene Id. Tag-Zähler undGET /api/v1/tags/{entityType}/entitieszählen nur sichtbare Zeilen. Typen ohne OData-Set behalten die Id-Prüfung;Authorize(...)ergänzt weiterhin Prüfungen, Hooks, die nur den Zeilenfilter wiederholten, können entfallen. Siehe Social. - Workflows: Für eine Entität, die der Command in derselben Unit of Work hinzugefügt hat, lädt
WorkflowEngine.LoadAsyncdieInclude(...)-Referenzen über ihren Fremdschlüssel, wenn nur die ID gesetzt war;Describe, Guards und Aktionen sehen dort keinnullmehr. CoworkeeDataTable: die fixierte Aktionsspalte bleibt auf Zeilen unter dem Mauszeiger und gestreiften Zeilen deckend (die durchscheinende Zeilenfarbe liegt über der Oberfläche), langer Zellentext scheint nicht mehr unter den Knöpfen durch, im hellen wie im dunklen Modus.coworkee entity add/module add: Navigationseinträge, Seitentitel und Berechtigungsgruppen zeigen Namen aus mehreren Wörtern als Wörter („Benefit enrollments“, „Time off: Benefit enrollments“) statt des PascalCase-Plurals; Dialogtitel und Berechtigungsnamen taten das schon.coworkee entity add/module add: erzeugte Seiten gebenPageHeaderundCreateTextTextschlüssel statt übersetzter Texte, die ein zweites Mal übersetzt wurden („People“ → „Personal“ → „Persönlich“). Regel:PageHeaderundCoworkeeDataTable.CreateTextübersetzen, Aufrufer übergeben Schlüssel. Erzeugte Seiten bestehender Apps entfernen das@L[...]um diese Werte.coworkee entity add/module add: Referenz-Facetten bekommenLabelPath = "it.{Navigation}.{Anzeige}", damit eine Navigation mit dem Namen eines Dynamic-LINQ-Schlüsselworts (Parent,It,Root, ...) die OData-Menge nicht mehr mit 500 antworten lässt, wenn Facetten angefragt werden. Handgeschriebene Facetten auf so einer Navigation ergänzen dasit.selbst. Siehe OData.ResourceCalendar: ein Monat passt in die verfügbare Breite (schmalere Tagesspalten und Ressourcenspalte) und blendet am rechten Rand aus, wenn er scrollt; Zeilen wachsen mit einer zweizeiligen Ressourcenzelle, statt sie abzuschneiden; eine Zeile mitUserIdzeigt Avatar mit Anwesenheit plusTitleundSubtitlestatt der Benutzerkarte ohne Untertitel.- Sicherheit, Workflows: Zustand, Historie, Kommentare, Übergänge, Entscheidungen und das Echtzeit-Topic eines Workflows verlangen jetzt, dass der Benutzer die Zeile über ihr OData-Set sieht (Berechtigung des Sets, Organisation, Zeilenfilter
IODataEntityFilter<T>); vorher reichtenWorkflows.Viewund eine Id. Genehmigende der offenen Genehmigung der Zeile sehen und entscheiden sie weiterhin.WorkflowEngine.LoadAsyncnimmt ein optionalesWorkflowAccess(StandardSee). Siehe Workflows. - Vorlage:
App.razorbindet das Scoped-CSS-Bundle ein, damit.razor.css-Dateien des Web-Clients ohne Weiteres greifen (das Logo der Startseite nutzt eine). Bestehende Apps, deren Web-Client.razor.css-Dateien hat, ergänzen<link href="@Assets["MyApp.Web.styles.css"]" rel="stylesheet" />(mit dem Namen ihres Web-Projekts) nach dem Stylesheet von MudBlazor.Extensions. - API-Tests einer App mit
--event-bus rabbitMqstarten ohne Broker: neue EinstellungCoworkee:EventBus:RabbitMq:Enabled(Standardtrue); die Fixture der Vorlage setzt sie auffalse, die Events erreichen die Handler im Prozess. Bestehende Apps ergänzenbuilder.UseSetting("Coworkee:EventBus:RabbitMq:Enabled", "false");in ihrerApiFactory. Siehe Testen. coworkee spec validatelehnt eine Entität ab, deren Plural dem Namen gleicht (der erzeugte Feature-Namespace verdeckte den Entitätstyp, die App baute nicht).
Neu¶
- Anhänge an Datensätzen: Echtzeit-Topic
attachments:{entityType}:{entityId}(AttachmentTopics.Record) bei jedem hinzugefügten, ersetzten oder gelöschten Anhang, für Benutzer, die einen Slot des Datensatzes lesen dürfen;RecordAttachmentfolgt ihm und lädt neu. Abschnittattachmentsim Export personenbezogener Daten (die Uploads des Benutzers, nur Metadaten; das Löschen behält die Dateien wie beim Abschnittfiles). Siehe Anhänge an Datensätzen. - Workflows:
State(...).Approval(ctx => ..., rejected, a => a.ApprovedTo(...))wählt den genehmigten (und mit einer zweiten Funktion den abgelehnten) Zustand bei der Entscheidung aus der Entität, etwa eine zweite Genehmigung für große Beträge hinter einem einzigen Einreichen-Übergang; die möglichen Zustände stehen inApprovedTo/RejectedTound werden beim Start und bei jeder Entscheidung geprüft.ApprovalStep.ApprovedState/RejectedStatesind bei solchen Genehmigungennull;ApprovedTargets/RejectedTargetslisten die Zustände. Siehe Workflows. - Blazor:
CoworkeeDetailPanel<TKey>, die Details eines Board- oder Listeneintrags in einer Schublade rechts (auf Smartphones bildschirmfüllend), gebunden an einen Query-Parameter (?item={id},QueryParameter): Links, Neuladen und die Zurück-Taste öffnen und schließen ihn; Escape und die Schließen-Schaltfläche schließen ihn; die FragmenteTitle,ActionsundChildContentbekommen den Schlüssel, dazuWidth,KeyChanged,Open(key). Siehe Detailbereich. - Anhänge an Datensätzen (
Coworkee.Files, ModulCoworkeeAttachmentsModule, Migration nötig für die Tabellecw.RecordAttachments):services.AddRecordAttachments<LeaveRequest>("LeaveRequests", a => a.Slot("certificate", maxBytes, "application/pdf", "image/*").MultiSlot("documents").WriteRequires(permission)); Endpunkte/api/v1/attachments/{type}/{id}/{slot}(Liste, inline oder Download, Upload, Löschen), deren Lesen der Zeilensichtbarkeit des OData-Sets des Typs folgt, Schreiben zusätzlich der Schreibberechtigung und denAuthorize-Prüfungen;IRecordAttachments(PutAsync/OpenAsync/DeleteAsync/DeleteAllAsync/ListAsync) für App-Code, HookIAttachmentScanner(Virenscan), Aufbewahrungszielorphaned-attachments. Client:IAttachmentsApiund die KomponenteRecordAttachment(ein Slot: Upload mitMudExUploadEdit, Ansicht mitMudExFileDisplay, Löschen).ODataEntityAccess.VisibleIdsAsync<T>: die Ids, die der aktuelle Benutzer sieht. Siehe Anhänge an Datensätzen. - Anonyme Datensätze:
IAnonymous(oder die BasisAnonymousEntitymit zufälligen IDs perGuid.NewGuid()) für Zeilen, die nicht auf ihren Autor zeigen dürfen: keinCreatedBy/ModifiedBy/DeletedBy, kein Audit-Eintrag, Snapshots ohneCreatedBy, Outbox-, Webhook- und Elsa-Trigger-Nachrichten ohne Benutzer und Trace-ID,CreatedAt/ModifiedAt/DeletedAtauf den UTC-Tag gekürzt (IAnonymous.TimestampPrecision,record.Truncate(time)für eigene Zeitstempel). Siehe Audit. - OData:
services.AddODataChildFilter<TalkItem, Talk>(i => i.Talk)lässt Kind-Zeilen den Zeilenfiltern ihres Elternteils folgen (sichtbar, wennODataEntityAccess.VisibleAsync<Talk>das Elternteil enthält; Unterabfrage auf den Fremdschlüssel), statt eines eigenen Filters, der den des Elternteils wiederholt. Siehe OData. - Blazor: kleine Diagramme ohne das Reporting-Modul in
Coworkee.Client.Blazor.Components.Charts:CoworkeeBarChart(horizontal oderVertical, Werte, Farben,ShowPercent,Max),CoworkeeDonut(Segmente, Label in der Mitte, Legende) undCoworkeeSparkline; reines HTML/SVG in den Theme-Farben (Dark Mode), Texte für Screenreader lesbar. Siehe Kleine Diagramme. - iCalendar-Dateien:
IcsCalendar/IcsEvent/IcsTimeinCoworkee.Application.Calendar(ganztägig, UTC- oderTZID-Zeiten, Ort, URL, Status, Kategorien, Organisator, Teilnehmende; Maskierung nach RFC 5545 und UTF-8-sicherer Umbruch nach 75 Oktetts) undResults.Ics(calendar, fileName?)inCoworkee.AspNetCore.Http(text/calendar; charset=utf-8). Siehe Kalender-Feeds für Abo-Adressen mit Token und Caching-Header. - Berichte: eigene Datenquellen.
services.AddReportSource<HeadcountRow>("HeadcountByMonth", "Mitarbeitende pro Monat", permission, async (context, ct) => rows)registriert Zeilen eines einfachen Typs (einIQueryableder Datenbank oder eine Liste, im Speicher aggregiert), die Berichte nach den Entitätsmengen listen (ReportSourceDto.Computed, Summen-Symbol im Designer) und wie eine Menge aggregieren: OData-Filter gegen den Zeilentyp geparst, Gruppierung mit Datums-Buckets, alle Funktionen;ReportSourceContextliefert die Dienste der Anfrage und den Zeitraum aus dem Filter des Widgets (DateFrom/DateTo). Tabellen lesen ihre Zeilen überPOST /api/v1/reports/rows(IReportsApi.RowsAsync, neues Interface-Mitglied); Datentabellen-Widget und Facetten gibt es dafür nicht, Drill-down nur, wennReportingOptions.DrillDowneine Seite nennt. Siehe Berichte. - AirWorks (
@coworkee/airworks): Berichtsansicht und Designer können berechnete Berichtsquellen: Tabellen darauf lesenPOST /api/v1/reports/rows, Datentabellen lassen sie aus (der Designer bietet dort nur Entitätsmengen an), die Quellenauswahl markiert sie mit ∑, der Zeitraum eines reinen Datumsfelds filtert nach Tagen.@coworkee/clienthatReportSourceDto.computed,ReportRowsRequestundReportRowsResult. - Social: Emoji-Reaktionen.
AddCoworkeeSocial(s => s.Reactions<Product>("Products").CommentReactions()), BerechtigungenSocial.Reactions.View/.Create,GET/POST /api/v1/reactions/{entityType}/{entityId}(umschalten mit{ "emoji" }), Realtime-Topicreactions:{entityType}:{entityId}, Komponente<CoworkeeReactions EntityType="Products" EntityId="id" />(Anzahl, eigene gefüllt, beim Überfahren, wer reagiert hat, Auswahl ausSocialReactions.Defaults); mitCommentReactions()bekommt jeder Kommentar vonCoworkeeCommentsdieselbe Zeile (CommentDto.Reactions,CommentThreadDto.ShowReactions/CanReact). Export und Löschung personenbezogener Daten umfassen Reaktionen.ISocialApihatGetReactionsAsync/ToggleReactionAsync. Siehe Kommentare, Tags, Bewertungen und Reaktionen. - Workflows:
WorkflowEngine.LoadAsyncfindet eine Entität, die die aktuelle Unit of Work hinzugefügt und noch nicht gespeichert hat, im Change Tracker; so legt ein Command einen Datensatz an und führt seinen ersten Übergang in einemSaveChangesaus (kein vorzeitiges Speichern; ein abgelehnter Übergang speichert nichts). Siehe Workflows. - Hintergrundjobs:
TenantJobRunner.ForEachTenantAsync((services, tenantId, ct) => ..., ct)führt Arbeit einmal pro aktiver Organisation als deren System und in einem eigenen Scope aus, protokolliert und liefert fehlgeschlagene Organisationen und macht mit den anderen weiter;ForTenantsAsync(ids, ...)für die Organisationen, die eine Abfrage gefunden hat.ITenantDirectory.GetActiveTenantIdsAsync. Der Jobworkflow-deadlinesnutzt ihn (ein Fehler in einer Organisation stoppt den Lauf nicht mehr). Siehe Hintergrundjobs. - Client:
ClientUser.From(principal)(IsAuthenticated,UserId,TenantId,Email,DisplayName) und das scopedCurrentUserInfo(GetAsync(),Changed), registriert vonAddCoworkeeClient; die Komponenten der Bibliothek nutzen es stattsubselbst zu parsen, und der Client-Principal trägt den Claimtenant_id. Siehe Client. - Berechtigungen: berechnete Berechtigungen vergeben eine definierte Berechtigung aus Daten (
services.AddComputedPermission(HrPermissions.LeadsPeople, (c, ct) => db.Employees.AnyAsync(e => e.LeaderUserId == c.UserId, ct))oderIComputedPermission), einmal pro Request und Benutzer nach den Rollenrechten geprüft; sie zählen für[RequiresPermission],IPermissionChecker, OData, MCP und die Berechtigungsliste des Clients (Navigation,PermissionGate, Policies).CoworkeeNavItem(..., Visible: sp => ...)blendet einen Eintrag über einen Callback im Client aus. Siehe Berechtigungen. ODataPicker/OptionalODataPicker:TextProperty="FirstName,LastName"zeigt mehrere Eigenschaften mit Leerzeichen verbunden, fragt sie per$selectab, sortiert nach ihnen und findet eine Zeile, wenn jedes Wort der Suche in einer davon vorkommt; mit einer Eigenschaft trifft eine Suche aus mehreren Wörtern jetzt auch, wenn die Wörter nicht nebeneinander stehen. Siehe Datentabellen.ResourceCalendar:CalendarResource.WorkDaysersetzt die Arbeitstage für eine Zeile (Teilzeit), und weitere Attribute (id,data-testid, ...) landen auf dem Wurzelelement. Siehe Ressourcenkalender.- Workflows:
EntityWorkflow<T>.Include(e => e.Employee)lädt eine Navigation mit der Entität, sodassDescribe, Guards und Aktionen sie nutzen können (Describe(l => $"Urlaub von {l.Employee.Name}")). Siehe Workflows. - OData-Sets:
AddODataEntity<T>(set, permission, o => o.Hide(...).HideUnless(e => e.Salary, permission))zeigt eine Eigenschaft nur Benutzern mit der Berechtigung; Listen, Einzelabrufe,$filter/$orderby/$select/$expand, Facetten, Exporte, Berichte, die globale Suche, die Daten-Tools des Assistenten, die Entity-Historie und Webhooks halten sich daran.ODataEntityRegistration.ProtectedProperties,ODataEntityAccess.DeniedPropertiesAsync. Siehe OData. ISystemDispatcher.SendAsync(request, permissions, ct)führt einen Request als System in der Organisation des Aufrufers aus, mit genau den genannten Berechtigungen und in einem eigenen Scope (Audit: system); darunterCurrentUserScope.BeginSystem(tenantId, permissions)undSystemActor.AnonymousCurrentUserundTestCurrentUserreichen jetztClientId/ClientPermissionsdes Umgebungs-Benutzers weiter. Siehe Berechtigungen.CoworkeeDataTable:RowHref(Func<T, string?>) undOnRowClick(EventCallback<T>) öffnen eine Zeile per Klick (nicht auf ihre Buttons oder Checkbox) oder über ihren Öffnen-Button in der Aktionsspalte (Tastatur, echter Link beiRowHref). Siehe Datentabellen.ResourceCalendar(Blazor): Ressourcen über Tagen mit Balken, halben Tagen, Spuren, Feiertagen und Ziehen zur Tagesauswahl. Siehe Ressourcenkalender.- Workflows:
ApprovalStep.UsersFrom(context => ...)ermittelt Genehmigende beim Start der Genehmigung aus der Entität, etwa die Führungskraft der antragstellenden Person.
Korrekturen¶
- Eine gespeicherte geheime Einstellung, die der aktuelle Data-Protection-Schlüsselring nicht entschlüsseln kann (etwa nach einem Wechsel des Schlüsselrings), gilt mit einer Warnung im Log als nicht gesetzt, statt jedes Lesen der Einstellungen scheitern zu lassen (
/api/v1/ai/statusantwortete auf jeder Seite mit 500). Das Secret einfach neu speichern.SettingProtectorbekommt einenILogger<SettingProtector>.
0.0.1.34 (2026-10-18)¶
Breaking Changes¶
- Migration nötig: Passkeys liegen in der neuen Tabelle
cw.UserPasskeys(Schema-Version 3 von ASP.NET Core Identity: SchlüsselCredentialId,Dataals JSON, Cascade voncw.Users). EigeneICoworkeeApi-Implementierungen ergänzenResetUserPasskeysAsync. - Migration nötig in Apps, die das neue Modul
Coworkee.Retentionaufnehmen: die Tabellecw.RetentionRuns. Apps ohne das Modul brauchen nichts. SettingWriterundSettingProtectorausCoworkee.Settingssind jetzt öffentlich (für Module, die eigene Einstellungen schreiben).
Neu¶
- Passkeys (WebAuthn) am Auth-Server: Mit Passkey anmelden und das Autofill des Browsers auf der Anmeldeseite, die Kontoseite Passkeys zum Hinzufügen (höchstens 20), Umbenennen und Entfernen, Passkeys (n) im Sicherheitsreiter des Profils, Passkeys: n mit Alle entfernen im Anmeldebereich der Benutzerverwaltung (
POST /api/v1/identity/users/{id}/passkeys/reset),PasskeyCountinProfileDtoundUserDetailDto, Passkeys im Export der persönlichen Daten. Eine Anmeldung per Passkey überspringt die Bestätigung in zwei Schritten. Die EinstellungAccount.Passkeys.Enabled(Standard an) und der Login-Modus External schalten sie ab;Coworkee:Auth:PasskeysbindetIdentityPasskeyOptions(ServerDomainhinter einem Proxy ohne Forwarded Headers). Siehe Passkeys. - Der Audit-Trail schreibt binäre Schlüssel als base64url in
EntityIdstattSystem.Byte[]. Coworkee.Retention: Tage pro Datenart (Audit-Trail, Snapshots, verarbeitete Outbox und Inbox, weich gelöschte Datensätze, KI-Werkzeugaufrufe und Unterhaltungen, Webhook-Zustellungen, Mail-Log, Benachrichtigungen, Import- und Export-Jobs, gelöschte Dateien mit ihren Blobs, Backups, OpenIddict-Tokens), der nächtliche Jobcoworkee-retention(Coworkee:Retention:Cron, Standard41 2 * * *), die Admin-Seite Aufbewahrung (/admin/retention, PermissionRetention.Manage, nur in der Systemorganisation) mit Live-Zählung und Jetzt löschen, Endpoints/api/v1/retentionund ein Abschnitt im Systemstatus. Jede Datenart, die bisher für immer blieb, bleibt so, bis jemand Tage einstellt; nur das Webhook-Protokoll behält seine 30 Tage. Module und Apps melden eigene Datenarten mitIRetentionTargetundservices.AddRetentionTarget<T>()an;ExecuteDeleteInBatchesAsynclöscht in Batches zu 1000. Template-, CLI- und Studio-Optionretention(an); neue Apps behalten verarbeitete Nachrichten und Tokens 14 Tage, Import- und Export-Jobs 30. Siehe Aufbewahrung.- Webhooks:
WebhookOptions.LogRetentionDaysist der Standard vonRetention.webhook-deliveries.Days; mit dem Retention-Modul tut die alte tägliche Bereinigung des Webhook-Protokolls nichts. CoworkeeMigrationsOptions.AfterSeedführt die eigenen Datenschritte der App nach Migration und Seed aus, bevor Keycloak abgeglichen wird.
0.0.1.33 (2026-10-17)¶
Breaking Changes¶
- Migration nötig: ein gefilterter eindeutiger Index
IX_Themes_Nameaufcw.Themes(eingebaute Themes,TenantIdnull) und die Spaltecw.ChatAttachments.ThumbnailKey. Der Index verhindert, dass zwei App-Instanzen die eingebauten Themes auf SQL Server und SQLite doppelt anlegen (Postgres lehnte das schon ab); der Seed räumt solche Doppel nicht mehr auf. Meldet die Migration einen doppelten Schlüssel, lösch zuerst die überzählige eingebaute Zeile. - Wiederherstellen aus dem Verlauf prüft seine Vorschau:
EntityHistoryPreviewDto.VersionundEntityHistoryRestoreRequest.Versionsind neu;POST /api/v1/history/{set}/{id}/restoreantwortet409, wenn die Version fehlt oder sich der Datensatz seit der Vorschau geändert hat. Verlaufsdialog, Rückgängig im Audit-Log und das AirWorks-Datensatzfenster zeigen dann die neue Vorschau. EigeneIEntityHistoryApi-Implementierungen und direkte API-Aufrufe geben die Version weiter; OpenAPI-Snapshots und SDKs neu erzeugen. Siehe Auditing. Coworkee.Chathängt vonCoworkee.BackgroundJobsab und bringt SkiaSharp (MIT) mit seinen nativen Linux-Bibliotheken für die Vorschaubilder.
Neu¶
coworkee updatefügt einzelne Zeilen selbst ein (Regelartinsertionsinupgrade-rules.json: nach der ersten Zeile, die zu einem Muster passt, mit deren Einrückung, übersprungen, wo die Zeile schon steht;--dry-runlistet sie nur). Der Schritt von 0.0.1.32 (builder.ConfigureContainer(new DefaultServiceProviderFactory());insrc/*.Migrations/Program.cs) passiert jetzt automatisch.- Neue Apps haben eine
AGENTS.mdmit den Regeln für Coding-Agenten (Aufbau, Pflichtmuster, Fallstricke, Migrationen, Tests, CLI und MCP) und eineCLAUDE.md, die sie einbindet. Bestehende Apps übernehmen sie aus einer neuen App mit denselben Optionen. - AirWorks: Der Theme- und der Mail-Vorlagen-Editor der Verwaltung sind die Seiten der Web-App in einem Fenster, wenn die BFF die Web-App kennt (
embedOr), die TypeScript-Editoren bleiben der Ersatz; Workflow-Designer öffnet Elsa Studio in einem Browser-Tab (Workflows.Elsa.Manage); das Datensatzfenster hat In der Web-App öffnen (die Seite des Treffers der globalen Suche). Neuer TextschlüsselOpen in the web app. Coworkee:Auth:FrameAncestors: Origins, die die Kontoseiten des Auth-Servers einrahmen dürfen (Standard: keine), damit ein AirWorks-Fenster die Anmeldeseite einer eingebetteten Webseite zeigen kann. Nur auf derselben Site; siehe AirWorks.app.UseCoworkeeSecurityHeaders()(Coworkee.Bff) schickt eine Content-Security-Policy für den Blazor-WebAssembly-Client (frame-ancestors ausCoworkee:Bff:FrameAncestors, MudEx-Dateivorschauen aus blob- und data-URLs, Hot Reload in Development),nosniffund eine Referrer-Policy; weitere Quellen pro Direktive oder eine eigene Policy überCoworkee:Bff:SecurityHeaders. Der Web-Host der Vorlage nutzt es; bestehende Apps ergänzen es vorUseStaticFilesund entfernen eine selbst geschriebene Policy. Apps mit der Cloud-Auswahl des Files-Moduls ergänzen deren Hosts. Siehe Sicherheits-Header der Web-App.Coworkee:Bff:MaxRequestBodySizebegrenzt, was der Web-Host an die API weiterleitet; ohne den Wert haben seine weitergeleiteten Routen keine Grenze und die API prüft ihre eigene. Ein selbst geschriebenesConfigureKestrel(k => k.Limits.MaxRequestBodySize = ...)im Web- oder AirWorks-Host ist unnötig und kann weg.builder.AddCoworkeeMigrations<TDbContext>(...)undhost.RunCoworkeeMigrationsAsync()(Coworkee.Identity.Setup) sind der Migrations-Host: migrieren mit Wiederholungen (Migrations:Attempts), die Entwicklungsbenutzer oder den ersten Administrator eines Deployments seeden, die Entwicklungsbenutzer im Stand halten, Keycloak abgleichen, dazu die Container-Korrektur von 0.0.1.32. Die Vorlage nutzt es; bestehende Apps ersetzen damit ihre selbst geschriebenesrc/*.Migrations/Program.cs, damit spätere Korrekturen mit dem Paket kommen (coworkee updateweist darauf hin). Siehe Migrationen und Seed.- Chat-Bilder bekommen ein kleines WebP, das der Server erzeugt (höchstens 480 px, aufrecht gedreht,
GET /api/v1/chat/attachments/{id}/thumbnail,ChatAttachmentDto.HasThumbnail); die Nachrichtenliste zeigt es, ein Klick öffnet die ganze Datei. - Jede Chat-Datei öffnet sich im MudEx-Datei-Viewer (
MudExFileDisplay), auch ein Upload vor dem Senden. Die Regel für eigene Seiten steht unter Dateien. - Die Policy von
UseCoworkeeSecurityHeaderserlaubt die CDNs, von denen der Datei-Viewer seine Viewer lädt (Coworkee:Bff:SecurityHeaders:FileViewerCdns: jsDelivr, esm.sh, unpkg), inscript-src,style-srcundconnect-src, damit Word, Excel, Karten und die anderen Viewer ohne weiteres Zutun gehen; leere die Liste für eine Policy ohne Fremd-Skript. Siehe Sicherheits-Header der Web-App. - In einen Chat hochgeladene, aber nie gesendete Dateien werden nach
Coworkee:Chat:UnsentAttachmentLifetimegelöscht (Standard 24 Stunden, ein stündlicher Job).
Korrekturen¶
- Die SDK-Seite beschrieb ein
sdk/openapi.jsonmit Snapshot-Test und einen Client insdk/typescript, die das Template nie hatte; sie zeigt beides jetzt als Rezept für Apps, die es brauchen. npm run buildvon@coworkee/clientund@coworkee/airworksleert zuerstdist, damit Pakete keine Dateien entfernter Quellen mehr mitnehmen.- Die eingebauten Themes Coworkee, Forest und Sunset erreichen WCAG AA beim Button-Text im hellen Modus (etwas dunklere Primär- oder Sekundärfarbe); der Kontrasttest prüft jedes eingebaute Theme.
- Der Datei-Viewer gibt eine private Datei nie an den Online-Office-Viewer von Microsoft.
- Dateien, die der Web-Host weiterleitet, behalten ihre eigene Sandbox-Policy:
UseCoworkeeSecurityHeaderssetzt die App-Policy nur bei Antworten ohne eigene (vorher trugen weitergeleitete Dateien beide). - Die Inhalts-Endpunkte von Dateien, öffentlichen Dateien, Chat-Dateien und Vorschaubildern beantworten
HEAD, das der Bild-Viewer zuerst schickt (bisher 405).
0.0.1.32 (2026-10-17)¶
Korrekturen¶
- Die Migrationen eines lokalen Laufs stürzten seit 0.0.1.31 beim Start ab: Sie laufen als Development, und dessen Prüfung der Dienste scheitert an den MVC-Diensten von OData in einem Host ohne Web-Umgebung. Die Vorlage baut den Container der Migrationen mit den Produktions-Standards; bestehende Apps ergänzen
builder.ConfigureContainer(new DefaultServiceProviderFactory());nachHost.CreateApplicationBuilder(args)insrc/*.Migrations/Program.cs(coworkee updateweist darauf hin).
0.0.1.31 (2026-10-17)¶
Breaking Changes¶
- Migration nötig (Apps mit Chat): Direkt- und Gruppenchats bringen die Tabellen
cw.ChatConversations,cw.ChatMembers,cw.ChatReactions,cw.ChatAttachmentsund neue Spalten voncw.ChatMessages. Frühere Direktnachrichten wandern beim nächsten Start in Unterhaltungen; Gelesenes bleibt gelesen. Coworkee.Chathängt vonCoworkee.Storageab (Anhänge) und referenziertCoworkee.Notifications(Benachrichtigungen bei Erwähnungen, wenn das Modul da ist).- Chat-Verträge:
ChatMessageDto.ToUserIdist nullable (leer in Gruppen), das DTO hat neue Eigenschaften (ConversationId,Html,Source,EditedAt,Deleted,ReplyTo,Reactions,Attachments,HasEntityMentions);ChatReadDtohatConversationId.IChatApihat die Mitglieder für Unterhaltungen dazubekommen; eigene Implementierungen ergänzen sie. Neue Endpunkte unter/api/v1/chat/chats,/messagesund/attachments: OpenAPI-Snapshots und SDKs neu erzeugen. Die Klartext-Endpunkte pro Person bleiben. UserAvataröffnet ein Menü mit Chat und Profil für andere Personen, wenn der Benutzer chatten darf (OfferChat, Standardtrue); setzeOfferChat="false", wo der Avatar in etwas sitzt, das auf Klicks reagiert. Neue Bibliotheksseitepersonunter/people/{id}.
Neu¶
- Acht weitere eingebaute Themes, jeweils mit heller und dunkler Palette, eigener Schrift, Radius und Optionen: Octo, Code, Arctic, Vampire, Solar, Atom, Paper und Aubergine (CLI/Spec
theme, Template, Studio-Galerie). Ihr Text erfüllt den Kontrast nach WCAG AA. Siehe Theming. - Das Setup-Token ist bei einem lokalen Lauf leicht zu finden: Der AppHost gibt allen Diensten dasselbe Token und zeigt es, solange das System nicht eingerichtet ist, in seiner Konsole (
coworkee run), im Log der API und im Aspire-Dashboard (Link Setup mit Token an der Web-Ressource, Eigenschaft Setup token an der API). Coworkee Studio zeigt es mit Kopier-Knopf in der Lauf-Karte und unter Meine Apps;coworkee deploynennt<APP>_SETUP_TOKEN, wenn kein Administrator gesetzt ist./setup?token=...füllt das Token ein. Siehe Setup-Assistent oder Seed. @coworkee/airworks: Eingang (coworkee.inbox, Genehmigungen und Benachrichtigungen) ersetztnotificationsApp; Datensätze mit einem Datensatzfenster (coworkee.record: Felder, Verlauf mit Rückgängig und Wiederherstellen, Workflow); das Profil ist der erste Tab der Einstellungen (profileAppentfällt,renderProfilebleibt); die API-Dokumentation liegt in der Administration (Überwachung). Apps, dienotificationsApp,profileAppoderapiDocsAppeinzeln registriert haben, nehmendefaultAppsoder die neuen Namen.- Seiten der Web-App in AirWorks-Fenstern:
embedApp,openWebPage;CoworkeeLayoutzeigt mit?embed=1nur die Seite.BffOptions.WebAppUrl(alsappUrlvon/bff/user) undBffOptions.FrameAncestors(diese Origins dürfen die Seiten einrahmen; dasX-Frame-Optionsdes Antiforgery entfällt dann);AddAirWorkssetzt beides unter Aspire. - Badges auf den Kacheln von Eingang und Chat mit
setBadgevon AirWorks (AirWorks 0.5; ältere Versionen zeigen keine). - Chat: Gruppenchats (Name, Personen hinzufügen und entfernen, austreten), formatierte Nachrichten mit dem Rich-Text-Editor (Enter sendet),
@-Erwähnungen von Personen (Link auf die Person, Benachrichtigung für Mitglieder des Chats) und#-Erwähnungen von Datensätzen aus der globalen Suche (nur für Leser sichtbar, deren Suche sie findet, sonst eingeschränkt), Antworten, Emoji-Reaktionen, eigene Nachrichten bearbeiten und löschen, Dateien und Bilder per Button, Drag-and-drop oder Einfügen mit Upload-Fortschritt (nur für Mitglieder, EinstellungChat.MaxAttachmentMb, ErweiterungIChatAttachmentScanner), Lesebestätigungen pro Leser, Tipp-Anzeige und Suche im Chat. Der Server bereinigt jede Nachricht. Siehe Chat. - Mini-Chats: ein Dock unten rechts mit bis zu drei einklappbaren Chatfenstern (ungelesene Nachrichten im minimierten Zustand, Im Chat öffnen, pro Benutzer über ein Neuladen hinweg gemerkt, auf dem Handy bildschirmfüllend). Sie öffnen sich von der Chatseite, der Befehlspalette (Chatten mit…,
@name), jedem Avatar, der Personenseite, der Snackbar einer neuen Nachricht und Erwähnungs-Benachrichtigungen.ChatDocköffnet sie aus eigenen Komponenten. RichTextEditornimmtTools(Werkzeugleiste) undPlaceholder.
Korrekturen¶
- Eine neue App zeigte lokal ihre Setup-Seite, statt mit den Entwicklungsbenutzern anzumelden: Das Migrationsprojekt hat kein Launch-Profil und lief als Production, also fiel
DemoSeedweg.AddMigrationsgibt den Migrationen eines lokalen Laufs die Umgebung des AppHosts mit (DOTNET_ENVIRONMENT). - Die Setup-Seite eines bereits eingerichteten Systems führt zur App; ein offener Setup-Tab wechselt zur App, sobald er wieder den Fokus bekommt oder ein anderer Tab das Setup abschließt.
- Eingebaute Themes standen doppelt in der Liste, wenn Anfragen sie gleichzeitig anlegten (SQL Server und SQLite lassen im eindeutigen Index leere Mandanten mehrfach zu). Das Anlegen läuft pro Prozess nur einmal gleichzeitig und entfernt solche Kopien (die Kopie, die eine Organisation nutzt, bleibt).
0.0.1.30 (2026-10-16)¶
Neu¶
- Das Audit-Log macht Änderungen von Entity-Sets mit
AddEntityHistoryrückgängig: pro Eintrag Diese Änderung rückgängig machen, Stand vor dieser Änderung wiederherstellen, Datensatz wiederherstellen (Soft Delete) und Verlauf des Datensatzes öffnen, über die Vorschau und das Wiederherstellen des Verlaufs.EntityHistoryRestoreRequesthat einenMode(After,Before,Undo), die Vorschau markiert Felder, die ein späterer Eintrag geändert hat (ChangedLater), undGET /api/v1/historylistet die lesbaren Sets.IEntityHistoryApihatGetSetsAsync(eigene Implementierungen ergänzen es).
0.0.1.29 (2026-10-16)¶
Neu¶
- Workflow-Zustand als Filter auf dem Server: Jede OData-Menge, deren Entität einen Workflow hat, bekommt die virtuellen Eigenschaften
WorkflowStateundWorkflowAwaitsMefür$filterund$orderby(eine nach Mandant gefilterte Unterabfrage oder dieMirrorTo-Spalte) und eine FacetteWorkflowStatemit den Zuständen der Definition und Wartet auf meine Genehmigung; beide brauchenWorkflows.View.CoworkeeWorkflowColumnfürCoworkeeDataTablefiltert, sortiert und gruppiert danach;WorkflowState<T>.WithColor(...)färbt einen Zustand. ErsetzeTemplateColumn+CoworkeeWorkflowActionsdurch<CoworkeeWorkflowColumn T="..." EntityType="..." />. - OData-Mengen können berechnete Eigenschaften anderer Module tragen (
IODataComputedPropertySource,ODataComputedProperty): in$filter/$orderbygebunden, nie serialisiert, optional mit Facette. Die OData-Route konfiguriert jetzt die Bibliothek selbst statt NextendedsAddODataAuto.
Korrekturen¶
SyncDevelopmentUsersAsync()läuft für die Migrationen eines lokalen Aspire-Laufs: Das Migrationsprojekt hat kein Startprofil und lief als Production, dort fand der Abgleich nie statt.AddMigrationssetzt in lokalen EntwicklungsläufenCoworkee:Identity:SyncDevelopmentUsers; der Abgleich läuft in Development oder mit dieser Einstellung.SyncDevelopmentUsersAsync()gibt Seed-Benutzern auch die seitdem im Seed ergänzten Rollen und Seed-Rollen die ergänzten Berechtigungen (nur in Development, entfernt wird nichts); so bekommt eine früher eingerichtete Datenbank z. B. eine neue Rolle eines Demo-Benutzers und neue Workflow-Berechtigungen einer Demo-Rolle.
0.0.1.28 (2026-10-16)¶
Breaking Changes¶
- Migration nötig (Apps mit Reporting): neue Tabelle
cw.DashboardReports. IReportsApihatGetDashboardAsyncundSetDashboardAsyncbekommen; eigene Implementierungen ergänzen sie. Neue Endpunkte unter/api/v1/reports/dashboard: OpenAPI-Snapshots und SDKs neu erzeugen.AddCommandPaletteEntitySetundCommandPaletteEntitySet<T>sind entfernt: Die Befehlspalette findet Datensätze über die neue globale Suche des Servers. Registriere das Set neben seinem OData-Set, z. B.services.AddGlobalSearch<Product>(s => s.Fields(p => p.Name, p => p.Barcode).Page("/catalog/products"))(coworkee updatezeigt auf die alten Aufrufe).- Neue Endpunkte
/api/v1/search,/api/v1/search/streamund/api/v1/search/providers: OpenAPI-Snapshots und SDKs neu erzeugen. ICoworkeeApihatGetMailTemplateHistoryAsyncbekommen; eigene Implementierungen ergänzen es.- MudBlazor.Extensions 9.11.4 und MudExRichTextEditor 9.5.5: Apps, die MudBlazor.Extensions selbst festlegen, gehen auf 9.11.4.
Neu¶
- Ein Bericht als Dashboard: jeder Benutzer kann einen Bericht zu seinem Dashboard machen, Inhaber der neuen Berechtigung
Reports.Dashboardzum Standard der Organisation; die Wahl des Benutzers gewinnt.ReportDashboardzeigt es live an und fällt auf das eigene Dashboard der App zurück. Die Dashboard-Seite des Templates nutzt es, führt Dashboard vor Berichte auf, und die Einrichtung mit Beispielen macht Catalog overview zum Dashboard der Organisation. Siehe Ein Bericht als Dashboard. - Globale Suche: eine Suche über Benutzer, Rollen, Gruppen, Dateien, Berichte, Chats und die eigenen Sets der App (
AddGlobalSearch<T>für OData-Sets,IGlobalSearchProviderfür alles andere,AddGlobalSearchIndexfür einen Elasticsearch-Alias). Anfang vor Teil,"exakt", Typfilter wieusers:maxund Ids; Bewertungen über Provider hinweg vergleichbar, ein Timeout pro Provider, dieselben Berechtigungen, Organisation und Zeilenregeln wie die OData-Listen. Die Befehlspalette zeigt die Treffer Gruppe für Gruppe, sobald sie da sind,/search?q=listet alle mit einem Filter pro Typ,GET /api/v1/search/streamstreamt JSON-Zeilen durch das BFF, und das MCP-Toolglobal_searchgibt sie an KI-Clients. Siehe Globale Suche. - Die Dateiseite öffnet die Vorschau von
?file={id}. - Beispiele der Vorlage: Marken, Produkte und Dokumente sind in der globalen Suche.
coworkee module addundcoworkee entity addtragen erzeugte Entitäten mit Texteigenschaften in die globale Suche ein (AddGlobalSearch<T>mit diesen Feldern und der Listenseite).- Einstellung Rich-Text-Editor:
Editors.RichTextEditor(TipTap, der Standard, oder Quill) wählt den Editor für jeden Rich-Text-Editor der Bibliothek;RichTextEditorbringt dieselbe Wahl auf eigene Seiten. Mail-Platzhalter funktionieren in beiden als Erwähnungen. Siehe Mailing. - Mail-Vorlageneditor: Der Editor füllt das Fenster, ein Vorschau-Panel folgt jeder Änderung (Desktop- oder Telefonbreite, helles oder dunkles Mailprogramm, Zeile im Posteingang), Versionsverlauf mit Zeilenvergleich und Wiederherstellen (
GET /api/v1/mail/templates/{name}/{culture}/history), Schutz vor dem Verlassen mit ungespeicherten Änderungen, Sprach-Chips neben dem Titel und eine durchsuchbare Platzhalterliste. - Workflow-Designer:
Coworkee.Workflows.Elsa.Studiohostet Elsa Studio als eigene App, die sich am Auth-Server anmeldet (BerechtigungWorkflows.Elsa.Manage) und mit der Elsa-API der API arbeitet;app.AddElsaStudio<T>()(oderAddProjects()für*_ElsaStudio) verdrahtet Client, API-Adresse und den Link zum Designer,coworkee deploy --without elsastudiolässt ihn weg. Template-OptionelsaStudio(--elsa-studio). Siehe Elsa Studio.
Korrekturen¶
- Dialoge und Seitenleisten der Bibliothek (Import-Assistent, Kampagnen-Assistent, Theme, Logo, Code-Editor, API-Schlüssel, Workflow-Kommentar) tragen die Startklasse von MudBlazor.Extensions und erscheinen damit erst an ihrer endgültigen Position, statt zuerst kurz in der Mitte aufzublitzen. Eigene Dialoge, die du mit
ShowExAsyncoderShowSideSheetAsyncöffnest, setzenClass="@MudExCss.Classes.Dialog.Initial"an ihremMudDialog. - Der Eingang für Genehmigungen zeigt Titel, Details und Frist einer Genehmigung in eigenen Zeilen.
- Apps mit
Coworkee.Workflows.Elsabehalten die Eigenschaftsnamen ihrer Validierungsfehler (Brand.Name); die Elsa-API (FastEndpoints) hatte FluentValidation für jeden Validator auf camelCase umgestellt (brand.Name). - Die Endpunkte von Elsa (
/api/elsa/...) landen nicht mehr im OpenAPI-Dokument der App: OpenAPI-Snapshots und SDKs von Apps mitCoworkee.Workflows.Elsaneu erzeugen.
0.0.1.27 (2026-10-16)¶
Korrekturen¶
- Links auf Adressen des Servers (z. B. Anmelden am Ende der Einrichtung,
/bff/login) laden wieder vom Server; seit die Seiten der Bibliothek eine Sammelroute haben, zeigte die App dafür Not found.. In Apps ist nichts zu ändern.
0.0.1.26 (2026-10-16)¶
Breaking Changes¶
ICoworkeeApihatGetSystemStatusAsyncdazubekommen; eigene Implementierungen ergänzen es.- Seiten der Bibliothek unter eigenem Präfix: die Admin-Seiten der Bibliothek liegen jetzt unter
/cw/admin/...statt/admin/...(CoworkeeClientOptions.PagePrefix,""behält die alten Adressen); alte Adressen leiten weiter. Die Seiten der Bibliothek haben keine@page-Routen mehr, eine Sammelseite (/{*Path}) zeigt sie: eine App mit eigener Sammelseite bekommt eine mehrdeutige Route. Apps ersetzen oder schalten Seiten per Schlüssel ab (options.Pages), siehe Komponenten ersetzen. - Deployments nehmen die Tools des App-Hosts mit: Ein veröffentlichter App-Host deployt jetzt auch Keycloak, Grafana, WebDataStudio, LibreTranslate und AspireUI. Apps aus einem älteren Template übernehmen drei Dinge aus einem neuen: in
Migrations/Program.csden Seed ausInitialAdmin.From(configuration)(mitDemoSeed.ConfigureDeployment) undReconcileKeycloakRealmAsync(), inServiceDefaults/Extensions.csden Grafana-Exporter pro Signal mitGRAFANA_OTLP_PROTOCOLund inAppHost.csapp.Deploys(...)für Ollama undaspireUi.WithCoworkee(app)für AspireUI.KeycloakRealmliegt jetzt inCoworkee.Contracts.Identity. - Migration nötig: neue Tabellen
cw.PushSubscriptions(Notifications),cw.DataTransferJobsundcw.ImportProfiles(OData, also jede App). Workflows und Webhooks bringen eigene Tabellen mit, wenn eine App sie aufnimmt. ICoworkeeApihatAddMailTemplateLanguageAsync,GetPushSettingsAsync,SavePushSubscriptionAsyncundDeletePushSubscriptionAsyncdazubekommen; eigene Implementierungen ergänzen sie.Notificationerbt jetzt vonAggregateRoot(sie löstNotificationCreatedaus, solange Push eingerichtet ist). Das Schema ändert sich dadurch nicht.IODataClient.ExportAsyncundImportAsyncentfernt: NutzeIDataTransferApi(Export der aktuellen Ansicht in drei Formaten, der Import-Assistent). EigeneIODataClient-Fakes streichen die beiden Methoden.ODataImportRegistrationhatCreate,UpdateundDeletestattSend;AddODataImportnimmt für Ändern und Rückgängig(Guid? id, row) => commandund optional einen Lösch-Command.CoworkeeODataModulehängt vonCoworkeeBackgroundJobsModuleab: Importe und große Exporte laufen als Jobs. Apps aus dem Template haben es bereits (Mailing); Test-Hosts setzenCoworkee:Jobs:ConnectionStringNameund legen ihre Tabellen vorUseCoworkee()an.- Der Import-Button von
CoworkeeDataTableöffnet den Import-Assistenten, statt sofort eine Excel-Datei hochzuladen; Excel-, CSV- und JSON-Exporte von OData-Sets kommen mit den sichtbaren Spalten vom Server. - Smartphone-Layout: Apps ergänzen
viewport-fit=coverim Viewport-Tag vonApp.razor(coworkee updateerledigt das). - Neue Endpunkte (Push, Sprachen von Mail-Vorlagen, Import und Export, Verlauf, Workflows, Webhooks): OpenAPI-Snapshots und SDKs neu erzeugen.
coworkee update kennt all das (Upgrade-Regeln von 0.0.1.26).
Neu¶
- Template-Beispiele: die Befehlspalette bietet Neues Produkt (öffnet den Dialog über
/catalog/products?new=true) und findet Produkte nach Name oder Barcode. - Webhooks: Secret erneuern: ein neues Geheimnis, einmal angezeigt; das alte signiert für
Coworkee:Webhooks:SecretGracePeriod(24 h) mit, siehe Webhooks. Zwei neue Spalten incw.WebhookSubscriptions(Apps mit Webhooks brauchen eine Migration). - Workflow-Chips in Tabellen laden die Zustände einer Seite in einer Anfrage (
POST /api/v1/workflows/{entityType}/states,IWorkflowApi.GetStatesAsync) statt einer pro Zeile; eigeneIWorkflowApi-Implementierungen ergänzen die Methode. - Schieberegler in Spaltenfiltern: zwischen auf Zahlen- und Datumsspalten regelt zwischen kleinstem und größtem Wert, den der Server meldet; eine Spalte mit Datums- oder Zahlenbereich-Facette (etwa das Bestelldatum der Demo) zeigt deren Histogramm und Regler statt einer Checkbox pro Bucket.
- Systemstatus-Seite für die Systemorganisation (
/cw/admin/system-status): Versionen, Datenbank und ausstehende Migrationen, Outbox, Hintergrundjobs mit den letzten Fehlern, Mail, Echtzeit, Redis, Dateien, Übersetzung und KI; Module und Apps steuern Karten überISystemStatusContributorbei, siehe Admin-Oberfläche. - Workflows und Genehmigungen (
Coworkee.Workflows, optional): Zustände und abgesicherte Übergänge pro Entitätstyp, Genehmigungen durch Rollen oder Benutzer mit Frist, Erinnerung und Eskalation, eine Historie, die KomponentenCoworkeeWorkflow,CoworkeeWorkflowActionsundCoworkeeWorkflowTimelineund die Seite Genehmigungen, siehe Workflows und Genehmigungen. - Elsa-Workflows (
Coworkee.Workflows.Elsa, optional): Elsa Workflows 3 in der API mit Coworkee-Aktivitäten und -Triggern, siehe Elsa-Workflows. - Webhooks (
Coworkee.Webhooks, optional): Endpunkte pro Organisation bekommen signierte Ereignisse (im Stil von Standard Webhooks) zu Änderungen freigeschalteter Entity-Sets, Workflow-Übergängen und eigenen Ereignissen der App, beschränkt auf das, was der Besitzer des Endpunkts lesen darf; Wiederholungen mit Backoff, Abschalten nach wiederholten Fehlern, Zustellprotokoll, Testereignisse und erneute Zustellung unter/admin/webhooks, Schutz vor SSRF. Siehe Webhooks. - Import-Assistent pro Entity-Set: CSV, Excel und JSON, erkannte Kodierung, Trennzeichen und Kopfzeile, Spaltenzuordnung mit Vorschlägen und gespeicherten Zuordnungen, Umwandlung in der Sprache der Datei, Vorschau mit der Validierung der Commands, Anlegen, Ändern per Schlüssel oder beides, Probelauf, Hintergrundjob mit Live-Fortschritt, Datei der fehlerhaften Zeilen und Rückgängig für den letzten Import, siehe Import und Export. Admin-Seite Import und Export (
/admin/data-transfer, BerechtigungDataTransfer.Use; gib sie den Rollen, die sie sehen sollen). Exporte der aktuellen Ansicht als Excel, CSV oder JSON (format,columns); mehr alsCoworkee:OData:BackgroundExportRowsZeilen (10 000) laufen als Job mit Download-Hinweis. - Verlauf eines Datensatzes und Wiederherstellen (
AddEntityHistory,History="true"anCoworkeeDataTable,CoworkeeEntityHistory): Zeitleiste mit Wort-Diff, Wiederherstellen eines Stands oder eines einzelnen Felds mit Vorschau, gelöschte Datensätze vonISoftDelete-Sets kommen zurück, siehe Audit. - Befehlspalette (Strg+K): Seiten, Datensätze, Aktionen und der Assistent als Rückfall, erweiterbar mit
AddCommandPaletteProvider, siehe Befehlspalette. - Smartphone-Layout der Blazor-Shell und der Anmeldeseiten: Überlaufmenü in der App-Leiste, Navigation als Overlay, Bearbeitungsblätter im Vollbild, Facetten-Menüs als Sheets von unten, fixierte Zeilenaktionen, 44-px-Touchziele und Safe Areas, siehe Smartphones, installierbare App und Push.
- Installierbare App:
<Coworkee.Bff.Pwa.CoworkeePwa />im Head vonApp.razorverlinkt das Web-Manifest in den Farben des Themes;Coworkee.Bffliefert Manifest, Service Worker (er speichert nur die Offline-Seite), Icon und Offline-Seite; Update- und Installationshinweise in der App. - Web-Push für die Benachrichtigungen:
options.UsePush()in Aspire oderCoworkee:Notifications:Push:PublicKey/PrivateKey; Benutzer schalten es pro Gerät auf der Benachrichtigungsseite ein. - Sprachen von Mail-Vorlagen: Sprache hinzufügen unter Administration > Mail-Vorlagen legt eine Variante für eine später eingeschaltete Sprache an, kopiert oder maschinell übersetzt (nur Texte, Tags und Platzhalter bleiben), und entfernt sie wieder, siehe Mailing. Eine Mail in einer Sprache ohne Variante nutzt vor Englisch die Standardsprache (Organisation, System, Sprachen-Seite;
IDefaultLanguage); die maschinelle Übersetzung steht anderen Modulen alsIMachineTranslatorzur Verfügung. - Deployment: Der App-Host veröffentlicht für Docker Compose, Azure Container Apps oder Kubernetes (öffentliche Adressen, Token-Zertifikate, Setup-Token, der erste Administrator und der Mailserver als Parameter, stabile Volumes, feste Ports); die Tools des App-Hosts (Keycloak mit seinem Realm, Grafana mit den Dashboards der App, WebDataStudio, LibreTranslate, Ollama, AspireUI) kommen mit Volumes und öffentlichen Adressen mit, jedes lässt sich weglassen (
DeployTool,coworkee deploy --without), Mailpit nur mit--with mailpit;coworkee deploy compose|azure|k8sfragt nach Administrator und Mailserver und listet am Ende jede Adresse und die Anmeldung; Coworkee Studio bietet nach dem Erstellen einer App Deployen an; Token-Zertifikate können als Base64 aus einem Secret kommen. Nur Dienste, die Jobs verarbeiten, legen das Hangfire-Schema an. Siehe Deployment. - CLI:
coworkee add/removeschalten Template-Optionen in einer bestehenden App ein oder aus (in deine Dateien zusammengeführt, Konflikte als.rej, Optionen in.coworkee/app.json),coworkee updatewendet die Upgrade-Regeln der Versionen dazwischen an,coworkee db reset|seed|backup|restore,coworkee entity import --from db|openapi|csv, sechs Startvorlagen (coworkee specs list|show,coworkee new --template-spec), Optionen--workflows,--elsa,--webhooksund--pwa(standardmäßig an). MCP-Werkzeugeadd_optionundimport_entities. Siehe Kommandozeile und Startvorlagen. - Studio: eine bestehende App öffnen und ändern, Meine Apps (starten, stoppen, Links), Mit einer Vorlage starten, Entity-Diagramm (ziehen, Referenzen zeichnen, automatisch anordnen, PNG-Export) und Entity-Import mit Vorschau.
- Template: Die Beispielprodukte werden weich gelöscht und zeigen mit Audit ihren Verlauf und kommen zurück; das Dashboard funktioniert mit SQLite (dort kam 500); Demo-Benutzer werden nur in Development angelegt, Migrationen warten auf die Datenbank.
coworkee doctorvergleicht in einer App Versionen: dotnet-ef mit EF Core, Aspire-CLI mit dem Aspire-SDK des App-Hosts, coworkee mitCoworkeeVersion, und findet belegte Ports der Startprofile, jeweils mit Lösung.- Entwicklungsbenutzer bleiben aktuell:
SyncDevelopmentUsersAsync()(nur in Development) legt Seed-Benutzer und -Rollen an, die einer früher eingerichteten Datenbank fehlen, und setzt das Passwort der Seed-Benutzer wieder auf das geseedete; der Migrations-Host des Templates ruft es nachSeedCoworkeeIdentityAsync()auf, so kommen Änderungen anDemoUsers.csin einer bestehenden Datenbank an.
0.0.1.25 (2026-10-15)¶
Nur Korrekturen: Das Skript des Ladebildschirms ist eine eigene Datei, damit eine strikte CSP es ausführt, und App-Seiten gehen mit Cache-Control: no-cache, no-store raus. In Apps ist nichts zu ändern.
0.0.1.24 (2026-10-15)¶
Breaking Changes¶
ClientEntityIndicatorentfernt: Jede Datentabelle einer gespiegelten Menge zeigt denClientEntitySwitchin ihrer Werkzeugleiste (hinzugefügt vonAddCoworkeeClientEntities). Entferne die Zeilen<ClientEntityIndicator>und einen eigenen Vergleichen-Knopf aus deinen Seiten.ODataPicker.Margin(undOptionalODataPicker.Margin) ist jetztMargin?: Ohne Wert folgt der Picker der Dichte des Themes. Code, der den Wert alsMarginliest, ergänzt?? Margin.Dense.CliOption(Coworkee.Cli.Spec) hat den KonstruktorparameterDependsOndazubekommen,OptionDependencydie EigenschaftOr: quellkompatibel, aber binär inkompatibel für Werkzeuge, die gegen ein älteres Coworkee.Cli.Spec kompiliert sind.LoadingScreenDtohat den positionalen ParameterSignedIndazubekommen; OpenAPI-Snapshots und SDKs neu erzeugen.- Enums in jedem
MudExObjectEditwerden mit demEnumEditordargestellt (lokalisierte Beschriftungen ausEnumLabels,[Flags]als Mehrfachauswahl der Einzelbit-Mitglieder), nullbare Enums mit dem leerbarenNullableEnumEditor. EinRenderWithder Meta gewinnt weiterhin. --sign-in-mediamit dem LayoutCardwird ignoriert (nicht übergeben, nicht kopiert), außer--loading-screen SignInMediazeigt das Medium hinter dem Ladebildschirm.
Neu¶
- Ladebildschirm in den Farben des Themes mit dem echten Download-Fortschritt, einer freundlichen Fehlermeldung, wenn der Start scheitert, sowie Farbmodus, Theme und Sprache des Benutzers, siehe Theming. Jede App ergänzt eine Zeile als erste Zeile von
<body>inMyApp.Web/App.razor:<Coworkee.Bff.CoworkeeLoadingScreen Logo="coworkee-icon.svg" />. Der Hintergrund (Animated,SignInMedia,Plain) wird auf der Karte Anmeldeseite gewählt, in Aspire mitoptions.SignIn.LoadingScreen, in der CLI mit--loading-screen. Die Seite geht jetzt mitCache-Control: no-cache, no-storehinaus; das Logo kommt von/bff/loading-screen/logo, das Skript von/bff/loading-screen/script.js(kein Inline-Skript, Apps mit strenger Content-Security-Policy laufen weiter). - Öffentliche Dateien: Manager machen eine Datei öffentlich (
PUT /api/v1/files/{id}/public, Leiste des Dateimanagers, Vorschau); jeder lädt sie überGET /api/v1/files/public/{id}/{name}, etwa als Medium der Anmeldeseite oder des Ladebildschirms, gewählt mit Aus Dateien wählen. Das Modell hatStoredFile.IsPublicdazubekommen: Apps mit dem Files-Modul fügen eine Migration hinzu. - Seitenmodus von Datentabellen:
CoworkeeDataTable.PageMode(Pager,EndlessScroll) und die EinstellungDataTable.DefaultPageMode(global und pro Benutzer), siehe Datentabelle. - Client-Entity-Schalter in der Werkzeugleiste gespiegelter Tabellen: Suche im Browser oder auf dem Server, Synchronisierungsstand als Ring, Vergleichen und das Schnellere wählen nimmt die schnellere Seite; die Wahl bleibt pro Benutzer erhalten.
ImageDataUrlEditlädt mitMudExUploadEdithoch; Enums erhalten überall denEnumEditor(siehe Breaking Changes).- Die Texte der MudBlazor.Extensions-Komponenten, die die Bibliothek zeigt (Upload-Feld, Stil- und Cron-Editor, Dateimanager, Dateivorschau), werden in allen Sprachen mitgeliefert; ihre gespeicherten maschinellen Übersetzungen werden beim nächsten Start ersetzt.
- CLI und Studio: Optionen, die eine andere brauchen (
--airworks-packagesohne--airworks,--ai-provider,--ai-modelund--ai-endpointohne--ai,--backupaußerhalb von Postgres,--sign-in-mediamit dem LayoutCard, außer der Ladebildschirm zeigt es,--sign-in-media-sideohne Medium), gehen nicht mehr an das Template; die CLI warnt (coworkee spec validateebenso) und das Studio blendet ihre Karten aus. Anmeldemedien einesCard-Layouts werden deshalb nicht mehr in die App kopiert. Neu:coworkee spec showund eine Spezifikation aus dem Studio, die jedes Bibliotheksmodul mittrue/falsenennt;coworkee optionsgibtNeeds:und die Webseiten der mitgebrachten Produkte aus, die Studio-Karten zeigen Symbole und Öffnen-Knöpfe, siehe CLI.
0.0.1.23 und älter¶
Breaking Changes¶
IMailSender: Die Schnittstelle verlangt jetztQueueAsync(to, template, model, culture, campaignId, cancellationToken)mit sechs Parametern. Die Überladung mit fünf Parametern ist eine Default-Methode, dienullals Kampagne übergibt. Eigene Implementierungen (Testdoubles, Wrapper) implementieren die Methode mit sechs Parametern; Aufrufer bleiben unverändert.IMailTemplateRenderer: hatRenderContentAsync(content, layout, model, cancellationToken)dazubekommen, das mit einem bereits aufgelösten Layout rendert (der Kampagnen-Job löst damit Vorlage und Layout einmal pro Lauf auf). Eigene Implementierungen ergänzen die Methode; Aufrufer bleiben unverändert.- Positionale Records haben Parameter dazubekommen:
MailTemplateSummaryDto(IsCustom,Description),MailTemplateDto(IsCustom) undOutgoingMailDto(CampaignId). Code, der sie positional erzeugt oder dekonstruiert, und generierte Clients brauchen die neuen Parameter. OpenAPI-Snapshots und SDKs neu erzeugen. - Entfernt mit der Übernahme von MudEx 9.11.3:
ChipListEditor,CronEditor,CronScheduleundSettingsFormRules.WrapInColumns. String-Listen werden alsMudExTagField<string>dargestellt,[Cron]alsMudExCronEdit; verwende diese direkt.
Neu¶
- AirWorks-Client: Chat, Berichte mit Designer, Assistent, die Administrations-Apps (
adminApps) und Realtime, siehe AirWorks-Client. Erzeugte Apps nehmen@coworkee/clientund@coworkee/airworksaus den Tarballs im Template; jedes Release veröffentlicht sie zusätzlich auf npm (--airworks-packages npm).coworkee new --template-sourceträgt den lokalen Paketordner in dienuget.configder App ein. Apps aus der ersten AirWorks-Version:...adminAppsinClientApp/src/main.tsergänzen und für die Dateivorschau das MudEx-Hosting des Templates übernehmen (mudex-webcomponents,vite.config.ts,.dat-Content-Type inProgram.cs). - Mail-Kampagnen mit Empfänger-Voreinstellungen, gespeicherten Abfragen und manueller Auswahl, geplantem Versand und einer Bestätigungsseite für die Abmeldung, siehe Mailing.
- Eigene Mail-Vorlagen mit visuellem und Quelltext-Editor sowie eine
CodeEditor-Komponente auf Basis von Monaco. - Änderungen am Entitätsmodell für Kampagnen, Empfängerlisten und eigene Vorlagen: Füge deiner App eine Migration hinzu.