-
-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Extensions/Projects page on the Gate Website (#424)
* wip - extensions page wip, still testing pages worker * fix issue while building with yarn * fix yarn version hopefully * move functions to where it's supposed to be had functions in the wrong folder i believe * Potential fix for duplicate repos * Auto change title based on search mode Automatically adjusts page title to Extensions or Minekube Libraries based on the current search mode being used. * feat: show cached data from browser if api unreachable Added the ability to show cached data in the browsers localstorage if the API is down/unable to be reached. If no cached data is found message shown is "Error reaching the API. To see updated results, please try again later. " If cached data is found, it is shown and a message saying "Warning: Showing locally cached results. To see updated results, please try again later. " is shown above the results. * fix: localstorage build fix Forgot to check for browser env when using localstorage since it breaks yarn build. * fix: cache message showing when API is still reachable cached message was showing due to us using cached data on second refresh to reduce api calls, we should only show the cached data message if we are using it as fallback due to api being unreachable * real fix: cache fallback * fix: this is annoying me, hopefully real fix this time * fix: cache & message i wish I could test this without commiting lol * real fix: hopefully. * fix: remove stuff * Update Extensions.vue * final fix: remove not needed message * chore: remove yarn state * fit hero actions to one line on wide screen --------- Co-authored-by: robinbraemer <[email protected]>
- Loading branch information
1 parent
c94333d
commit 81a1528
Showing
11 changed files
with
1,519 additions
and
1,116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
/.web/docs/.vitepress/cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
nodeLinker: node-modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
<template> | ||
<div class="VPDoc px-[32px] py-[48px]"> | ||
<div class="flex flex-col gap-2 relative mx-auto max-w-[948px]"> | ||
<h1 class="text-vp-c-text-1 text-3xl font-semibold mb-4"> | ||
{{ searchMode === 'extensions' ? 'Extensions' : 'Projects using Minekube Libraries' }} | ||
</h1> | ||
<p class="text-vp-c-text-3 font-normal text-md mb-4"> | ||
<span v-if="searchMode === 'extensions'"> | ||
Here you can find useful extensions that can improve your Gate proxy! | ||
<br /> | ||
To add your own extension, simply add the <code class="font-bold mx-1">gate-extension</code> topic to your repository on GitHub. | ||
</span> | ||
<span v-else> | ||
Here you can find projects that use Minekube libraries on GitHub! | ||
<br /> | ||
To add your own project, simply import any <code class="font-bold mx-1">go.minekube.com</code> library in your go.mod file. | ||
</span> | ||
</p> | ||
|
||
<!-- Toggle Button for Search Mode --> | ||
<div class="mb-4"> | ||
<label class="font-semibold mr-2">Search Mode:</label> | ||
<button | ||
@click="toggleSearchMode" | ||
:class="{'bg-vp-c-brand-3 text-white': searchMode === 'extensions', 'bg-vp-c-border text-vp-c-text-1': searchMode === 'go-modules'}" | ||
class="rounded-lg px-4 py-2 mr-2 focus:outline-none" | ||
> | ||
{{ searchMode === 'extensions' ? 'Extensions' : 'Minekube Libraries' }} | ||
</button> | ||
</div> | ||
|
||
<!-- Search Input --> | ||
<input | ||
v-model="searchText" | ||
class="rounded-lg px-3 py-2 w-[calc(100%-2px)] translate-x-[1px] bg-vp-c-bg focus:ring-vp-c-brand-2 text-vp-c-text-2 transition-colors font-base ring-vp-c-border ring-1" | ||
placeholder="Search..." | ||
/> | ||
|
||
<!-- Show message when cached data is being used --> | ||
<div v-if="isCachedData && !loading" class="my-3 text-center text-yellow-600"> | ||
<strong>Warning:</strong> Showing locally cached results. To see updated results, please try again later. | ||
</div> | ||
|
||
<!-- Show loading indicator while data is being fetched --> | ||
<div v-if="loading" class="my-3 text-center">Loading...</div> | ||
|
||
<!-- Show error message --> | ||
<div v-if="error && !isCachedData" class="my-3 text-center text-red-600"> | ||
Error reaching the API. To see updated results, please try again later. | ||
</div> | ||
|
||
<!-- Display Results --> | ||
<ul | ||
v-else-if="filteredExtensions.length > 0" | ||
class="grid grid-cols-1 lg:grid-cols-2 gap-2" | ||
> | ||
<a | ||
v-for="item in filteredExtensions" | ||
:key="item.name" | ||
:href="item.url" | ||
class="p-4 group bg-vp-c-bg transition-all flex flex-col rounded-lg border border-vp-c-border hover:border-vp-c-brand-2 animate-in fade-in-40 relative" | ||
> | ||
<h2 class="font-bold"> | ||
{{ item.name }} | ||
<span class="font-normal"> by </span> | ||
<span>{{ item.owner }}</span> | ||
</h2> | ||
<p class="text-vp-c-text-2 mb-2"> | ||
{{ item.description }} | ||
</p> | ||
<p class="text-vp-c-text-3 mt-auto flex flex-row"> | ||
<span class="mr-auto">{{ item.stars }} stars</span> | ||
<span | ||
class="group-hover:text-vp-c-brand-2 transition-colors" | ||
>View on GitHub</span | ||
> | ||
</p> | ||
</a> | ||
</ul> | ||
|
||
<!-- No Results Message --> | ||
<p v-else class="my-3">{{ noResultsMessage }}</p> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
name: "ExtensionsList", | ||
data() { | ||
return { | ||
extensions: [], // To store extensions data | ||
goModules: [], // To store go-modules data | ||
searchText: "", | ||
loading: false, | ||
searchMode: "extensions", // Default mode is 'extensions' | ||
error: null, // To store error message | ||
isCachedData: false, // Flag to indicate if we're showing cached data | ||
}; | ||
}, | ||
created() { | ||
this.fetchData(); // Fetch data for both categories on initial load | ||
this.updateTitle(); // Set the initial title based on default searchMode | ||
}, | ||
methods: { | ||
toggleSearchMode() { | ||
// Toggle between 'extensions' and 'go-modules' | ||
this.searchMode = this.searchMode === "extensions" ? "go-modules" : "extensions"; | ||
this.updateTitle(); // Update the title when searchMode changes | ||
}, | ||
async fetchData() { | ||
const cacheKey = "extensionsAndGoModulesData"; | ||
this.loading = true; | ||
this.error = null; // Reset error message before fetching data | ||
try { | ||
// Attempt to fetch data from the API | ||
const [extensionsResponse, goModulesResponse] = await Promise.all([ | ||
fetch("/api/extensions"), | ||
fetch("/api/go-modules") | ||
]); | ||
if (!extensionsResponse.ok || !goModulesResponse.ok) { | ||
throw new Error("Error fetching data from API"); | ||
} | ||
const extensionsData = await extensionsResponse.json(); | ||
const goModulesData = await goModulesResponse.json(); | ||
// Process and sort data | ||
this.extensions = extensionsData | ||
.map(item => ({ ...item, stars: Number(item.stars) })) | ||
.sort((a, b) => b.stars - a.stars); | ||
this.goModules = goModulesData | ||
.map(item => ({ ...item, stars: Number(item.stars) })) | ||
.sort((a, b) => b.stars - a.stars); | ||
// Cache the data if API request is successful (only if window is available) | ||
if (typeof window !== "undefined" && window.localStorage) { | ||
const currentTime = new Date().getTime(); | ||
localStorage.setItem(cacheKey, JSON.stringify({ | ||
extensions: this.extensions, | ||
goModules: this.goModules, | ||
timestamp: currentTime, | ||
})); | ||
} | ||
this.isCachedData = false; // No need to show cached data warning | ||
} catch (error) { | ||
console.error("Error fetching data:", error); | ||
this.error = "Error reaching the API."; // Set error message | ||
// Check if there is cached data (only if window is available) | ||
if (typeof window !== "undefined" && window.localStorage) { | ||
const cachedData = JSON.parse(localStorage.getItem(cacheKey)); | ||
if (cachedData) { | ||
this.extensions = cachedData.extensions; | ||
this.goModules = cachedData.goModules; | ||
this.isCachedData = true; // Indicate cached data is being used | ||
} | ||
} | ||
} finally { | ||
this.loading = false; | ||
} | ||
}, | ||
updateTitle() { | ||
// Dynamically set the tab title based on the current search mode | ||
const title = this.searchMode === "extensions" | ||
? "Extensions | Gate Proxy" | ||
: "Minekube Libraries | Gate Proxy"; | ||
document.title = title; | ||
}, | ||
}, | ||
computed: { | ||
filteredExtensions() { | ||
const data = this.searchMode === "extensions" ? this.extensions : this.goModules; | ||
return data.filter((item) => | ||
item.name.toLowerCase().includes(this.searchText.toLowerCase()) | ||
); | ||
}, | ||
noResultsMessage() { | ||
// If no results are found, show a generic message | ||
if (this.filteredExtensions.length === 0) { | ||
const message = this.searchMode === "extensions" | ||
? "No extensions found" | ||
: "No projects found"; | ||
return `${message}. Make sure you're typing the name correctly, or try again later.`; | ||
} | ||
return ""; | ||
}, | ||
}, | ||
watch: { | ||
// Watch for changes in searchMode and update the title accordingly | ||
searchMode() { | ||
this.updateTitle(); | ||
}, | ||
}, | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
title: "Extensions" # This is technically overridden by the updateTitle function on line 145 in Extensions.vue | ||
sidebar: false | ||
layout: Extensions | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"include": [ | ||
"/api/extensions", | ||
"/api/go-modules" | ||
], | ||
"exclude": [ | ||
"/*" | ||
] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// functions/api/extensions.js | ||
|
||
const CACHE_DURATION = 60 * 60; // Cache duration in seconds | ||
|
||
export async function onRequest(context) { | ||
const githubApiUrl = 'https://api.github.com/search/repositories?q=topic:gate-extension&sort=stars&order=desc'; | ||
const cacheKey = 'gate-extension-repositories'; | ||
|
||
// Access the KV namespace and GitHub token from context.env | ||
const GITHUB_CACHE = context.env.GITHUB_CACHE; | ||
const githubToken = context.env.GITHUB_TOKEN; | ||
|
||
// Check for cached data | ||
const cachedResponse = await GITHUB_CACHE.get(cacheKey); | ||
if (cachedResponse) { | ||
return new Response(cachedResponse, { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
}); | ||
} | ||
|
||
try { | ||
// Fetch data from GitHub API | ||
const response = await fetch(githubApiUrl, { | ||
headers: { | ||
'Accept': 'application/vnd.github.v3+json', | ||
'Authorization': `token ${githubToken}`, | ||
'User-Agent': 'CloudflarePagesGateExtension/1.0 (+https://developers.cloudflare.com/pages)', | ||
}, | ||
}); | ||
|
||
if (!response.ok) { | ||
return new Response('Error fetching data from GitHub API', { status: 500 }); | ||
} | ||
|
||
const data = await response.json(); | ||
const libraries = data.items.map((item) => ({ | ||
name: item.name, | ||
owner: item.owner.login, | ||
description: item.description, | ||
stars: item.stargazers_count, | ||
url: item.html_url, | ||
})); | ||
|
||
// Cache the response | ||
await GITHUB_CACHE.put(cacheKey, JSON.stringify(libraries), { expirationTtl: CACHE_DURATION }); | ||
|
||
return new Response(JSON.stringify(libraries), { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
'Access-Control-Allow-Methods': 'GET', | ||
'Access-Control-Allow-Headers': 'Content-Type', | ||
}, | ||
}); | ||
} catch (error) { | ||
return new Response(`Error fetching data: ${error}`, { status: 500 }); | ||
} | ||
} |
Oops, something went wrong.