Compare commits
5 Commits
65cd9a73bb
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
5f8238cbc2 | ||
|
a64e79e3fc | ||
|
e2a2665fb1 | ||
|
fe3e9fc27e | ||
|
34ef0803d8 |
127
CONFIGURATION.md
127
CONFIGURATION.md
@@ -1,127 +0,0 @@
|
||||
# Docmost Cloudron Configuration
|
||||
|
||||
Your Docmost instance is configured with Cloudron defaults but can be customized after installation.
|
||||
|
||||
## Current Configuration
|
||||
|
||||
**Email**: Uses Cloudron's internal email server by default
|
||||
**Storage**: Uses local storage in `/app/data/uploads` by default
|
||||
**Database**: PostgreSQL (managed by Cloudron)
|
||||
**Cache**: Redis (managed by Cloudron)
|
||||
|
||||
## Custom Configuration
|
||||
|
||||
To customize your Docmost installation, you can create a `.env` file in the app's data directory:
|
||||
|
||||
### 1. Access Your App's Data Directory
|
||||
|
||||
```bash
|
||||
# SSH into your Cloudron server
|
||||
cloudron exec --app docmost
|
||||
|
||||
# Navigate to the data directory
|
||||
cd /app/data
|
||||
|
||||
# Copy the sample configuration
|
||||
cp env.sample .env
|
||||
```
|
||||
|
||||
### 2. Edit the Configuration
|
||||
|
||||
```bash
|
||||
# Edit the .env file
|
||||
nano .env
|
||||
```
|
||||
|
||||
### 3. Restart the App
|
||||
|
||||
After making changes to the `.env` file, restart the app:
|
||||
|
||||
```bash
|
||||
cloudron restart --app docmost
|
||||
```
|
||||
|
||||
## Common Customizations
|
||||
|
||||
### Custom Email Server
|
||||
|
||||
To use Gmail instead of Cloudron's email server:
|
||||
|
||||
```bash
|
||||
# In /app/data/.env
|
||||
MAIL_DRIVER=smtp
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your-email@gmail.com
|
||||
SMTP_PASSWORD=your-app-password
|
||||
SMTP_SECURE=true
|
||||
MAIL_FROM_ADDRESS=your-email@gmail.com
|
||||
MAIL_FROM_NAME=Docmost
|
||||
```
|
||||
|
||||
### S3 Storage
|
||||
|
||||
To use Amazon S3 or compatible storage:
|
||||
|
||||
```bash
|
||||
# In /app/data/.env
|
||||
STORAGE_DRIVER=s3
|
||||
AWS_S3_ACCESS_KEY_ID=your-access-key
|
||||
AWS_S3_SECRET_ACCESS_KEY=your-secret-key
|
||||
AWS_S3_REGION=us-east-1
|
||||
AWS_S3_BUCKET=your-bucket-name
|
||||
AWS_S3_ENDPOINT=https://s3.amazonaws.com
|
||||
```
|
||||
|
||||
### File Upload Limits
|
||||
|
||||
To increase file upload limits:
|
||||
|
||||
```bash
|
||||
# In /app/data/.env
|
||||
FILE_UPLOAD_SIZE_LIMIT=100MB
|
||||
FILE_IMPORT_SIZE_LIMIT=100MB
|
||||
```
|
||||
|
||||
### Custom Draw.io Server
|
||||
|
||||
To use a self-hosted draw.io server:
|
||||
|
||||
```bash
|
||||
# In /app/data/.env
|
||||
DRAWIO_URL=https://your-drawio-server.com
|
||||
```
|
||||
|
||||
## Available Environment Variables
|
||||
|
||||
See the full list of available environment variables in the [Docmost documentation](https://docmost.com/docs/self-hosting/environment-variables).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check Current Configuration
|
||||
|
||||
```bash
|
||||
cloudron exec --app docmost -- env | grep -E "(MAIL|STORAGE|S3)" | sort
|
||||
```
|
||||
|
||||
### View App Logs
|
||||
|
||||
```bash
|
||||
cloudron logs --app docmost
|
||||
```
|
||||
|
||||
### Reset to Defaults
|
||||
|
||||
To reset to Cloudron defaults, simply remove the custom .env file:
|
||||
|
||||
```bash
|
||||
cloudron exec --app docmost -- rm -f /app/data/.env
|
||||
cloudron restart --app docmost
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
- The `.env` file is stored in `/app/data/` which is included in Cloudron backups
|
||||
- Database and Redis credentials are managed by Cloudron and should not be changed
|
||||
- Email credentials are stored in plaintext in the `.env` file
|
||||
- Consider using app-specific passwords for email providers
|
@@ -1,93 +0,0 @@
|
||||
# Docmost Cloudron Package - Deployment Status
|
||||
|
||||
## Current Status: 🔄 In Progress
|
||||
|
||||
The Docmost Cloudron package has been successfully created with all required components but is experiencing a Redis URL parsing issue preventing startup.
|
||||
|
||||
## Package Components Created ✅
|
||||
|
||||
### Core Files
|
||||
- **CloudronManifest.json** - Complete app configuration with all addons
|
||||
- **Dockerfile** - Production build with Node.js 20 and pnpm
|
||||
- **start.sh** - Initialization script with database setup
|
||||
- **nginx.conf** - Reverse proxy with WebSocket support
|
||||
- **supervisord.conf** - Process management configuration
|
||||
- **README.md** - Comprehensive documentation
|
||||
|
||||
### Features Implemented
|
||||
- ✅ PostgreSQL database integration
|
||||
- ✅ Email notifications via Cloudron SMTP
|
||||
- ✅ File storage in persistent directory
|
||||
- ✅ Health checks and logging
|
||||
- ✅ Production-ready build process
|
||||
- ✅ WebSocket support for real-time collaboration
|
||||
- ⚠️ Redis integration (currently blocked by URL parsing issue)
|
||||
|
||||
## Current Issue 🔧
|
||||
|
||||
**Problem**: Redis URL parsing incompatibility
|
||||
- **Error**: `RangeError [ERR_SOCKET_BAD_PORT]: Port should be >= 0 and < 65536. Received type number (NaN)`
|
||||
- **Root Cause**: Cloudron Redis URL format differs from what Docmost expects
|
||||
- **Impact**: App cannot start due to Redis validation failure
|
||||
|
||||
## Build Information
|
||||
|
||||
**Latest Version**: andreasdueren/docmost-cloudron:0.1.8
|
||||
**Status**: App installs and runs but returns 502 due to Redis parsing issue
|
||||
|
||||
### Build Commands
|
||||
```bash
|
||||
# Build
|
||||
cloudron build --set-build-service builder.docker.due.ren \
|
||||
--build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \
|
||||
--set-repository andreasdueren/docmost-cloudron --tag 0.1.8
|
||||
|
||||
# Install
|
||||
cloudron install --location docmost.due.ren \
|
||||
--image andreasdueren/docmost-cloudron:0.1.8
|
||||
```
|
||||
|
||||
## Next Steps 🎯
|
||||
|
||||
1. **Debug Redis URL format** - Add logging to understand Cloudron Redis URL structure
|
||||
2. **Fix URL parsing** - Implement correct Redis URL transformation
|
||||
3. **Test without Redis** - Investigate if Docmost can run without Redis for basic functionality
|
||||
4. **Final deployment** - Complete working package
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
docmost-cloudron/
|
||||
├── CloudronManifest.json # App configuration
|
||||
├── Dockerfile # Container build
|
||||
├── start.sh # Startup script
|
||||
├── nginx.conf # Reverse proxy
|
||||
├── supervisord.conf # Process management
|
||||
├── oidc-middleware.js # Authentication (future)
|
||||
├── package.json # Dependencies
|
||||
├── README.md # Documentation
|
||||
└── DEPLOYMENT.md # This file
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**For Redis URL Issues:**
|
||||
```bash
|
||||
# Check logs
|
||||
cloudron logs --app docmost.due.ren
|
||||
|
||||
# Shell into container
|
||||
cloudron exec --app docmost.due.ren
|
||||
|
||||
# Check Redis URL format
|
||||
echo $CLOUDRON_REDIS_URL
|
||||
```
|
||||
|
||||
**For Build Issues:**
|
||||
```bash
|
||||
# Clean build
|
||||
git clean -fdx
|
||||
cloudron build --tag $(date +%s)
|
||||
```
|
||||
|
||||
The package is 95% complete with all infrastructure in place. Only the Redis URL compatibility issue remains to be resolved for full functionality.
|
@@ -24,17 +24,15 @@ RUN mkdir -p /tmp/data /app/data && \
|
||||
|
||||
# Copy startup scripts and configuration files
|
||||
COPY start.sh /app/code/
|
||||
COPY healthcheck.js /app/code/
|
||||
COPY nginx.conf /etc/nginx/sites-available/default
|
||||
COPY env.sample /app/code/env.sample
|
||||
COPY CONFIGURATION.md /app/code/CONFIGURATION.md
|
||||
|
||||
# Override nginx global logs to prevent read-only filesystem errors
|
||||
RUN sed -i 's|error_log /var/log/nginx/error.log;|error_log /dev/stderr;|' /etc/nginx/nginx.conf && \
|
||||
sed -i 's|access_log /var/log/nginx/access.log;|access_log /dev/stdout;|' /etc/nginx/nginx.conf
|
||||
|
||||
# Make scripts executable
|
||||
RUN chmod +x /app/code/start.sh /app/code/healthcheck.js
|
||||
RUN chmod +x /app/code/start.sh
|
||||
|
||||
# Install supervisord and netcat for process management and connectivity checks
|
||||
RUN apt-get update && \
|
||||
|
15
README.md
15
README.md
@@ -28,18 +28,14 @@ Docmost is a collaborative documentation platform featuring:
|
||||
|
||||
2. Build the Cloudron package:
|
||||
```bash
|
||||
cloudron build --set-build-service builder.docker.due.ren \
|
||||
--build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \
|
||||
--set-repository andreasdueren/docmost-cloudron \
|
||||
--tag 0.1.2
|
||||
cloudron build
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install the package on your Cloudron:
|
||||
```bash
|
||||
cloudron install --location docmost.yourdomain.com \
|
||||
--image andreasdueren/docmost-cloudron:0.1.2
|
||||
cloudron install --location docmost
|
||||
```
|
||||
|
||||
2. After installation, access your Docmost instance at the configured domain.
|
||||
@@ -99,15 +95,12 @@ If the build fails, try:
|
||||
1. **Clean build**:
|
||||
```bash
|
||||
git clean -fdx
|
||||
cloudron build --set-build-service builder.docker.due.ren \
|
||||
--build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \
|
||||
--set-repository andreasdueren/docmost-cloudron \
|
||||
--tag 0.1.3
|
||||
cloudron build
|
||||
```
|
||||
|
||||
2. **Check logs**:
|
||||
```bash
|
||||
cloudron logs --app docmost.yourdomain.com -f
|
||||
cloudron logs --app docmost
|
||||
```
|
||||
|
||||
### Installation Issues
|
||||
|
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const http = require('http');
|
||||
|
||||
// Simple health check that attempts to connect to the app
|
||||
const options = {
|
||||
hostname: 'localhost',
|
||||
port: 3001,
|
||||
path: '/',
|
||||
method: 'GET',
|
||||
timeout: 5000
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
console.log(`Health check: ${res.statusCode}`);
|
||||
if (res.statusCode === 200) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
req.on('error', (err) => {
|
||||
console.error('Health check failed:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
req.on('timeout', () => {
|
||||
console.error('Health check timeout');
|
||||
req.abort();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
req.end();
|
@@ -1,145 +0,0 @@
|
||||
const express = require('express');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const axios = require('axios');
|
||||
|
||||
class CloudronOIDCMiddleware {
|
||||
constructor(options = {}) {
|
||||
this.clientId = process.env.CLOUDRON_OIDC_CLIENT_ID;
|
||||
this.clientSecret = process.env.CLOUDRON_OIDC_CLIENT_SECRET;
|
||||
this.issuer = process.env.CLOUDRON_OIDC_ISSUER;
|
||||
this.redirectUri = process.env.OIDC_REDIRECT_URI;
|
||||
this.appOrigin = process.env.CLOUDRON_APP_ORIGIN;
|
||||
}
|
||||
|
||||
// Middleware to check authentication
|
||||
authenticate() {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
// Check for existing session/token
|
||||
const token = req.headers.authorization?.replace('Bearer ', '') ||
|
||||
req.cookies?.authToken ||
|
||||
req.session?.token;
|
||||
|
||||
if (token && this.verifyToken(token)) {
|
||||
req.user = jwt.decode(token);
|
||||
return next();
|
||||
}
|
||||
|
||||
// If no valid token, redirect to OIDC login
|
||||
if (req.path.startsWith('/api/')) {
|
||||
return res.status(401).json({ error: 'Authentication required' });
|
||||
}
|
||||
|
||||
// Redirect to OIDC authorization
|
||||
const authUrl = this.buildAuthUrl();
|
||||
res.redirect(authUrl);
|
||||
} catch (error) {
|
||||
console.error('Authentication error:', error);
|
||||
res.status(500).json({ error: 'Authentication failed' });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Build OIDC authorization URL
|
||||
buildAuthUrl() {
|
||||
const params = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: this.clientId,
|
||||
redirect_uri: this.redirectUri,
|
||||
scope: 'openid profile email',
|
||||
state: this.generateState()
|
||||
});
|
||||
|
||||
return `${this.issuer}/auth?${params.toString()}`;
|
||||
}
|
||||
|
||||
// Handle OIDC callback
|
||||
async handleCallback(req, res) {
|
||||
try {
|
||||
const { code, state } = req.query;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: 'Authorization code required' });
|
||||
}
|
||||
|
||||
// Exchange code for tokens
|
||||
const tokenResponse = await this.exchangeCodeForTokens(code);
|
||||
const { access_token, id_token } = tokenResponse.data;
|
||||
|
||||
// Verify and decode the ID token
|
||||
const userInfo = jwt.decode(id_token);
|
||||
|
||||
// Create user session
|
||||
const sessionToken = this.createSessionToken(userInfo);
|
||||
|
||||
// Set cookie and redirect
|
||||
res.cookie('authToken', sessionToken, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'lax',
|
||||
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
|
||||
});
|
||||
|
||||
res.redirect('/');
|
||||
} catch (error) {
|
||||
console.error('OIDC callback error:', error);
|
||||
res.status(500).json({ error: 'Authentication callback failed' });
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange authorization code for tokens
|
||||
async exchangeCodeForTokens(code) {
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
code,
|
||||
redirect_uri: this.redirectUri,
|
||||
client_id: this.clientId,
|
||||
client_secret: this.clientSecret
|
||||
});
|
||||
|
||||
return axios.post(`${this.issuer}/token`, params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create session token
|
||||
createSessionToken(userInfo) {
|
||||
const payload = {
|
||||
sub: userInfo.sub,
|
||||
email: userInfo.email,
|
||||
name: userInfo.name,
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
exp: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days
|
||||
};
|
||||
|
||||
return jwt.sign(payload, process.env.APP_SECRET);
|
||||
}
|
||||
|
||||
// Verify JWT token
|
||||
verifyToken(token) {
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.APP_SECRET);
|
||||
return decoded.exp > Math.floor(Date.now() / 1000);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate random state for CSRF protection
|
||||
generateState() {
|
||||
return Math.random().toString(36).substring(2, 15) +
|
||||
Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
|
||||
// Logout handler
|
||||
logout() {
|
||||
return (req, res) => {
|
||||
res.clearCookie('authToken');
|
||||
res.redirect('/');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CloudronOIDCMiddleware;
|
14
package.json
14
package.json
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "docmost-cloudron",
|
||||
"version": "1.0.0",
|
||||
"description": "Cloudron package for Docmost",
|
||||
"scripts": {
|
||||
"start": "./start.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"axios": "^1.6.0",
|
||||
"cookie-parser": "^1.4.6"
|
||||
}
|
||||
}
|
21
start.sh
21
start.sh
@@ -11,12 +11,11 @@ if [ ! -d "/app/data/client-dist" ]; then
|
||||
chown -R cloudron:cloudron /app/data/client-dist
|
||||
fi
|
||||
|
||||
# Copy sample .env file and documentation for user reference
|
||||
# Copy sample .env file for user reference
|
||||
if [ ! -f "/app/data/env.sample" ]; then
|
||||
echo "=> Copying configuration files to /app/data/"
|
||||
cp /app/code/env.sample /app/data/env.sample
|
||||
cp /app/code/CONFIGURATION.md /app/data/CONFIGURATION.md
|
||||
chown cloudron:cloudron /app/data/env.sample /app/data/CONFIGURATION.md
|
||||
chown cloudron:cloudron /app/data/env.sample
|
||||
fi
|
||||
|
||||
# Initialize /app/data if it's empty (first run)
|
||||
@@ -102,9 +101,19 @@ fi
|
||||
# Load custom environment variables if they exist
|
||||
if [ -f "/app/data/.env" ]; then
|
||||
echo "=> Loading custom environment variables from /app/data/.env"
|
||||
set -a # automatically export all variables
|
||||
source /app/data/.env
|
||||
set +a # stop automatically exporting
|
||||
# Validate .env file format before sourcing
|
||||
if grep -q "^[[:space:]]*[^#][^=]*=" /app/data/.env 2>/dev/null; then
|
||||
set -a # automatically export all variables
|
||||
if source /app/data/.env 2>/dev/null; then
|
||||
echo "=> Custom .env file loaded successfully"
|
||||
else
|
||||
echo "=> Warning: Error loading .env file, using Cloudron defaults"
|
||||
echo "=> Check /app/data/.env for syntax errors (values with spaces need quotes)"
|
||||
fi
|
||||
set +a # stop automatically exporting
|
||||
else
|
||||
echo "=> Warning: /app/data/.env appears to be empty or invalid, using Cloudron defaults"
|
||||
fi
|
||||
else
|
||||
echo "=> No custom .env file found, using Cloudron defaults"
|
||||
fi
|
||||
|
Reference in New Issue
Block a user