feat(integration) : extract Gitea/BookStack/Zimbra/Share front into module layer
LST-68 (2.6) front. Completes the Integration module and Phase 2. - New frontend/modules/integration/ layer (auto-detected): services (gitea, bookstack, zimbra, share, share-settings) + their DTOs, and the useShareStatus composable. - Consumers repointed to ~/modules/integration/...: admin tabs (Gitea/BookStack/Zimbra/Share), PM task sections (TaskGitSection, TaskBookStackLinks, TaskDocumentShareLinker), ProjectDrawer, TaskModal, pages/documents.vue, components/share/SharedFilePreview.vue. - Admin tabs, SharedFilePreview and documents/admin pages stay at their location (only imports updated). i18n stays global. nuxt build passes; all routes preserved.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { useShareService } from '~/modules/integration/services/share'
|
||||
|
||||
export function useShareStatus() {
|
||||
const enabled = useState<boolean | null>('share-enabled', () => null)
|
||||
const { getStatus } = useShareService()
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const status = await getStatus()
|
||||
enabled.value = status.enabled
|
||||
} catch {
|
||||
enabled.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureLoaded() {
|
||||
if (enabled.value === null) {
|
||||
await refresh()
|
||||
}
|
||||
}
|
||||
|
||||
return { enabled, refresh, ensureLoaded }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export default defineNuxtConfig({})
|
||||
@@ -0,0 +1,66 @@
|
||||
import type {
|
||||
BookStackSettings,
|
||||
BookStackSettingsWrite,
|
||||
BookStackTestResult,
|
||||
BookStackShelf,
|
||||
BookStackLink,
|
||||
BookStackLinkCreate,
|
||||
BookStackSearchResult,
|
||||
} from './dto/bookstack'
|
||||
import type { HydraCollection } from '~/utils/api'
|
||||
import { extractHydraMembers } from '~/utils/api'
|
||||
|
||||
export function useBookStackService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getSettings(): Promise<BookStackSettings> {
|
||||
return api.get<BookStackSettings>('/settings/bookstack')
|
||||
}
|
||||
|
||||
async function saveSettings(payload: BookStackSettingsWrite): Promise<BookStackSettings> {
|
||||
return api.put<BookStackSettings>('/settings/bookstack', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'bookstack.settings.saved',
|
||||
})
|
||||
}
|
||||
|
||||
async function testConnection(): Promise<BookStackTestResult> {
|
||||
return api.post<BookStackTestResult>('/settings/bookstack/test')
|
||||
}
|
||||
|
||||
async function listShelves(): Promise<BookStackShelf[]> {
|
||||
const data = await api.get<HydraCollection<BookStackShelf>>('/bookstack/shelves')
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function getLinks(taskId: number): Promise<BookStackLink[]> {
|
||||
const data = await api.get<HydraCollection<BookStackLink>>(`/tasks/${taskId}/bookstack/links`)
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function addLink(taskId: number, payload: BookStackLinkCreate): Promise<BookStackLink> {
|
||||
return api.post<BookStackLink>(`/tasks/${taskId}/bookstack/links`, payload as Record<string, unknown>)
|
||||
}
|
||||
|
||||
async function removeLink(taskId: number, linkId: number): Promise<void> {
|
||||
await api.delete(`/tasks/${taskId}/bookstack/links/${linkId}`)
|
||||
}
|
||||
|
||||
async function search(taskId: number, query: string): Promise<BookStackSearchResult[]> {
|
||||
const data = await api.get<HydraCollection<BookStackSearchResult>>(
|
||||
`/tasks/${taskId}/bookstack/search`,
|
||||
{ q: query },
|
||||
)
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
return {
|
||||
getSettings,
|
||||
saveSettings,
|
||||
testConnection,
|
||||
listShelves,
|
||||
getLinks,
|
||||
addLink,
|
||||
removeLink,
|
||||
search,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
export type BookStackSettings = {
|
||||
url: string | null
|
||||
hasToken: boolean
|
||||
}
|
||||
|
||||
export type BookStackSettingsWrite = {
|
||||
url: string | null
|
||||
tokenId: string | null
|
||||
tokenSecret: string | null
|
||||
}
|
||||
|
||||
export type BookStackTestResult = {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export type BookStackShelf = {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export type BookStackLink = {
|
||||
id: number
|
||||
bookstackId: number
|
||||
bookstackType: 'page' | 'book'
|
||||
title: string
|
||||
url: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type BookStackLinkCreate = {
|
||||
bookstackId: number
|
||||
bookstackType: 'page' | 'book'
|
||||
title: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export type BookStackSearchResult = {
|
||||
id: number
|
||||
type: 'page' | 'book'
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export type GiteaSettings = {
|
||||
url: string | null
|
||||
hasToken: boolean
|
||||
}
|
||||
|
||||
export type GiteaSettingsWrite = {
|
||||
url: string | null
|
||||
token: string | null
|
||||
}
|
||||
|
||||
export type GiteaRepository = {
|
||||
fullName: string
|
||||
name: string
|
||||
owner: string
|
||||
}
|
||||
|
||||
export type GiteaBranch = {
|
||||
name: string
|
||||
commits: GiteaCommit[]
|
||||
}
|
||||
|
||||
export type GiteaCommit = {
|
||||
sha: string
|
||||
message: string
|
||||
author: string
|
||||
date: string
|
||||
}
|
||||
|
||||
export type GiteaBranchCreate = {
|
||||
type: string
|
||||
baseBranch: string
|
||||
}
|
||||
|
||||
export type GiteaPullRequest = {
|
||||
number: number
|
||||
title: string
|
||||
state: string
|
||||
merged: boolean
|
||||
headBranch: string
|
||||
author: string
|
||||
url: string
|
||||
ciStatuses: GiteaCiStatus[]
|
||||
}
|
||||
|
||||
export type GiteaCiStatus = {
|
||||
context: string
|
||||
status: string
|
||||
target_url: string
|
||||
}
|
||||
|
||||
export type GiteaBranchName = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export type GiteaTestResult = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
export type FileEntry = {
|
||||
name: string
|
||||
path: string
|
||||
isDir: boolean
|
||||
size: number
|
||||
modifiedAt: number | null
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
export type Breadcrumb = {
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
|
||||
export type ShareBrowseResult = {
|
||||
path: string
|
||||
breadcrumb: Breadcrumb[]
|
||||
entries: FileEntry[]
|
||||
}
|
||||
|
||||
export type ShareSearchResult = {
|
||||
query: string
|
||||
entries: FileEntry[]
|
||||
}
|
||||
|
||||
export type ShareStatus = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type ShareSettings = {
|
||||
host: string | null
|
||||
shareName: string | null
|
||||
basePath: string | null
|
||||
domain: string | null
|
||||
username: string | null
|
||||
enabled: boolean
|
||||
hasPassword: boolean
|
||||
}
|
||||
|
||||
export type ShareSettingsWrite = {
|
||||
host: string | null
|
||||
shareName: string | null
|
||||
basePath: string | null
|
||||
domain: string | null
|
||||
username: string | null
|
||||
password?: string | null
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type ShareTestResult = {
|
||||
success: boolean
|
||||
message: string | null
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export type ZimbraSettings = {
|
||||
serverUrl: string | null
|
||||
username: string | null
|
||||
calendarPath: string | null
|
||||
enabled: boolean
|
||||
hasPassword: boolean
|
||||
}
|
||||
|
||||
export type ZimbraSettingsWrite = {
|
||||
serverUrl: string | null
|
||||
username: string | null
|
||||
calendarPath: string | null
|
||||
password?: string | null
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export type ZimbraTestResult = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import type {
|
||||
GiteaSettings,
|
||||
GiteaSettingsWrite,
|
||||
GiteaRepository,
|
||||
GiteaBranch,
|
||||
GiteaBranchCreate,
|
||||
GiteaPullRequest,
|
||||
GiteaBranchName,
|
||||
GiteaTestResult,
|
||||
} from './dto/gitea'
|
||||
import type { HydraCollection } from '~/utils/api'
|
||||
import { extractHydraMembers } from '~/utils/api'
|
||||
|
||||
export function useGiteaService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getSettings(): Promise<GiteaSettings> {
|
||||
return api.get<GiteaSettings>('/settings/gitea')
|
||||
}
|
||||
|
||||
async function saveSettings(payload: GiteaSettingsWrite): Promise<GiteaSettings> {
|
||||
return api.put<GiteaSettings>('/settings/gitea', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'gitea.settings.saved',
|
||||
})
|
||||
}
|
||||
|
||||
async function testConnection(): Promise<GiteaTestResult> {
|
||||
return api.post<GiteaTestResult>('/settings/gitea/test')
|
||||
}
|
||||
|
||||
async function listRepositories(): Promise<GiteaRepository[]> {
|
||||
const data = await api.get<HydraCollection<GiteaRepository>>('/gitea/repositories')
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function listBranches(taskId: number): Promise<GiteaBranch[]> {
|
||||
const data = await api.get<HydraCollection<GiteaBranch>>(`/tasks/${taskId}/gitea/branches`)
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function createBranch(taskId: number, payload: GiteaBranchCreate): Promise<GiteaBranch> {
|
||||
return api.post<GiteaBranch>(`/tasks/${taskId}/gitea/branches`, payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'gitea.branch.created',
|
||||
})
|
||||
}
|
||||
|
||||
async function listPullRequests(taskId: number): Promise<GiteaPullRequest[]> {
|
||||
const data = await api.get<HydraCollection<GiteaPullRequest>>(`/tasks/${taskId}/gitea/pull-requests`)
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function getBranchName(taskId: number, type: string): Promise<GiteaBranchName> {
|
||||
return api.get<GiteaBranchName>(`/tasks/${taskId}/gitea/branch-name/${type}`)
|
||||
}
|
||||
|
||||
return {
|
||||
getSettings,
|
||||
saveSettings,
|
||||
testConnection,
|
||||
listRepositories,
|
||||
listBranches,
|
||||
createBranch,
|
||||
listPullRequests,
|
||||
getBranchName,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ShareSettings, ShareSettingsWrite, ShareTestResult } from './dto/share'
|
||||
|
||||
export function useShareSettingsService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getSettings(): Promise<ShareSettings> {
|
||||
return api.get<ShareSettings>('/settings/share')
|
||||
}
|
||||
|
||||
async function saveSettings(payload: ShareSettingsWrite): Promise<ShareSettings> {
|
||||
return api.put<ShareSettings>('/settings/share', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'adminShare.saved',
|
||||
})
|
||||
}
|
||||
|
||||
async function testConnection(): Promise<ShareTestResult> {
|
||||
return api.post<ShareTestResult>('/settings/share/test', {})
|
||||
}
|
||||
|
||||
return { getSettings, saveSettings, testConnection }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { ShareBrowseResult, ShareSearchResult, ShareStatus } from './dto/share'
|
||||
|
||||
export function useShareService() {
|
||||
const api = useApi()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
async function browse(path: string): Promise<ShareBrowseResult> {
|
||||
const query = path ? `?path=${encodeURIComponent(path)}` : ''
|
||||
return api.get<ShareBrowseResult>(`/share/browse${query}`)
|
||||
}
|
||||
|
||||
async function search(query: string): Promise<ShareSearchResult> {
|
||||
return api.get<ShareSearchResult>(`/share/search?q=${encodeURIComponent(query)}`)
|
||||
}
|
||||
|
||||
async function getStatus(): Promise<ShareStatus> {
|
||||
return api.get<ShareStatus>('/share/status')
|
||||
}
|
||||
|
||||
function getDownloadUrl(path: string, disposition: 'inline' | 'attachment' = 'inline'): string {
|
||||
const base = config.public.apiBase || '/api'
|
||||
return `${base}/share/download?path=${encodeURIComponent(path)}&disposition=${disposition}`
|
||||
}
|
||||
|
||||
return { browse, search, getStatus, getDownloadUrl }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ZimbraSettings, ZimbraSettingsWrite, ZimbraTestResult } from './dto/zimbra'
|
||||
|
||||
export function useZimbraService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getSettings(): Promise<ZimbraSettings> {
|
||||
return api.get<ZimbraSettings>('/settings/zimbra')
|
||||
}
|
||||
|
||||
async function saveSettings(payload: ZimbraSettingsWrite): Promise<ZimbraSettings> {
|
||||
return api.put<ZimbraSettings>('/settings/zimbra', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'zimbra.settings.saved',
|
||||
})
|
||||
}
|
||||
|
||||
async function testConnection(): Promise<ZimbraTestResult> {
|
||||
return api.post<ZimbraTestResult>('/settings/zimbra/test', {})
|
||||
}
|
||||
|
||||
return { getSettings, saveSettings, testConnection }
|
||||
}
|
||||
@@ -125,11 +125,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { Project, ProjectWrite } from '~/modules/project-management/services/dto/project'
|
||||
import type { Client } from '~/modules/directory/services/dto/client'
|
||||
import type { GiteaRepository } from '~/services/dto/gitea'
|
||||
import type { BookStackShelf } from '~/services/dto/bookstack'
|
||||
import type { GiteaRepository } from '~/modules/integration/services/dto/gitea'
|
||||
import type { BookStackShelf } from '~/modules/integration/services/dto/bookstack'
|
||||
import { useProjectService } from '~/modules/project-management/services/projects'
|
||||
import { useGiteaService } from '~/services/gitea'
|
||||
import { useBookStackService } from '~/services/bookstack'
|
||||
import { useGiteaService } from '~/modules/integration/services/gitea'
|
||||
import { useBookStackService } from '~/modules/integration/services/bookstack'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
|
||||
@@ -75,8 +75,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { BookStackLink, BookStackSearchResult } from '~/services/dto/bookstack'
|
||||
import { useBookStackService } from '~/services/bookstack'
|
||||
import type { BookStackLink, BookStackSearchResult } from '~/modules/integration/services/dto/bookstack'
|
||||
import { useBookStackService } from '~/modules/integration/services/bookstack'
|
||||
|
||||
const props = defineProps<{
|
||||
taskId: number
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Breadcrumb, FileEntry } from '~/services/dto/share'
|
||||
import { useShareService } from '~/services/share'
|
||||
import type { Breadcrumb, FileEntry } from '~/modules/integration/services/dto/share'
|
||||
import { useShareService } from '~/modules/integration/services/share'
|
||||
import { useTaskDocumentService } from '~/modules/project-management/services/task-documents'
|
||||
import { formatFileSize } from '~/utils/format'
|
||||
|
||||
|
||||
@@ -227,8 +227,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Task } from '~/modules/project-management/services/dto/task'
|
||||
import type { GiteaBranch, GiteaPullRequest } from '~/services/dto/gitea'
|
||||
import { useGiteaService } from '~/services/gitea'
|
||||
import type { GiteaBranch, GiteaPullRequest } from '~/modules/integration/services/dto/gitea'
|
||||
import { useGiteaService } from '~/modules/integration/services/gitea'
|
||||
import { copyToClipboard } from '~/utils/clipboard'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { Task, TaskWrite } from '~/modules/project-management/services/dto/task'
|
||||
import type { TaskDocument } from '~/modules/project-management/services/dto/task-document'
|
||||
import { useGiteaService } from '~/services/gitea'
|
||||
import { useGiteaService } from '~/modules/integration/services/gitea'
|
||||
import { useTaskDocumentService } from '~/modules/project-management/services/task-documents'
|
||||
import ConfirmDeleteDocumentModal from '~/components/ui/ConfirmDeleteDocumentModal.vue'
|
||||
import type { TaskStatus } from '~/modules/project-management/services/dto/task-status'
|
||||
|
||||
Reference in New Issue
Block a user