@@ -5,8 +5,9 @@ com contrato de entrada/saída, trilhas (**Pipelines**), execução assíncrona
55cron e painel Vue opcional.
66
77> Projeto em desenvolvimento. Esta versão entrega o ** Core Runner** (execução síncrona de Stages e de
8- > trilhas), o SDK Python ** ` psrunner ` ** , a ** persistência em PostgreSQL** (catálogo versionado e histórico) e a
9- > ** execução assíncrona** com RabbitMQ, worker e agendamento cron.
8+ > trilhas), o SDK Python ** ` psrunner ` ** , a ** persistência em PostgreSQL** (catálogo versionado e histórico), a
9+ > ** execução assíncrona** com RabbitMQ, worker e agendamento cron e o ** painel embutível** (API, tempo real e acesso
10+ > configurável; a interface Vue chega numa próxima versão).
1011
1112## Como funciona
1213
@@ -155,6 +156,67 @@ O host `hosts/PythonScriptRunner.WorkerHost` é um worker pronto, configurado po
155156(` ConnectionStrings__psr ` , ` ConnectionStrings__rabbitmq ` , ` PythonScriptRunner__Worker__MaxConcurrency ` ,
156157` PythonScriptRunner__Secrets__<NOME> ` , ` PSR_AUTO_MIGRATE=true ` ), com ` /health/live ` , ` /health/ready ` , logs JSON e OTLP.
157158
159+ ## Painel (Dashboard)
160+
161+ Com ` PythonScriptRunner.Dashboard ` , o painel é embutido na sua aplicação, no estilo do Hangfire: API interna, atualizações
162+ em tempo real (SignalR) e a interface servida no prefixo escolhido. Sem ` MapPythonScriptRunnerDashboard ` não existe painel.
163+
164+ ``` csharp
165+ builder .Services
166+ .AddPythonScriptRunner ()
167+ .AddEntityFrameworkCore (o => o .UseNpgsql (builder .Configuration .GetConnectionString (" psr" )! ))
168+ .AddRabbitMq (o => o .ConnectionString = builder .Configuration .GetConnectionString (" rabbitmq" ))
169+ .AddDashboard (o => o .UseFixedLogin (login => login .BindConfiguration ())); // seção PythonScriptRunner:Dashboard:Login
170+
171+ var app = builder .Build ();
172+ app .MapPythonScriptRunnerDashboard (" /psr" ); // API em /psr/api, tempo real em /psr/hubs/runs, painel em /psr/
173+ ```
174+
175+ Escolha ** um** modo de acesso; os papéis são cumulativos: ` Reader ` (vê tudo) → ` Executor ` (dispara, cancela,
176+ reprocessa) → ` Author ` (edita, testa, publica, importa, agenda) → ` Admin ` (exclui, retenção, segredos, auditoria).
177+
178+ | Modo | Configuração |
179+ | ------| --------------|
180+ | Login fixo | ` o.UseFixedLogin(l => l.BindConfiguration()) ` com usuários na configuração (abaixo) ou ` l.Users ` em código |
181+ | Filtro próprio | ` o.UseAuthorizationFilter<MeuFiltro>() ` : ` IDashboardAuthorizationFilter ` devolve ` DashboardUser(nome, papel) ` ou ` null ` |
182+ | Autenticação do host | ` o.UseHostAuthentication(r => { r.Reader = p => p.RequireRole("psr-leitor"); /* Executor, Author, Admin */ r.LoginUrl = "/conta/entrar?returnUrl={returnUrl}"; }) ` |
183+ | Somente local | ` o.AllowLocalRequests = true ` — só em Development (fora dele a inicialização falha); soma-se a um modo principal |
184+
185+ ``` json
186+ "PythonScriptRunner" : {
187+ "Dashboard" : {
188+ "Login" : {
189+ "Users" : [
190+ { "Name" : " ana" , "Role" : " Admin" , "PasswordHash" : " AQAAAAIAAYagAAAAE..." },
191+ { "Name" : " bruno" , "Role" : " Reader" , "PasswordHash" : " AQAAAAIAAYagAAAAE..." }
192+ ]
193+ }
194+ }
195+ }
196+ ```
197+
198+ A senha nunca vai para a configuração, só o hash (PBKDF2, formato do ASP.NET Identity), gerado pela ferramenta ` psr ` :
199+
200+ ``` bash
201+ dotnet tool install -g PythonScriptRunner.Tool
202+ psr hash-password # digita a senha duas vezes, sem eco
203+ echo " senha" | psr hash-password --stdin # em scripts
204+ ```
205+
206+ No login fixo a sessão é um cookie ` psr.session ` (HttpOnly, ` SameSite=Lax ` ) de 8 h deslizantes e no máximo 24 h; 5
207+ falhas seguidas bloqueiam o usuário por 15 min e um IP pode tentar 10 vezes por minuto. Alterações na configuração valem
208+ sem reiniciar (usuário removido ou senha trocada perde a sessão; papel novo vale na hora). As chaves do cookie ficam em
209+ ` psr.dashboard_keys ` , então a sessão vale em todas as instâncias e sobrevive a reinícios.
210+
211+ Na API, requisições que alteram algo exigem o cabeçalho ` X-PSR-Request: 1 ` , edições usam ` ETag ` /` If-Match ` e erros
212+ seguem ` ProblemDetails ` com ` code ` . Em Development, o documento OpenAPI (interno) fica em ` /psr/api/openapi/v1.json ` .
213+
214+ O host ` hosts/PythonScriptRunner.DashboardHost ` é um painel pronto: login fixo por variáveis de ambiente
215+ (` PythonScriptRunner__Dashboard__Login__Users__0__Name ` , ` __Role ` , ` __PasswordHash ` ) ou OpenID Connect com
216+ ` Oidc__Authority ` , ` Oidc__ClientId ` e ` Oidc__ClientSecret ` (papéis pela claim ` Dashboard__RoleClaim ` , padrão ` roles ` , e
217+ ` Dashboard__Roles__Reader|Executor|Author|Admin ` ). Também aceita ` Dashboard__PathPrefix ` (padrão ` /psr ` ),
218+ ` ASPNETCORE_FORWARDEDHEADERS_ENABLED=true ` atrás de proxy e ` PSR_AUTO_MIGRATE=true ` .
219+
158220## Requisitos
159221
160222- .NET 10 SDK
@@ -170,7 +232,9 @@ O host `hosts/PythonScriptRunner.WorkerHost` é um worker pronto, configurado po
170232| ` src/PythonScriptRunner.EntityFrameworkCore ` | PostgreSQL: catálogo versionado, histórico, arquivos, idempotência, outbox, auditoria, retenção, agendamentos e ciclo das execuções enfileiradas |
171233| ` src/PythonScriptRunner.RabbitMQ ` | topologia, relay do outbox, ` IRunQueue ` , comando público, eventos e assinaturas |
172234| ` src/PythonScriptRunner.Worker ` | execução dos passos enfileirados, * lease* , retry, cancelamento, cron |
173- | ` hosts ` | ` WorkerHost ` e ` ServiceDefaults ` (Serilog, OpenTelemetry, health checks) |
235+ | ` src/PythonScriptRunner.Dashboard ` | painel embutível: API interna, hub SignalR, modos de acesso (login fixo, filtro, host, local) |
236+ | ` src/PythonScriptRunner.Tool ` | dotnet tool ` psr ` (` psr hash-password ` ) |
237+ | ` hosts ` | ` WorkerHost ` , ` DashboardHost ` e ` ServiceDefaults ` (Serilog, OpenTelemetry, health checks) |
174238| ` python/psrunner ` | SDK Python (` @stage ` , ` Context ` , ` File ` , ` StageError ` , ` psrunner.testing ` , CLI) |
175239| ` schemas ` | JSON Schemas do ` stage.json ` , do ` *.pipeline.json ` e das mensagens públicas |
176240| ` tests ` | testes .NET (unitários e de integração com Python real) e fixtures de trilha |
@@ -183,12 +247,17 @@ dotnet test --project tests/PythonScriptRunner.Core.Tests
183247dotnet test --project tests/PythonScriptRunner.EntityFrameworkCore.Tests # precisa de Docker
184248dotnet test --project tests/PythonScriptRunner.RabbitMQ.Tests # precisa de Docker
185249dotnet test --project tests/PythonScriptRunner.Worker.Tests # precisa de Docker
250+ dotnet test --project tests/PythonScriptRunner.Dashboard.Tests # precisa de Docker
186251
187252cd python/psrunner
188253uv sync
189254uv run pytest
190255```
191256
257+ O teste de desempenho do painel usa ~ 200 mil execuções; ` PSR_PERF_RUNS_PER_DAY=50000 ` roda o volume médio completo (90 dias).
258+ O documento OpenAPI do painel é comparado com ` tests/PythonScriptRunner.Dashboard.Tests/Snapshots/openapi-v1.verified.json ` ;
259+ depois de revisar uma mudança intencional, ` PSR_ACCEPT_SNAPSHOTS=1 ` atualiza o snapshot.
260+
192261## Licença
193262
194263[ MIT] ( LICENSE )
0 commit comments