Skip to content

Conversation

@WIStudent
Copy link

Description

This is another try to implement project links that use name and version and do not require uuid. The previous try at #45 seems to be abandoned.

Addressed Issue

resolves #41

Additional Details

This PR adds two additional vue routes:

projects/:name/:version/*

Its beforeEach navigation guard tries to lookup the uuid of the project using the /v1/project/lookup API endpoint. If a uuid was found, it redirects to projects/:uuid. If not, it redirects to the 404 page.

The /* at the end is used to keep trailing subpaths.

projects/MyProject/1.2.3/ -> projects/:uuid
projects/MyProject/1.2.3/components -> projects/:uuid/components

projects/:name/:version

The projects/:name/:version/* route will not match projects/MyProject/1.2.3 because it does not end with a /. That's why projects/:name/:version will redirect to projects/:name/:version/.

projects/:name/:version -> projects/:name/:version/ -> projects/:uuid

Checklist

Signed-off-by: Tobias Trumm <tobias@tobiastrumm.de>
Copilot AI review requested due to automatic review settings January 19, 2026 15:41
@owasp-dt-bot
Copy link

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a feature to allow linking to projects using their name and version instead of requiring the UUID. It adds two new Vue Router routes that perform a lookup of the project UUID via a new API endpoint and redirect to the existing UUID-based project routes.

Changes:

  • Added URL_PROJECT_LOOKUP API endpoint constant for project lookup by name and version
  • Added projects/:name/:version/* route with navigation guard that looks up UUID and redirects to UUID-based routes
  • Added projects/:name/:version route that redirects to the wildcard variant with a trailing slash

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/shared/api.json Adds the URL constant for the new project lookup API endpoint
src/router/index.js Implements two new routes to support name/version-based project linking with UUID lookup and redirection logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

},
{
path: 'projects/:name/:version',
redirect: 'projects/:name/:version/',
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

The redirect path should be absolute (starting with '/') or use a function-based redirect. The current relative path may not work as expected. Consider changing to redirect: (to) => ({ path: /projects/${to.params.name}/${to.params.version}/ }) to ensure proper route resolution.

Suggested change
redirect: 'projects/:name/:version/',
redirect: (to) => ({
path: `/projects/${to.params.name}/${to.params.version}/`,
}),

Copilot uses AI. Check for mistakes.
? `/${to.params.pathMatch}`
: '';
next({
path: `projects/${result.data.uuid}${trailingPathSegments}`,
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

The navigation redirect should include leading slash to make the path absolute. Change path: projects/${result.data.uuid}${trailingPathSegments} to `path: `/projects/${result.data.uuid}${trailingPathSegments} to ensure proper path resolution.

Suggested change
path: `projects/${result.data.uuid}${trailingPathSegments}`,
path: `/projects/${result.data.uuid}${trailingPathSegments}`,

Copilot uses AI. Check for mistakes.
Comment on lines +233 to +239
path: `projects/${result.data.uuid}${trailingPathSegments}`,
replace: true,
});
})
.catch(() => {
next({
name: '404',
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

Query parameters from the original URL are not preserved during the redirect. If the original URL contains query parameters (e.g., for filtering or sorting), they will be lost. Consider preserving them by including query: to.query in the next() object alongside the path.

Suggested change
path: `projects/${result.data.uuid}${trailingPathSegments}`,
replace: true,
});
})
.catch(() => {
next({
name: '404',
path: `projects/${result.data.uuid}${trailingPathSegments}`,
query: to.query,
replace: true,
});
})
.catch(() => {
next({
name: '404',
query: to.query,

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +241
.catch(() => {
next({
name: '404',
replace: true,
});
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

The error handling catches all errors uniformly, but different error types should potentially be handled differently. For example, a 404 (not found) is different from a 401 (unauthorized) or 500 (server error). Consider checking the error response status to provide more appropriate error handling, especially for authorization errors which might need different treatment than simple "not found" scenarios.

Suggested change
.catch(() => {
next({
name: '404',
replace: true,
});
.catch((error) => {
const status = error && error.response && error.response.status;
if (status === 404) {
next({
name: '404',
replace: true,
});
} else if (status === 401 || status === 403) {
next({
name: 'Login',
replace: true,
});
} else {
// Cancel navigation on unexpected errors to avoid misreporting them as "not found"
next(false);
}

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +222
const searchParams = new URLSearchParams({
name: to.params.name,
version: to.params.version,
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

Project names and versions may contain special characters that need URL encoding. The current implementation doesn't decode these parameters before sending them to the API. If a project name contains special characters (e.g., spaces, slashes), the URL parameters extracted from to.params will already be decoded by Vue Router, but this should be verified. Additionally, consider validating that the name and version parameters don't contain path separators or other invalid characters that could cause routing issues.

Suggested change
const searchParams = new URLSearchParams({
name: to.params.name,
version: to.params.version,
const decodeParam = (value) => {
if (typeof value !== 'string') {
return '';
}
try {
return decodeURIComponent(value);
} catch (e) {
// If decoding fails (malformed escape sequences), fall back to the original value
return value;
}
};
const isValidSegment = (value) =>
typeof value === 'string' && !/[\\/]/.test(value);
const name = decodeParam(to.params.name);
const version = decodeParam(to.params.version);
if (!isValidSegment(name) || !isValidSegment(version)) {
next({
name: '404',
replace: true,
});
return;
}
const searchParams = new URLSearchParams({
name,
version,

Copilot uses AI. Check for mistakes.
Comment on lines +233 to +239
path: `projects/${result.data.uuid}${trailingPathSegments}`,
replace: true,
});
})
.catch(() => {
next({
name: '404',
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

URL hash fragments are not preserved during the redirect. If the original URL contains a hash (e.g., for in-page navigation or state), it will be lost. Consider preserving the hash by including hash: to.hash in the next() object alongside the path.

Suggested change
path: `projects/${result.data.uuid}${trailingPathSegments}`,
replace: true,
});
})
.catch(() => {
next({
name: '404',
path: `projects/${result.data.uuid}${trailingPathSegments}`,
hash: to.hash,
replace: true,
});
})
.catch(() => {
next({
name: '404',
hash: to.hash,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Link to project page by name and version

2 participants