diff --git a/frontend/server/src/index.ts b/frontend/server/src/index.ts
index 7a3e6c54f..2b09ead10 100644
--- a/frontend/server/src/index.ts
+++ b/frontend/server/src/index.ts
@@ -48,6 +48,7 @@ server.get("*", async (request: Request, response: Response) => {
});
response.contentType("text/html");
+ response.setHeader("X-Powered-By", "HangarSSR");
response.writeHead(status || 200, statusText || headers, headers);
response.end(html);
});
diff --git a/frontend/src/components/layout/Header.vue b/frontend/src/components/layout/Header.vue
index 66f643ec1..6f3bd7e2b 100644
--- a/frontend/src/components/layout/Header.vue
+++ b/frontend/src/components/layout/Header.vue
@@ -344,7 +344,7 @@ function isRecent(date: string): boolean {
-
+
{{ t("nav.login") }}
diff --git a/frontend/src/components/projects/MemberList.vue b/frontend/src/components/projects/MemberList.vue
index b0fdb19f2..622144c0b 100644
--- a/frontend/src/components/projects/MemberList.vue
+++ b/frontend/src/components/projects/MemberList.vue
@@ -132,7 +132,7 @@ interface EditableMember {
- {{ i18n.t("project.members") }}
+
{{ i18n.t("project.members") }}
diff --git a/frontend/src/components/projects/ProjectCard.vue b/frontend/src/components/projects/ProjectCard.vue
index 9434d280f..fac4e76af 100644
--- a/frontend/src/components/projects/ProjectCard.vue
+++ b/frontend/src/components/projects/ProjectCard.vue
@@ -36,17 +36,17 @@ function getBorderClasses(): string {
/>
-
-
+
+
{{ project.name }}
by
{{ project.namespace.owner }}
-
+
-
+
-
{{ project.description }}
+
{{ project.description }}
{{ i18n.t("project.category." + project.category) }}
diff --git a/frontend/src/components/projects/ProjectHeader.vue b/frontend/src/components/projects/ProjectHeader.vue
index 6085a1451..f44c3c773 100644
--- a/frontend/src/components/projects/ProjectHeader.vue
+++ b/frontend/src/components/projects/ProjectHeader.vue
@@ -123,7 +123,7 @@ function requiresConfirmation(): boolean {
/>
{{ project.namespace.owner }}
/
- {{ project.name }}
+ {{ project.name }}
{{ project.description }}
diff --git a/frontend/src/components/projects/ProjectInfo.vue b/frontend/src/components/projects/ProjectInfo.vue
index 69ccb3c01..7233993c3 100644
--- a/frontend/src/components/projects/ProjectInfo.vue
+++ b/frontend/src/components/projects/ProjectInfo.vue
@@ -11,7 +11,6 @@ import { NamedPermission } from "~/types/enums";
import { HangarProject } from "hangar-internal";
import DonationModal from "~/components/donation/DonationModal.vue";
import VisibilityChangerModal from "~/components/modals/VisibilityChangerModal.vue";
-import { MenuItem } from "@headlessui/vue";
const props = defineProps<{
project: HangarProject;
@@ -22,7 +21,9 @@ const slug = computed(() => props.project.namespace.owner + "/" + props.project.
- {{ i18n.t("project.info.title") }}
+
+ {{ i18n.t("project.info.title") }}
+
diff --git a/frontend/src/components/projects/ProjectPageList.vue b/frontend/src/components/projects/ProjectPageList.vue
index 8c9d28e33..0d83e286a 100644
--- a/frontend/src/components/projects/ProjectPageList.vue
+++ b/frontend/src/components/projects/ProjectPageList.vue
@@ -22,7 +22,7 @@ const route = useRoute();
- {{ i18n.t("page.plural") }}
+
{{ i18n.t("page.plural") }}
diff --git a/frontend/src/components/projects/ProjectPageMarkdown.vue b/frontend/src/components/projects/ProjectPageMarkdown.vue
index d74ea2c67..1a82735bd 100644
--- a/frontend/src/components/projects/ProjectPageMarkdown.vue
+++ b/frontend/src/components/projects/ProjectPageMarkdown.vue
@@ -9,6 +9,7 @@ import { useSeo } from "~/composables/useSeo";
import { inject } from "vue";
import { useInternalApi } from "~/composables/useApi";
import { handleRequestError } from "~/composables/useErrorHandling";
+import { projectIconUrl } from "~/composables/useUrlHelper";
const props = defineProps<{
project: HangarProject;
@@ -23,7 +24,8 @@ const updateProjectPages = inject<(pages: HangarProjectPage[]) => void>("updateP
const { editingPage, changeEditingPage, page, savePage, deletePage } = await useProjectPage(route, router, ctx, i18n, props.project);
if (page) {
- useHead(useSeo(page.value?.name, props.project.description, route, null));
+ const title = page.value?.name === "Home" ? props.project.name : page.value?.name + " | " + props.project.name;
+ useHead(useSeo(title, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));
}
async function deletePageAndUpdateProject() {
diff --git a/frontend/src/composables/useSeo.ts b/frontend/src/composables/useSeo.ts
index 37910c6d5..8aa04368f 100644
--- a/frontend/src/composables/useSeo.ts
+++ b/frontend/src/composables/useSeo.ts
@@ -9,7 +9,7 @@ export function useSeo(
image: string | null
): HeadObject {
description = description || "Plugin repository for Paper plugins and more!";
- const canonical = baseUrl() + (route.fullPath.endsWith("/") ? route.fullPath : `${route.fullPath}/`);
+ const canonical = baseUrl() + (route.fullPath.endsWith("/") ? route.fullPath.substring(0, route.fullPath.length - 1) : route.fullPath);
image = image || "https://docs.papermc.io/img/paper.png";
image = image.startsWith("http") ? image : baseUrl() + image;
title = title ? title + " | Hangar" : "Hangar";
@@ -17,49 +17,40 @@ export function useSeo(
title,
link: [{ rel: "canonical", href: canonical }],
meta: [
- { hid: "description", name: "description", content: description },
+ { property: "description", name: "description", content: description },
{
property: "og:description",
name: "og:description",
- vmid: "og:description",
- hid: "og:description",
content: description,
},
{
property: "twitter:description",
name: "twitter:description",
- vmid: "twitter:description",
- hid: "twitter:description",
content: description,
},
{
property: "og:title",
name: "og:title",
- hid: "og:title",
content: title,
},
{
property: "twitter:title",
name: "twitter:title",
- hid: "twitter:title",
content: title,
},
{
property: "og:url",
name: "og:url",
- hid: "og:url",
content: canonical,
},
{
property: "twitter:url",
name: "twitter:url",
- hid: "twitter:url",
content: canonical,
},
{
property: "og:image",
name: "og:image",
- hid: "og:image",
content: image,
},
],
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index c2760b9c1..064ca3ff6 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -52,6 +52,7 @@
"noProjects": "There are no projects. 😢",
"noProjectsFound": "Found 0 projects. 😢",
"title": "Find your favorite plugins",
+ "subTitle": "Hangar allows you to find the best Velocity, Waterfall or Paper plugins for your Minecraft Servers.",
"categories": "Categories",
"licenses": "Licenses",
"versions": "Minecraft versions",
diff --git a/frontend/src/pages/[user]/[project].vue b/frontend/src/pages/[user]/[project].vue
index 9d55dc246..000c4a545 100644
--- a/frontend/src/pages/[user]/[project].vue
+++ b/frontend/src/pages/[user]/[project].vue
@@ -9,10 +9,7 @@ import { handleRequestError } from "~/composables/useErrorHandling";
import { useErrorRedirect } from "~/lib/composables/useErrorRedirect";
import ProjectHeader from "~/components/projects/ProjectHeader.vue";
import ProjectNav from "~/components/projects/ProjectNav.vue";
-import { useHead } from "@vueuse/head";
-import { useSeo } from "~/composables/useSeo";
-import { projectIconUrl } from "~/composables/useUrlHelper";
-import { HangarProject, HangarProjectPage } from "hangar-internal";
+import { HangarProjectPage } from "hangar-internal";
defineProps({
user: {
@@ -27,8 +24,6 @@ const route = useRoute();
const project = await useProject(route.params.user as string, route.params.project as string).catch((e) => handleRequestError(e, ctx, i18n));
if (!project || !project.value) {
await useRouter().replace(useErrorRedirect(route, 404, "Not found"));
-} else {
- useHead(useSeo(project.value.name, project.value.description, route, projectIconUrl(project.value.namespace.owner, project.value.namespace.slug)));
}
provide("updateProjectPages", function (pages: HangarProjectPage[]) {
diff --git a/frontend/src/pages/[user]/[project]/index.vue b/frontend/src/pages/[user]/[project]/index.vue
index 5b08db066..c36af3c7e 100644
--- a/frontend/src/pages/[user]/[project]/index.vue
+++ b/frontend/src/pages/[user]/[project]/index.vue
@@ -12,9 +12,6 @@ import { useRoute, useRouter } from "vue-router";
import { useContext } from "vite-ssr/vue";
import Markdown from "~/components/Markdown.vue";
import ProjectPageList from "~/components/projects/ProjectPageList.vue";
-import { useHead } from "@vueuse/head";
-import { useSeo } from "~/composables/useSeo";
-import { projectIconUrl } from "~/composables/useUrlHelper";
import { ref } from "vue";
import { useInternalApi } from "~/composables/useApi";
import { handleRequestError } from "~/composables/useErrorHandling";
@@ -56,7 +53,7 @@ function createPinnedVersionUrl(version: PinnedVersion): string {
return `/${props.project.namespace.owner}/${props.project.namespace.slug}/versions/${version.name}`;
}
-useHead(useSeo(props.project.name, props.project.description, route, projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)));
+// useSeo is in ProjectPageMarkdown
@@ -92,7 +89,7 @@ useHead(useSeo(props.project.name, props.project.description, route, projectIcon
@save="saveSponsors"
/>
- {{ i18n.t("project.sponsors") }}
+ {{ i18n.t("project.sponsors") }}
@@ -100,7 +97,9 @@ useHead(useSeo(props.project.name, props.project.description, route, projectIcon
- {{ i18n.t("project.pinnedVersions") }}
+
+ {{ i18n.t("project.pinnedVersions") }}
+
-
diff --git a/frontend/src/pages/[user]/[project]/pages/[...all].vue b/frontend/src/pages/[user]/[project]/pages/[...all].vue
index 8fe16fcc1..df1a12c7a 100644
--- a/frontend/src/pages/[user]/[project]/pages/[...all].vue
+++ b/frontend/src/pages/[user]/[project]/pages/[...all].vue
@@ -10,9 +10,6 @@ import MarkdownEditor from "~/components/MarkdownEditor.vue";
import { hasPerms } from "~/composables/usePerm";
import { NamedPermission } from "~/types/enums";
import Card from "~/lib/components/design/Card.vue";
-import { useProjectPage } from "~/composables/useProjectPage";
-import { useHead } from "@vueuse/head";
-import { useSeo } from "~/composables/useSeo";
import ProjectPageMarkdown from "~/components/projects/ProjectPageMarkdown.vue";
import { useOpenProjectPages } from "~/composables/useOpenProjectPages";
@@ -27,6 +24,7 @@ const route = useRoute();
const router = useRouter();
const open = await useOpenProjectPages(route, props.project);
+// useSeo is in ProjectPageMarkdown
diff --git a/frontend/src/pages/[user]/[project]/versions/[version].vue b/frontend/src/pages/[user]/[project]/versions/[version].vue
index 4d9b08ae9..9978a1473 100644
--- a/frontend/src/pages/[user]/[project]/versions/[version].vue
+++ b/frontend/src/pages/[user]/[project]/versions/[version].vue
@@ -46,15 +46,6 @@ if (!route.params.platform) {
const entry = versionMap.keys().next();
await (entry.value ? useRouter().replace({ path: `${path}/${entry.value.toLowerCase()}` }) : useRouter().replace(useErrorRedirect(route, 404, "Not found")));
}
-
-useHead(
- useSeo(
- (props.project.name + " " + route.params.version) as string,
- props.project.description,
- route,
- projectIconUrl(props.project.namespace.owner, props.project.namespace.slug)
- )
-);
diff --git a/frontend/src/pages/[user]/[project]/versions/[version]/[platform]/index.vue b/frontend/src/pages/[user]/[project]/versions/[version]/[platform]/index.vue
index 3cc5e1b21..bb536e0d9 100644
--- a/frontend/src/pages/[user]/[project]/versions/[version]/[platform]/index.vue
+++ b/frontend/src/pages/[user]/[project]/versions/[version]/[platform]/index.vue
@@ -152,11 +152,11 @@ async function restoreVersion() {
-
+
{{ projectVersion.name }}
-
-
+
+
{{ i18n.t("version.page.subheader", [projectVersion.author, lastUpdated(new Date(projectVersion.createdAt))]) }}
@@ -164,7 +164,7 @@ async function restoreVersion() {
{{ filesize(projectVersion.fileInfo.sizeBytes) }}
-
+
{{ i18n.t("version.page.adminMsg", [projectVersion.approvedBy, i18n.d(projectVersion.createdAt, "date")]) }}
@@ -201,7 +201,9 @@ async function restoreVersion() {
- {{ i18n.t("version.page.manage") }}
+
+ {{ i18n.t("version.page.manage") }}
+
@@ -274,7 +276,7 @@ async function restoreVersion() {
-
{{ i18n.t("version.page.platform") }}
+
{{ i18n.t("version.page.platform") }}
@@ -289,7 +291,7 @@ async function restoreVersion() {
- {{ i18n.t("version.page.dependencies") }}
+
{{ i18n.t("version.page.dependencies") }}
diff --git a/frontend/src/pages/[user]/[project]/versions/index.vue b/frontend/src/pages/[user]/[project]/versions/index.vue
index 7a34b413c..67f5153f8 100644
--- a/frontend/src/pages/[user]/[project]/versions/index.vue
+++ b/frontend/src/pages/[user]/[project]/versions/index.vue
@@ -129,7 +129,7 @@ function getVisibilityTitle(visibility: Visibility) {
-
{{ item.name }}
+
{{ item.name }}
@@ -183,10 +183,10 @@ function getVisibilityTitle(visibility: Visibility) {
- {{ i18n.t("version.channels") }}
+
{{ i18n.t("version.channels") }}
-
+
+
@@ -203,7 +203,7 @@ function getVisibilityTitle(visibility: Visibility) {
- {{ i18n.t("version.platforms") }}
+
{{ i18n.t("version.platforms") }}
diff --git a/frontend/src/pages/[user]/index.vue b/frontend/src/pages/[user]/index.vue
index d2ef55e9e..153b18291 100644
--- a/frontend/src/pages/[user]/index.vue
+++ b/frontend/src/pages/[user]/index.vue
@@ -74,7 +74,7 @@ const buttons = computed
(() => {
const isCurrentUser = computed(() => authStore.user != null && authStore.user.name === props.user.name);
-useHead(useSeo(props.user.name, props.user.tagline, route, avatarUrl(props.user.name)));
+useHead(useSeo(props.user.name, props.user.name + " is an author on Hangar. " + props.user.tagline, route, avatarUrl(props.user.name)));
diff --git a/frontend/src/pages/api.vue b/frontend/src/pages/api.vue
index 257e1a900..f037f9f39 100644
--- a/frontend/src/pages/api.vue
+++ b/frontend/src/pages/api.vue
@@ -47,12 +47,16 @@ onMounted(() => {
bundle.onload = () => document.body.append(script);
});
-useHead(useSeo(i18n.t("apiDocs.title"), null, route, null));
+useHead(useSeo(i18n.t("apiDocs.title"), "API Docs for the Hangar REST API", route, null));
-
+
+
Hangar API
+
API Docs for the Hangar REST API
+
Loading...
+
diff --git a/frontend/src/pages/authors.vue b/frontend/src/pages/authors.vue
index 0f76b9712..69cb98ca1 100644
--- a/frontend/src/pages/authors.vue
+++ b/frontend/src/pages/authors.vue
@@ -23,7 +23,7 @@ const headers = [
{ name: "projectCount", title: i18n.t("pages.headers.projects"), sortable: true },
] as Header[];
-useHead(useSeo(i18n.t("pages.authorsTitle"), null, route, null));
+useHead(useSeo(i18n.t("pages.authorsTitle"), "Hangar Project Authors", route, null));
diff --git a/frontend/src/pages/index.vue b/frontend/src/pages/index.vue
index 770b1eab3..6cac935e9 100644
--- a/frontend/src/pages/index.vue
+++ b/frontend/src/pages/index.vue
@@ -124,7 +124,8 @@ useHead(meta);
{{ i18n.t("hangar.loggedOut") }}
- {{ i18n.t("hangar.projectSearch.title") }}
+ {{ i18n.t("hangar.projectSearch.title") }}
+ {{ i18n.t("hangar.projectSearch.subTitle") }}
@@ -167,12 +168,12 @@ useHead(meta);
-
{{ i18n.t("hangar.projectSearch.versions") }}
+
{{ i18n.t("hangar.projectSearch.versions") }}
-
{{ i18n.t("hangar.projectSearch.categories") }}
+
{{ i18n.t("hangar.projectSearch.categories") }}
-
{{ i18n.t("hangar.projectSearch.licenses") }}
+
{{ i18n.t("hangar.projectSearch.licenses") }}