Skip to content

Commit ac4a175

Browse files
authored
Merge pull request #1388 from getfider/content-moderation
Content Moderation, revamped UI, and new "open core" licensing for some features.
2 parents 2b1350f + eb4c252 commit ac4a175

File tree

246 files changed

+8147
-3298
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

246 files changed

+8147
-3298
lines changed

.example.env

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,21 @@ EMAIL_SMTP_HOST=localhost
3333
EMAIL_SMTP_PORT=1025
3434
EMAIL_SMTP_USERNAME=
3535
EMAIL_SMTP_PASSWORD=
36+
37+
# Commercial License (Optional)
38+
#
39+
# COMMERCIAL_KEY: Your commercial license key from the hosted Fider platform
40+
# - Purchase a Pro subscription to get a license key
41+
# - Add it here to enable commercial features (content moderation)
42+
# - Leave blank to run Fider in free/open-source mode
43+
# COMMERCIAL_KEY=FIDER-COMMERCIAL-123-1638360000-abc123...
44+
#
45+
# LICENSE_PUBLIC_KEY: (Advanced - usually not needed)
46+
# - The public key is embedded in Fider and updates automatically
47+
# - Only set this if instructed by support or for testing
48+
# LICENSE_PUBLIC_KEY=lPHzCZhOBBihIusKWs5lzXCgGxZEKBpCiplkmZSjGpU=
49+
#
50+
# LICENSE_PRIVATE_KEY: (Internal - for hosted instance only)
51+
# - Used by the hosted Fider platform to generate license keys
52+
# - Self-hosted users should NOT set this
53+
# LICENSE_PRIVATE_KEY=your-base64-encoded-private-key-here

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
- name: make test-server
7373
run: |
7474
mkdir ./dist
75-
make test-server
75+
make test-server SHORT=false
7676
env:
7777
BLOB_STORAGE_S3_ENDPOINT_URL: http://localhost:9000
7878
DATABASE_URL: postgres://fider_ci:fider_ci_pw@localhost:5432/fider_ci?sslmode=disable

.github/workflows/locale.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
name: Auto-Translate Missing Keys
22

3+
# Workflow automatic triggers disabled - only manual trigger is available
34
on:
45
workflow_dispatch:
56
inputs:
@@ -8,11 +9,10 @@ on:
89
required: false
910
default: "false"
1011

11-
push:
12-
branches:
13-
- main
14-
paths:
15-
- "locale/en/**.json"
12+
# Automatic trigger temporarily disabled
13+
# push:
14+
# paths:
15+
# - "locale/en/**.json"
1616

1717
jobs:
1818
translate:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Manually Publish PR with ARM64
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
pr_number:
7+
description: 'PR number'
8+
required: true
9+
type: string
10+
11+
jobs:
12+
publish-pr-multiarch:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v6
16+
17+
- name: Set up QEMU
18+
uses: docker/setup-qemu-action@v3
19+
20+
- name: Set up Docker Buildx
21+
uses: docker/setup-buildx-action@v3
22+
23+
- name: Login to Docker Hub
24+
uses: docker/login-action@v3
25+
with:
26+
username: ${{ secrets.DOCKER_USER }}
27+
password: ${{ secrets.DOCKER_PASS }}
28+
29+
- name: Build and push
30+
uses: docker/build-push-action@v6
31+
with:
32+
push: true
33+
context: .
34+
build-args: COMMITHASH=${{ github.sha }}
35+
platforms: linux/amd64,linux/arm64/v8
36+
tags: getfider/fider:PR_${{ inputs.pr_number }}
37+
cache-from: type=gha
38+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ tsconfig.tsbuildinfo
2525
etc/*.pem
2626
fider_schema.sql
2727
fider.sql
28+
.zed/debug.json
29+
WARP.md

.test.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,7 @@ EMAIL_MAILGUN_DOMAIN=mydomain.com
4040

4141
USER_LIST_ENABLED=true
4242
USER_LIST_APIKEY=abcdefg
43+
44+
# Ed25519 license keys for testing
45+
LICENSE_PUBLIC_KEY=9o3OnNs9tnnD780fZcTYas8uhcA90MWtin1rWGhXVPE=
46+
LICENSE_PRIVATE_KEY=fwRNcFEiusrH0Hx1aBlOobE+6hm2/CPXUkUTWXj3cQn2jc6c2z22ecPvzR9lxNhqzy6FwD3Qxa2KfWtYaFdU8Q==

COMMERCIAL_MODERATION_PLAN.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Commercial Content Moderation Restructure Plan
2+
3+
## Overview
4+
5+
Moving Fider's content moderation feature from open source (AGPL) to commercial licensing using an "open core" model. The commercial code will reside in a `commercial/` folder with a restrictive license, while the open source core provides infrastructure and gracefully degrades when commercial features aren't licensed.
6+
7+
## Key Architectural Decisions
8+
9+
### 1. Webpack Integration (Simple Solution)
10+
- Update `webpack.config.js` to scan both folders:
11+
```js
12+
paths: [
13+
...glob.sync(`./public/**/*.{html,tsx}`, { nodir: true }),
14+
...glob.sync(`./commercial/**/*.{html,tsx}`, { nodir: true })
15+
]
16+
```
17+
- Commercial code gets bundled but legal license prevents usage
18+
- No complex conditional compilation needed
19+
20+
### 2. Backend Package Separation Strategy
21+
22+
**Handlers - Dynamic Route Registration**
23+
- Open source: Register stub routes returning "upgrade" messages
24+
- Commercial: Override routes with real handlers via dynamic registration
25+
- License validation controls active routes
26+
27+
**Services - Stub + Override Pattern**
28+
- Open source: Register stub bus handlers returning "not licensed" errors
29+
- Commercial: Override with real implementations via bus registration
30+
- Same command/query types, different implementations
31+
32+
## What Moves to Commercial (Strong Legal Protection)
33+
34+
### Commercial Folder Structure
35+
```
36+
commercial/
37+
├── LICENSE (restrictive commercial license)
38+
├── pages/
39+
│ └── Administration/
40+
│ └── ContentModeration.page.tsx (~300 lines)
41+
├── components/
42+
│ └── ModerationIndicator.tsx
43+
├── handlers/
44+
│ ├── moderation.go (ModerationPage, GetModerationItems, GetModerationCount)
45+
│ └── apiv1/
46+
│ ├── moderation.go (all API endpoints)
47+
│ └── moderation_test.go
48+
├── services/
49+
│ └── moderation.go (approve/decline business logic)
50+
└── init.go (service/route registration)
51+
```
52+
53+
### Frontend (Moves to Commercial)
54+
- Complete moderation admin UI (ContentModeration.page.tsx + styles)
55+
- Moderation indicator component showing pending counts
56+
- All moderation-specific UI components
57+
58+
### Backend (Moves to Commercial)
59+
- All HTTP handlers (`ModerationPage`, `GetModerationItems`, `GetModerationCount`)
60+
- All API endpoints (`/api/v1/admin/moderation/*` routes)
61+
- Business logic implementations (approve/decline/verify/block functions)
62+
- Route registrations for moderation endpoints
63+
- Tests for commercial functionality
64+
65+
## What Stays Open Source (Minimal Infrastructure)
66+
67+
### Database/Models (Cannot Move - Bus System Dependency)
68+
- `app/models/cmd/moderation.go` - Command type definitions
69+
- `app/models/query/moderation.go` - Query type definitions
70+
- Database schema, migrations (`is_moderation_enabled`, `is_approved` columns)
71+
- Entity definitions (`ModerationItem` structs)
72+
73+
### Settings Infrastructure
74+
- Privacy settings toggle (shows "upgrade" message if unlicensed)
75+
- Basic tenant property (`isModerationEnabled`)
76+
- License validation service
77+
78+
### Content Flow Logic
79+
- Logic that marks content as needing approval
80+
- Basic "content requires moderation" checks throughout codebase
81+
- Content queuing when moderation is enabled
82+
83+
### Locale Files
84+
- All `moderation.*` translation strings (used by upgrade messages too)
85+
86+
## Technical Implementation Details
87+
88+
### 1. Handler Registration Pattern
89+
```go
90+
// Open source routes.go - stub routes
91+
ui.Get("/admin/moderation", upgradeHandler("content-moderation"))
92+
ui.Get("/_api/admin/moderation/items", upgradeHandler("content-moderation"))
93+
94+
// Commercial init.go - overrides routes
95+
func init() {
96+
if license.IsCommercialFeatureEnabled("content-moderation") {
97+
web.RegisterRoute("GET", "/admin/moderation", handlers.ModerationPage())
98+
web.RegisterRoute("GET", "/_api/admin/moderation/items", handlers.GetModerationItems())
99+
// ... all other moderation routes
100+
}
101+
}
102+
```
103+
104+
### 2. Service Registration Pattern
105+
```go
106+
// Open source postgres.go - register stubs
107+
func (s *Service) Init() {
108+
bus.AddHandler(approvePostStub) // Returns "feature not licensed"
109+
bus.AddHandler(declinePostStub)
110+
bus.AddHandler(getModerationItemsStub)
111+
// ... other stubs
112+
}
113+
114+
// Commercial service init - override with real handlers
115+
func (cs *CommercialService) Init() {
116+
if license.IsCommercialFeatureEnabled("content-moderation") {
117+
bus.AddHandler(approvePost) // Real implementation
118+
bus.AddHandler(declinePost)
119+
bus.AddHandler(getModerationItems)
120+
// ... other real handlers
121+
}
122+
}
123+
```
124+
125+
### 3. License Validation Service
126+
```go
127+
// app/services/license.go
128+
type LicenseService interface {
129+
IsCommercialFeatureEnabled(feature string) bool
130+
}
131+
132+
// Implementation checks for valid commercial license
133+
// Controls route registration and service overrides
134+
```
135+
136+
## Implementation Phases
137+
138+
### Phase 1: Setup Commercial Infrastructure
139+
1. Create `commercial/` folder structure
140+
2. Add restrictive LICENSE file to commercial folder
141+
3. Update webpack.config.js to scan commercial folder
142+
4. Create license validation service interface
143+
144+
### Phase 2: Move Frontend Components
145+
1. Move `ContentModeration.page.tsx` to `commercial/pages/Administration/`
146+
2. Move `ModerationIndicator.tsx` to `commercial/components/`
147+
3. Test that webpack builds both locations correctly
148+
4. Add license checks in components to show upgrade messages
149+
150+
### Phase 3: Implement Backend Service Separation
151+
1. Create stub implementations for all moderation commands/queries
152+
2. Register stubs in open source postgres service
153+
3. Move real implementations to `commercial/services/moderation.go`
154+
4. Implement commercial service registration with license checks
155+
156+
### Phase 4: Implement Route Separation
157+
1. Replace direct handler calls with upgrade handlers in routes.go
158+
2. Move real handlers to `commercial/handlers/`
159+
3. Implement dynamic route registration in commercial init
160+
4. Test route overriding works correctly
161+
162+
### Phase 5: Testing & Validation
163+
1. Move tests to commercial folder
164+
2. Test open source build without commercial features
165+
3. Test commercial build with license validation
166+
4. Verify graceful degradation and upgrade messaging
167+
5. Test that forking open source works without commercial parts
168+
169+
## Key Benefits
170+
171+
### Strong Commercial Protection
172+
- Recreating moderation requires rebuilding entire UI + API + business logic
173+
- Substantial engineering effort (days/weeks) to replicate functionality
174+
- Clear legal separation under different licenses
175+
176+
### Clean Architecture
177+
- Open source provides database infrastructure and settings
178+
- Commercial enhances with actual moderation functionality
179+
- No broken states - content flows normally when moderation disabled
180+
- True open core model: commercial builds on open source foundation
181+
182+
### Simple Build Process
183+
- Single repository with clear folder separation
184+
- Standard webpack build process
185+
- License provides protection, not technical hiding
186+
- Easy development workflow
187+
188+
## Migration Considerations
189+
190+
### Existing Moderation Data
191+
- All existing moderation settings and data remain compatible
192+
- Database schema stays in open source (infrastructure)
193+
- Only the management interface becomes commercial
194+
195+
### User Experience
196+
- **With License**: Full moderation functionality as before
197+
- **Without License**: Moderation simply disabled, standard user flow
198+
- **Upgrade Path**: Clear messaging and sales funnel for commercial features
199+
200+
### Development Workflow
201+
- Developers can see all code (legal license controls usage)
202+
- Standard build process works for both open source and commercial
203+
- Clean separation makes it easy to add more commercial features
204+
205+
## Success Metrics
206+
207+
1. **Legal Protection**: Someone forking open source cannot easily recreate moderation
208+
2. **Functional Separation**: Open source works perfectly without moderation
209+
3. **Build Compatibility**: Both open source and commercial versions build successfully
210+
4. **Clean Boundaries**: Clear understanding of what's core vs commercial
211+
5. **Scalability**: Pattern works for future commercial features
212+
213+
This plan provides genuine open core protection while maintaining clean architecture and manageable implementation complexity.
480 KB
Loading
369 KB
Loading
220 KB
Loading

0 commit comments

Comments
 (0)