From 2c812ed48bc65ca823b0889aec7029fcce48eb32 Mon Sep 17 00:00:00 2001 From: felix Date: Tue, 15 Sep 2026 19:12:26 +0200 Subject: [PATCH] WIP --- config/authorization/config.lisp | 38 +++++++++++++++++-- config/delta/rules.js | 21 +++++++++++ config/dispatcher/dispatcher.ex | 64 ++++++++++++++++++++++++++++++++ config/resources/auth.json | 6 +++ config/resources/domain.lisp | 1 + docker-compose.yml | 37 ++++++++++++++++++ 6 files changed, 164 insertions(+), 3 deletions(-) diff --git a/config/authorization/config.lisp b/config/authorization/config.lisp index 4d234aa..947f44e 100644 --- a/config/authorization/config.lisp +++ b/config/authorization/config.lisp @@ -56,6 +56,9 @@ :reporting "http://lblod.data.gift/vocabularies/reporting/" :oslc "http://open-services.net/ns/core#" :cogs "http://vocab.deri.ie/cogs#" + :sioc "http://rdfs.org/sioc/ns#" + :sioct "http://rdfs.org/sioc/types#" + :as "https://www.w3.org/ns/activitystreams#" ) (type-cache::add-type-for-prefix "http://mu.semte.ch/sessions/" "http://mu.semte.ch/vocabularies/session/Session") @@ -220,7 +223,8 @@ ("code:EredienstBeroepen" -> _) ("code:Rechtsvormtype" -> _) ("prov:Location" -> _) - ("code:BijkomendeKwalificatieCode" -> _)) + ("code:BijkomendeKwalificatieCode" -> _) + ("prov:SoftwareAgent" -> _)) (define-graph shared ("http://mu.semte.ch/graphs/shared") ("foaf:Image" -> _) @@ -266,6 +270,22 @@ ("nfo:DataContainer" -> _) ("nfo:FileDataObject" -> _)) +;; Chat: one graph per account, only its owner reads or writes it. +;; The four chat types appear in no other group: a graph-less insert lands +;; in every graph whose group allows the type. +(define-graph chat ("http://mu.semte.ch/graphs/users/") + ("reporting:Report" -> _) + ("oslc:Error" -> _) + ("nfo:DataContainer" -> _) + ("nfo:FileDataObject" -> _) + ("cogs:Job" -> _) + ("nfo:DataContainer" -> _) + ("nfo:FileDataObject" -> _) + ("sioc:Thread" -> _) + ("sioc:Post" -> _) + ("sioct:InstantMessage" -> _) + ("as:Document" -> _)) + (supply-allowed-group "public") (supply-allowed-group "o-admin-rwf" @@ -336,12 +356,20 @@ FILTER( ?session_role = \"ABBOrganisatiePortaalErediensten-beheerder\" ) }") +(supply-allowed-group "chat-owner" + :parameters ("session_account") + :query "PREFIX session: + PREFIX mu: + SELECT DISTINCT ?session_account WHERE { + session:account/mu:uuid ?session_account. + }") + (grant (read) :to-graph (acmidm-lezer acmidm-vendor) :for-allowed-group "ABBOrganisatiePortaalGebruiker-lezer") (grant (read write) - :to-graph (acmidm-editeerder acmidm-vendor) + :to-graph (acmidm-editeerder acmidm-vendor chat) :for-allowed-group "ABBOrganisatiePortaalGebruiker-editeerder") (grant (read write) @@ -366,4 +394,8 @@ (grant (read write) :to-graph (reports jobs system-jobs) - :for-allowed-group "o-admin-rwf") \ No newline at end of file + :for-allowed-group "o-admin-rwf") + +(grant (read write) + :to-graph (chat) + :for-allowed-group "chat-owner") diff --git a/config/delta/rules.js b/config/delta/rules.js index 33289e8..1cc784b 100644 --- a/config/delta/rules.js +++ b/config/delta/rules.js @@ -199,6 +199,27 @@ export default [ gracePeriod: 1000, ignoreFromSelf: true } + }, + // The chat's data (per-user graphs) is written straight to the store by + // the natural-language-report service, so mu-cl-resources only sees it + // fresh when a delta clears its caches. + { + match: { + subject: {} + }, + callback: { + url: 'http://resource/.mu/delta', + method: 'POST' + }, + options: { + resourceFormat: 'v0.0.1', + gracePeriod: 250, + ignoreFromSelf: true, + optOutMuScopeIds: [ + "http://redpencil.data.gift/id/concept/muScope/deltas/initialSync", + "http://redpencil.data.gift/id/concept/muScope/deltas/vendor-data" + ] + } } /* Commenting ldes rules until ready to go to qa/prod. In the meantime, we override the configuration on dev. diff --git a/config/dispatcher/dispatcher.ex b/config/dispatcher/dispatcher.ex index 1fcc85b..35c5e2a 100644 --- a/config/dispatcher/dispatcher.ex +++ b/config/dispatcher/dispatcher.ex @@ -248,6 +248,47 @@ defmodule Dispatcher do Proxy.forward(conn, path, "http://resource/kbo-organizations/") end + ############################################################### + # CHAT + # To the resource service, not the cache: the data is per user and + # changes every few seconds. + ############################################################### + + match "/chat-conversations/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://resource/chat-conversations/") + end + + match "/chat-messages/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://resource/chat-messages/") + end + + match "/chat-instant-messages/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://resource/chat-instant-messages/") + end + + match "/chat-documents/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://resource/chat-documents/") + end + + match "/chat-agents/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://resource/chat-agents/") + end + + # The chat frontend's login screens speak the gebruiker/bestuurseenheid + # resource names (frontend-lblod-chat, used verbatim). The same data as + # /users and /groups, seen through the loket naming. + match "/gebruikers/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://cache/gebruikers/") + end + + match "/bestuurseenheden/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://cache/bestuurseenheden/") + end + + match "/bestuurseenheid-classificatie-codes/*path", %{accept: [:json], layer: :api} do + Proxy.forward(conn, path, "http://cache/bestuurseenheid-classificatie-codes/") + end + # Dashboard match "/jobs/*path", %{accept: [:json], layer: :api} do Proxy.forward(conn, path, "http://cache/jobs/") @@ -417,6 +458,15 @@ defmodule Dispatcher do forward(conn, path, "http://error-report-service/error-reports/") end + ################################################################# + # CHAT ASSISTANT + # The chat's turn endpoint: POST /assistant/conversations/:id/turns + ################################################################# + + match "/assistant/*path" do + forward(conn, path, "http://natural-language-report/assistant/") + end + ################################################################# # FILES ################################################################# @@ -491,6 +541,20 @@ defmodule Dispatcher do forward conn, [], "http://frontend-dashboard/index.html" end + # The chat frontend (frontend-lblod-chat). Hosted on the "chat" + # subdomain (reverse_host), so the SPA stays a separate app. + get "/assets/*path", %{ reverse_host: ["chat" | _rest], layer: :api } do + forward conn, path, "http://chat/assets/" + end + + get "/@appuniversum/*path", %{ reverse_host: ["chat" | _rest], layer: :api } do + forward conn, path, "http://chat/@appuniversum/" + end + + match "/*_path", %{ reverse_host: ["chat" | _rest], layer: :api, accept: [:html] } do + forward conn, [], "http://chat/index.html" + end + match "/assets/*path", %{layer: :api} do Proxy.forward(conn, path, "http://frontend/assets/") end diff --git a/config/resources/auth.json b/config/resources/auth.json index 2046ef1..89d7781 100644 --- a/config/resources/auth.json +++ b/config/resources/auth.json @@ -53,6 +53,12 @@ "target": "user", "cardinality": "one", "inverse": true + }, + "gebruiker": { + "predicate": "foaf:account", + "target": "gebruikers", + "cardinality": "one", + "inverse": true } }, "new-resource-base": "http://data.lblod.info/id/account/" diff --git a/config/resources/domain.lisp b/config/resources/domain.lisp index f2d2fd4..2e93235 100644 --- a/config/resources/domain.lisp +++ b/config/resources/domain.lisp @@ -16,6 +16,7 @@ (read-domain-file "domain.json") (read-domain-file "auth.json") +(read-domain-file "chat-domain.json") (read-domain-file "change-events.json") (read-domain-file "file.json") (read-domain-file "dcat.json") diff --git a/docker-compose.yml b/docker-compose.yml index 71d640a..b589ddd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -684,6 +684,43 @@ services: restart: always logging: *default-logging + ################################################################################ + # CHAT / NATURAL LANGUAGE REPORTS + ################################################################################ + natural-language-report: + image: lblod/natural-language-report-service:0.1.0 + build: ../natural-language-report-service/ + volumes: + - ./data/files:/share + - ./config/report-profiles/:/config/profiles/ + environment: + MU_SPARQL_ENDPOINT: "http://db:8890/sparql" + CSV_SEPARATOR: ";" + SUBJECT_CHUNK_SIZE: "100" + ROW_LIMIT: "200000" + MAX_PATH_DEPTH: "8" + # The chat's turn answers in the background; full-organisation runs + # easily outgrow a minute. + RUN_TIMEOUT: "300" + INLINE_VALUES_MAX: "50" + VALUES_TTL: "3600" + labels: + - "logging=true" + restart: always + logging: *default-logging + chat: + image: lblod/frontend-lblod-chat:0.1.0 + build: ../frontend-lblod-chat/ + links: + - identifier:backend + environment: + EMBER_ASSISTANT_PATH: "/assistant" + EMBER_APP_NAME: "Rapportassistent" + labels: + - "logging=true" + restart: always + logging: *default-logging + ################################################################################### # MU SCRIPTS # #################################################################################