devfactory-apps-console
A Firebase-console-like internal developer platform (IDP) for the DevFactory Dev
Componentes
Páginas 28
| Arquivo | Título |
|---|---|
404.html | Página não encontrada | Dev Factory Console |
activity/index.html | Atividade | Console Dev Factory |
admin/index.html | Plataforma | Console Dev Factory |
admin-users/index.html | Usuários | Console Dev Factory |
applications/index.html | Aplicações | Console Dev Factory |
auth/index.html | Signing in | Dev Factory Console |
docs/configuracao-ambiente.html | Dev Factory — Configuração de Ambiente |
docs/index.html | Documentação | Console Dev Factory |
index.html | Dashboard | Console Dev Factory |
my-requests/index.html | Minhas solicitações | Console Dev Factory |
plugins/index.html | Plugins | Console Dev Factory |
projects/applications.html | Aplicações | Console Dev Factory |
projects/auth.html | Autenticação | Console Dev Factory |
projects/connect.html | Connect | Console Dev Factory |
projects/detail.html | Projeto | Console Dev Factory |
projects/documentation.html | Documentação | Console Dev Factory |
projects/index.html | Projetos | Console Dev Factory |
projects/members.html | Membros | Console Dev Factory |
projects/observability.html | Observabilidade | Console Dev Factory |
projects/rbac.html | App RBAC | Console Dev Factory |
projects/releases.html | Releases | Console Dev Factory |
projects/security-report.html | Relatório de Segurança | Console Dev Factory |
projects/security.html | Segurança do projeto | Console Dev Factory |
requests/index.html | Solicitações | Console Dev Factory |
resources/index.html | Recursos | Console Dev Factory |
security/index.html | Segurança | Console Dev Factory |
settings/index.html | Configurações | Console Dev Factory |
usage/index.html | Meu consumo | Console Dev Factory |
Estrutura
README
Dev Factory — Console
A Firebase-console-like internal developer platform (IDP) for the DevFactory Dev Factory. Developers sign in, create projects, and provision AWS resources (S3, ECR, DynamoDB, EKS) with a click. Each resource moves through a lifecycle (pending → provisioning → active, or failed), which the console polls and reflects live with status badges.
This is a fully static site — plain HTML + Vue 3, Tailwind, Font Awesome and LESS, all from CDNs, compiled in the browser. No build step, no bundler.
Stack & conventions
- Vue 3 via CDN (
vue.global.js), Tailwind via CDN, Font Awesome - View / ViewModel pattern: every page folder has
index.html(ordetail.html) — the markup shell,*-view.js— a one-liner that imports the viewmodel,*-viewmodel.js— theVue.createApp({...}).mount('#app').- Directory-based routing — one
.htmlfile per section.src/serve.json - Shared code under
src/_models/(config + services) andsrc/_assets/
via CDN, LESS compiled in-browser (less@4).
adds clean-URL rewrites (/projects, /resources, /settings, …).
(styles, scripts, favicon).
File tree
src/
env.js # runtime config (apiBaseUrl, authMode, environment)
index.html / -view / -viewmodel # Dashboard (stat cards + recent activity)
version.json # version badge source
serve.json # clean-URL rewrites for `serve`
auth/ # MSAL redirect landing page (/auth/)
projects/
index.* # project card grid + "Create project" modal
detail.* # project header, resources table, provision modal, activity, polling
resources/
index.* # flat cross-project resource list with type/status filters
settings/
index.* # current user, API base URL, environment, version
_models/
config.js # Entra ids (placeholders) + window.__ENV__ accessor
services/
auth-service.js # MSAL/Entra wrapper + dev bypass
auth-guard.js # requireAuth() page guard
api-client.js # fetch wrapper (Bearer token, baseUrl from env)
projects-service.js # /projects, /projects/{id}/resources, /activity
resources-service.js # /resources/{id} + cross-project aggregation
catalog-service.js # /catalog + defaultConfigFor() helper
tags-service.js # /projects/{id}/tags, /tags, /tags/taxonomy
saved-views-service.js # /saved-views (named, pinnable tag filters)
observability-service.js # /projects/{id}/observability (register + explore telemetry)
tag-filter.js # pure faceting/drill-down helpers (unit-testable)
_assets/
favicon.svg
styles/app.less + variables/*.less # DevFactory energy design tokens
scripts/toast.js, version-display.js
tests/ # Playwright specs (e01 dashboard … e08 tags, e09 observability)
playwright.config.js
package.json
wrangler.toml # Cloudflare Pages: pages_build_output_dir = ./src
Project tags, filtering & saved views
The Projects page is a faceted catalog. Projects carry AWS-style key/value tags; the page builds an emergent hierarchy from the tag *keys* (e.g. organization → workspace → team) — there are no fixed org/workspace entities, just tags.
- Faceted filter panel (left of the grid) — one collapsible section per tag key, ordered by the
- Hierarchical drill-down — each facet's value counts are computed against the set narrowed by
- Saved views — name the current filter and reuse it.
personalviews are yours;globalviews - Tag editor — on a project's detail page (header card); each tag chip links to the list
platform taxonomy (/tags/taxonomy). Tick values to filter; the grid narrows live and the URL reflects the selection (?tags=organization:energia;workspace:ops — shareable / bookmarkable).
every *other* active facet, so picking organization=energia makes the workspace facet show only the workspaces under energia, and so on, for any number of levels. Logic lives in the pure, unit-testable _models/tag-filter.js (AND across keys, OR within a key).
(platform admins) are visible to everyone. Pinned views appear in the views bar *and* the sidebar (?view=<id> opens the list pre-filtered, then you keep drilling down). Persisted server-side via /saved-views.
pre-filtered by that tag.
Covered by tests/e08-tags-filter.spec.js.
Observability explorer
Each project has an Observability page (projects/observability.html) — the data explorer, but for app telemetry (events / logs / metrics / traces). It shows summary cards (signals, errors, error rate, by-kind), a 24h sparkline, filterable rows (env / kind / level / search) with expandable attributes, and a Send test signal action. Apps register telemetry with their project key; in aws mode the platform mirrors it to Amazon CloudWatch. Service: observability-service.js; covered by tests/e09-observability.spec.js.
Running locally
npx serve src # serves the static site (uses src/serve.json rewrites)
# → open http://localhost:3000 (serve picks a free port; default 3000)
No install is required to *view* the site — every dependency is a CDN. Out of the box src/env.js ships with authMode: 'none', so it runs immediately with a stub Dev User and no real identity provider.
Configuration (src/env.js)
env.js is loaded as a plain <script> before every module on every page and is the single place to point the console at an API and toggle auth:
window.__ENV__ = {
apiBaseUrl: 'http://localhost:8080', // Dev Factory API base (no trailing slash)
authMode: 'none', // 'none' = dev bypass, 'entra' = real MSAL login
environment:'local'
};
How routing / auth / the API client work
Routing
There is no client-side router. Each section is its own .html file linked from the sidebar. serve.json (and an equivalent rewrite in production hosting) maps clean paths like /projects to /projects/index.html. The project-detail page reads its id from the query string: projects/detail.html?id=<projectId>.
Auth
_models/services/auth-service.js wraps MSAL.js for Microsoft Entra. It is loop-safe (one MSAL instance per origin; handleRedirectPromise() runs on every load). Protected pages call requireAuth() (from auth-guard.js) at the start of mounted(); if unauthenticated it redirects to /auth/, which performs the login redirect and returns the user to where they started.
Dev bypass — so the app runs with no real tenant — is active when ANY of:
window.__ENV__.authMode === 'none'(the local default), or- the URL has
?devauth=1, or localStorage['df_dev_auth'] === '1'.
In bypass mode getToken() returns the dummy token 'dev-token', isAuthenticated() is true, and getUser() returns { name: 'Dev User', email: 'dev@devfactory' }.
Real Entra ids live in _models/config.js as placeholders marked // TODO: replace with DevFactory Entra tenant — replace them before using authMode: 'entra'.
API client
_models/services/api-client.js is a small fetch wrapper. Base URL comes from window.__ENV__.apiBaseUrl (default /api). Every request sends Authorization: Bearer <token> using the token from auth-service. Domain services build on it:
| Endpoint | Service method |
|---|---|
GET /health | apiClient.get('/health') |
GET /projects, POST /projects | projectsService.listProjects/createProject |
GET /projects/{id} | projectsService.getProject |
GET /projects/{id}/resources, POST …/resources | projectsService.listResources/provisionResource |
GET /projects/{id}/activity | projectsService.getActivity |
GET /catalog | catalogService.getCatalog |
GET/DELETE /resources/{id} | resourcesService.getResource/deleteResource |
The provision-resource modal renders its type picker and dynamic config fields from /catalog (ResourceTypeSpec.fields, supporting bool, string, number, select). After provisioning, the detail page polls resources every 4s while any are pending/provisioning/deleting and updates the status badges.
All pages degrade gracefully when the API is unreachable (empty states + a non-blocking warning banner).
Testing
Playwright specs live in tests/ and run against the static site served by serve (see playwright.config.js, webServer → npx serve src -l 3000, baseURL http://localhost:3000).
npm test # all specs
npm run test:e01 # dashboard
npm run test:e02 # projects + create modal
npm run test:e03 # provision-resource (catalog-driven fields)
Tests use ?devauth=1 to bypass auth and install an in-browser API mock (tests/helpers/mock-api.js) matching the API contract, so the suite is green standalone — no real API needed. API-dependent assertions are driven by that mock; the app itself still works (with empty states) when no API is up.
Deployment
Configured for Cloudflare Pages (wrangler.toml, pages_build_output_dir = "./src"). Point env.js at the production API and set authMode: 'entra' (after filling in the real Entra ids in _models/config.js).