@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.
- package/.data/.internal/idp/keys/cookie-secret$.json +1 -0
- package/.data/.internal/idp/keys/jwks$.json +1 -0
- package/.data/.internal/setup/current-base-url$.json +1 -0
- package/PLUGIN_DEVELOPER_GUIDE.md +213 -0
- package/README.md +25 -28
- package/components/context.jsonld +6 -245
- package/config/default.json +14 -28
- package/dist/ModerationHandler.d.ts +21 -0
- package/dist/ModerationHandler.d.ts.map +1 -0
- package/dist/ModerationHandler.js +158 -0
- package/dist/ModerationHandler.js.map +1 -0
- package/dist/ModerationHandler.jsonld +126 -0
- package/dist/components/components.jsonld +13 -0
- package/dist/components/context.jsonld +11 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/providers/SightEngineProvider.jsonld +2 -2
- package/package.json +11 -11
- package/src/ModerationHandler.ts +189 -0
- package/src/index.ts +1 -6
- package/ARCHITECTURE.md +0 -52
- package/CONFIG-GUIDE.md +0 -49
- package/DEVELOPMENT.md +0 -129
- package/ENV-VARIABLES.md +0 -137
- package/INSTALLATION.md +0 -90
- package/MIGRATION.md +0 -81
- package/PRODUCTION.md +0 -186
- package/PUBLISHING.md +0 -104
- package/TESTING.md +0 -93
- package/components/components.jsonld +0 -18
- package/dist/ModerationConfig.d.ts +0 -16
- package/dist/ModerationConfig.d.ts.map +0 -1
- package/dist/ModerationConfig.js +0 -18
- package/dist/ModerationConfig.js.map +0 -1
- package/dist/ModerationConfig.jsonld +0 -66
- package/dist/ModerationMixin.d.ts +0 -13
- package/dist/ModerationMixin.d.ts.map +0 -1
- package/dist/ModerationMixin.js +0 -136
- package/dist/ModerationMixin.js.map +0 -1
- package/dist/ModerationMixin.jsonld +0 -180
- package/dist/ModerationOperationHandler.d.ts +0 -16
- package/dist/ModerationOperationHandler.d.ts.map +0 -1
- package/dist/ModerationOperationHandler.js +0 -45
- package/dist/ModerationOperationHandler.js.map +0 -1
- package/dist/ModerationOperationHandler.jsonld +0 -140
- package/dist/ModerationRecord.d.ts +0 -20
- package/dist/ModerationRecord.d.ts.map +0 -1
- package/dist/ModerationRecord.js +0 -3
- package/dist/ModerationRecord.js.map +0 -1
- package/dist/ModerationRecord.jsonld +0 -59
- package/dist/ModerationResourceStore.d.ts +0 -30
- package/dist/ModerationResourceStore.d.ts.map +0 -1
- package/dist/ModerationResourceStore.js +0 -167
- package/dist/ModerationResourceStore.js.map +0 -1
- package/dist/ModerationResourceStore.jsonld +0 -157
- package/dist/ModerationStore.d.ts +0 -12
- package/dist/ModerationStore.d.ts.map +0 -1
- package/dist/ModerationStore.js +0 -37
- package/dist/ModerationStore.js.map +0 -1
- package/dist/ModerationStore.jsonld +0 -59
- package/dist/util/GuardedStream.d.ts +0 -33
- package/dist/util/GuardedStream.d.ts.map +0 -1
- package/dist/util/GuardedStream.js +0 -89
- package/dist/util/GuardedStream.js.map +0 -1
- package/simple-test.json +0 -7
- package/src/ModerationConfig.ts +0 -29
- package/src/ModerationMixin.ts +0 -153
- package/src/ModerationOperationHandler.ts +0 -64
- package/src/ModerationRecord.ts +0 -19
- package/src/ModerationResourceStore.ts +0 -227
- package/src/ModerationStore.ts +0 -41
- package/src/util/GuardedStream.ts +0 -101
package/PRODUCTION.md
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# Production Deployment Guide
|
|
2
|
-
|
|
3
|
-
## Installation
|
|
4
|
-
|
|
5
|
-
1. **Install the plugin:**
|
|
6
|
-
```bash
|
|
7
|
-
npm install @solid/moderation-plugin
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
2. **Create config file** in your CSS project (e.g., `config-with-moderation.json`):
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"@context": [
|
|
14
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
|
|
15
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/moderation-plugin/^1.0.0/components/context.jsonld"
|
|
16
|
-
],
|
|
17
|
-
"import": [
|
|
18
|
-
"css:config/file.json",
|
|
19
|
-
"@solid/moderation-plugin:config/default.json"
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
3. **Set environment variables** (choose one method):
|
|
25
|
-
|
|
26
|
-
## Environment Variable Setup
|
|
27
|
-
|
|
28
|
-
### Option 1: Shell Profile (Simple)
|
|
29
|
-
Add to `~/.bashrc` or `~/.zshrc`:
|
|
30
|
-
```bash
|
|
31
|
-
export SIGHTENGINE_API_USER=your_api_user
|
|
32
|
-
export SIGHTENGINE_API_SECRET=your_api_secret
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Option 2: Systemd Service (Recommended for Production)
|
|
36
|
-
Create `/etc/systemd/system/solid-server.service`:
|
|
37
|
-
```ini
|
|
38
|
-
[Unit]
|
|
39
|
-
Description=Community Solid Server with Moderation
|
|
40
|
-
After=network.target
|
|
41
|
-
|
|
42
|
-
[Service]
|
|
43
|
-
Type=simple
|
|
44
|
-
User=solid
|
|
45
|
-
WorkingDirectory=/opt/solid-server
|
|
46
|
-
Environment="SIGHTENGINE_API_USER=your_api_user"
|
|
47
|
-
Environment="SIGHTENGINE_API_SECRET=your_api_secret"
|
|
48
|
-
ExecStart=/usr/bin/npx @solid/community-server -c config-with-moderation.json
|
|
49
|
-
Restart=always
|
|
50
|
-
|
|
51
|
-
[Install]
|
|
52
|
-
WantedBy=multi-user.target
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Enable and start:
|
|
56
|
-
```bash
|
|
57
|
-
sudo systemctl enable solid-server
|
|
58
|
-
sudo systemctl start solid-server
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Option 3: Docker
|
|
62
|
-
Create `docker-compose.yml`:
|
|
63
|
-
```yaml
|
|
64
|
-
version: '3'
|
|
65
|
-
services:
|
|
66
|
-
solid-server:
|
|
67
|
-
image: node:20
|
|
68
|
-
working_dir: /app
|
|
69
|
-
volumes:
|
|
70
|
-
- ./:/app
|
|
71
|
-
environment:
|
|
72
|
-
- SIGHTENGINE_API_USER=your_api_user
|
|
73
|
-
- SIGHTENGINE_API_SECRET=your_api_secret
|
|
74
|
-
command: npx @solid/community-server -c config-with-moderation.json
|
|
75
|
-
ports:
|
|
76
|
-
- "3000:3000"
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Run:
|
|
80
|
-
```bash
|
|
81
|
-
docker-compose up -d
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Option 4: PM2 Process Manager
|
|
85
|
-
Create `ecosystem.config.js`:
|
|
86
|
-
```javascript
|
|
87
|
-
module.exports = {
|
|
88
|
-
apps: [{
|
|
89
|
-
name: 'solid-server',
|
|
90
|
-
script: 'npx',
|
|
91
|
-
args: '@solid/community-server -c config-with-moderation.json',
|
|
92
|
-
env: {
|
|
93
|
-
SIGHTENGINE_API_USER: 'your_api_user',
|
|
94
|
-
SIGHTENGINE_API_SECRET: 'your_api_secret'
|
|
95
|
-
}
|
|
96
|
-
}]
|
|
97
|
-
};
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Run:
|
|
101
|
-
```bash
|
|
102
|
-
pm2 start ecosystem.config.js
|
|
103
|
-
pm2 save
|
|
104
|
-
pm2 startup
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## How It Works in Production
|
|
108
|
-
|
|
109
|
-
### Local Testing vs Production
|
|
110
|
-
|
|
111
|
-
| Aspect | Local Testing | Production |
|
|
112
|
-
|--------|--------------|------------|
|
|
113
|
-
| Plugin Location | `/Users/opendata/solid-moderation` (source) | `node_modules/@solid/moderation-plugin` (npm) |
|
|
114
|
-
| Installation | `npm link` | `npm install @solid/moderation-plugin` |
|
|
115
|
-
| Config Import | Full path to local file | `@solid/moderation-plugin:config/default.json` |
|
|
116
|
-
| Components | Loaded from local `dist/` | Loaded from `node_modules/@solid/moderation-plugin/dist/` |
|
|
117
|
-
|
|
118
|
-
### What Happens When CSS Starts
|
|
119
|
-
|
|
120
|
-
1. **CSS reads your config file**
|
|
121
|
-
2. **Sees import:** `@solid/moderation-plugin:config/default.json`
|
|
122
|
-
3. **Resolves to:** `node_modules/@solid/moderation-plugin/config/default.json`
|
|
123
|
-
4. **Loads components from:** `node_modules/@solid/moderation-plugin/dist/components/`
|
|
124
|
-
5. **Instantiates:** ModerationOperationHandler wrapping PUT/POST/PATCH
|
|
125
|
-
6. **Reads env vars:** `${SIGHTENGINE_API_USER}` → actual value
|
|
126
|
-
7. **Server starts** with moderation active
|
|
127
|
-
|
|
128
|
-
### File Locations in Production
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
your-css-project/
|
|
132
|
-
├── node_modules/
|
|
133
|
-
│ └── @solid/
|
|
134
|
-
│ └── moderation-plugin/ ← Installed here
|
|
135
|
-
│ ├── dist/
|
|
136
|
-
│ │ ├── components/
|
|
137
|
-
│ │ ├── *.js
|
|
138
|
-
│ │ └── *.jsonld
|
|
139
|
-
│ ├── config/
|
|
140
|
-
│ │ └── default.json ← Imported by your config
|
|
141
|
-
│ └── package.json
|
|
142
|
-
│
|
|
143
|
-
├── config-with-moderation.json ← Your config file
|
|
144
|
-
├── data/
|
|
145
|
-
│ └── moderation-logs/ ← Runtime logs
|
|
146
|
-
└── package.json
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## Customizing Thresholds in Production
|
|
150
|
-
|
|
151
|
-
Override specific settings in your config:
|
|
152
|
-
```json
|
|
153
|
-
{
|
|
154
|
-
"import": [
|
|
155
|
-
"css:config/file.json",
|
|
156
|
-
"@solid/moderation-plugin:config/default.json"
|
|
157
|
-
],
|
|
158
|
-
"@graph": [
|
|
159
|
-
{
|
|
160
|
-
"@id": "urn:solid-moderation:default:Config",
|
|
161
|
-
"ModerationConfig:_images_thresholds": {
|
|
162
|
-
"ModerationConfig:_images_thresholds_nudity": 0.3
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
]
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Monitoring
|
|
170
|
-
|
|
171
|
-
Check logs:
|
|
172
|
-
```bash
|
|
173
|
-
# Systemd
|
|
174
|
-
sudo journalctl -u solid-server -f
|
|
175
|
-
|
|
176
|
-
# PM2
|
|
177
|
-
pm2 logs solid-server
|
|
178
|
-
|
|
179
|
-
# Docker
|
|
180
|
-
docker-compose logs -f
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
View moderation violations:
|
|
184
|
-
```bash
|
|
185
|
-
cat data/moderation-logs/$(date +%Y-%m-%d).jsonl
|
|
186
|
-
```
|
package/PUBLISHING.md
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# Publishing Guide
|
|
2
|
-
|
|
3
|
-
## Before Publishing
|
|
4
|
-
|
|
5
|
-
Replace `@kkuffour` with your actual npm username/organization in these files:
|
|
6
|
-
- `package.json` (name, lsd:module, lsd:contexts)
|
|
7
|
-
- `components/context.jsonld` (smp prefix)
|
|
8
|
-
- `components/components.jsonld` (@context, @id, requireName)
|
|
9
|
-
- `config-with-moderation.json` (@context)
|
|
10
|
-
|
|
11
|
-
## Steps to Publish
|
|
12
|
-
|
|
13
|
-
### 1. Login to npm
|
|
14
|
-
```bash
|
|
15
|
-
npm login
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### 2. Build the package
|
|
19
|
-
```bash
|
|
20
|
-
cd /Users/opendata/solid-moderation
|
|
21
|
-
npm run build
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### 3. Test locally first (optional)
|
|
25
|
-
```bash
|
|
26
|
-
npm pack
|
|
27
|
-
# This creates a .tgz file you can test with
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### 4. Publish to npm
|
|
31
|
-
```bash
|
|
32
|
-
# For scoped package (recommended for testing)
|
|
33
|
-
npm publish --access public
|
|
34
|
-
|
|
35
|
-
# Or for private testing
|
|
36
|
-
npm publish --access restricted
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 5. Install in CSS
|
|
40
|
-
```bash
|
|
41
|
-
cd /Users/opendata/CommunitySolidServer
|
|
42
|
-
npm install @kkuffour/solid-moderation-plugin@latest
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 6. Update config reference
|
|
46
|
-
The config file path stays the same:
|
|
47
|
-
```bash
|
|
48
|
-
npm start -- -c /Users/opendata/solid-moderation/config-with-moderation.json -f data/ -p 3009 -l debug
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Version Updates
|
|
52
|
-
|
|
53
|
-
After making changes:
|
|
54
|
-
```bash
|
|
55
|
-
# Update version
|
|
56
|
-
npm version patch # 0.1.0 -> 0.1.1
|
|
57
|
-
# or
|
|
58
|
-
npm version minor # 0.1.0 -> 0.2.0
|
|
59
|
-
|
|
60
|
-
# Rebuild and republish
|
|
61
|
-
npm run build
|
|
62
|
-
npm publish
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Moving to @solid Namespace
|
|
66
|
-
|
|
67
|
-
Once tested and ready for official release:
|
|
68
|
-
|
|
69
|
-
1. Request access to @solid npm organization
|
|
70
|
-
2. Update all references from `@kkuffour/solid-moderation-plugin` to `@solid/moderation-plugin`
|
|
71
|
-
3. Update version to `1.0.0`
|
|
72
|
-
4. Publish to @solid namespace
|
|
73
|
-
|
|
74
|
-
## Troubleshooting
|
|
75
|
-
|
|
76
|
-
**If publish fails with 403:**
|
|
77
|
-
- Check you're logged in: `npm whoami`
|
|
78
|
-
- Verify package name is available: `npm view @kkuffour/solid-moderation-plugin`
|
|
79
|
-
- Ensure `--access public` for scoped packages
|
|
80
|
-
|
|
81
|
-
**If Components.js can't find module after install:**
|
|
82
|
-
- Verify `lsd:module`, `lsd:components`, and `lsd:contexts` paths in package.json
|
|
83
|
-
- Check that `components/` directory exists with `.jsonld` files
|
|
84
|
-
- Rebuild: `npm run build`
|
|
85
|
-
|
|
86
|
-
## Current Configuration
|
|
87
|
-
|
|
88
|
-
**Package Name**: `@kkuffour/solid-moderation-plugin`
|
|
89
|
-
**Version**: `0.1.0`
|
|
90
|
-
**Entry Point**: `dist/index.js`
|
|
91
|
-
**Components**: `components/components.jsonld`
|
|
92
|
-
**Context**: `components/context.jsonld`
|
|
93
|
-
|
|
94
|
-
**Environment Variables Required**:
|
|
95
|
-
```bash
|
|
96
|
-
export SIGHTENGINE_API_USER="1060049443"
|
|
97
|
-
export SIGHTENGINE_API_SECRET="QRQ8HUmh4hyvhZjksBJq5ZaNYPLPEKXu"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Thresholds** (configurable in config-with-moderation.json):
|
|
101
|
-
- Image Nudity: 0.1 (10%)
|
|
102
|
-
- Text Sexual: 0.1 (10%)
|
|
103
|
-
- Text Toxic: 0.1 (10%)
|
|
104
|
-
- Video Nudity: 0.1 (10%)
|
package/TESTING.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Testing ResourceStore Approach Locally
|
|
2
|
-
|
|
3
|
-
## Setup
|
|
4
|
-
|
|
5
|
-
1. Build the plugin:
|
|
6
|
-
```bash
|
|
7
|
-
npm run build
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
2. Link locally for testing:
|
|
11
|
-
```bash
|
|
12
|
-
npm link
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
3. In your CSS directory:
|
|
16
|
-
```bash
|
|
17
|
-
npm link @kkuffour/solid-moderation-plugin
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
4. Set environment variables:
|
|
21
|
-
```bash
|
|
22
|
-
export SIGHTENGINE_API_USER=1060049443
|
|
23
|
-
export SIGHTENGINE_API_SECRET=QRQ8HUmh4hyvhZjksBJq5ZaNYPLPEKXu
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
5. Create test config in CSS directory (`config-moderation-test.json`):
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"@context": [
|
|
30
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
|
|
31
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@kkuffour/solid-moderation-plugin/^0.2.0/components/context.jsonld"
|
|
32
|
-
],
|
|
33
|
-
"import": [
|
|
34
|
-
"css:config/file.json"
|
|
35
|
-
],
|
|
36
|
-
"@graph": [
|
|
37
|
-
{
|
|
38
|
-
"@id": "urn:solid-moderation:SightEngineProvider",
|
|
39
|
-
"@type": "ksmp:dist/providers/SightEngineProvider.jsonld#SightEngineProvider",
|
|
40
|
-
"ksmp:dist/providers/SightEngineProvider.jsonld#SightEngineProvider_apiUser": "1060049443",
|
|
41
|
-
"ksmp:dist/providers/SightEngineProvider.jsonld#SightEngineProvider_apiSecret": "QRQ8HUmh4hyvhZjksBJq5ZaNYPLPEKXu"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"@id": "urn:solid-server:default:ResourceStore_Original",
|
|
45
|
-
"@type": "DataAccessorBasedStore",
|
|
46
|
-
"identifierStrategy": { "@id": "urn:solid-server:default:IdentifierStrategy" },
|
|
47
|
-
"auxiliaryStrategy": { "@id": "urn:solid-server:default:AuxiliaryStrategy" },
|
|
48
|
-
"accessor": { "@id": "urn:solid-server:default:DataAccessor" },
|
|
49
|
-
"metadataStrategy": { "@id": "urn:solid-server:default:MetadataStrategy" }
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"@id": "urn:solid-server:default:ResourceStore",
|
|
53
|
-
"@type": "ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore",
|
|
54
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_source": {
|
|
55
|
-
"@id": "urn:solid-server:default:ResourceStore_Original"
|
|
56
|
-
},
|
|
57
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_client": {
|
|
58
|
-
"@id": "urn:solid-moderation:SightEngineProvider"
|
|
59
|
-
},
|
|
60
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_enabled": true,
|
|
61
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_imageNudityThreshold": 0.1,
|
|
62
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_textSexualThreshold": 0.1,
|
|
63
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_textToxicThreshold": 0.1,
|
|
64
|
-
"ksmp:dist/ModerationResourceStore.jsonld#ModerationResourceStore_videoNudityThreshold": 0.1
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
6. Start CSS:
|
|
71
|
-
```bash
|
|
72
|
-
npx @solid/community-server -c config-moderation-test.json
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Expected Behavior
|
|
76
|
-
|
|
77
|
-
- Server should start without errors
|
|
78
|
-
- Log should show: "ModerationResourceStore initialized"
|
|
79
|
-
- Uploading images/videos/text should trigger moderation
|
|
80
|
-
- Content exceeding thresholds should be blocked with 403 Forbidden
|
|
81
|
-
|
|
82
|
-
## Testing
|
|
83
|
-
|
|
84
|
-
Upload a test image:
|
|
85
|
-
```bash
|
|
86
|
-
curl -X PUT http://localhost:3000/test.jpg \
|
|
87
|
-
-H "Content-Type: image/jpeg" \
|
|
88
|
-
--data-binary @test-image.jpg
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Check logs for:
|
|
92
|
-
- "Moderating image/jpeg upload to /test.jpg"
|
|
93
|
-
- "Image APPROVED" or "Image BLOCKED"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": [
|
|
3
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@kkuffour/solid-moderation-plugin/^0.2.0/components/context.jsonld",
|
|
4
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@kkuffour/solid-moderation-plugin/^0.2.0/config/context.jsonld"
|
|
5
|
-
],
|
|
6
|
-
"@id": "npmd:@kkuffour/solid-moderation-plugin",
|
|
7
|
-
"@type": "Module",
|
|
8
|
-
"requireName": "@kkuffour/solid-moderation-plugin",
|
|
9
|
-
"import": [
|
|
10
|
-
"ksmp:dist/ModerationOperationHandler.jsonld",
|
|
11
|
-
"ksmp:dist/ModerationResourceStore.jsonld",
|
|
12
|
-
"ksmp:dist/ModerationConfig.jsonld",
|
|
13
|
-
"ksmp:dist/ModerationStore.jsonld",
|
|
14
|
-
"ksmp:dist/ModerationRecord.jsonld",
|
|
15
|
-
"ksmp:dist/ModerationMixin.jsonld",
|
|
16
|
-
"ksmp:dist/providers/SightEngineProvider.jsonld"
|
|
17
|
-
]
|
|
18
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface ModerationConfig {
|
|
2
|
-
enabled: boolean;
|
|
3
|
-
auditLoggingEnabled: boolean;
|
|
4
|
-
auditLoggingStorePath: string;
|
|
5
|
-
sightEngineApiUser: string;
|
|
6
|
-
sightEngineApiSecret: string;
|
|
7
|
-
imagesEnabled: boolean;
|
|
8
|
-
textEnabled: boolean;
|
|
9
|
-
videoEnabled: boolean;
|
|
10
|
-
imageNudityThreshold: number;
|
|
11
|
-
textSexualThreshold: number;
|
|
12
|
-
textToxicThreshold: number;
|
|
13
|
-
videoNudityThreshold: number;
|
|
14
|
-
}
|
|
15
|
-
export declare const DEFAULT_MODERATION_CONFIG: ModerationConfig;
|
|
16
|
-
//# sourceMappingURL=ModerationConfig.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ModerationConfig.d.ts","sourceRoot":"","sources":["../src/ModerationConfig.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,yBAAyB,EAAE,gBAavC,CAAC"}
|
package/dist/ModerationConfig.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_MODERATION_CONFIG = void 0;
|
|
4
|
-
exports.DEFAULT_MODERATION_CONFIG = {
|
|
5
|
-
enabled: true,
|
|
6
|
-
auditLoggingEnabled: true,
|
|
7
|
-
auditLoggingStorePath: './data/moderation-logs',
|
|
8
|
-
sightEngineApiUser: '',
|
|
9
|
-
sightEngineApiSecret: '',
|
|
10
|
-
imagesEnabled: true,
|
|
11
|
-
textEnabled: true,
|
|
12
|
-
videoEnabled: true,
|
|
13
|
-
imageNudityThreshold: 0.5,
|
|
14
|
-
textSexualThreshold: 0.5,
|
|
15
|
-
textToxicThreshold: 0.5,
|
|
16
|
-
videoNudityThreshold: 0.5,
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=ModerationConfig.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ModerationConfig.js","sourceRoot":"","sources":["../src/ModerationConfig.ts"],"names":[],"mappings":";;;AAea,QAAA,yBAAyB,GAAqB;IACzD,OAAO,EAAE,IAAI;IACb,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,wBAAwB;IAC/C,kBAAkB,EAAE,EAAE;IACtB,oBAAoB,EAAE,EAAE;IACxB,aAAa,EAAE,IAAI;IACnB,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,oBAAoB,EAAE,GAAG;IACzB,mBAAmB,EAAE,GAAG;IACxB,kBAAkB,EAAE,GAAG;IACvB,oBAAoB,EAAE,GAAG;CAC1B,CAAC"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"@context": [
|
|
3
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@kkuffour/solid-moderation-plugin/^0.2.0/components/context.jsonld",
|
|
4
|
-
"https://linkedsoftwaredependencies.org/bundles/npm/@kkuffour/solid-moderation-plugin/^0.2.0/config/context.jsonld"
|
|
5
|
-
],
|
|
6
|
-
"@id": "npmd:@kkuffour/solid-moderation-plugin",
|
|
7
|
-
"components": [
|
|
8
|
-
{
|
|
9
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig",
|
|
10
|
-
"@type": "AbstractClass",
|
|
11
|
-
"requireElement": "ModerationConfig",
|
|
12
|
-
"parameters": [],
|
|
13
|
-
"memberFields": [
|
|
14
|
-
{
|
|
15
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_enabled",
|
|
16
|
-
"memberFieldName": "enabled"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_auditLoggingEnabled",
|
|
20
|
-
"memberFieldName": "auditLoggingEnabled"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_auditLoggingStorePath",
|
|
24
|
-
"memberFieldName": "auditLoggingStorePath"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_sightEngineApiUser",
|
|
28
|
-
"memberFieldName": "sightEngineApiUser"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_sightEngineApiSecret",
|
|
32
|
-
"memberFieldName": "sightEngineApiSecret"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_imagesEnabled",
|
|
36
|
-
"memberFieldName": "imagesEnabled"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_textEnabled",
|
|
40
|
-
"memberFieldName": "textEnabled"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_videoEnabled",
|
|
44
|
-
"memberFieldName": "videoEnabled"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_imageNudityThreshold",
|
|
48
|
-
"memberFieldName": "imageNudityThreshold"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_textSexualThreshold",
|
|
52
|
-
"memberFieldName": "textSexualThreshold"
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_textToxicThreshold",
|
|
56
|
-
"memberFieldName": "textToxicThreshold"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"@id": "ksmp:dist/ModerationConfig.jsonld#ModerationConfig__member_videoNudityThreshold",
|
|
60
|
-
"memberFieldName": "videoNudityThreshold"
|
|
61
|
-
}
|
|
62
|
-
],
|
|
63
|
-
"constructorArguments": []
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Operation } from '@solid/community-server';
|
|
2
|
-
import type { ModerationConfig } from './ModerationConfig';
|
|
3
|
-
export declare class ModerationMixin {
|
|
4
|
-
protected readonly logger: import("@solid/community-server").Logger;
|
|
5
|
-
private readonly moderationConfig;
|
|
6
|
-
private readonly moderationStore?;
|
|
7
|
-
constructor(moderationConfig: ModerationConfig);
|
|
8
|
-
moderateContent(operation: Operation): Promise<void>;
|
|
9
|
-
private moderateImageContent;
|
|
10
|
-
private moderateTextContent;
|
|
11
|
-
private moderateVideoContent;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=ModerationMixin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ModerationMixin.d.ts","sourceRoot":"","sources":["../src/ModerationMixin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,qBAAa,eAAe;IAC1B,SAAS,CAAC,QAAQ,CAAC,MAAM,2CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAkB;gBAEhC,gBAAgB,EAAE,gBAAgB;IAOxC,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YAenD,oBAAoB;YAwCpB,mBAAmB;YAuCnB,oBAAoB;CAsCnC"}
|
package/dist/ModerationMixin.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ModerationMixin = void 0;
|
|
4
|
-
const node_stream_1 = require("node:stream");
|
|
5
|
-
const node_fs_1 = require("node:fs");
|
|
6
|
-
const community_server_1 = require("@solid/community-server");
|
|
7
|
-
const SightEngineProvider_1 = require("./providers/SightEngineProvider");
|
|
8
|
-
const ModerationStore_1 = require("./ModerationStore");
|
|
9
|
-
class ModerationMixin {
|
|
10
|
-
constructor(moderationConfig) {
|
|
11
|
-
this.logger = (0, community_server_1.getLoggerFor)(this);
|
|
12
|
-
this.moderationConfig = moderationConfig;
|
|
13
|
-
this.moderationStore = moderationConfig.auditLoggingEnabled ?
|
|
14
|
-
new ModerationStore_1.ModerationStore(moderationConfig.auditLoggingStorePath) :
|
|
15
|
-
undefined;
|
|
16
|
-
}
|
|
17
|
-
async moderateContent(operation) {
|
|
18
|
-
if (!this.moderationConfig.enabled || !operation.body?.data) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
const contentType = operation.body.metadata.contentType;
|
|
22
|
-
if (contentType?.startsWith('image/') && this.moderationConfig.imagesEnabled) {
|
|
23
|
-
await this.moderateImageContent(operation);
|
|
24
|
-
}
|
|
25
|
-
else if (contentType?.startsWith('text/') && this.moderationConfig.textEnabled) {
|
|
26
|
-
await this.moderateTextContent(operation);
|
|
27
|
-
}
|
|
28
|
-
else if (contentType?.startsWith('video/') && this.moderationConfig.videoEnabled) {
|
|
29
|
-
await this.moderateVideoContent(operation);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async moderateImageContent(operation) {
|
|
33
|
-
const chunks = [];
|
|
34
|
-
for await (const chunk of operation.body.data) {
|
|
35
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
36
|
-
}
|
|
37
|
-
const buffer = Buffer.concat(chunks);
|
|
38
|
-
operation.body.data = (0, community_server_1.guardStream)(node_stream_1.Readable.from(buffer));
|
|
39
|
-
const tempFile = `/tmp/moderation_${Date.now()}.jpg`;
|
|
40
|
-
await node_fs_1.promises.writeFile(tempFile, buffer);
|
|
41
|
-
try {
|
|
42
|
-
const client = new SightEngineProvider_1.SightEngineProvider(this.moderationConfig.sightEngineApiUser, this.moderationConfig.sightEngineApiSecret);
|
|
43
|
-
const result = await client.analyzeImage(tempFile);
|
|
44
|
-
this.logger.info(`MODERATION: Image analysis result - nudity score: ${result.nudity?.raw}, threshold: ${this.moderationConfig.imageNudityThreshold}`);
|
|
45
|
-
if (result.nudity?.raw && result.nudity.raw > this.moderationConfig.imageNudityThreshold) {
|
|
46
|
-
if (this.moderationStore) {
|
|
47
|
-
await this.moderationStore.recordViolation({
|
|
48
|
-
contentType: 'image',
|
|
49
|
-
resourcePath: operation.target.path,
|
|
50
|
-
violations: [{ model: 'nudity', score: result.nudity.raw, threshold: this.moderationConfig.imageNudityThreshold }],
|
|
51
|
-
contentSize: buffer.length,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
throw new community_server_1.ForbiddenHttpError('Upload blocked: Content violates community guidelines');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
if (error instanceof community_server_1.ForbiddenHttpError) {
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
this.logger.warn(`MODERATION: Image check failed (fail-open): ${error}`);
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
await node_fs_1.promises.unlink(tempFile).catch(() => { });
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async moderateTextContent(operation) {
|
|
68
|
-
const chunks = [];
|
|
69
|
-
for await (const chunk of operation.body.data) {
|
|
70
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
71
|
-
}
|
|
72
|
-
const buffer = Buffer.concat(chunks);
|
|
73
|
-
const text = buffer.toString('utf8');
|
|
74
|
-
operation.body.data = (0, community_server_1.guardStream)(node_stream_1.Readable.from(buffer));
|
|
75
|
-
if (!text?.trim()) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const client = new SightEngineProvider_1.SightEngineProvider(this.moderationConfig.sightEngineApiUser, this.moderationConfig.sightEngineApiSecret);
|
|
80
|
-
const result = await client.analyzeText(text);
|
|
81
|
-
if (result.sexual > this.moderationConfig.textSexualThreshold || result.toxic > this.moderationConfig.textToxicThreshold) {
|
|
82
|
-
if (this.moderationStore) {
|
|
83
|
-
await this.moderationStore.recordViolation({
|
|
84
|
-
contentType: 'text',
|
|
85
|
-
resourcePath: operation.target.path,
|
|
86
|
-
violations: [{ model: 'text', score: Math.max(result.sexual, result.toxic), threshold: Math.min(this.moderationConfig.textSexualThreshold, this.moderationConfig.textToxicThreshold) }],
|
|
87
|
-
contentSize: buffer.length,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
throw new community_server_1.ForbiddenHttpError('Upload blocked: Content violates community guidelines');
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
if (error instanceof community_server_1.ForbiddenHttpError) {
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
this.logger.warn(`MODERATION: Text check failed (fail-open): ${error}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async moderateVideoContent(operation) {
|
|
101
|
-
const chunks = [];
|
|
102
|
-
for await (const chunk of operation.body.data) {
|
|
103
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
104
|
-
}
|
|
105
|
-
const buffer = Buffer.concat(chunks);
|
|
106
|
-
operation.body.data = (0, community_server_1.guardStream)(node_stream_1.Readable.from(buffer));
|
|
107
|
-
const tempFile = `/tmp/moderation_${Date.now()}.mp4`;
|
|
108
|
-
await node_fs_1.promises.writeFile(tempFile, buffer);
|
|
109
|
-
try {
|
|
110
|
-
const client = new SightEngineProvider_1.SightEngineProvider(this.moderationConfig.sightEngineApiUser, this.moderationConfig.sightEngineApiSecret);
|
|
111
|
-
const result = await client.analyzeVideo(tempFile);
|
|
112
|
-
if (result.nudity?.raw && result.nudity.raw > this.moderationConfig.videoNudityThreshold) {
|
|
113
|
-
if (this.moderationStore) {
|
|
114
|
-
await this.moderationStore.recordViolation({
|
|
115
|
-
contentType: 'video',
|
|
116
|
-
resourcePath: operation.target.path,
|
|
117
|
-
violations: [{ model: 'nudity', score: result.nudity.raw, threshold: this.moderationConfig.videoNudityThreshold }],
|
|
118
|
-
contentSize: buffer.length,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
throw new community_server_1.ForbiddenHttpError('Upload blocked: Content violates community guidelines');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
if (error instanceof community_server_1.ForbiddenHttpError) {
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
this.logger.warn(`MODERATION: Video check failed (fail-open): ${error}`);
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
await node_fs_1.promises.unlink(tempFile).catch(() => { });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
exports.ModerationMixin = ModerationMixin;
|
|
136
|
-
//# sourceMappingURL=ModerationMixin.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ModerationMixin.js","sourceRoot":"","sources":["../src/ModerationMixin.ts"],"names":[],"mappings":"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"}
|