Skip to content

feat(template): support managed template repository layout#6999

Open
mlhiter wants to merge 6 commits into
labring:release-v5.1from
mlhiter:feat/template-management
Open

feat(template): support managed template repository layout#6999
mlhiter wants to merge 6 commits into
labring:release-v5.1from
mlhiter:feat/template-management

Conversation

@mlhiter

@mlhiter mlhiter commented Jun 13, 2026

Copy link
Copy Markdown
Member

What this PR does

This PR adds Template App support for the managed template repository layout produced by Sealos Admin.

  • Reads category metadata from config/categories.json in the cloned template repository, with the existing env fallback kept server-side.
  • Serves repository-local README/icon assets through /api/templateAsset so private or offline stores do not depend on public raw URLs.
  • Supports split template manifests such as template/<name>/index.yaml plus template/<name>/manifests/*.yaml while keeping legacy single-file templates compatible.

Why this is needed

The paired Admin template-management flow writes templates, categories, README files, icons, and manifests into an internal Git/Gogs repository. Template App needs to consume that same repository format for the store and deploy flows.

Test

  • pnpm -C frontend/providers/template build

Note: the build completed successfully with existing React Hook lint warnings.

@mlhiter mlhiter requested a review from a team as a code owner June 13, 2026 09:00

const originalPath = process.cwd();
const jsonPath = path.resolve(originalPath, 'templates.json');
const result = readTemplateAssetFile({ jsonPath, templateName, assetUrl: asset });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This makes the new public asset endpoint a generic file reader for the cloned template repository. asset comes directly from the query string and is only constrained later to stay under templates/, so an unauthenticated caller can request files that were never declared as this template’s README/icon, for example enough ../ segments to reach templates/.git/config or /config/categories.json. In private/offline repo deployments this can leak repo config, unpublished templates, and other non-asset files. Please authorize the asset against the template’s declared spec.readme/spec.icon/i18n assets, and only serve derived README images from an explicit allowlist of safe locations/extensions.

function assertInRepo(repoRootPath: string, targetPath: string) {
const root = path.resolve(repoRootPath);
const target = path.resolve(targetPath);
if (target !== root && !target.startsWith(`${root}${path.sep}`)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This boundary check is path-string based, so it does not protect against symlinks inside the template repository. A repo entry such as template/foo/leak.txt -> /etc/passwd still resolves under templates/ here, but fs.readFileSync() follows the symlink and returns the target outside the repo. The split-manifest reader has the same shape when reading files from manifests/. Please validate real paths before reading and reject symlinks, or use lstat plus a realpath repo-root check and only allow regular files.

clearTemplateRuntimeCaches();
}

export async function ensureRepoFresh({ force = false }: { force?: boolean } = {}) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Moving repo refresh from the existing background cron into ensureRepoFresh() changes the freshness model in a problematic way. With no traffic the catalog no longer updates in the background, and every read path now has to remember to call this helper before touching templates.json; any new or existing endpoint that misses it will silently serve stale data. It also puts git pull on the user request path whenever the TTL expires. Please keep a single background refresh mechanism (or another centralized scheduler) and let read handlers only read the current catalog/cache, instead of making each API endpoint responsible for repository synchronization.

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