@kkuffour/solid-moderation-plugin 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/.data/.internal/idp/keys/cookie-secret$.json +1 -0
  2. package/.data/.internal/idp/keys/jwks$.json +1 -0
  3. package/.data/.internal/setup/current-base-url$.json +1 -0
  4. package/PLUGIN_DEVELOPER_GUIDE.md +213 -0
  5. package/README.md +25 -28
  6. package/components/context.jsonld +6 -245
  7. package/config/default.json +14 -28
  8. package/dist/ModerationHandler.d.ts +21 -0
  9. package/dist/ModerationHandler.d.ts.map +1 -0
  10. package/dist/ModerationHandler.js +158 -0
  11. package/dist/ModerationHandler.js.map +1 -0
  12. package/dist/ModerationHandler.jsonld +126 -0
  13. package/dist/components/components.jsonld +13 -0
  14. package/dist/components/context.jsonld +11 -0
  15. package/dist/index.d.ts +1 -6
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -6
  18. package/dist/index.js.map +1 -1
  19. package/dist/providers/SightEngineProvider.jsonld +2 -2
  20. package/package.json +11 -11
  21. package/src/ModerationHandler.ts +189 -0
  22. package/src/index.ts +1 -6
  23. package/ARCHITECTURE.md +0 -52
  24. package/CONFIG-GUIDE.md +0 -49
  25. package/DEVELOPMENT.md +0 -129
  26. package/ENV-VARIABLES.md +0 -137
  27. package/INSTALLATION.md +0 -90
  28. package/MIGRATION.md +0 -81
  29. package/PRODUCTION.md +0 -186
  30. package/PUBLISHING.md +0 -104
  31. package/TESTING.md +0 -93
  32. package/components/components.jsonld +0 -18
  33. package/dist/ModerationConfig.d.ts +0 -16
  34. package/dist/ModerationConfig.d.ts.map +0 -1
  35. package/dist/ModerationConfig.js +0 -18
  36. package/dist/ModerationConfig.js.map +0 -1
  37. package/dist/ModerationConfig.jsonld +0 -66
  38. package/dist/ModerationMixin.d.ts +0 -13
  39. package/dist/ModerationMixin.d.ts.map +0 -1
  40. package/dist/ModerationMixin.js +0 -136
  41. package/dist/ModerationMixin.js.map +0 -1
  42. package/dist/ModerationMixin.jsonld +0 -180
  43. package/dist/ModerationOperationHandler.d.ts +0 -16
  44. package/dist/ModerationOperationHandler.d.ts.map +0 -1
  45. package/dist/ModerationOperationHandler.js +0 -45
  46. package/dist/ModerationOperationHandler.js.map +0 -1
  47. package/dist/ModerationOperationHandler.jsonld +0 -140
  48. package/dist/ModerationRecord.d.ts +0 -20
  49. package/dist/ModerationRecord.d.ts.map +0 -1
  50. package/dist/ModerationRecord.js +0 -3
  51. package/dist/ModerationRecord.js.map +0 -1
  52. package/dist/ModerationRecord.jsonld +0 -59
  53. package/dist/ModerationResourceStore.d.ts +0 -30
  54. package/dist/ModerationResourceStore.d.ts.map +0 -1
  55. package/dist/ModerationResourceStore.js +0 -167
  56. package/dist/ModerationResourceStore.js.map +0 -1
  57. package/dist/ModerationResourceStore.jsonld +0 -157
  58. package/dist/ModerationStore.d.ts +0 -12
  59. package/dist/ModerationStore.d.ts.map +0 -1
  60. package/dist/ModerationStore.js +0 -37
  61. package/dist/ModerationStore.js.map +0 -1
  62. package/dist/ModerationStore.jsonld +0 -59
  63. package/dist/util/GuardedStream.d.ts +0 -33
  64. package/dist/util/GuardedStream.d.ts.map +0 -1
  65. package/dist/util/GuardedStream.js +0 -89
  66. package/dist/util/GuardedStream.js.map +0 -1
  67. package/simple-test.json +0 -7
  68. package/src/ModerationConfig.ts +0 -29
  69. package/src/ModerationMixin.ts +0 -153
  70. package/src/ModerationOperationHandler.ts +0 -64
  71. package/src/ModerationRecord.ts +0 -19
  72. package/src/ModerationResourceStore.ts +0 -227
  73. package/src/ModerationStore.ts +0 -41
  74. package/src/util/GuardedStream.ts +0 -101
package/ARCHITECTURE.md DELETED
@@ -1,52 +0,0 @@
1
- # Architecture Decision: ResourceStore vs OperationHandler
2
-
3
- ## Branch History
4
-
5
- ### `main` branch (v0.1.0 - v0.1.2)
6
- **Approach**: OperationHandler wrapping
7
- **Status**: ❌ Does not work
8
- **Reason**: Components.js cannot resolve cross-module component overrides. When trying to override `urn:solid-server:default:PutOperationHandler` with a type from an external npm package, Components.js looks for the type in the CSS module, not the plugin module.
9
-
10
- **Key Learning**: Components.js uses module-scoped component resolution. External plugins cannot override core CSS handler IDs.
11
-
12
- ### `resourcestore-approach` branch (v0.2.0+)
13
- **Approach**: ResourceStore wrapping
14
- **Status**: 🚧 In development
15
- **Strategy**: Wrap `urn:solid-server:default:ResourceStore` instead of individual operation handlers.
16
-
17
- ## Why ResourceStore Works
18
-
19
- 1. **Single interception point**: All write operations (PUT/POST/PATCH) call `ResourceStore.setRepresentation()`
20
- 2. **Designed for swapping**: CSS architecture expects ResourceStore to be configurable/replaceable
21
- 3. **Proper abstraction boundary**: ResourceStore is the storage layer interface
22
- 4. **Components.js compatible**: ResourceStore wrapping is a supported pattern
23
-
24
- ## Implementation Plan
25
-
26
- ```
27
- Request Flow:
28
- HTTP Request → OperationHandler → ResourceStore → Backend
29
-
30
- INTERCEPT HERE
31
- ```
32
-
33
- ### Core Changes
34
- - Create `ModerationResourceStore` implementing `ResourceStore` interface
35
- - Wrap original store and intercept `setRepresentation()`
36
- - Moderate content before passing to wrapped store
37
- - Delegate all read operations (getRepresentation, etc.)
38
-
39
- ### Config Pattern
40
- ```json
41
- {
42
- "@id": "urn:solid-server:default:ResourceStore",
43
- "@type": "ModerationResourceStore",
44
- "source": { "@id": "urn:solid-server:default:ResourceStore_Original" }
45
- }
46
- ```
47
-
48
- ## Version Strategy
49
-
50
- - **v0.1.x**: OperationHandler approach (archived, doesn't work)
51
- - **v0.2.x**: ResourceStore approach (current development)
52
- - Users can install specific versions if needed: `npm install @kkuffour/solid-moderation-plugin@0.1.2`
package/CONFIG-GUIDE.md DELETED
@@ -1,49 +0,0 @@
1
- # Configuration Guide
2
-
3
- ## Understanding the Configuration Files
4
-
5
- ### `config/default.json` (Production - Installed with npm)
6
- - Located in `node_modules/@solid/moderation-plugin/config/default.json` after installation
7
- - Contains the complete moderation setup with default thresholds
8
- - Imported by users in their CSS config using: `"@solid/moderation-plugin:config/default.json"`
9
- - **This is what production users import**
10
-
11
- ### `config-with-moderation.json` (Local Testing Only)
12
- - Located in the plugin source directory
13
- - Used for testing the plugin locally before publishing
14
- - Points to local files, not npm package
15
- - **Not included in npm package**
16
-
17
- ## For Production Users
18
-
19
- When you install the plugin via npm, you create a config file in **your CSS project**:
20
-
21
- ```json
22
- {
23
- "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
24
- "import": [
25
- "css:config/file.json",
26
- "@solid/moderation-plugin:config/default.json"
27
- ]
28
- }
29
- ```
30
-
31
- Then run:
32
- ```bash
33
- export SIGHTENGINE_API_USER=your_api_user
34
- export SIGHTENGINE_API_SECRET=your_api_secret
35
- npx @solid/community-server -c config-with-moderation.json
36
- ```
37
-
38
- ## For Local Development/Testing
39
-
40
- When testing the plugin locally (before publishing):
41
-
42
- 1. Use `npm link` to link the local plugin
43
- 2. Use the `config-with-moderation.json` file in the plugin directory
44
- 3. Run CSS pointing to that config file
45
-
46
- ## Key Difference
47
-
48
- - **Production**: Import `@solid/moderation-plugin:config/default.json` (from npm package)
49
- - **Local Testing**: Use full path to `config-with-moderation.json` (from source directory)
package/DEVELOPMENT.md DELETED
@@ -1,129 +0,0 @@
1
- # Development Notes
2
-
3
- ## Components.js Module Loading Issue
4
-
5
- ### Problem
6
- Components.js cannot load modules via `npm link` due to symlink resolution issues. When using `npm link`, the plugin appears in `node_modules/@solid/moderation-plugin` as a symlink, but Components.js's module loader cannot follow it to instantiate classes.
7
-
8
- ### Symptoms
9
- - Server starts without errors
10
- - Config file is loaded successfully
11
- - No moderation logs appear when uploading files
12
- - ModerationOperationHandler is never instantiated
13
- - Uploads succeed without any moderation checks
14
-
15
- ### Root Cause
16
- Components.js uses its own module resolution that doesn't properly handle symlinked packages. The `requireElement` in the generated `.jsonld` files points to the class name, but Components.js cannot resolve the actual module path through the symlink.
17
-
18
- ### Solutions
19
-
20
- #### Option 1: File Path Dependency (Recommended for Development)
21
- Add to CSS's `package.json`:
22
- ```json
23
- {
24
- "dependencies": {
25
- "@solid/moderation-plugin": "file:../solid-moderation"
26
- }
27
- }
28
- ```
29
-
30
- Then run:
31
- ```bash
32
- cd /Users/opendata/CommunitySolidServer
33
- npm install
34
- ```
35
-
36
- This creates a proper copy (not symlink) in node_modules.
37
-
38
- **After code changes:**
39
- ```bash
40
- cd /Users/opendata/solid-moderation
41
- npm run build
42
- cd /Users/opendata/CommunitySolidServer
43
- npm install # Re-copies the updated files
44
- ```
45
-
46
- #### Option 2: Publish to npm (Recommended for Production)
47
- ```bash
48
- cd /Users/opendata/solid-moderation
49
- npm publish
50
- ```
51
-
52
- Then in CSS:
53
- ```bash
54
- npm install @solid/moderation-plugin@latest
55
- ```
56
-
57
- #### Option 3: Manual Copy (Quick Testing Only)
58
- ```bash
59
- rm -rf /Users/opendata/CommunitySolidServer/node_modules/@solid/moderation-plugin
60
- cp -r /Users/opendata/solid-moderation /Users/opendata/CommunitySolidServer/node_modules/@solid/moderation-plugin
61
- ```
62
-
63
- **Note:** Must repeat after every code change.
64
-
65
- ## Current Configuration
66
-
67
- ### Environment Variables
68
- ```bash
69
- export SIGHTENGINE_API_USER="1060049443"
70
- export SIGHTENGINE_API_SECRET="QRQ8HUmh4hyvhZjksBJq5ZaNYPLPEKXu"
71
- ```
72
-
73
- ### Thresholds (Configurable in config-with-moderation.json)
74
- - **Image Nudity**: 0.1 (10% - very strict)
75
- - **Text Sexual**: 0.1 (10% - very strict)
76
- - **Text Toxic**: 0.1 (10% - very strict)
77
- - **Video Nudity**: 0.1 (10% - very strict)
78
-
79
- Lower values = stricter moderation (blocks more content)
80
- Higher values = lenient moderation (blocks less content)
81
- Range: 0.0 (block everything) to 1.0 (block nothing)
82
-
83
- ### Starting the Server
84
- ```bash
85
- cd /Users/opendata/CommunitySolidServer
86
- export SIGHTENGINE_API_USER="1060049443"
87
- export SIGHTENGINE_API_SECRET="QRQ8HUmh4hyvhZjksBJq5ZaNYPLPEKXu"
88
- npm start -- -c /Users/opendata/solid-moderation/config-with-moderation.json -f data/ -p 3009 -l debug > logs/server.log 2>&1 &
89
- ```
90
-
91
- ### Checking Logs
92
- ```bash
93
- # Watch for moderation activity
94
- tail -f logs/server.log | grep -i moderation
95
-
96
- # Check if handler is invoked
97
- grep "MODERATION: Checking" logs/server.log
98
-
99
- # Check for API failures
100
- grep "MODERATION:.*failed" logs/server.log
101
- ```
102
-
103
- ## Features Implemented
104
-
105
- ### Code Structure
106
- - **ModerationOperationHandler**: Wraps POST/PUT/PATCH handlers
107
- - **ModerationMixin**: Performs actual content checks
108
- - **ModerationStore**: Audit logging for violations
109
- - **SightEngineProvider**: API client for SightEngine
110
- - **Configurable thresholds**: All thresholds adjustable via config
111
-
112
- ### Moderation Checks
113
- - **Images**: Nudity detection
114
- - **Text**: Sexual content and toxic language
115
- - **Video**: Nudity detection
116
- - **Fail-open policy**: Allows content if API fails
117
-
118
- ### Debug Logging
119
- - Handler invocation logs
120
- - Content type detection logs
121
- - API response with scores
122
- - Threshold comparison logs
123
- - Failure reason logs
124
-
125
- ## Known Issues
126
-
127
- 1. **npm link doesn't work** - Use file path dependency instead
128
- 2. **Environment variables** - Must be set before starting server
129
- 3. **Audit logs** - Only record violations, not all checks
package/ENV-VARIABLES.md DELETED
@@ -1,137 +0,0 @@
1
- # Environment Variables Explained
2
-
3
- ## What `export` Does
4
-
5
- ```bash
6
- export SIGHTENGINE_API_USER=your_api_user
7
- export SIGHTENGINE_API_SECRET=your_api_secret
8
- ```
9
-
10
- These commands set **environment variables** in your shell session:
11
-
12
- - `export` makes the variable available to all programs you run in that terminal
13
- - `SIGHTENGINE_API_USER` is the variable name
14
- - `your_api_user` is the value (replace with your actual API credentials)
15
-
16
- ## How the Plugin Uses These Variables
17
-
18
- In `config/default.json`, you'll see:
19
-
20
- ```json
21
- "ModerationConfig:_sightEngine": {
22
- "ModerationConfig:_sightEngine_apiUser": "${SIGHTENGINE_API_USER}",
23
- "ModerationConfig:_sightEngine_apiSecret": "${SIGHTENGINE_API_SECRET}"
24
- }
25
- ```
26
-
27
- The `${SIGHTENGINE_API_USER}` syntax means:
28
- - "Look for an environment variable named SIGHTENGINE_API_USER"
29
- - "Replace this placeholder with its value"
30
-
31
- ## Example Flow
32
-
33
- 1. **You set the variables:**
34
- ```bash
35
- export SIGHTENGINE_API_USER=abc123
36
- export SIGHTENGINE_API_SECRET=xyz789
37
- ```
38
-
39
- 2. **You start CSS:**
40
- ```bash
41
- npx @solid/community-server -c config.json
42
- ```
43
-
44
- 3. **The plugin reads the config and replaces placeholders:**
45
- - `${SIGHTENGINE_API_USER}` becomes `abc123`
46
- - `${SIGHTENGINE_API_SECRET}` becomes `xyz789`
47
-
48
- 4. **The plugin uses these credentials to call SightEngine API**
49
-
50
- ## Why Use Environment Variables?
51
-
52
- ✅ **Security**: Credentials aren't stored in config files (which might be committed to git)
53
- ✅ **Flexibility**: Different credentials for dev/staging/production without changing code
54
- ✅ **Standard Practice**: Common pattern for managing secrets
55
-
56
- ## Alternative Methods
57
-
58
- ### Option 1: Set per command (temporary)
59
- ```bash
60
- SIGHTENGINE_API_USER=abc123 SIGHTENGINE_API_SECRET=xyz789 npx @solid/community-server -c config.json
61
- ```
62
-
63
- ### Option 2: Use a .env file (with dotenv)
64
- Create `.env` file:
65
- ```
66
- SIGHTENGINE_API_USER=abc123
67
- SIGHTENGINE_API_SECRET=xyz789
68
- ```
69
-
70
- ### Option 3: Set in shell profile (permanent) ⭐ RECOMMENDED FOR PRODUCTION
71
- Add to `~/.bashrc` or `~/.zshrc`:
72
- ```bash
73
- export SIGHTENGINE_API_USER=abc123
74
- export SIGHTENGINE_API_SECRET=xyz789
75
- ```
76
-
77
- ### Option 4: System service environment file (for systemd)
78
- Create `/etc/systemd/system/solid-server.service.d/override.conf`:
79
- ```ini
80
- [Service]
81
- Environment="SIGHTENGINE_API_USER=abc123"
82
- Environment="SIGHTENGINE_API_SECRET=xyz789"
83
- ```
84
-
85
- ## What Happens on Server Restart?
86
-
87
- ### ❌ If you only used `export` in terminal:
88
- - Variables are **LOST** when terminal closes or server restarts
89
- - Server will fail to moderate content (falls back to fail-open policy)
90
- - You must re-export before starting server again
91
-
92
- ### ✅ If you added to shell profile (`~/.bashrc` or `~/.zshrc`):
93
- - Variables are **PRESERVED** across terminal sessions
94
- - But still lost on full system reboot unless server auto-starts with those variables
95
-
96
- ### ✅ If you use systemd service file:
97
- - Variables are **ALWAYS AVAILABLE** to the service
98
- - Survives server restarts and system reboots
99
- - **BEST for production servers**
100
-
101
- ### ✅ If you use Docker:
102
- ```yaml
103
- services:
104
- solid-server:
105
- environment:
106
- - SIGHTENGINE_API_USER=abc123
107
- - SIGHTENGINE_API_SECRET=xyz789
108
- ```
109
- Variables are preserved in container configuration.
110
-
111
- ## Production Recommendation
112
-
113
- For a production server that needs to survive restarts:
114
-
115
- 1. **Use systemd service** with environment variables in service file
116
- 2. **Or use Docker** with environment variables in docker-compose.yml
117
- 3. **Or use a process manager** like PM2 with ecosystem.config.js:
118
- ```javascript
119
- module.exports = {
120
- apps: [{
121
- name: 'solid-server',
122
- script: 'npx',
123
- args: '@solid/community-server -c config.json',
124
- env: {
125
- SIGHTENGINE_API_USER: 'abc123',
126
- SIGHTENGINE_API_SECRET: 'xyz789'
127
- }
128
- }]
129
- }
130
- ```
131
-
132
- ## Getting Your API Credentials
133
-
134
- 1. Sign up at https://sightengine.com/
135
- 2. Go to your dashboard
136
- 3. Find your API User and API Secret
137
- 4. Replace `your_api_user` and `your_api_secret` with those values
package/INSTALLATION.md DELETED
@@ -1,90 +0,0 @@
1
- # Files Generated in Community Solid Server
2
-
3
- When you install `@solid/moderation-plugin` in your Community Solid Server, the following files will be generated in `node_modules/@solid/moderation-plugin/`:
4
-
5
- ## Directory Structure
6
-
7
- ```
8
- node_modules/@solid/moderation-plugin/
9
- ├── dist/ # Compiled JavaScript and metadata
10
- │ ├── components/ # Components.js metadata
11
- │ │ ├── components.jsonld # Component definitions
12
- │ │ └── context.jsonld # JSON-LD context
13
- │ │
14
- │ ├── providers/ # API providers
15
- │ │ ├── SightEngineProvider.js
16
- │ │ ├── SightEngineProvider.d.ts
17
- │ │ ├── SightEngineProvider.js.map
18
- │ │ ├── SightEngineProvider.d.ts.map
19
- │ │ └── SightEngineProvider.jsonld
20
- │ │
21
- │ ├── util/ # Utilities
22
- │ │ ├── GuardedStream.js
23
- │ │ ├── GuardedStream.d.ts
24
- │ │ └── *.map files
25
- │ │
26
- │ ├── index.js # Main entry point
27
- │ ├── index.d.ts # TypeScript definitions
28
- │ ├── ModerationOperationHandler.js
29
- │ ├── ModerationOperationHandler.d.ts
30
- │ ├── ModerationOperationHandler.jsonld
31
- │ ├── ModerationConfig.js
32
- │ ├── ModerationConfig.d.ts
33
- │ ├── ModerationConfig.jsonld
34
- │ ├── ModerationStore.js
35
- │ ├── ModerationStore.d.ts
36
- │ ├── ModerationStore.jsonld
37
- │ ├── ModerationMixin.js
38
- │ ├── ModerationMixin.d.ts
39
- │ ├── ModerationMixin.jsonld
40
- │ ├── ModerationRecord.js
41
- │ ├── ModerationRecord.d.ts
42
- │ ├── ModerationRecord.jsonld
43
- │ └── *.map files # Source maps
44
-
45
- ├── config/ # Configuration templates
46
- │ └── default.json # Default moderation settings
47
-
48
- ├── package.json # Package metadata
49
- ├── README.md # Documentation
50
- └── LICENSE # MIT License
51
- ```
52
-
53
- ## Runtime Files (Created During Operation)
54
-
55
- When CSS runs with moderation enabled, these files/folders are created:
56
-
57
- ```
58
- your-css-project/
59
- ├── data/
60
- │ └── moderation-logs/ # Audit logs (if enabled)
61
- │ ├── 2024-01-20.jsonl # Daily log files
62
- │ ├── 2024-01-21.jsonl
63
- │ └── ...
64
-
65
- └── /tmp/ # Temporary files during analysis
66
- └── moderation_*.jpg/mp4 # Auto-deleted after analysis
67
- ```
68
-
69
- ## Key Files Explained
70
-
71
- ### Components.js Files (*.jsonld)
72
- - Used by CSS's dependency injection system
73
- - Define how classes are instantiated and wired together
74
- - Allow configuration through JSON-LD config files
75
-
76
- ### JavaScript Files (*.js)
77
- - Compiled TypeScript code
78
- - Main application logic
79
-
80
- ### TypeScript Definitions (*.d.ts)
81
- - Type information for TypeScript users
82
- - Enable IDE autocomplete and type checking
83
-
84
- ### Source Maps (*.map)
85
- - Map compiled JS back to original TypeScript
86
- - Used for debugging
87
-
88
- ### config/default.json
89
- - Template configuration with default thresholds
90
- - Can be imported in CSS config files
package/MIGRATION.md DELETED
@@ -1,81 +0,0 @@
1
- # Migration Guide: Extracting Moderation to External Plugin
2
-
3
- ## Overview
4
-
5
- This guide walks through migrating the moderation feature from CSS core to the standalone `@solid/moderation-plugin` package.
6
-
7
- ## Steps
8
-
9
- ### 1. Publish Plugin Package
10
-
11
- ```bash
12
- cd mod-package-struct
13
- npm install
14
- npm run build
15
- npm publish --access public
16
- ```
17
-
18
- ### 2. Update CSS to Use Plugin
19
-
20
- In your CSS project:
21
-
22
- ```bash
23
- npm install @solid/moderation-plugin
24
- ```
25
-
26
- ### 3. Update Configuration
27
-
28
- Replace `config/file-moderation.json` with:
29
-
30
- ```json
31
- {
32
- "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
33
- "import": [
34
- "css:config/file.json",
35
- "@solid/moderation-plugin:config/default.json"
36
- ]
37
- }
38
- ```
39
-
40
- ### 4. Remove Core Moderation Code
41
-
42
- Delete from CSS repository:
43
- - `src/moderation/` directory
44
- - `src/http/ldp/ModerationOperationHandler.ts`
45
- - `src/http/ldp/ModerationMixin.ts`
46
- - `config/moderation-settings.json`
47
- - `config/file-moderation.json`
48
-
49
- ### 5. Update Exports
50
-
51
- Remove from `src/index.ts`:
52
- - ModerationOperationHandler export
53
- - ModerationConfig export
54
- - ModerationStore export
55
- - ModerationMixin export
56
-
57
- ### 6. Test
58
-
59
- ```bash
60
- # Start server with plugin
61
- npm start -- -c config-with-moderation.json
62
-
63
- # Test image upload
64
- curl -X PUT http://localhost:3000/test.jpg \
65
- -H "Content-Type: image/jpeg" \
66
- --data-binary @test-image.jpg
67
- ```
68
-
69
- ## Rollback Plan
70
-
71
- If issues occur:
72
- 1. Keep plugin package unpublished
73
- 2. Revert CSS changes
74
- 3. Continue using embedded moderation
75
-
76
- ## Timeline
77
-
78
- - Week 1: Extract and test plugin locally
79
- - Week 2: Publish plugin to npm
80
- - Week 3: Update CSS to use plugin
81
- - Week 4: Remove core moderation code