8214ca6121
Adds a "Git Sync" section in the Integrations rail panel that lets project owners configure an HTTPS remote URL (with embedded auth token) and force-push all project files as a single commit. Backend: - GitSyncHandler: assembles project docs + binary files into a temp dir, runs git init/commit/push --force, then cleans up - GitSyncController: GET/POST /project/:id/git-sync (configure), POST /project/:id/git-sync/push (trigger) - Project model: gitRemote field - Dockerfile: ensures git is present at runtime - Env flag: OVERLEAF_ENABLE_GIT_SYNC=true (set in k8s manifest) Frontend: - GitSyncWidget: URL input + Save + Push Now buttons, success/error feedback - Integrations panel: shows widget when gitSyncEnabled - Rail: shows Integrations tab when gitSyncEnabled (was only gitBridgeEnabled) - i18n: en + fr translations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import { useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import getMeta from '@/utils/meta'
|
|
import { postJSON } from '@/infrastructure/fetch-json'
|
|
import OLButton from '@/shared/components/ol/ol-button'
|
|
import OLFormControl from '@/shared/components/ol/ol-form-control'
|
|
import OLFormGroup from '@/shared/components/ol/ol-form-group'
|
|
import OLFormLabel from '@/shared/components/ol/ol-form-label'
|
|
import MaterialIcon from '@/shared/components/material-icon'
|
|
import OLNotification from '@/shared/components/ol/ol-notification'
|
|
|
|
type Action = 'save' | 'push'
|
|
type Status = 'idle' | 'busy' | 'success' | 'error'
|
|
|
|
export default function GitSyncWidget() {
|
|
const { t } = useTranslation()
|
|
const projectId = getMeta('ol-project_id')
|
|
const initialRemote = getMeta('ol-gitRemote')
|
|
|
|
const [remoteUrl, setRemoteUrl] = useState(initialRemote ?? '')
|
|
const [lastAction, setLastAction] = useState<Action>('save')
|
|
const [status, setStatus] = useState<Status>('idle')
|
|
const [errorMsg, setErrorMsg] = useState('')
|
|
|
|
async function handleSave(e: React.FormEvent) {
|
|
e.preventDefault()
|
|
setLastAction('save')
|
|
setStatus('busy')
|
|
setErrorMsg('')
|
|
try {
|
|
await postJSON(`/project/${projectId}/git-sync`, {
|
|
body: { remoteUrl },
|
|
})
|
|
setStatus('success')
|
|
} catch (err: any) {
|
|
setStatus('error')
|
|
setErrorMsg(err?.data?.error ?? String(err))
|
|
}
|
|
}
|
|
|
|
async function handlePush() {
|
|
setLastAction('push')
|
|
setStatus('busy')
|
|
setErrorMsg('')
|
|
try {
|
|
await postJSON(`/project/${projectId}/git-sync/push`, { body: {} })
|
|
setStatus('success')
|
|
} catch (err: any) {
|
|
setStatus('error')
|
|
setErrorMsg(err?.data?.error ?? String(err))
|
|
}
|
|
}
|
|
|
|
const isBusy = status === 'busy'
|
|
|
|
return (
|
|
<div className="git-sync-widget">
|
|
<div className="git-sync-widget-header">
|
|
<MaterialIcon type="merge" />
|
|
<span className="git-sync-widget-title">{t('git_sync')}</span>
|
|
</div>
|
|
<p className="git-sync-widget-description">{t('git_sync_description')}</p>
|
|
|
|
<form onSubmit={handleSave}>
|
|
<OLFormGroup controlId="git-sync-remote-url">
|
|
<OLFormLabel>{t('git_sync_remote_url')}</OLFormLabel>
|
|
<OLFormControl
|
|
type="text"
|
|
value={remoteUrl}
|
|
onChange={e => setRemoteUrl(e.target.value)}
|
|
placeholder="https://user:token@github.com/org/repo.git"
|
|
disabled={isBusy}
|
|
/>
|
|
</OLFormGroup>
|
|
|
|
<div className="git-sync-widget-actions">
|
|
<OLButton
|
|
type="submit"
|
|
variant="secondary"
|
|
size="sm"
|
|
disabled={isBusy || !remoteUrl.trim()}
|
|
isLoading={isBusy && lastAction === 'save'}
|
|
loadingLabel={t('saving')}
|
|
>
|
|
{t('save')}
|
|
</OLButton>
|
|
<OLButton
|
|
type="button"
|
|
variant="primary"
|
|
size="sm"
|
|
onClick={handlePush}
|
|
disabled={isBusy || !remoteUrl.trim()}
|
|
isLoading={isBusy && lastAction === 'push'}
|
|
loadingLabel={t('git_sync_pushing')}
|
|
>
|
|
{t('git_sync_push_now')}
|
|
</OLButton>
|
|
</div>
|
|
</form>
|
|
|
|
{status === 'success' && (
|
|
<OLNotification
|
|
type="success"
|
|
content={
|
|
lastAction === 'push'
|
|
? t('git_sync_push_success')
|
|
: t('git_sync_saved')
|
|
}
|
|
/>
|
|
)}
|
|
{status === 'error' && (
|
|
<OLNotification type="error" content={errorMsg} />
|
|
)}
|
|
</div>
|
|
)
|
|
}
|