scheduling features
This commit is contained in:
189
mkdocs/docs/assets/js/scheduling-poll.js
Normal file
189
mkdocs/docs/assets/js/scheduling-poll.js
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Scheduling Poll Block Hydration for MkDocs
|
||||
*
|
||||
* Scans for .scheduling-poll-block elements, fetches poll data from the API,
|
||||
* and renders a read-only poll summary with a "Vote Now" link to the full
|
||||
* interactive voting page on the app.
|
||||
*
|
||||
* Follows the gancio-events.js hydration pattern.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function getApiUrl() {
|
||||
// env-config.js injects these globals
|
||||
if (window.PAYMENT_API_URL) return window.PAYMENT_API_URL;
|
||||
if (window.API_URL) return window.API_URL;
|
||||
|
||||
var host = window.location.hostname;
|
||||
if (host !== 'localhost' && host.indexOf('.') !== -1) {
|
||||
var parts = host.split('.');
|
||||
var base = parts.slice(-2).join('.');
|
||||
return window.location.protocol + '//api.' + base;
|
||||
}
|
||||
return 'http://localhost:4000';
|
||||
}
|
||||
|
||||
function getAppUrl() {
|
||||
if (window.APP_URL) return window.APP_URL;
|
||||
|
||||
var host = window.location.hostname;
|
||||
if (host !== 'localhost' && host.indexOf('.') !== -1) {
|
||||
var parts = host.split('.');
|
||||
var base = parts.slice(-2).join('.');
|
||||
return window.location.protocol + '//app.' + base;
|
||||
}
|
||||
return 'http://localhost:3000';
|
||||
}
|
||||
|
||||
var STATUS_COLORS = {
|
||||
OPEN: '#52c41a',
|
||||
CLOSED: '#fa8c16',
|
||||
FINALIZED: '#1890ff',
|
||||
CANCELLED: '#ff4d4f',
|
||||
};
|
||||
|
||||
var STATUS_LABELS = {
|
||||
OPEN: 'Open for Voting',
|
||||
CLOSED: 'Closed',
|
||||
FINALIZED: 'Date Confirmed',
|
||||
CANCELLED: 'Cancelled',
|
||||
};
|
||||
|
||||
function formatDate(dateStr) {
|
||||
var d = new Date(dateStr + 'T00:00:00');
|
||||
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
return days[d.getDay()] + ', ' + months[d.getMonth()] + ' ' + d.getDate();
|
||||
}
|
||||
|
||||
function hydrateBlocks() {
|
||||
var blocks = document.querySelectorAll('.scheduling-poll-block');
|
||||
if (blocks.length === 0) return;
|
||||
|
||||
var apiUrl = getApiUrl();
|
||||
var appUrl = getAppUrl();
|
||||
|
||||
blocks.forEach(function (block) {
|
||||
// Skip if already hydrated
|
||||
if (block.getAttribute('data-hydrated') === 'true') return;
|
||||
|
||||
var slug = block.getAttribute('data-poll-slug');
|
||||
if (!slug) return;
|
||||
|
||||
var showComments = block.getAttribute('data-show-comments') !== 'false';
|
||||
var title = block.getAttribute('data-title') || '';
|
||||
|
||||
block.setAttribute('data-hydrated', 'true');
|
||||
block.innerHTML = '<div style="text-align:center; padding:20px; opacity:0.6;">Loading poll...</div>';
|
||||
|
||||
fetch(apiUrl + '/api/meeting-planner/public/' + encodeURIComponent(slug))
|
||||
.then(function (res) {
|
||||
if (!res.ok) throw new Error('Poll not found');
|
||||
return res.json();
|
||||
})
|
||||
.then(function (poll) {
|
||||
var statusColor = STATUS_COLORS[poll.status] || '#666';
|
||||
var statusLabel = STATUS_LABELS[poll.status] || poll.status;
|
||||
var isFinalized = poll.status === 'FINALIZED';
|
||||
var options = poll.options || [];
|
||||
var bestScore = 0;
|
||||
options.forEach(function (o) {
|
||||
if ((o.score || 0) > bestScore) bestScore = o.score || 0;
|
||||
});
|
||||
|
||||
var html = '';
|
||||
|
||||
// Title
|
||||
if (title) {
|
||||
html += '<h2 style="text-align:center; margin:0 0 8px; font-size:1.5rem;">' + title + '</h2>';
|
||||
}
|
||||
|
||||
// Poll title + status
|
||||
html += '<h3 style="margin:0 0 8px; font-size:1.2rem;">' + poll.title + '</h3>';
|
||||
if (poll.description) {
|
||||
html += '<p style="opacity:0.75; margin:0 0 8px; line-height:1.5;">' + poll.description + '</p>';
|
||||
}
|
||||
html += '<div style="margin-bottom:12px;">';
|
||||
html += '<span style="display:inline-block; padding:2px 10px; border-radius:4px; font-size:12px; font-weight:600; background:' + statusColor + '22; color:' + statusColor + '; border:1px solid ' + statusColor + '44;">' + statusLabel + '</span>';
|
||||
if (poll.location) {
|
||||
html += ' <span style="font-size:13px; opacity:0.65; margin-left:8px;">' + poll.location + '</span>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Finalized banner
|
||||
if (isFinalized && poll.finalizedOption) {
|
||||
html += '<div style="padding:10px 14px; border-radius:6px; background:rgba(82,196,26,0.1); border:1px solid rgba(82,196,26,0.3); margin-bottom:12px; color:#52c41a;">';
|
||||
html += '<strong>Confirmed:</strong> ' + formatDate(poll.finalizedOption.date) + ' — ' + poll.finalizedOption.startTime + '–' + poll.finalizedOption.endTime;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Options table
|
||||
if (options.length > 0) {
|
||||
html += '<div style="overflow-x:auto; margin-bottom:12px;">';
|
||||
html += '<table style="width:100%; border-collapse:collapse; font-size:13px;">';
|
||||
html += '<thead><tr>';
|
||||
html += '<th style="padding:8px 12px; border-bottom:2px solid rgba(255,255,255,0.15); text-align:left;">Date / Time</th>';
|
||||
html += '<th style="padding:8px 12px; border-bottom:2px solid rgba(255,255,255,0.15); text-align:center;">Yes</th>';
|
||||
html += '<th style="padding:8px 12px; border-bottom:2px solid rgba(255,255,255,0.15); text-align:center;">If Need Be</th>';
|
||||
html += '<th style="padding:8px 12px; border-bottom:2px solid rgba(255,255,255,0.15); text-align:center;">No</th>';
|
||||
html += '<th style="padding:8px 12px; border-bottom:2px solid rgba(255,255,255,0.15); text-align:center;">Score</th>';
|
||||
html += '</tr></thead><tbody>';
|
||||
|
||||
options.forEach(function (opt) {
|
||||
var isBest = bestScore > 0 && (opt.score || 0) === bestScore;
|
||||
var isConfirmed = isFinalized && poll.finalizedOptionId === opt.id;
|
||||
var rowBg = isConfirmed ? 'rgba(82,196,26,0.08)' : isBest ? 'rgba(82,196,26,0.04)' : '';
|
||||
html += '<tr style="background:' + rowBg + ';">';
|
||||
html += '<td style="padding:8px 12px; border-bottom:1px solid rgba(255,255,255,0.1);">';
|
||||
html += '<strong>' + formatDate(opt.date) + '</strong><br><span style="font-size:11px; opacity:0.7;">' + opt.startTime + '–' + opt.endTime + '</span>';
|
||||
if (isConfirmed) html += ' <span style="color:#52c41a; font-size:10px; font-weight:600;">✓</span>';
|
||||
html += '</td>';
|
||||
html += '<td style="padding:8px 12px; border-bottom:1px solid rgba(255,255,255,0.1); text-align:center; color:#52c41a;">' + (opt.yesCount || 0) + '</td>';
|
||||
html += '<td style="padding:8px 12px; border-bottom:1px solid rgba(255,255,255,0.1); text-align:center; color:#faad14;">' + (opt.ifNeedBeCount || 0) + '</td>';
|
||||
html += '<td style="padding:8px 12px; border-bottom:1px solid rgba(255,255,255,0.1); text-align:center; color:#d9d9d9;">' + (opt.noCount || 0) + '</td>';
|
||||
html += '<td style="padding:8px 12px; border-bottom:1px solid rgba(255,255,255,0.1); text-align:center; font-weight:600;">' + (opt.score || 0) + '</td>';
|
||||
html += '</tr>';
|
||||
});
|
||||
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
// Comments count
|
||||
if (showComments && poll.comments && poll.comments.length > 0) {
|
||||
html += '<p style="font-size:13px; opacity:0.65;">' + poll.comments.length + ' comment' + (poll.comments.length !== 1 ? 's' : '') + '</p>';
|
||||
}
|
||||
|
||||
// Vote Now CTA
|
||||
if (poll.status === 'OPEN') {
|
||||
html += '<div style="text-align:center; margin-top:16px;">';
|
||||
html += '<a href="' + appUrl + '/poll/' + encodeURIComponent(slug) + '" target="_blank" rel="noopener noreferrer" ';
|
||||
html += 'style="display:inline-block; padding:12px 32px; background:#fa8c16; color:#fff; text-decoration:none; border-radius:6px; font-weight:600; font-size:14px;">';
|
||||
html += 'Vote Now →</a></div>';
|
||||
}
|
||||
|
||||
block.innerHTML = '<div style="max-width:700px; margin:0 auto;">' + html + '</div>';
|
||||
})
|
||||
.catch(function () {
|
||||
block.innerHTML = '<div style="text-align:center; padding:24px; opacity:0.5;">' +
|
||||
'<p>Poll unavailable</p>' +
|
||||
'<a href="' + appUrl + '/poll/' + encodeURIComponent(slug) + '" target="_blank" rel="noopener noreferrer" style="color:#fa8c16;">View poll →</a>' +
|
||||
'</div>';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Initial hydration
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', hydrateBlocks);
|
||||
} else {
|
||||
hydrateBlocks();
|
||||
}
|
||||
|
||||
// Re-hydrate on MkDocs SPA navigation
|
||||
if (typeof document$ !== 'undefined') {
|
||||
document$.subscribe(function () {
|
||||
setTimeout(hydrateBlocks, 100);
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -95,10 +95,23 @@ The setup script automatically:
|
||||
- Saves the API key to `~/.bashrc`
|
||||
- Requests SMS and Contacts permissions (tap **Allow** when prompted)
|
||||
- Creates a Termux:Boot auto-start script (if Termux:Boot is installed)
|
||||
- Starts the SMS server with the watchdog (auto-restarts on crash)
|
||||
- Starts the SMS server
|
||||
|
||||
When done, note the **Phone URL** displayed (e.g. `http://100.64.0.5:5001`).
|
||||
|
||||
#### Recommended: Install Service Supervisor
|
||||
|
||||
After initial setup, install `termux-services` for reliable process management. This uses runit, a proper UNIX service supervisor that automatically restarts the server if it crashes:
|
||||
|
||||
```bash
|
||||
cd ~/sms-server && bash android/setup-services.sh
|
||||
```
|
||||
|
||||
This registers two supervised services:
|
||||
|
||||
- **sms-api** — Flask SMS API server (port 5001)
|
||||
- **sshd-custom** — SSH daemon for remote management (port 8022)
|
||||
|
||||
### Step 4: Prevent Android from Killing Termux
|
||||
|
||||
This is **required** for the server to run reliably in the background:
|
||||
@@ -115,17 +128,35 @@ To pull the latest server code and re-run setup:
|
||||
cd ~/sms-server && git pull && bash android/setup.sh YOUR_API_KEY_HERE
|
||||
```
|
||||
|
||||
### Manual Control
|
||||
### Service Management
|
||||
|
||||
If you installed `termux-services` (recommended):
|
||||
|
||||
```bash
|
||||
# Check if the server is running
|
||||
curl http://127.0.0.1:5001/health
|
||||
# Check status
|
||||
sv status sms-api
|
||||
|
||||
# Restart
|
||||
sv restart sms-api
|
||||
|
||||
# Stop
|
||||
sv down sms-api
|
||||
|
||||
# Start
|
||||
sv up sms-api
|
||||
|
||||
# View logs
|
||||
tail -f ~/logs/sms-api.log
|
||||
|
||||
# Stop the server
|
||||
pkill -f sms-watchdog.sh && pkill -f termux-sms-api-server.py
|
||||
# Health check
|
||||
curl http://127.0.0.1:5001/health
|
||||
```
|
||||
|
||||
Without `termux-services` (legacy watchdog):
|
||||
|
||||
```bash
|
||||
# Check if the server is running
|
||||
curl http://127.0.0.1:5001/health
|
||||
|
||||
# Restart manually
|
||||
cd ~/sms-server/android && bash sms-watchdog.sh
|
||||
@@ -353,8 +384,8 @@ export SMS_API_SECRET='correct-key-from-admin-panel'
|
||||
echo 'export SMS_API_SECRET="correct-key-from-admin-panel"' >> ~/.bashrc
|
||||
|
||||
# Restart the server
|
||||
pkill -f termux-sms-api-server.py
|
||||
cd ~/sms-server/android && python termux-sms-api-server.py
|
||||
sv restart sms-api
|
||||
# Or without termux-services: pkill -f termux-sms-api-server.py && cd ~/sms-server/android && python termux-sms-api-server.py
|
||||
```
|
||||
|
||||
### SMS not sending
|
||||
@@ -372,12 +403,13 @@ cd ~/sms-server/android && python termux-sms-api-server.py
|
||||
|
||||
**Symptoms:** Server stops after some time, especially when phone screen is off.
|
||||
|
||||
**Fix:** Disable battery optimization for Termux:
|
||||
**Fix:**
|
||||
|
||||
1. Android Settings → Apps → Termux → Battery → **Unrestricted**
|
||||
2. Lock Termux in recent apps (long-press app card → Lock)
|
||||
3. Some phones: Settings → Battery → Battery Optimization → find Termux → Don't Optimize
|
||||
4. Samsung: Settings → Device Care → Battery → App Power Management → add Termux to "Never sleeping apps"
|
||||
1. **Install `termux-services`** (if not already): `bash ~/sms-server/android/setup-services.sh` — this uses runit, a proper service supervisor that auto-restarts the server immediately if it crashes
|
||||
2. **Disable battery optimization:** Android Settings → Apps → Termux → Battery → **Unrestricted**
|
||||
3. **Lock Termux in recent apps** — long-press the app card → Lock/Pin
|
||||
4. Samsung: also add Termux, Termux:API, and Termux:Boot to **Settings → Device Care → Battery → Never Sleeping Apps**
|
||||
5. **Acquire wake lock:** Run `termux-wake-lock` in Termux (included in boot script)
|
||||
|
||||
### Server won't start — "Missing SMS_API_SECRET"
|
||||
|
||||
@@ -425,6 +457,6 @@ cd ~/sms-server
|
||||
git pull
|
||||
|
||||
# Restart the server
|
||||
pkill -f termux-sms-api-server.py
|
||||
cd android && python termux-sms-api-server.py
|
||||
sv restart sms-api
|
||||
# Or without termux-services: pkill -f termux-sms-api-server.py && cd android && python termux-sms-api-server.py
|
||||
```
|
||||
|
||||
@@ -183,6 +183,7 @@ Listmonk handles newsletter/marketing campaigns. Sync with the main platform is
|
||||
| `LISTMONK_ADMIN_USER` | `v2-api` | Same as `LISTMONK_API_USER` (used by the sync service). |
|
||||
| `LISTMONK_ADMIN_PASSWORD` | — | Same as `LISTMONK_API_TOKEN`. |
|
||||
| `LISTMONK_SYNC_ENABLED` | `false` | :material-flask: Set to `true` to sync participants/locations/users to Listmonk lists. |
|
||||
| `LISTMONK_WEBHOOK_SECRET` | *(empty)* | Shared secret for Listmonk webhook callbacks. |
|
||||
| `LISTMONK_PROXY_PORT` | `9002` | Nginx proxy port for Listmonk. |
|
||||
|
||||
??? example "Listmonk SMTP settings"
|
||||
@@ -253,6 +254,7 @@ Self-hosted Git repository. Optional service.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `GITEA_URL` | `http://gitea-changemaker:3000` | Internal container URL for Gitea. |
|
||||
| `GITEA_PORT` / `GITEA_WEB_PORT` | `3030` | Gitea web UI port. |
|
||||
| `GITEA_SSH_PORT` | `2222` | Gitea SSH port for git operations. |
|
||||
| `GITEA_DB_TYPE` | `mysql` | Database type (Gitea uses its own MySQL). |
|
||||
@@ -264,6 +266,18 @@ Self-hosted Git repository. Optional service.
|
||||
| `GITEA_ROOT_URL` | `https://git.cmlite.org` | Public-facing URL for Gitea. |
|
||||
| `GITEA_DOMAIN` | `git.cmlite.org` | Domain used in git clone URLs. |
|
||||
|
||||
??? example "Gitea Docs Comments"
|
||||
Enable comments on MkDocs documentation pages, backed by Gitea Issues.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `GITEA_COMMENTS_ENABLED` | `false` | :material-flask: Enable comments on MkDocs pages. |
|
||||
| `GITEA_API_TOKEN` | *(empty)* | Personal access token with repo write scope. Create in Gitea → Settings → Applications. |
|
||||
| `GITEA_COMMENTS_REPO_OWNER` | *(empty)* | Gitea username that owns the docs-comments repo. |
|
||||
| `GITEA_COMMENTS_REPO_NAME` | `docs-comments` | Repository name (auto-created via admin setup). |
|
||||
| `GITEA_OAUTH_CLIENT_ID` | *(empty)* | OAuth2 application client ID (create in Gitea → Settings → Applications → OAuth2). |
|
||||
| `GITEA_OAUTH_CLIENT_SECRET` | *(empty)* | OAuth2 application client secret. |
|
||||
|
||||
---
|
||||
|
||||
## n8n (Workflow Automation) :material-tune-variant:
|
||||
@@ -382,6 +396,48 @@ Self-hosted event management platform. Uses the shared PostgreSQL database (auto
|
||||
|
||||
---
|
||||
|
||||
## Jitsi Meet (Video Conferencing) :material-flask:
|
||||
|
||||
Self-hosted video conferencing with JWT authentication. Integrates with Rocket.Chat for in-channel video calls.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_MEET` | `false` | :material-flask: Set to `true` to enable the Jitsi Meet integration. The initial default; once saved in admin Settings, the DB value is authoritative. |
|
||||
| `JITSI_APP_ID` | `changemaker` | JWT application ID. Must match across Jitsi Prosody, Rocket.Chat app settings, and `JWT_ACCEPTED_ISSUERS`/`JWT_ACCEPTED_AUDIENCES`. |
|
||||
| `JITSI_APP_SECRET` | — | :material-alert-circle:{ .text-red } JWT secret for signing Jitsi tokens. Generate with `openssl rand -hex 32`. Shared between Jitsi Prosody, Rocket.Chat, and the API. |
|
||||
| `JITSI_JICOFO_AUTH_PASSWORD` | — | Internal XMPP password for Jicofo (conference focus). Generate with `openssl rand -hex 16`. |
|
||||
| `JITSI_JVB_AUTH_PASSWORD` | — | Internal XMPP password for JVB (video bridge). Generate with `openssl rand -hex 16`. |
|
||||
| `JITSI_EMBED_PORT` | `8893` | Port for iframe embedding in admin. |
|
||||
| `JITSI_URL` | `http://jitsi-web-changemaker:80` | Internal container URL. |
|
||||
| `JVB_ADVERTISE_IP` | *(empty)* | Server's public IP address. **Required in production** for NAT traversal so remote participants can connect. |
|
||||
| `JVB_PORT` | `10000` | UDP port for media traffic. Must be open in your firewall. |
|
||||
|
||||
!!! warning "Production requirements"
|
||||
- `JVB_ADVERTISE_IP` must be set to your server's public IP for calls to work outside the local network.
|
||||
- Port `10000/udp` must be open in your firewall for media traffic.
|
||||
- Calls must go through the production domain (not localhost) for SSL/JWT to work.
|
||||
|
||||
---
|
||||
|
||||
## SMS Campaigns (Termux Android Bridge) :material-flask:
|
||||
|
||||
Send SMS messages via an Android phone running the Termux API server. The phone acts as an SMS gateway.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ENABLE_SMS` | `false` | :material-flask: Set to `true` to enable SMS campaigns. The initial default; once saved in admin Settings, the DB value is authoritative. |
|
||||
| `TERMUX_API_URL` | `http://10.0.0.193:5001` | URL of the Termux API server running on the Android phone. |
|
||||
| `TERMUX_API_KEY` | *(empty)* | API key for authenticating with the Termux server (HMAC auth via `X-API-Key` header). |
|
||||
| `SMS_DELAY_BETWEEN_MS` | `3000` | Delay between sending individual SMS messages (ms). Prevents carrier throttling. |
|
||||
| `SMS_MAX_RETRIES` | `3` | Maximum retry attempts for failed SMS sends. |
|
||||
| `SMS_RESPONSE_SYNC_INTERVAL_MS` | `30000` | How often to poll the phone's inbox for responses (ms). |
|
||||
| `SMS_DEVICE_MONITOR_INTERVAL_MS` | `30000` | How often to check device health — battery, connectivity (ms). |
|
||||
|
||||
!!! tip "GUI configuration"
|
||||
The Termux API URL and API key can also be configured from **Admin → Settings → SMS**. Database values override these env vars when set.
|
||||
|
||||
---
|
||||
|
||||
## MailHog (Development Email)
|
||||
|
||||
| Variable | Default | Description |
|
||||
@@ -474,6 +530,20 @@ docker compose --profile monitoring up -d
|
||||
| `GOTIFY_PORT` | `8889` | Gotify push notification port. |
|
||||
| `GOTIFY_ADMIN_USER` | `admin` | Gotify admin username. |
|
||||
| `GOTIFY_ADMIN_PASSWORD` | `admin` | :material-tune-variant: Change in production. |
|
||||
| `GRAFANA_EMBED_PORT` | `8894` | Port for iframe embedding Grafana in admin. |
|
||||
| `ALERTMANAGER_EMBED_PORT` | `8895` | Port for iframe embedding Alertmanager in admin. |
|
||||
|
||||
---
|
||||
|
||||
## Bunker Ops (Fleet Management) :material-flask:
|
||||
|
||||
Remote metrics push for managing multiple Changemaker Lite instances from a central monitoring server.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `INSTANCE_LABEL` | *(empty)* | Unique label for this instance (used as a Prometheus metric label). Falls back to `DOMAIN` if empty. |
|
||||
| `BUNKER_OPS_ENABLED` | `false` | :material-flask: Enable remote metrics push to a central VictoriaMetrics server. |
|
||||
| `BUNKER_OPS_REMOTE_WRITE_URL` | *(empty)* | VictoriaMetrics `remote_write` endpoint (e.g., `https://ops.example.com/api/v1/write`). |
|
||||
|
||||
---
|
||||
|
||||
@@ -516,6 +586,11 @@ echo "ROCKETCHAT_ADMIN_PASSWORD=$(openssl rand -hex 16)"
|
||||
|
||||
# Gancio
|
||||
echo "GANCIO_ADMIN_PASSWORD=$(openssl rand -hex 16)"
|
||||
|
||||
# Jitsi Meet
|
||||
echo "JITSI_APP_SECRET=$(openssl rand -hex 32)"
|
||||
echo "JITSI_JICOFO_AUTH_PASSWORD=$(openssl rand -hex 16)"
|
||||
echo "JITSI_JVB_AUTH_PASSWORD=$(openssl rand -hex 16)"
|
||||
```
|
||||
|
||||
!!! tip
|
||||
@@ -551,6 +626,8 @@ echo "GANCIO_ADMIN_PASSWORD=$(openssl rand -hex 16)"
|
||||
ENABLE_MEDIA_FEATURES=true
|
||||
ENABLE_PAYMENTS=true
|
||||
ENABLE_CHAT=true
|
||||
ENABLE_MEET=true
|
||||
ENABLE_SMS=true
|
||||
LISTMONK_SYNC_ENABLED=true
|
||||
GANCIO_SYNC_ENABLED=true
|
||||
LISTMONK_DB_PASSWORD=...
|
||||
@@ -564,6 +641,10 @@ echo "GANCIO_ADMIN_PASSWORD=$(openssl rand -hex 16)"
|
||||
VAULTWARDEN_ADMIN_TOKEN=...
|
||||
ROCKETCHAT_ADMIN_PASSWORD=...
|
||||
GANCIO_ADMIN_PASSWORD=...
|
||||
JITSI_APP_SECRET=...
|
||||
JITSI_JICOFO_AUTH_PASSWORD=...
|
||||
JITSI_JVB_AUTH_PASSWORD=...
|
||||
JVB_ADVERTISE_IP=your.public.ip.here
|
||||
EMAIL_TEST_MODE=false
|
||||
SMTP_HOST=smtp.your-provider.com
|
||||
SMTP_PORT=587
|
||||
|
||||
@@ -87,6 +87,7 @@ extra_javascript:
|
||||
- assets/js/image-gallery.js
|
||||
- assets/js/gancio-events.js
|
||||
- assets/js/payment-widgets.js
|
||||
- assets/js/scheduling-poll.js
|
||||
- javascripts/ad-widgets.js
|
||||
- javascripts/docs-comments.js
|
||||
|
||||
|
||||
Reference in New Issue
Block a user