fix(plugins): respect USE_FRONTEND_BACKUP and create plugins dir

Resolve plugin directory based on USE_FRONTEND_BACKUP so /api/plugins
works with both frontend-backup and built frontend, aligning with
index.ts.

Ensure /app/frontend/plugins is created during non-backup Docker
builds to avoid missing-directory issues at runtime.

Update backup asset import reference and adjust local helper
script path.
This commit is contained in:
2025-10-04 16:48:59 -07:00
parent a86cd87964
commit 785032a8ad
6 changed files with 141 additions and 6 deletions
+4 -2
View File
@@ -30,7 +30,9 @@ ARG USE_FRONTEND_BACKUP
RUN if [ "$USE_FRONTEND_BACKUP" = "true" ]; then \ RUN if [ "$USE_FRONTEND_BACKUP" = "true" ]; then \
rm -rf /app/frontend && mkdir -p /app/frontend && cp -R /app/frontend-backup/. /app/frontend/; \ rm -rf /app/frontend && mkdir -p /app/frontend && cp -R /app/frontend-backup/. /app/frontend/; \
else \ else \
cd frontend-src && npm install && npm run build; \ cd frontend-src && npm install && npm run build && \
mkdir -p /app/frontend/plugins && \
echo "Plugins directory created for frontend build"; \
fi fi
# Create login.html from join.html if it doesn't exist # Create login.html from join.html if it doesn't exist
@@ -65,7 +67,7 @@ RUN pnpm db:generate
# Copy built application from builder stage # Copy built application from builder stage
COPY --from=builder /app/dist ./dist COPY --from=builder /app/dist ./dist
# Copy built frontend from builder stage # Copy built frontend from builder stage (includes SDK and plugins directory)
COPY --from=builder /app/frontend ./frontend COPY --from=builder /app/frontend ./frontend
# Expose port # Expose port
+125
View File
@@ -0,0 +1,125 @@
# FurryPlace Plugins System
The FurryPlace SDK provides an extensible plugin system that allows you to add custom buttons and functionality to the UI without modifying the core codebase.
## Quick Start
### Development (Local)
1. Add your plugin file to `frontend-backup/plugins/`:
```bash
# Example: Create a custom button plugin
cat > frontend-backup/plugins/my-plugin.js << 'EOF'
(function() {
function waitForSDK(callback) {
if (window.FurryPlaceSDK) {
callback();
} else {
setTimeout(() => waitForSDK(callback), 100);
}
}
waitForSDK(() => {
window.FurryPlaceSDK.registerButton({
id: 'my-button',
title: 'My Custom Button',
position: 'bottom',
icon: '<svg>...</svg>',
onClick: () => alert('Clicked!')
});
});
})();
EOF
```
2. Refresh your browser - the plugin loads automatically!
### Production (Docker)
When using `USE_FRONTEND_BACKUP=true`:
1. **Before building the Docker image**, add plugins to `frontend-backup/plugins/`
2. Build the image:
```bash
docker build --build-arg USE_FRONTEND_BACKUP=true -t furryplace .
```
3. The plugins will be included in the image automatically
When using the regular frontend build (not `USE_FRONTEND_BACKUP`):
1. Create a `frontend-src/public/plugins/` directory
2. Add your plugin files there
3. Build normally - the plugins will be copied to the final build
### Runtime Plugin Loading (Docker Volume)
You can also mount a plugins directory at runtime to add/update plugins without rebuilding:
```bash
docker run -d \
-v ./my-plugins:/app/frontend/plugins \
-e USE_FRONTEND_BACKUP=false \
furryplace
```
Or with docker-compose:
```yaml
services:
furryplace:
image: furryplace
volumes:
- ./my-plugins:/app/frontend/plugins
environment:
USE_FRONTEND_BACKUP: "false"
```
## How It Works
1. **Backend**: The `/api/plugins` endpoint (in `src/routes/plugins.ts`) scans the plugins directory and returns a list of `.js` files
2. **Frontend**: The SDK (`furryplace-sdk.js`) automatically fetches this list and dynamically loads each plugin
3. **Plugins**: Each plugin registers buttons or functionality using the SDK API
## Plugin Development
See the full documentation in [frontend-backup/plugins/README.md](frontend-backup/plugins/README.md) for:
- SDK API reference
- Button configuration options
- Example plugins
- Troubleshooting tips
## Environment Variables
The plugin system respects the `USE_FRONTEND_BACKUP` environment variable:
- `USE_FRONTEND_BACKUP=true` → Loads from `frontend-backup/plugins/`
- `USE_FRONTEND_BACKUP=false` → Loads from `frontend/plugins/`
This is automatically handled by the backend routes.
## Disabling Plugins
To disable a plugin without deleting it:
1. Rename it to anything that doesn't end in `.js` (e.g., `my-plugin.js.disabled`)
2. Refresh the page
The SDK only loads files ending in `.js`.
## Security Notes
- Plugins have full access to the browser's JavaScript environment
- Only load plugins from trusted sources
- Consider reviewing plugin code before deployment
- Plugins run in the same origin as your application
## Examples
Example plugins are included in `frontend-backup/plugins/example-button.js`:
- Help button with tooltip
- Debug info button
- Conditional rendering example
- External link button (Discord)
To try them out:
1. The example plugin is automatically loaded
2. Check the browser console for loading messages
3. Look for the new buttons in the UI
@@ -29,7 +29,7 @@ const __vite__mapDeps = (
"../chunks/D3yDgRbd.js", "../chunks/D3yDgRbd.js",
"../chunks/wZ7b5CwQ.js", "../chunks/wZ7b5CwQ.js",
"../nodes/3.DOMAwJeg.js", "../nodes/3.DOMAwJeg.js",
"../nodes/4.DB4WphWP.js", "../nodes/4.CrDfIbdR.js",
"../chunks/DueIxFLX.js", "../chunks/DueIxFLX.js",
"../chunks/CgCA7Awo.js", "../chunks/CgCA7Awo.js",
"../chunks/Dpga8uG-.js", "../chunks/Dpga8uG-.js",
@@ -6133,7 +6133,7 @@ const Ll = ai(gl),
), ),
() => () =>
L( L(
() => import("../nodes/4.DB4WphWP.js"), () => import("../nodes/4.CrDfIbdR.js"),
__vite__mapDeps([ __vite__mapDeps([
26, 1, 2, 3, 4, 5, 10, 12, 22, 11, 20, 19, 6, 7, 8, 9, 27, 13, 28, 29, 26, 1, 2, 3, 4, 5, 10, 12, 22, 11, 20, 19, 6, 7, 8, 9, 27, 13, 28, 29,
30, 31, 32, 33, 34, 24, 35, 36, 37, 38, 39, 40, 15, 18, 23, 14, 41, 30, 31, 32, 33, 34, 24, 35, 36, 37, 38, 39, 40, 15, 18, 23, 14, 41,
+5
View File
@@ -0,0 +1,5 @@
# Ignore all plugin files except the example
*
!.gitignore
!README.md
!example-button.js
+1 -1
View File
@@ -1,7 +1,7 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const filePath = 'f:\\place\\openplace\\frontend-backup\\_app\\immutable\\nodes\\4.CrDfIbdR.js'; const filePath = 'f:\\place\\FurryPlace\\frontend-backup\\_app\\immutable\\nodes\\4.CrDfIbdR.js';
// Read the file // Read the file
let content = fs.readFileSync(filePath, 'utf8'); let content = fs.readFileSync(filePath, 'utf8');
+4 -1
View File
@@ -6,11 +6,14 @@ import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
// Determine which frontend directory to use (matches index.ts logic)
const frontendDir = process.env['USE_FRONTEND_BACKUP'] === 'true' ? 'frontend-backup' : 'frontend';
export function setupPluginRoutes(app: App) { export function setupPluginRoutes(app: App) {
// Public endpoint - Get list of available plugins // Public endpoint - Get list of available plugins
app.get('/api/plugins', async (_req, res) => { app.get('/api/plugins', async (_req, res) => {
try { try {
const pluginsDir = path.join(__dirname, '..', '..', 'frontend-backup', 'plugins'); const pluginsDir = path.join(__dirname, '..', '..', frontendDir, 'plugins');
// Check if plugins directory exists // Check if plugins directory exists
if (!fs.existsSync(pluginsDir)) { if (!fs.existsSync(pluginsDir)) {