Skip to content

feat(collectives-publish) static site update endpoint - #2830

Merged
kjoscha merged 2 commits into
feature/collectives-publishfrom
publish-feature/static-site-update-endpoint
Oct 22, 2026
Merged

kjoscha merged 2 commits into
feature/collectives-publishfrom
publish-feature/static-site-update-endpoint

Conversation

@kjoscha

@kjoscha kjoscha commented Sep 30, 2026 •

Copy link
Copy Markdown

📝 Summary

The external build service of the publish feature sends back a success (or error) message to Collectives after a build ran or failed. The payload is expected as the following:

{ "status": "published",
  "data": {
    "publish_url": "https://publish.schnubbel.com/publish/<static_site_id>"
  }
}

or in case of failure

{ "status": "failed",
  "data": {
    "error_message": "<some error message>"
  }
}

🏁 Checklist

  • API endpoint exist and is tested
  • Delete tar.gz after success response arrived from build service

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI tools
  • The AI-generated content was reviewed, comprehended and tested by a human

Testing manually

You can tiun a manually update from the terminal via curl like this (replace the static site id):

curl -X PATCH "http://localhost/ocs/v2.php/apps/collectives/api/v1.0/p/static-sites/01a1112f-7cb5-747a-be61-07c74093449a" \
  -H "Host: nextcloud.local" \
  -H "OCS-APIREQUEST: true" \
  -H "Content-Type: application/json" \
  -d '{"status": "published", "data": {"publish_url": "https://example.com/site"}}'
image image

@kjoscha kjoscha changed the title Publish feature/static site update endpoint feat(collectives-publish) static site update endpoint Sep 30, 2026
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 3 times, most recently from ce0e101 to 7ec2f7d Compare October 6, 2026 11:57
@code4candy
code4candy force-pushed the feature/collectives-publish branch from 9ca1098 to 37ee4ef Compare October 7, 2026 21:03
Comment thread openapi.json Resolved
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 3 times, most recently from 598b848 to a3f4235 Compare October 13, 2026 14:45
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch from d37a7ef to 4b588b6 Compare October 14, 2026 07:10
@kjoscha
kjoscha requested a review from code4candy October 14, 2026 07:10
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch from 4b588b6 to 2399d3e Compare October 14, 2026 07:11
@kjoscha
kjoscha marked this pull request as ready for review October 14, 2026 07:51
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch from 2399d3e to 02db61d Compare October 14, 2026 11:46
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@code4candy
code4candy force-pushed the feature/collectives-publish branch 2 times, most recently from 090efb9 to 6ac7363 Compare October 15, 2026 15:11
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 3 times, most recently from a8c5f03 to 6b1934f Compare October 20, 2026 08:59
Comment thread appinfo/routes.php Resolved

@code4candy code4candy left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we should discuss:

  • workflow/architecture for the publish process for create and update
    • SSG-callback - belongs to which build is unclear
    • define status changes

Comments from AI Review:


Blocking

  • 1. Callback endpoint lacks authentication (PublicStaticSiteController.php:55–57)
    --> decided on not using token for now
  • Only #[PublicPage] and #[AnonRateLimit]; no Bearer token check.
  • The archive download validates the token (StaticSiteArchiveController.php:52).
  • The only protection is the unguessable staticSiteId, which is also sent to the generator and may appear in its logs.
  • Agreed design: Bearer auth in both directions.

  • 2. Static sites can get stuck in provided (StaticSite.php:52–55, 71–73)
  • STATUS_PROVIDED was added to IN_PROGRESS_STATUSES.
  • The timeout in isInProgress() only applies to STATUS_PENDING.
  • If the callback never arrives (generator crash, callback URL unreachable), update() rejects the site permanently with "Publication of static site is already in progress".
  • The comment on PENDING_TIMEOUT and staticSitePublicationTimeout in constants.js ("Matches StaticSite::PENDING_TIMEOUT") no longer reflect this.

Medium


  • 3. publish_url not validated (StaticSiteService.php:222)
  • Stored as received and rendered in (CollectivePublishModal.vue:17).
  • Vue does not sanitize javascript: URLs in href.
  • Combined with finding 1: anyone knowing a staticSiteId can inject a javascript: link into the modal.
  • Option: allow only http/https schemes in the service.

  • 4. Callback does not check the current status (StaticSiteService.php:220)
  • Concurrent builds are prevented: unique index on slug (Version040602Date20260908000000.php:71); update() rejects sites in pending/provided (StaticSiteService.php:123, StaticSite::IN_PROGRESS_STATUSES).
  • updateStatus() accepts callbacks regardless of the current status, e.g. for sites already published or failed.
  • staticSiteId is reused across rebuilds via update(), so callbacks cannot be attributed to a specific build.
  • Scenario: a late or duplicate callback of a previous build arriving while a rebuild is provided sets the status and, for published, deletes the new archive.
  • Relevance depends on whether the generator retries callbacks. Option: accept callbacks only for status provided.

  • 5. Missing tests for the new endpoint
  • No unit test for PublicStaticSiteController; the other static site controllers have tests in tests/Unit/Controller/.
  • No Behat scenario for the callback; tests/Integration/features/staticSites.feature is unchanged.
  • Playwright tests mock the API entirely; criterion 4 is only verified against fake data.

Minor


  • 6. Polling continues after published without a new URL (staticSites.js:56)
  • Stops only on a changed publishedUrl, on failed, or on timeout.
  • If the generator reports published without publish_url (allowed per criterion 3) or with the same URL as before, the frontend polls every 10 s for 15 minutes.
  • Option: also stop on status === 'published'.

  • 7. Error responses for the caller
  • Invalid status → UnprocessableEntityException → OCSBadRequestException (400).
  • A missing status parameter results in a framework error, not a clean 400.
  • Relevant when documenting the API contract for the generator.

  • 8. Unrelated change: .gitignore
  • Adds AGENTS.md, CLAUDE.md, dev-tools.
  • Unrelated to the feature; alternative: .git/info/exclude locally, or a separate commit.

  • 9. Routing and naming
  • /p/ prefix and Public… controller name suggest share-token access; no share token is involved.
  • Route not grouped with the other generator-facing routes (archive routes.php:25, health routes.php:85).
  • Route name casing: publicStaticSite (camelCase) vs. static_site, static_site_archive, static_site_generator (snake_case).
  • Terminology: "static site builder service" vs. "static site generator" elsewhere.
  • "export" in docblocks (StaticSiteService.php:196, PublicStaticSiteController.php:26, 43) and the log message (StaticSiteService.php:231); the status refers to the publication, not an export.

  • 10. Modal details (CollectivePublishModal.vue)
  • loadStaticSites() wraps a single call and is used once (created()); could be inlined.
  • Load errors only go to console.error; other components use displayError (already imported).
  • title, slug and selectedPageIds of an existing static site are returned by the API but not used to prefill the modal.

Comment thread lib/Service/StaticSiteService.php Resolved
Comment thread lib/Service/StaticSiteService.php Resolved
Comment thread lib/Service/StaticSiteService.php Resolved
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 2 times, most recently from 49336f3 to 51b075d Compare October 21, 2026 12:28
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 2 times, most recently from d55ef0d to 9df7631 Compare October 21, 2026 13:08
…tus + delete archive after successful deployment message

Signed-off-by: kayoshi <jo5cha@web.de>
Assisted-by: ClaudeCode:claude-sonnet-5
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch 3 times, most recently from a7d885c to c142fa4 Compare October 22, 2026 09:48
Assisted-by: ClaudeCode:claude-sonnet-5
Signed-off-by: kayoshi <jo5cha@web.de>
@kjoscha
kjoscha force-pushed the publish-feature/static-site-update-endpoint branch from c142fa4 to 45c67b2 Compare October 22, 2026 10:19
@kjoscha
kjoscha merged commit ff5585a into feature/collectives-publish Oct 22, 2026
53 checks passed
@kjoscha
kjoscha deleted the publish-feature/static-site-update-endpoint branch October 22, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants