mirror of
https://github.com/velocitatem/cvfs.git
synced 2026-05-31 08:43:37 +00:00
- dlib/integrations/paperless.py: sync HTTP client wrapping the paperless-ngx
REST API (upload doc, poll task, create/delete share links, delete document)
- config: PAPERLESS_ENABLED, PAPERLESS_BASE_URL, PAPERLESS_TOKEN, PAPERLESS_TAG_IDS
- PublicAsset model: paperless_document_id + paperless_share_slug columns
- publication service: after creating the asset, if paperless is enabled upload
the patched PDF and create a share link; stores doc id + share slug on the asset
- public routes: pass expires_at through to publish_version; new
POST /{slug}/share-links endpoint to (re)create expiring share links on demand
- schemas: PublishRequest.expires_at, PublicAssetResponse.paperless_share_url,
new ShareLinkRequest model
- frontend: paperless_share_url field on PublicAsset type, createShareLink()
and expiresAt param on publishVersion() in api.ts
- .env.example: documented paperless env vars
https://claude.ai/code/session_01YPVs6uBwCvcwVMvrfLBBdu
92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import re
|
|
from datetime import datetime
|
|
from uuid import uuid4
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.config import get_settings
|
|
from app.models import CvDocument, CvVersion, PublicAsset, Submission
|
|
from app.services.storage import storage_client
|
|
from dlib.cv import docx_bytes_to_pdf, generate_patched_docx
|
|
from dlib.integrations.paperless import get_paperless_client
|
|
|
|
|
|
async def publish_version(
|
|
session: AsyncSession,
|
|
*,
|
|
owner_id: str,
|
|
version_id: str | None,
|
|
submission_id: str | None,
|
|
slug: str | None,
|
|
expires_at: datetime | None = None,
|
|
) -> PublicAsset | None:
|
|
target_version: CvVersion | None = None
|
|
target_submission: Submission | None = None
|
|
|
|
if submission_id:
|
|
stmt = (
|
|
select(Submission)
|
|
.join(Submission.version)
|
|
.join(CvVersion.document)
|
|
.where(Submission.id == submission_id, CvDocument.owner_id == owner_id)
|
|
)
|
|
result = await session.execute(stmt)
|
|
target_submission = result.scalars().one_or_none()
|
|
target_version = target_submission.version if target_submission else None
|
|
elif version_id:
|
|
stmt = (
|
|
select(CvVersion)
|
|
.join(CvVersion.document)
|
|
.where(CvVersion.id == version_id, CvDocument.owner_id == owner_id)
|
|
)
|
|
result = await session.execute(stmt)
|
|
target_version = result.scalars().one_or_none()
|
|
else:
|
|
return None
|
|
|
|
if not target_version or not target_version.artifact_docx_key:
|
|
return None
|
|
|
|
resolved_slug = slugify(slug or target_version.version_label or "cv")
|
|
if not resolved_slug:
|
|
resolved_slug = f"cv-{uuid4().hex[:6]}"
|
|
|
|
asset = PublicAsset(
|
|
submission_id=target_submission.id if target_submission else None,
|
|
version_id=target_version.id,
|
|
slug=resolved_slug,
|
|
artifact_key=target_version.artifact_docx_key,
|
|
is_public=True,
|
|
expires_at=expires_at,
|
|
)
|
|
session.add(asset)
|
|
await session.commit()
|
|
await session.refresh(asset)
|
|
|
|
settings = get_settings()
|
|
client = get_paperless_client(settings)
|
|
if client:
|
|
docx = storage_client.download_bytes(target_version.artifact_docx_key)
|
|
blocks = target_version.structured_blocks or []
|
|
pdf = docx_bytes_to_pdf(generate_patched_docx(docx, blocks))
|
|
doc_id = await asyncio.to_thread(
|
|
client.upload_document, pdf, resolved_slug, settings.paperless_tag_ids or []
|
|
)
|
|
_, share_url = await asyncio.to_thread(client.create_share_link, doc_id, expires_at)
|
|
asset.paperless_document_id = doc_id
|
|
asset.paperless_share_slug = share_url.split("/share/")[-1]
|
|
await session.commit()
|
|
await session.refresh(asset)
|
|
|
|
return asset
|
|
|
|
|
|
def slugify(value: str) -> str:
|
|
safe = re.sub(r"[^a-zA-Z0-9-]+", "-", value.strip().lower())
|
|
safe = re.sub(r"-+", "-", safe).strip("-")
|
|
return safe[:120]
|