feat(jitsi): video meetings — /api/jitsi/token route + jitsi-meet plugin + feature gate
Publish Docker Image / prepare (push) Successful in 2s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 8s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
Publish Docker Image / prepare (push) Successful in 2s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 8s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Video meetings — SRC Advisory webmail plugin (VNCtalk / Jitsi).
|
||||
*
|
||||
* Adds a "Video meeting" entry to Settings. "Start a meeting" generates a room
|
||||
* name, asks the webmail server for a signed JWT (the server holds the shared
|
||||
* JITSI_APP_SECRET — it never reaches the browser), then opens the room in
|
||||
* meet.src-advisory.com with the token as a query param.
|
||||
*/
|
||||
|
||||
const host = require('@plugin-host');
|
||||
const React = require('react');
|
||||
const h = React.createElement;
|
||||
const { useState } = React;
|
||||
|
||||
const card = {
|
||||
border: '1px solid var(--color-border, #e2e8f0)',
|
||||
borderRadius: '8px',
|
||||
padding: '16px',
|
||||
background: 'var(--color-card, #fff)',
|
||||
color: 'var(--color-foreground, #0f172a)',
|
||||
maxWidth: '720px',
|
||||
};
|
||||
const btnPrimary = {
|
||||
font: 'inherit',
|
||||
padding: '8px 14px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid var(--color-primary, #2563eb)',
|
||||
background: 'var(--color-primary, #2563eb)',
|
||||
color: '#fff',
|
||||
cursor: 'pointer',
|
||||
};
|
||||
const input = {
|
||||
font: 'inherit',
|
||||
padding: '6px 8px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid var(--color-input, #cbd5e1)',
|
||||
background: 'var(--color-background, #fff)',
|
||||
color: 'var(--color-foreground, #0f172a)',
|
||||
marginRight: '8px',
|
||||
};
|
||||
|
||||
function generateRoomName() {
|
||||
const suffix = Math.random().toString(36).slice(2, 10);
|
||||
return `meeting-${suffix}`;
|
||||
}
|
||||
|
||||
function SettingsSection() {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [joinRoom, setJoinRoom] = useState('');
|
||||
|
||||
async function openRoom(room) {
|
||||
setBusy(true);
|
||||
try {
|
||||
const resp = await fetch('/api/jitsi/token', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ room }),
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!resp.ok || !data.token) {
|
||||
throw new Error(data.error || 'could not obtain meeting token');
|
||||
}
|
||||
const url = `${data.url}?jwt=${encodeURIComponent(data.token)}`;
|
||||
try { host.ui.openExternalUrl(url); }
|
||||
catch { window.open(url, '_blank', 'noopener,noreferrer'); }
|
||||
} catch (err) {
|
||||
const msg = err && err.message ? err.message : String(err);
|
||||
try { host.toast.error(`Could not start meeting: ${msg}`); } catch { /* ignore */ }
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return h('div', { style: card },
|
||||
h('div', { style: { fontWeight: 600, marginBottom: '6px' } }, 'Video meeting'),
|
||||
h('div', { style: { fontSize: '13px', lineHeight: 1.5, marginBottom: '14px', color: 'var(--color-muted-foreground, #64748b)' } },
|
||||
'Start or join a SRC video meeting. The room is opened in meet.src-advisory.com.'),
|
||||
h('div', { style: { display: 'flex', alignItems: 'center', marginBottom: '10px' } },
|
||||
h('button', { type: 'button', style: btnPrimary, disabled: busy, onClick: () => openRoom(generateRoomName()) },
|
||||
busy ? 'Starting…' : 'Start a meeting →'),
|
||||
),
|
||||
h('div', { style: { display: 'flex', alignItems: 'center' } },
|
||||
h('input', {
|
||||
type: 'text',
|
||||
style: input,
|
||||
placeholder: 'Room name',
|
||||
value: joinRoom,
|
||||
onChange: (e) => setJoinRoom(e.target.value),
|
||||
}),
|
||||
h('button', { type: 'button', style: btnPrimary, disabled: busy || !joinRoom.trim(), onClick: () => openRoom(joinRoom.trim()) },
|
||||
'Join'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export const slots = {
|
||||
'settings-section': { component: SettingsSection, order: 90 },
|
||||
};
|
||||
|
||||
export async function activate(api) {
|
||||
api.log.info('jitsi-meet plugin activated');
|
||||
}
|
||||
Reference in New Issue
Block a user