Руководство по подаче работ
В этом руководстве описаны два способа подачи работ, поддерживаемые Papex, и приведён полный справочник по подаче исходного пакета вместе с его манифестом papex.json и инструментарием XeLaTeX.
1. Обзор
Papex предлагает два входа подачи для разных сценариев работы:
| Способ | Точка входа | Аудитория | Особенности |
|---|---|---|---|
| Подача через форму | Веб-страница «Подать → Форма» / POST /api/papers | Эпизодические авторы | Заполнение названия, аннотации, авторов и т. д. в браузере; прямая загрузка PDF полного текста (≤50 МБ) |
| Загрузка исходного пакета | Веб-страница «Подать → Исходный пакет» / POST /api/submit/archive | Авторы на LaTeX | Упакуйте свои исходники с манифестом papex.json в tar.gz; платформа создаёт работу, связывает цитирования и собирает PDF автоматически |
Оба способа используют одну и ту же логику приёма (
createSubmission+addCitation). Они отличаются лишь источником метаданных и тем, как создаются тело/PDF.
Не хотите работать с командной строкой? Вы также можете использовать встроенный модуль Онлайн-авторинг, чтобы визуально редактировать
papex.json, писать тело работы и «экспортироватьtar.gz» или «опубликовать в один клик» прямо в браузере — архив, который он создаёт, полностью эквивалентен загрузке исходного пакета.
2. Способ 1: Подача через форму
Нажмите Подать в верхней навигации, выберите вкладку Форма, заполните поля и нажмите «Подать работу»:
- Название, Аннотация
- Основная категория (обязательно, код из дерева категорий, напр.
cs.LG), Дополнительные категории (через запятую, необязательно) - Авторы (добавьте сколько нужно; порядок — это порядок авторов)
- Загрузить PDF (необязательно): перетащите или выберите PDF (≤50 МБ); платформа сохраняет его и автоматически связывает ссылки. DOI (необязательно), Лицензия (по умолчанию
CC-BY-4.0), Примечание к версии (необязательно)
Работа затем попадает в очередь рецензирования. Подача через форму отправляется как multipart/form-data: meta — это JSON-строка метаданных, pdf — необязательный PDF-файл.
POST /api/papers
Content-Type: multipart/form-data; boundary=...
--boundary
Content-Disposition: form-data; name="meta"
{"title":"…","abstract":"…","primaryCategoryId":"cs.LG","secondaryCategoryIds":["stat.ML"],"authors":[{"name":"Ming Zhang","order":0}],"doi":"","license":"CC-BY-4.0","comments":"","basePaperId":null}
--boundary
Content-Disposition: form-data; name="pdf"; filename="paper.pdf"
Content-Type: application/pdf
<binary PDF data>
--boundary--PDF необязателен. При наличии конечная точка сохраняет его для версии работы, разбирает тело, связывает внутренние цитирования и возвращает
{ pdfUrl, pages, referencesExtracted, referencesLinked }. Веб-форма отправляет это автоматически; API-клиенты могут также отправлять обычный JSON (без
3. Способ 2: Загрузка исходного пакета
Загрузка исходного пакета — это сценарий для авторов: вы пишете работу на LaTeX, описываете метаданные и ссылки в структурированном papex.json, упаковываете всё в tar.gz и загружаете за один шаг. Серверная часть выполняет «распаковка → проверка → приём →
связывание цитирований → сборка PDF» end to end.
3.1 Структура пакета
Рекомендуемая, хотя и минимальная, структура пакета:
my-paper.tar.gz
├── papex.json # обязательно: манифест работы (метаданные + разделы + ссылки)
├── papex-template.tex # основной документ (используйте предоставленный в репозитории papex-template.tex)
├── papex.cls # класс документа (необязательно; сервер копирует из PAPEX_LATEX_DIR, если отсутствует)
├── references.bib # необязательно: написанный вручную BibTeX; иначе автоматически генерируется из ссылок
└── sections/ # разделы тела (.tex-фрагменты, на которые ссылаются по порядку из papex.json)
├── 00-intro.tex
├── 01-related.tex
└── …Пакет должен содержать
papex.json; иначе загрузка отклоняется (HTTP 400).
3.2 Справочник полей papex.json
Полная JSON-схема находится в papex-latex/papex.schema.json. Ключевые поля и их назначения:
| Поле | Тип | Обязательно | Примечания / цель в БД |
|---|---|---|---|
paper.id | string (YYMM.NNNNN) | нет | Если совпадает с вашей (или админской) существующей работой → подаётся как новая версия; иначе назначается новый ID работы |
paper.title | string | да | → papers.title / paper_versions.title |
paper.abstract | string | да | → paper_versions.abstract |
paper.keywords | string[] | нет | Отображается после аннотации в PDF (отдельно не хранится) |
paper.primaryCategoryId | string | да | → papers.primaryCategoryId; должна существовать в таблице категорий, иначе 400 |
paper.secondaryCategoryIds | string[] | нет | → paper_categories (не основные) |
paper.doi | string | нет | → paper_versions.doi, также записывается в граф цитирований (target_doi) |
paper.license | string | нет | → paper_versions.license, по умолчанию CC-BY-4.0 |
paper.versionNote | string | нет | → paper_versions.comments |
paper.subtitle | string | нет | Отображается под названием в PDF |
paper.venue | string | нет | Отображается в блоке названия (напр. конференция/журнал) |
authors[].name | string | да | → authors + paper_authors (по order) |
authors[].orcid | string | нет | Сноска об авторе |
authors[].email | string | нет | Используется как контакт ответственного автора |
authors[].affiliation | string | нет | Строка → разрешается в affiliations.id через findOrCreateAffiliation |
authors[].corresponding | boolean | нет | Сноска «Ответственный автор» |
authors[].equalContribution | boolean | нет | Сноска «Равный вклад» |
authors[].footnote | string | нет | Свободная сноска |
references[].key | string | да | BibTeX-ключ цитирования |
references[].doi / arxivId | string | нет | Разрешается во внутреннюю работу через resolveTarget; иначе url/title попадают в citations |
references[].url / title / authors / year / venue | string | нет | Заполняют citations и автогенерируемый references.bib |
sections[] | string[] | да | Упорядоченный список путей к .tex разделов; используется только LaTeX, не хранится в таблицах |
appendices[] | string[] | нет | Упорядоченный список путей к .tex приложений |
acknowledgments / funding | string | нет | Отображается в разделе благодарностей/финансирования PDF |
build | object | нет | Параметры сборки: style (numeric/authoryear), fontset (fandol/windows/mac/ubuntu), passthrough (поля, освобождённые от экранирования) и т. д. |
Отличие от подачи через форму:
papex.jsonиспользуетaffiliationкак строку вместо числовогоaffiliationId; слой отображения ищет или создаёт строкуaffiliations. Также добавляются специфичные для LaTeX поляsections,references,appendices,build.
3.3 Инструментарий XeLaTeX (papex-latex/)
Специальный инструментарий XeLaTeX поставляется в papex-latex/:
papex-latex/
├── papex.cls # класс документа (ctex + authblk + biblatex, CJK+английский, макросы метаданных, колонтитулы)
├── papex-template.tex # основной документ, автоматически \input сгенерированных _papex_*.tex и разделов
├── papex-build.py # сборщик без зависимостей (только стандартная библиотека; опционально jsonschema)
├── papex.schema.json # контракт манифеста draft-07
├── latexmkrc # необязательная конфигурация latexmk
├── README.md # использование инструментария
└── example/ # полный пример пакета (китайская работа + 5 разделов + приложение)Ключевые особенности papex.cls
- CJK + английский: основан на
ctex(scheme=plain), по умолчаниюfontset=fandol(поставляется с TeX Live, компилируется на сервере «из коробки»); локально переключается черезwindows/mac/ubuntu. - Авторы/аффилиации:
authblkс общими аффилиациями, сносками ответственного автора и равного вклада. - Ссылки:
biblatex+biber, выборnumeric/authoryear. - Макросы метаданных:
\papexPaperId(ID работы над названием),\papexSubtitle,\papexVenue,\papexDoi(автоссылка doi.org),\papexVersionNote,\papexKeywords(после аннотации),\papexLicense(нижний колонтитул),\papexRunningTitle(верхний колонтитул). - Независимость от бренда: без формулировок preprints / arXiv, в соответствии с «свободным от arXiv» соглашением о продукте.
Процесс papex-build.py
- Прочитать
papex.json(ввод может быть каталогом / одним json /.tar.gz). - Проверить (предпочтительно
jsonschema, иначе встроенные проверки). - Экранировать текстовые поля (
title/abstract/authors/affiliation/keywords/acknowledgments…), генерируя_papex_meta.tex,_papex_abstract.tex,_papex_sections.tex,_papex_backmatter.tex,_papex_appendices.texиreferences.bib(пропускается, если архив уже содержитreferences.bib). - Скомпилировать через
latexmk -xelatex(--emit-onlyвыдаёт только промежуточные файлы,--validateтолько проверяет). - Файлы разделов
.texпишутся автором вручную и поддерживают полный LaTeX (включая математику); они не экранируются. Используйтеbuild.passthrough, чтобы освободить текстовые поля JSON от экранирования.
3.4 Локальный предпросмотр и сборка
# перейти в пример пакета
cd papex-latex/example
# выдать только промежуточные .tex/.bib (TeX не нужен — полезно для проверки экранирования/структуры)
python3 ../papex-build.py . --emit-only
# проверить только papex.json
python3 ../papex-build.py . --validate
# сгенерировать и скомпилировать PDF (требуется локальный TeX Live)
python3 ../papex-build.py .Упаковка и подача:
tar -czf my-paper.tar.gz papex.json papex-template.tex references.bib sections/3.5 Загрузка на сайте
- После входа нажмите Подать в верхней навигации и выберите вкладку Исходный пакет.
- Перетащите
tar.gzв зону сброса или нажмите для выбора файла (только.tar.gz/.tgz, ≤ 50 МБ). - Нажмите «Загрузить и подать»; платформа возвращает ID работы и версию с примечаниями об обработке (напр. PDF собирается в фоне).
- Нажмите «Перейти к работе», чтобы открыть созданную страницу работы.
4. Сквозная обработка (серверная часть)
После загрузки сервер обрабатывает пакет следующим образом (исходный код в src/lib/latex/):
author ──tar.gz──> POST /api/submit/archive (multipart: file)
│
▼
① распаковка (tar.ts)
gunzip + парсер ustar/GNU/PAX без зависимостей, защита от обхода путей
│
▼
② чтение papex.json → coerceManifest() проверяет обязательные поля
│
▼
③ mapToCreatePaperInput()
· primaryCategoryId должна существовать (иначе 400)
· строка affiliation → affiliations.id (findOrCreateAffiliation)
· paper.id совпадает с собственной/привилегированной работой → новая версия
│
▼
④ createSubmission() принимает (повторно использует существующую транзакцию)
│
▼
⑤ mapReferencesToCitations() → addCitation() связывает граф цитирований
│
▼
⑥ опциональная сборка XeLaTeX (серверный latexmk)
→ savePdfBuffer() сохраняет → обновляет paper_versions.pdfUrl
(отсутствие latexmk → только предупреждение, приём не затрагивается)
│
▼
возвращает { paperId, version, warnings, pdfUrl? }Ключевые модули
| Файл | Ответственность |
|---|---|
src/lib/latex/tar.ts | gunzip + parseTar без зависимостей (ustar / длинные имена GNU / расширенные заголовки PAX), writeEntries с защитой от обхода путей |
src/lib/latex/papex-json.ts | типы PapexManifest, coerceManifest, mapToCreatePaperInput, mapReferencesToCitations |
src/lib/latex/papex-archive.ts | оркестрация processSubmissionArchive; buildAndStorePdf проверяет latexmk и компилирует/сохраняет PDF |
src/app/api/submit/archive/route.ts | принимает multipart/form-data file (≤50 МБ), аутентифицирует, сопоставляет ошибки с HTTP-статусом |
Сопоставление кодов ошибок (HTTP)
| Внутренняя ошибка | HTTP | Значение |
|---|---|---|
MANIFEST_MISSING | 400 | В архиве отсутствует papex.json |
MANIFEST_JSON_INVALID | 400 | papex.json не является корректным JSON |
MANIFEST_INVALID:… | 400 | Отсутствует обязательное поле (title/abstract/primaryCategoryId/authors/sections) |
CATEGORY_NOT_FOUND:cs.X | 400 | Код категории не существует |
ARCHIVE_PARSE_FAILED / ARCHIVE_EMPTY | 400 | Архив повреждён или пуст |
FORBIDDEN | 403 | Нет прав подавать новую версию этой работы |
PAPER_NOT_FOUND | 404 | Заявленная целевая работа для новой версии не существует |
| прочее | 500 | Внутренняя ошибка (вкл. ID_GENERATION_FAILED) |
5. Справочник API
POST /api/papers
Конечная точка подачи через форму. Запрос — multipart/form-data (см. Раздел 2): поле meta — JSON-строка метаданных, поле pdf — необязательный PDF-файл (≤50 МБ). Требуется аутентификация. Возвращает { paperId, version }, а при загрузке PDF — дополнительно pdf: { pdfUrl, pages, referencesExtracted, referencesLinked }. API-клиенты также могут отправлять обычный JSON (без pdf).
POST /api/submit/archive
Конечная точка исходного пакета.
Аутентификация: требуется (cookie).
Запрос:
multipart/form-data, полеfile— этоtar.gz(≤ 50 МБ).Успех (201):
json{ "paperId": "2608.00007", "version": 1, "warnings": [], "pdfUrl": "https://…/api/papers/2608.00007/pdf/1" }Ошибка: JSON с соответствующим сообщением об ошибке; коды состояний согласно таблице ошибок.
6. Развёртывание и эксплуатация
- TeX Live: серверу нужен
texlive(сxelatex,biber,latexmk) иcollection-langchinese, чтобы были доступны шрифтыfandol. - Переменные окружения:
PAPEX_LATEX_BIN: путь к latexmk (по умолчаниюPATH).PAPEX_LATEX_DIR: каталог сpapex.cls; копируется, когда архив его не содержит.
- Песочница и ресурсы: запускайте компиляцию LaTeX в изолированной среде с ограничениями CPU/памяти/таймаута и отключите
\write18(shell-escape) и сетевой доступ, чтобы вредоносные исходники не могли выполнять команды. - Асинхронность: компиляция медленная; в production предпочтительна очередь асинхронных задач (вернуть
paperIdсразу, обратный вызов для обновленияpdfUrl, когда PDF будет готов), чтобы не блокировать запрос. - Деградация при отсутствии: если
latexmkнедоступен,processSubmissionArchiveзаписываетwarningsи пропускает сборку PDF; приём и связывание цитирований продолжают работать. - Хранение PDF: повторно использует
savePdfBuffer(потоковый маршрут/api/papers/{id}/pdf/{version}); новый слой хранения не нужен.
7. Безопасность
- Обход путей:
writeEntriesпроверяет реальный путь каждой записи черезpath.relative, отклоняя..и абсолютные пути;parseTarудаляет начальный./. - Ограничение размера: маршрут ограничивает
fileдо ≤ 50 МБ. - Злоупотребление ресурсами: компиляция имеет лимиты таймаута/ресурсов; рассмотрите ограничение частоты по пользователям.
- shell-escape: команда компиляции не передаёт
-shell-escape, не позволяя исходникам выполнять системные команды.
8. Часто задаваемые вопросы
В: Дублирует ли исходный пакет данные подачи через форму? Нет. Оба используют одну и ту же логику приёма; отличается только источник метаданных.
В: Обязательно ли использовать шаблон XeLaTeX?papex.cls и papex-template.tex определяют итоговую вёрстку PDF; вы пишете только файлы разделов .tex и papex.json. Если архив не содержит papex.cls, сервер использует тот, что из PAPEX_LATEX_DIR.
В: Могу ли я использовать математику, рисунки, свои команды в разделах? Да. Файлы разделов .tex пишутся вручную и поддерживают полный LaTeX, без экранирования. Поместите свои команды преамбулы в файлы разделов или papex-template.tex.
В: Я не вижу PDF сразу после подачи? Если TeX Live не настроен на сервере, pdfUrl пуст, и страница сообщает «PDF собирается в фоне». Настройте его и повторите подачу; в production используйте вместе с очередью асинхронных задач.
В: Как подать новую версию работы? Укажите paper.id в papex.json равным вашему существующему ID работы (и у вас должно быть право подачи для неё); платформа примет её как новую версию.
В: Как цитирования связываются автоматически?doi / arxivId из массива references разрешаются во внутренние работы через resolveTarget, и создаётся ребро цитирования; прочие записи сохраняются как url / title в графе цитирований.