Skip to content

Commit 09445b1

Browse files
authored
Feat/v5 support (#102)
* feat: Strapi v5 Support with Dashboard Widget & Mobile-Optimized UI ## Features - ✅ Strapi v5.3.2 compatibility with @strapi/sdk-plugin - ✅ Dashboard widget showing real-time Socket.IO statistics - ✅ Mobile-optimized Monitoring & Settings pages with responsive design - ✅ Enhanced input fields (48px height, 16px font on mobile) - ✅ Fixed GitHub Issues #95 (images collection) and #82 (CollectionTypes with relations) - ✅ Entity Service API instead of deprecated db.query() - ✅ Multi-language support (EN, DE, ES, FR, PT) ## UI/UX Improvements - Modern styled-components for responsive layouts - Touch-friendly inputs with proper sizing - Hidden number spinners on mobile for better UX - Gradient stat cards with hover effects - Auto-refresh every 60 seconds to reduce database load ## Technical Changes - Uses Entity Service API for proper relation handling - Transform service for media and relation fields - Proper sanitization and permission checks - Redis adapter support for multi-server scaling - Namespace support for isolated Socket.IO endpoints - Role-based permissions with content-type granularity * fix: Security & Transaction Fixes for Strapi v5 🔒 Security: - Add sanitize-sensitive-fields middleware - Filters password, tokens, secrets from Socket.IO events - Recursive deep sanitization for nested objects 🔧 Transaction Fixes: - Use transactionCtx.onCommit for lifecycle hooks - Prevent 'Transaction query already complete' errors - afterDelete uses raw() to avoid sanitization queries - Disable bulkDeleteMany hooks (Strapi v5 transaction limitations) ✅ Event Matrix: - Create (single/bulk): Full events with sanitization - Update (single/bulk): Full events with sanitization - Delete (single): ID-only events via raw() - Delete (bulk): Disabled due to transaction conflicts * fix: Remove eval() from transaction context loading - Replace eval('require') with lazy-loaded require() - Cleaner transaction context initialization - No more build warnings - Maintains same functionality for post-commit hooks * refactor: apply Strapi v5 best practices - Remove emoji from service welcome message - Convert React default imports to named imports (3 files) - Document legitimate Query Engine usage for admin::api-token * admin::api-token is Core Admin Entity * Follows official Strapi Core implementation * Source: strapi/strapi/packages/core/admin/server/strategies/api-token.js - Build clean without warnings Note: Using strapi.db.query() for admin::api-token is correct per Strapi Core implementation * chore: migrate to @strapi-community/plugin-io v5.0.0 - Rename package from strapi-plugin-io to @strapi-community/plugin-io - Update version from 3.0.0 to 5.0.0 (matching Strapi v5) - Modernize README with better structure and examples - Update all documentation references to new package name - Update repository URLs to strapi-community organization - Update TypeScript import paths - Remove internal DOCUMENTATION_UPDATE.md file - Update migration guide with v5 versioning - Enhance Getting Started guide - Build and verify successful for v5.0.0 * fix: remove spinner buttons from number inputs in settings - Hide increment/decrement buttons for cleaner UI - Removed browser native spinners and Strapi NumberInput spinners - Simplified CSS for number inputs - Better mobile and desktop experience * feat: improve dark mode support for monitoring page - Replace hardcoded colors with Strapi theme system - Use theme.colors.neutral0, neutral100, neutral150 etc. - Proper background colors for cards and items - Dark mode compatible shadows - Better contrast in both light and dark modes - Keep gradient header with white text for visual impact * fix: add dark mode support for event type filter select - Created StyledSelect component with theme-aware colors - Removed hardcoded white background and border colors - Added proper hover and focus states - Dark mode compatible dropdown options * docs: add comprehensive CHANGELOG.md - Document v5.0.0 release with all breaking changes - Include migration path from v2 to v5 - List all new features and improvements - Document UI/UX improvements - Add version support matrix - Include deprecation notices - Add upcoming features roadmap * docs: comprehensive README documentation update - Fix broken documentation links to non-existent files - Remove invalid 'populate' option from contentTypes config examples - Document all 12 helper functions with examples - Add complete Monitoring Service documentation - Add Entity Subscription helpers documentation - Clarify admin panel structure and widget requirements (Strapi v5.13+) - Update types.d.ts with entity subscription interfaces - Update api-reference.json with new methods - Update docs/api/io-class.md with entity subscription section - Fix version references (3.x -> 5.x) * ci: fix npm publish workflow - Remove npm ci (not needed, dist is pre-built) - Update actions to v4 - Use Node 20 - Add --access public for scoped package - Include dist/ in repository for direct publish * feat: add populate option for content type events - Add populate configuration to content type schema (Zod) - Implement refetch logic in lifecycle hooks for create/update events - Support multiple populate formats: '*', true, string[], object - Add TypeScript types for PopulateConfig - Document populate option in README with examples - Update npm-publish workflow to build before publish Populate allows including relations in emitted Socket.IO events. When configured, the plugin refetches entities with populated relations after create/update operations before emitting. * security: add sensitive fields protection for all emitted data - Add recursive removal of sensitive fields (password, token, apiKey, etc.) - Support custom sensitiveFields in plugin config - Apply sanitization to both emit() and raw() methods - Update TypeScript types and documentation Default blocked fields: password, resetPasswordToken, confirmationToken, refreshToken, accessToken, secret, apiKey, privateKey, token, salt, hash * ci: improve npm publish workflow with best practices - Add build job for PR/push validation - Add npm cache for faster CI - Verify plugin structure before publish - Check version matches release tag - Support npm tags: latest, beta, alpha, next - Add post-publish verification - Upload build artifacts between jobs * fix: correct peerDependencies and remove legacy-peer-deps workaround - Remove @strapi/sdk-plugin from peerDependencies (build-only dep) - Use flexible version ranges for peerDependencies - Mark design-system and icons as optional peers - Remove --legacy-peer-deps from CI workflow * chore: update devDependencies to latest compatible versions - @strapi/strapi: 5.3.2 -> 5.33.0 - @strapi/design-system: 2.0.0-rc.30 -> 2.0.2 - @strapi/icons: 2.0.0-rc.30 -> 2.0.2 - prettier: 3.6.2 -> 3.7.4 - eslint: 8.57.0 -> 8.57.1 - eslint-config-prettier: 9.1.0 -> 9.1.2 Note: React 18, react-router-dom 6, zod 3 kept for Strapi v5 compatibility
1 parent 758c6d6 commit 09445b1

35 files changed

+197968
-23375
lines changed

.github/workflows/npm-publish.yml

Lines changed: 146 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,162 @@
1-
# This workflow will publish a package to NPM when a release is created
1+
# Strapi Plugin CI/CD Pipeline
2+
# Best Practices workflow for building, testing, and publishing Strapi v5 plugins
23

3-
name: Publish to NPM
4+
name: CI/CD
45

56
on:
7+
push:
8+
branches: [main, feat/v5-support]
9+
pull_request:
10+
branches: [main]
611
release:
712
types: [published]
813

14+
env:
15+
NODE_VERSION: '20.x'
16+
917
jobs:
10-
publish-npm:
18+
# ============================================
19+
# Build & Verify Job
20+
# Runs on every push and PR to validate the plugin
21+
# ============================================
22+
build:
23+
name: Build & Verify
1124
runs-on: ubuntu-latest
25+
1226
steps:
13-
- name: Checkout branch
14-
uses: actions/checkout@v2
27+
- name: Checkout repository
28+
uses: actions/checkout@v4
1529

16-
- name: Install Node v18
17-
uses: actions/setup-node@v2
30+
- name: Setup Node.js ${{ env.NODE_VERSION }}
31+
uses: actions/setup-node@v4
1832
with:
19-
node-version: '18.x'
20-
registry-url: 'https://registry.npmjs.org'
33+
node-version: ${{ env.NODE_VERSION }}
34+
cache: 'npm'
2135

22-
- name: Clean install deps
36+
- name: Install dependencies
2337
run: npm ci
2438

39+
- name: Build plugin
40+
run: npm run build
41+
42+
- name: Verify plugin structure
43+
run: npm run verify
44+
45+
- name: Check package.json exports
46+
run: |
47+
echo "[INFO] Checking package exports..."
48+
node -e "
49+
const pkg = require('./package.json');
50+
const required = ['./strapi-admin', './strapi-server'];
51+
for (const exp of required) {
52+
if (!pkg.exports[exp]) {
53+
console.error('[ERROR] Missing export:', exp);
54+
process.exit(1);
55+
}
56+
}
57+
console.log('[SUCCESS] All required exports present');
58+
"
59+
60+
- name: Upload build artifacts
61+
uses: actions/upload-artifact@v4
62+
with:
63+
name: dist
64+
path: dist/
65+
retention-days: 7
66+
67+
# ============================================
68+
# Publish to NPM Job
69+
# Only runs when a GitHub Release is published
70+
# ============================================
71+
publish:
72+
name: Publish to NPM
73+
runs-on: ubuntu-latest
74+
needs: build
75+
if: github.event_name == 'release'
76+
77+
permissions:
78+
contents: read
79+
id-token: write
80+
81+
steps:
82+
- name: Checkout repository
83+
uses: actions/checkout@v4
84+
85+
- name: Setup Node.js ${{ env.NODE_VERSION }}
86+
uses: actions/setup-node@v4
87+
with:
88+
node-version: ${{ env.NODE_VERSION }}
89+
registry-url: 'https://registry.npmjs.org'
90+
cache: 'npm'
91+
92+
- name: Download build artifacts
93+
uses: actions/download-artifact@v4
94+
with:
95+
name: dist
96+
path: dist/
97+
98+
- name: Verify version matches release tag
99+
run: |
100+
PKG_VERSION=$(node -p "require('./package.json').version")
101+
RELEASE_TAG="${{ github.event.release.tag_name }}"
102+
# Remove 'v' prefix if present
103+
RELEASE_VERSION="${RELEASE_TAG#v}"
104+
105+
echo "[INFO] Package version: $PKG_VERSION"
106+
echo "[INFO] Release tag: $RELEASE_TAG (version: $RELEASE_VERSION)"
107+
108+
if [ "$PKG_VERSION" != "$RELEASE_VERSION" ]; then
109+
echo "[ERROR] Version mismatch! Package: $PKG_VERSION, Release: $RELEASE_VERSION"
110+
echo "[INFO] Please update package.json version to match the release tag."
111+
exit 1
112+
fi
113+
114+
echo "[SUCCESS] Version match confirmed"
115+
116+
- name: Determine npm tag
117+
id: npm-tag
118+
run: |
119+
RELEASE_TAG="${{ github.event.release.tag_name }}"
120+
121+
# Determine npm dist-tag based on release type
122+
if [[ "$RELEASE_TAG" == *"-beta"* ]]; then
123+
echo "tag=beta" >> $GITHUB_OUTPUT
124+
elif [[ "$RELEASE_TAG" == *"-alpha"* ]]; then
125+
echo "tag=alpha" >> $GITHUB_OUTPUT
126+
elif [[ "$RELEASE_TAG" == *"-rc"* ]]; then
127+
echo "tag=next" >> $GITHUB_OUTPUT
128+
else
129+
echo "tag=latest" >> $GITHUB_OUTPUT
130+
fi
131+
132+
echo "[INFO] NPM tag: $(cat $GITHUB_OUTPUT | grep tag | cut -d= -f2)"
133+
25134
- name: Publish to NPM
26-
run: npm publish
135+
run: |
136+
echo "[INFO] Publishing with tag: ${{ steps.npm-tag.outputs.tag }}"
137+
npm publish --access public --tag ${{ steps.npm-tag.outputs.tag }}
27138
env:
28-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
139+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
140+
141+
- name: Post-publish verification
142+
run: |
143+
PKG_NAME=$(node -p "require('./package.json').name")
144+
PKG_VERSION=$(node -p "require('./package.json').version")
145+
146+
echo "[INFO] Waiting for NPM registry to update..."
147+
sleep 10
148+
149+
echo "[INFO] Verifying package on NPM..."
150+
npm view "$PKG_NAME@$PKG_VERSION" version || echo "[WARNING] Package may take a few minutes to appear on NPM"
151+
152+
echo ""
153+
echo "============================================"
154+
echo "[SUCCESS] Published $PKG_NAME@$PKG_VERSION"
155+
echo "============================================"
156+
echo ""
157+
echo "Install with:"
158+
echo " npm install $PKG_NAME@$PKG_VERSION"
159+
echo ""
160+
echo "View on NPM:"
161+
echo " https://www.npmjs.com/package/$PKG_NAME"
162+
echo ""

README.md

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,24 @@ module.exports = {
165165
io: {
166166
enabled: true,
167167
config: {
168-
// Content types with specific actions
168+
// Content types with specific actions and populate
169169
contentTypes: [
170170
{
171171
uid: 'api::article.article',
172-
actions: ['create', 'update'] // Only these events
172+
actions: ['create', 'update'], // Only these events
173+
populate: '*' // Include all relations
173174
},
174175
{
175176
uid: 'api::comment.comment',
176-
actions: ['create', 'delete']
177+
actions: ['create', 'delete'],
178+
populate: ['author', 'article'] // Only specific relations
179+
},
180+
{
181+
uid: 'api::order.order',
182+
populate: { // Strapi populate syntax
183+
customer: { fields: ['name', 'email'] },
184+
items: { populate: ['product'] }
185+
}
177186
}
178187
],
179188

@@ -217,7 +226,105 @@ module.exports = {
217226
};
218227
```
219228

220-
> **Note**: Relations are included globally via the admin panel settings (`Settings > Socket.IO > Events > Include Relations`), not per content type in the config file.
229+
### Populate Configuration
230+
231+
Include relations in emitted events by adding the `populate` option to content types. When configured, the plugin refetches entities with populated relations after create/update operations.
232+
233+
#### Populate Formats
234+
235+
| Format | Example | Description |
236+
|--------|---------|-------------|
237+
| `'*'` or `true` | `populate: '*'` | Include all relations (1 level deep) |
238+
| String array | `populate: ['author', 'category']` | Only specific relations |
239+
| Object | `populate: { author: { fields: ['name'] } }` | Full Strapi populate syntax |
240+
241+
#### Examples
242+
243+
```javascript
244+
contentTypes: [
245+
// All relations with wildcard
246+
{ uid: 'api::article.article', populate: '*' },
247+
248+
// Specific relations only
249+
{
250+
uid: 'api::comment.comment',
251+
populate: ['author', 'post']
252+
},
253+
254+
// Strapi populate syntax with field selection
255+
{
256+
uid: 'api::order.order',
257+
populate: {
258+
customer: { fields: ['username', 'email'] },
259+
items: {
260+
populate: { product: { fields: ['name', 'price'] } }
261+
}
262+
}
263+
},
264+
265+
// No populate (only base fields)
266+
{ uid: 'api::log.log' } // populate not set = no relations
267+
]
268+
```
269+
270+
> **Note**: The `populate` option only affects `create` and `update` events. Delete events always contain minimal data (id, documentId) since the entity no longer exists.
271+
272+
### Sensitive Fields Protection
273+
274+
The plugin automatically removes sensitive fields from all emitted data for security. This works in addition to Strapi's built-in `private: true` field filtering.
275+
276+
#### Default Blocked Fields
277+
278+
The following fields are **always removed** from emitted data:
279+
280+
- `password`, `salt`, `hash`
281+
- `resetPasswordToken`, `confirmationToken`
282+
- `refreshToken`, `accessToken`, `token`
283+
- `secret`, `apiKey`, `api_key`
284+
- `privateKey`, `private_key`
285+
286+
#### Custom Sensitive Fields
287+
288+
Add your own sensitive fields to the block list:
289+
290+
```javascript
291+
// config/plugins.js
292+
module.exports = {
293+
io: {
294+
enabled: true,
295+
config: {
296+
contentTypes: ['api::user.user'],
297+
298+
// Additional fields to never emit
299+
sensitiveFields: [
300+
'creditCard',
301+
'ssn',
302+
'socialSecurityNumber',
303+
'bankAccount',
304+
'internalNotes'
305+
]
306+
}
307+
}
308+
};
309+
```
310+
311+
#### How It Works
312+
313+
1. **Schema-level filtering**: Strapi's `sanitize.contentAPI` removes `private: true` fields
314+
2. **Blocklist filtering**: Plugin removes all sensitive field names recursively
315+
3. **Applies to all emits**: Both `emit()` and `raw()` methods are protected
316+
317+
```javascript
318+
// Even with populate, sensitive fields are stripped
319+
contentTypes: [
320+
{
321+
uid: 'api::user.user',
322+
populate: '*' // Relations included, but passwords etc. still removed
323+
}
324+
]
325+
```
326+
327+
> **Security Note**: Fields are matched case-insensitively and also partial matches work (e.g., `apiKey` blocks `myApiKey`, `user_api_key`, etc.)
221328
222329
### Environment Variables
223330

0 commit comments

Comments
 (0)