Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
aeeca02
fix: Remove nldesign header-override CSS and fix webpack build
rubenvdlinde Mar 19, 2026
416004e
fix: Resolve PHPCS line-length violation and fix invalid named parame…
rubenvdlinde Mar 19, 2026
fe4436d
fix: Use grype step output for SBOM CVE scan
rubenvdlinde Mar 19, 2026
a540751
chore: move widgets-vs-tiles doc from website/ to docs/
rubenvdlinde Mar 19, 2026
2dc7b3f
fix: Add grype ignore list for known false-positive CVEs
rubenvdlinde Mar 19, 2026
eb76a84
chore: update SBOM
github-actions[bot] Mar 19, 2026
97c5da6
chore: remove duplicate docusaurus/ folder and standardize workflow
rubenvdlinde Mar 19, 2026
84397d7
chore: remove accidentally committed .docusaurus cache
rubenvdlinde Mar 19, 2026
0a0f408
fix: exclude node_modules from Docusaurus docs path
rubenvdlinde Mar 19, 2026
c8d0949
feat: add Dutch (nl) locale support for documentation
rubenvdlinde Mar 19, 2026
aff8f31
chore: add missing generated artifact entries to .gitignore
rubenvdlinde Mar 19, 2026
0512b35
chore: Update openspec config and gitignore docs build artifacts
rubenvdlinde Mar 19, 2026
b4a53ac
feat: Enrich all 9 MyDash dashboard specs with deep research
rubenvdlinde Mar 20, 2026
c751b25
chore: Convert all specs to change proposals
rubenvdlinde Mar 20, 2026
1a604fa
feat: Implement + archive dashboards
rubenvdlinde Mar 21, 2026
28ad4dd
feat: Implement + archive widgets
rubenvdlinde Mar 21, 2026
20204a4
feat: Implement + archive tiles
rubenvdlinde Mar 21, 2026
989201d
feat: Implement + archive grid-layout
rubenvdlinde Mar 21, 2026
d6957f7
feat: Implement + archive permissions
rubenvdlinde Mar 21, 2026
81f7fb9
feat: Implement + archive conditional-visibility
rubenvdlinde Mar 21, 2026
e0aeece
feat: Implement + archive admin-settings
rubenvdlinde Mar 21, 2026
cbeb699
feat: Implement + archive admin-templates
rubenvdlinde Mar 21, 2026
f79c14d
feat: Implement + archive prometheus-metrics
rubenvdlinde Mar 21, 2026
6dbc06b
chore: Add ADR-011 rule to openspec config
rubenvdlinde Mar 21, 2026
33a6c2c
docs: Add review report and archive processed changes
rubenvdlinde Mar 21, 2026
136b528
docs: Add features/README.md with standards table and feature index
rubenvdlinde Mar 23, 2026
1b40d81
Merge pull request #18 from ConductionNL/chore/specs-to-proposals
rubenvdlinde Mar 23, 2026
1104126
chore: add missing test/build artifact entries to .gitignore
rubenvdlinde Apr 9, 2026
b747b68
chore: add docs/node_modules and docs/.docusaurus to .gitignore
rubenvdlinde Apr 9, 2026
3260ccf
feat(i18n): fix missing translation and add MyDash key
rubenvdlinde Apr 16, 2026
e9f2903
fix(i18n): convert title case to sentence case in translation keys
rubenvdlinde Apr 16, 2026
5449ac0
fix(headers): @author email typo dev@conductio.nl -> info@conduction.nl
rubenvdlinde Apr 20, 2026
c6257da
scan: coverage-report for mydash (opsx-coverage-scan v1, manual drive)
rubenvdlinde Apr 24, 2026
f8f37b8
retrofit: draft legacy-widget-bridge spec + annotate 5 methods
rubenvdlinde Apr 24, 2026
6eb3f39
retrofit: archive legacy-widget-bridge change
rubenvdlinde Apr 24, 2026
0bfc52f
Merge pull request #12 from ConductionNL/fix/ci-quality-checks
rubenvdlinde Apr 24, 2026
201740d
Merge pull request #22 from ConductionNL/fix/header-info-email
rubenvdlinde Apr 24, 2026
075e8f6
Merge pull request #13 from ConductionNL/chore/gitignore-cleanup
rubenvdlinde Apr 24, 2026
888c044
Merge pull request #14 from ConductionNL/chore/openspec-config
rubenvdlinde Apr 24, 2026
b26f223
Merge pull request #23 from ConductionNL/retrofit/reverse-spec-mydash…
rubenvdlinde Apr 24, 2026
061dd10
Merge pull request #21 from ConductionNL/feature/i18n-complete-transl…
rubenvdlinde Apr 24, 2026
22d1696
chore(phpstan): fix OCP autoload (725->0 errors, 17 in baseline) (#48)
rubenvdlinde Apr 30, 2026
63abb6d
chore(test): add Vitest infrastructure (#43)
rubenvdlinde Apr 30, 2026
1da56c2
feat(infra): implement typed initial-state contract per REQ-INIT-001.…
rubenvdlinde Apr 30, 2026
8bcf2aa
feat(admin-settings): group_order setting + admin endpoints per REQ-A…
rubenvdlinde Apr 30, 2026
e5daf28
feat(resources): implement admin-only base64 upload pipeline per REQ-…
rubenvdlinde Apr 30, 2026
dfeab74
feat(dashboards): implement group-shared scope per REQ-DASH-011..014 …
rubenvdlinde Apr 30, 2026
ffede45
feat(admin-templates): primary-group routing resolver (#49)
rubenvdlinde Apr 30, 2026
e010e20
feat(resources): SVG sanitiser + invalid_svg gate per REQ-RES-009..01…
rubenvdlinde Apr 30, 2026
68e2721
feat(dashboards): default group_shared dashboard per REQ-DASH-015..01…
rubenvdlinde Apr 30, 2026
4f42fbb
feat(resources): public read endpoint + listing per REQ-RES-006..008 …
rubenvdlinde Apr 30, 2026
1e5af58
feat(widgets): text-display widget per REQ-TXT-001..005 (#50)
rubenvdlinde Apr 30, 2026
3ee5485
feat(widgets): label widget (#54)
rubenvdlinde Apr 30, 2026
7c105bd
feat(widgets): image widget with upload pipeline (#55)
rubenvdlinde Apr 30, 2026
172f636
Add 25 OpenSpec proposals for multi-tenant dashboard platform (#42)
rubenvdlinde Apr 30, 2026
e8af3d8
feat(grid): widget collision placement helper (#56)
rubenvdlinde Apr 30, 2026
0cba84f
feat(grid): responsive breakpoints (1400/1100/768/480 cols) (#57)
rubenvdlinde Apr 30, 2026
89171dc
feat(dashboard-icons): IconPicker (built-in + upload) (#58)
rubenvdlinde Apr 30, 2026
b7ee764
feat(dashboard-switcher): slide-in sidebar with 3 sections per REQ-SW…
rubenvdlinde Apr 30, 2026
07fe224
feat(widgets): right-click context menu (Edit/Remove/Cancel) per REQ-…
rubenvdlinde Apr 30, 2026
56a6381
feat(admin-settings): allow_user_dashboards runtime gate (#61)
rubenvdlinde Apr 30, 2026
d1f08e2
feat(widgets): Nextcloud Dashboard widget proxy (#63)
rubenvdlinde Apr 30, 2026
24fd5ad
feat(dashboards): active-dashboard resolution chain per REQ-DASH-018.…
rubenvdlinde Apr 30, 2026
c8c3e57
feat(dashboard-sharing): notifications + bulk management + delete cas…
rubenvdlinde Apr 30, 2026
4310e3c
feat(dashboards): fork visible dashboard as personal copy (#66)
rubenvdlinde Apr 30, 2026
0a9e248
feat(widgets): link-button widget + file-creation endpoint per REQ-LB…
rubenvdlinde Apr 30, 2026
053491c
feat(widgets): unified Add/Edit modal + form contract per REQ-WDG-010…
rubenvdlinde Apr 30, 2026
29e3c8f
feat(runtime-shell): integrate sidebar + modal + context-menu + canEd…
rubenvdlinde Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 15 additions & 1 deletion .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,23 @@ jobs:

- name: Install Grype
uses: anchore/scan-action/download-grype@v5
id: grype-install

- name: Create Grype ignore list for known false positives
run: |
if [ ! -f .grype.yaml ]; then
cat > .grype.yaml << 'GRYPE'
ignore:
# False positives: Grype matches unscoped CycloneDX component names
# against malware advisories for typosquatting packages. Actual deps
# are scoped (@jridgewell/gen-mapping, @babel/helper-validator-identifier).
- vulnerability: GHSA-8rmg-jf7p-4p22
- vulnerability: GHSA-pvjq-589m-3mc8
GRYPE
fi

- name: CVE scan SBOM
run: grype sbom:sbom.cdx.json --fail-on critical
run: ${{ steps.grype-install.outputs.cmd }} sbom:sbom.cdx.json --fail-on critical

- name: Composer audit
run: composer audit --format=json || true
Expand Down
13 changes: 11 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ vendor/

# Build artifacts
/js/
/docusaurus/build/
/docusaurus/.docusaurus/
/docs/node_modules/
/docs/build/
/docs/.docusaurus/
/docusaurus/

# Development
.DS_Store
Expand All @@ -29,7 +31,14 @@ Thumbs.db
/coverage/
/phpqa/

.phpunit.cache/
phpmetrics/

# Environment
.env
.env.local
node_modules

# Test/build artifacts
.phpunit.cache
.phpunit.result.cache
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

All notable changes to this project will be documented in this file.

## Unreleased

### Added

- **Initial-state contract** (REQ-INIT-001..REQ-INIT-005). Workspace and admin
pages now route every initial-state key through the typed PHP service
`OCA\MyDash\Service\InitialStateBuilder` (with a `Page` enum and required-key
enforcement via `MissingInitialStateException`). The mirrored typed JS reader
`src/utils/loadInitialState.js` returns a default-filled object and warns when
`INITIAL_STATE_SCHEMA_VERSION` (currently `1`) drifts between server and
client. To add a key: update the spec Data Model (REQ-INIT-002), bump
`INITIAL_STATE_SCHEMA_VERSION` in both PHP and JS, and add the setter +
reader entry in the same commit. Direct calls to
`IInitialState::provideInitialState` from controllers / settings, and direct
`loadState('mydash', ...)` calls from `src/`, are forbidden by lint.

## 0.1.0 - Initial Release

- Initial app structure
Expand Down
14 changes: 14 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,17 @@ npm start # Dev server at http://localhost:3000 with hot reload
### Adding documentation

Simply add or edit Markdown files in the `docs/` folder. The sidebar is auto-generated from the folder structure. Changes will appear on the product page after pushing to `development`.

## Security review checkboxes

### Extending the SVG whitelist

`lib/Service/SvgSanitiser.php` ships a deliberately conservative whitelist of 24 element types and 50 attribute types — see `ALLOWED_ELEMENTS` and `ALLOWED_ATTRIBUTES` (REQ-RES-010 / REQ-RES-011 in the `resource-uploads` capability).

Adding any element or attribute to either constant is a **security review checkbox**, not an editorial change. Before merging an addition, confirm:

- the element / attribute carries no executable surface (no script, no event handler, no foreign content, no URL fetch outside the existing `href` filter);
- the addition is justified by a real upload that is currently being rejected, not a speculative future need;
- a corresponding unit test covers the new whitelist entry.

The sanitiser runs server-side BEFORE the size cap (REQ-RES-009), so an over-permissive whitelist becomes stored XSS the moment a sanitised-looking SVG is rendered back into a logged-in user's browser.
190 changes: 190 additions & 0 deletions REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# MyDash Review

**Date:** 2026-03-21
**Reviewer:** Claude (automated)
**App path:** `/home/rubenlinde/nextcloud-docker-dev/workspace/server/apps-extra/mydash`

---

## 1. OpenSpec Status

**Result: CLEAN -- all changes archived, specs complete.**

| Metric | Count |
|--------|-------|
| Specs in `openspec/specs/` | 9 |
| Active changes | 0 |
| Archived changes | 9 |

All 9 specs have been implemented and archived:

1. `admin-settings` -- Admin configuration panel
2. `admin-templates` -- Group-based dashboard templates
3. `conditional-visibility` -- Rule-based widget show/hide
4. `dashboards` -- Core dashboard CRUD
5. `grid-layout` -- GridStack drag-and-drop layout
6. `permissions` -- Permission levels (add-only, full, etc.)
7. `prometheus-metrics` -- Prometheus-format metrics endpoint
8. `tiles` -- Custom shortcut tiles (link cards)
9. `widgets` -- Nextcloud widget API integration (v1 + v2)

Each spec directory contains `spec.md`. Each archive contains `proposal.md`, `design.md`, `specs/`, and `tasks.md`.

---

## 2. Unit Test Results

**Result: ALL PASSING**

```
PHPUnit 10.5.63
OK (104 tests, 291 assertions)
Time: 00:00.150, Memory: 36.00 MB
```

Test config: `phpunit.xml` (note: `phpunit-unit.xml` does not exist -- the review task template assumed it did).

Test coverage spans:
- Dashboard entity and mapper
- WidgetPlacement entity (grid position, widget ID, style config, tile fields, JSON serialization)
- Tile entity (icon type, colors, link type/value, timestamps, serialization)
- VisibilityChecker service (include/exclude rules, OR/AND logic, mixed rules)
- DashboardTemplate entity and mapper
- Permission-related logic

No warnings, no skipped tests.

---

## 3. Browser Test Results

### Main Dashboard (`/apps/mydash/`)

**Result: RENDERS SUCCESSFULLY with some Vue warnings**

The dashboard loads and displays:
- **Grid layout** with multiple widget tiles arranged in a responsive grid
- **Client Search** widget (Pipelinq) -- renders with placeholder avatars ("?"), data fetch fails (HTTP error for OpenRegister objects endpoint, expected since those objects may not exist)
- **Files** tile -- shortcut link to /apps/files, renders correctly with icon
- **Recommended files** widget -- renders with 7 file items (UUIDs from Open Registers), links work
- **Cards due today** widget (Deck) -- renders, shows "No upcoming cards"
- **Recommended files** (second instance) -- duplicate widget placement, renders identically
- **Upcoming events** widget (Calendar) -- renders with "Example event - open me!" item
- **Customize** button -- visible, opens a sidebar panel with:
- Widget search/add panel with search box
- Available widgets listed (Favorite files, Teams, Important mail, etc.)
- "Already added" indicators for active widgets
- "Create Tile" button
- Tabs for "Widgets" and "Dashboards"
- **Documentation** button -- visible alongside Customize

**Console issues (MyDash-specific):**
- `[Vue warn]: Invalid prop: type check failed` -- repeated ~15 times across widget items (likely widget item `subtitle` prop receiving wrong type)
- `[Vue warn]: Duplicate keys detected` -- duplicate key `1773...` in recommended files list
- `[NcModal] You need either set...` -- NcModal missing required prop
- `[NcSelect] An inputLabel or...` -- NcSelect accessibility warning (2 occurrences in admin)
- `[vue-select warn]: Label key "option.Ico..."` -- label key mismatch in a select component

These are Vue warnings, not blocking errors. The app remains functional.

### Admin Settings (`/settings/admin/mydash`)

**Result: RENDERS CORRECTLY**

The admin settings page displays:
- **Title:** "MyDash Settings" with external documentation link to mydash.app
- **Subtitle:** "Configure dashboard permissions and defaults"
- **Default settings section:**
- Default permission level dropdown (set to "Add only")
- Checkbox: "Allow users to create custom dashboards" (checked)
- Checkbox: "Allow users to have multiple dashboards" (checked)
- Default grid columns dropdown (set to "12")
- **Dashboard templates section:**
- "Create template" button
- Description: "Create dashboard templates that will be applied to users based on their groups."
- Empty state: "No templates yet"
- **Setting as default app section:**
- Instructions to set MyDash as default via Theming settings

---

## 4. Documentation Status

### Feature Docs (`docs/features/`)

**Result: 9 feature docs present, all with content**

| File | Lines | Topic |
|------|-------|-------|
| admin-settings.md | 29 | Global admin config |
| admin-templates.md | 34 | Group-based templates |
| conditional-visibility.md | 32 | Rule-based visibility |
| dashboards.md | 28 | Core dashboard unit |
| grid-layout.md | 29 | GridStack layout system |
| permissions.md | 23 | Permission levels |
| prometheus-metrics.md | 36 | Metrics endpoint |
| tiles.md | 25 | Custom shortcut tiles |
| widgets.md | 26 | Widget API integration |

### Screenshots (`docs/screenshots/`)

**Result: 1 screenshot present**

| File | Size |
|------|------|
| mydash-dashboard-overview.png | 140,246 bytes |

The screenshot is valid (non-zero, confirmed viewable).

### Other Docs

- `project.md` exists at repo root (3,291 bytes)
- `docs/` also contains a `node_modules/` directory (likely from a documentation site build tool like Docusaurus/Storybook) -- this should ideally be in `.gitignore`

---

## 5. Issues Found

### Blocking Issues
None. The app loads, renders, and all tests pass.

### Non-Blocking Issues

1. **Vue prop type warnings (MEDIUM):** ~15 `Invalid prop: type check failed` warnings in console. These are likely `subtitle` or similar string props receiving non-string values from widget item data. Should be fixed for cleanliness.

2. **Duplicate keys in widget list (LOW):** `Duplicate keys detected: '1773...'` -- likely the same recommended file appearing twice in the v-for list. Needs a unique key strategy.

3. **NcSelect accessibility warning (LOW):** Two NcSelect components in admin settings missing `inputLabel` prop. Easy fix.

4. **NcModal prop warning (LOW):** Modal component missing a required prop setup.

5. **`docs/node_modules/` committed or present (LOW):** The `docs/` directory contains a full `node_modules/` tree. If committed to git, this bloats the repository. Should be added to `.gitignore`.

6. **Missing `phpunit-unit.xml` (COSMETIC):** The standard Conduction app pattern uses `phpunit-unit.xml` but MyDash uses `phpunit.xml`. Not a problem functionally, but inconsistent with other apps.

7. **Client Search widget data error (EXPECTED):** The Pipelinq Client Search widget fails to fetch from `/api/objects/228/498` -- this is expected when the referenced register/schema does not exist in the current environment.

---

## 6. Codebase Size

| Category | Count |
|----------|-------|
| PHP files (`lib/`) | 64 |
| Vue/JS/TS files (`src/`) | 19 |
| Unit tests | 104 |
| Assertions | 291 |

---

## 7. Overall Assessment

**Status: GOOD -- production-ready with minor polish needed**

MyDash is in solid shape. All 9 OpenSpec features have been implemented, spec'd, and archived. The unit test suite is comprehensive (104 tests, 291 assertions, all passing). The dashboard renders correctly with a functional grid layout, widget rendering (both API and legacy callback widgets), tile shortcuts, a customize sidebar, and admin settings with permission controls and template management.

The main areas for improvement are:
- Fix the ~15 Vue prop type warnings to clean up the console
- Add `inputLabel` to NcSelect components for accessibility compliance
- Ensure `docs/node_modules/` is gitignored
- Consider adding more screenshots to `docs/screenshots/` to document the customize panel, admin settings, and template creation flows
67 changes: 67 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,51 @@

// User dashboard endpoints
['name' => 'dashboard_api#list', 'url' => '/api/dashboards', 'verb' => 'GET'],
['name' => 'dashboard_api#visible', 'url' => '/api/dashboards/visible', 'verb' => 'GET'],
// REQ-DASH-019: persist active-dashboard preference. Registered BEFORE
// the group-scoped routes that share the /api/dashboards/ prefix so the
// router matches the literal 'active' segment before any {groupId} wildcard.
['name' => 'dashboard_api#setActiveDashboard', 'url' => '/api/dashboards/active', 'verb' => 'POST'],
// REQ-DASH-020..022: fork a visible dashboard as a personal copy.
// Registered BEFORE the group-scoped {groupId} wildcard routes to
// prevent the literal 'fork' suffix being consumed by any wildcard.
['name' => 'dashboard_api#fork', 'url' => '/api/dashboards/{uuid}/fork', 'verb' => 'POST',
'requirements' => ['uuid' => '[A-Za-z0-9\-]+']],
['name' => 'dashboard_api#getActive', 'url' => '/api/dashboard', 'verb' => 'GET'],
['name' => 'dashboard_api#create', 'url' => '/api/dashboard', 'verb' => 'POST'],
['name' => 'dashboard_api#update', 'url' => '/api/dashboard/{id}', 'verb' => 'PUT'],
['name' => 'dashboard_api#delete', 'url' => '/api/dashboard/{id}', 'verb' => 'DELETE'],
['name' => 'dashboard_api#activate', 'url' => '/api/dashboard/{id}/activate', 'verb' => 'POST'],

// Group-shared dashboard endpoints (REQ-DASH-014). The groupId
// path parameter accepts any valid Nextcloud group ID plus the
// reserved 'default' sentinel (REQ-DASH-012).
['name' => 'dashboard_api#listGroup', 'url' => '/api/dashboards/group/{groupId}', 'verb' => 'GET',
'requirements' => ['groupId' => '[^/]+']],
['name' => 'dashboard_api#createGroup', 'url' => '/api/dashboards/group/{groupId}', 'verb' => 'POST',
'requirements' => ['groupId' => '[^/]+']],
['name' => 'dashboard_api#getGroup', 'url' => '/api/dashboards/group/{groupId}/{uuid}', 'verb' => 'GET',
'requirements' => ['groupId' => '[^/]+', 'uuid' => '[A-Za-z0-9\-]+']],
['name' => 'dashboard_api#updateGroup', 'url' => '/api/dashboards/group/{groupId}/{uuid}', 'verb' => 'PUT',
'requirements' => ['groupId' => '[^/]+', 'uuid' => '[A-Za-z0-9\-]+']],
['name' => 'dashboard_api#deleteGroup', 'url' => '/api/dashboards/group/{groupId}/{uuid}', 'verb' => 'DELETE',
'requirements' => ['groupId' => '[^/]+', 'uuid' => '[A-Za-z0-9\-]+']],
// Default-flip endpoint (REQ-DASH-015). Body: {"uuid": "..."}.
['name' => 'dashboard_api#setGroupDefault', 'url' => '/api/dashboards/group/{groupId}/default', 'verb' => 'POST',
'requirements' => ['groupId' => '[^/]+']],

// Dashboard sharing endpoints (REQ-SHARE-001..010).
// Per-row operations.
['name' => 'dashboard_share_api#index', 'url' => '/api/dashboard/{id}/shares', 'verb' => 'GET'],
['name' => 'dashboard_share_api#create', 'url' => '/api/dashboard/{id}/shares', 'verb' => 'POST'],
['name' => 'dashboard_share_api#destroy', 'url' => '/api/dashboard/share/{shareId}', 'verb' => 'DELETE'],
// Bulk replace — REQ-SHARE-009.
['name' => 'dashboard_share_api#replace', 'url' => '/api/dashboard/{id}/shares', 'verb' => 'PUT'],
// Revoke all for recipient — REQ-SHARE-010.
['name' => 'dashboard_share_api#revokeForRecipient',
'url' => '/api/sharees/{shareType}/{shareWith}', 'verb' => 'DELETE',
'requirements' => ['shareType' => '[^/]+', 'shareWith' => '[^/]+']],

// Widget endpoints
['name' => 'widget_api#listAvailable', 'url' => '/api/widgets', 'verb' => 'GET'],
['name' => 'widget_api#getItems', 'url' => '/api/widgets/items', 'verb' => 'GET'],
Expand Down Expand Up @@ -52,5 +91,33 @@
['name' => 'admin#deleteTemplate', 'url' => '/api/admin/templates/{id}', 'verb' => 'DELETE'],
['name' => 'admin#getSettings', 'url' => '/api/admin/settings', 'verb' => 'GET'],
['name' => 'admin#updateSettings', 'url' => '/api/admin/settings', 'verb' => 'PUT'],
['name' => 'admin#listGroups', 'url' => '/api/admin/groups', 'verb' => 'GET'],
['name' => 'admin#updateGroupOrder', 'url' => '/api/admin/groups', 'verb' => 'POST'],

// Resource uploads (admin-only base64 mini file API)
['name' => 'resource#upload', 'url' => '/api/resources', 'verb' => 'POST'],

// File creation endpoint (REQ-LBN-004). Non-admin; strict server-side
// validation via FileService (filename regex, dir traversal, extension
// allow-list). See lib/Controller/FileController.php.
['name' => 'file#createFile', 'url' => '/api/files/create', 'verb' => 'POST'],

// Resource listing — REQ-RES-007. Logged-in user only (no admin
// gate); the listed names are already referenced from rendered
// dashboards so admin gating would lock dashboards out of their
// own assets. Registered under the standard `routes` array
// alongside the existing POST upload (mydash currently has no
// OCS infrastructure — using a plain web route keeps the read
// surface consistent with the upload surface).
['name' => 'resource_serve#listResources', 'url' => '/api/resources', 'verb' => 'GET'],

// Public resource serving — REQ-RES-006. NON-OCS plain web
// route returning a StreamResponse with extension-derived
// Content-Type and a one-year immutable cache header. The
// `[^/]+` requirement on {filename} blocks path traversal at
// the routing layer (the controller also re-checks for
// defence in depth).
['name' => 'resource_serve#getResource', 'url' => '/resource/{filename}', 'verb' => 'GET',
'requirements' => ['filename' => '[^/]+']],
],
];
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
}
],
"require": {
"php": "^8.1"
"php": "^8.1",
"ramsey/uuid": "^4.9"
},
"require-dev": {
"cyclonedx/cyclonedx-php-composer": "^6.2",
Expand Down
Loading
Loading