Skip to content
This repository has been archived by the owner on Aug 3, 2024. It is now read-only.

Commit

Permalink
Use server routes to supply client data
Browse files Browse the repository at this point in the history
Re-building and re-deploying the site every thirty minutes just to
update data is very expensive and hurts user experience by constantly
blowing out the client's cache without meaningful changes to the bundle.
Even ignoring that, hardcoding changing data, even though it would be
rebuilt eventually is considered a very bad practice. Overall, this all
feels like a terrible hack.

This commit should address this issue by removing client data generation
logic and instead creating the supply logic using Nitro server cached
routes that will refresh on the server every thirty minutes unless
there's a re-deploy due to actual changes.
  • Loading branch information
brawaru committed Jun 13, 2023
1 parent 177a487 commit 107d5b7
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 156 deletions.
13 changes: 13 additions & 0 deletions composables/homepage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const homepageRefreshInterval = 60 * 10 // 10 minutes

export async function useHomepageProjects() {
const state = useNuxtApp().$homepageState
const projects = state.projects
const lastFetchedAt = state.lastFetchedAt.value
const now = Date.now() / 1000
if (now - lastFetchedAt >= homepageRefreshInterval) {
await projects.refresh()
state.lastFetchedAt.value = now
}
return projects.data
}
63 changes: 0 additions & 63 deletions composables/tag.js

This file was deleted.

62 changes: 62 additions & 0 deletions composables/tag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
export function useTags() {
return useFetch('/api/tags', {
transform(data) {
return {
...data,
projectTypes: [
{
actual: 'mod',
id: 'mod',
display: 'mod',
},
{
actual: 'mod',
id: 'plugin',
display: 'plugin',
},
{
actual: 'mod',
id: 'datapack',
display: 'data pack',
},
{
actual: 'shader',
id: 'shader',
display: 'shader',
},
{
actual: 'resourcepack',
id: 'resourcepack',
display: 'resource pack',
},
{
actual: 'modpack',
id: 'modpack',
display: 'modpack',
},
],
loaderData: {
pluginLoaders: ['bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'folia'],
pluginPlatformLoaders: ['bungeecord', 'waterfall', 'velocity'],
allPluginLoaders: [
'bukkit',
'spigot',
'paper',
'purpur',
'sponge',
'bungeecord',
'waterfall',
'velocity',
'folia',
],
dataPackLoaders: ['datapack'],
modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift'],
},
projectViewModes: ['list', 'grid', 'gallery'],
approvedStatuses: ['approved', 'archived', 'unlisted', 'private'],
rejectedStatuses: ['rejected', 'withheld'],
staffRoles: ['moderator', 'admin'],
}
},
})
}
68 changes: 0 additions & 68 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,74 +102,6 @@ export default defineNuxtConfig({
],
},
hooks: {
async 'build:before'() {
// 30 minutes
const TTL = 30 * 60 * 1000

let state: {
lastGenerated?: string
apiUrl?: string
categories?: any[]
loaders?: any[]
gameVersions?: any[]
donationPlatforms?: any[]
reportTypes?: any[]
} = {}
let homePageProjects: any[] = []

try {
state = JSON.parse(await fs.readFile('./generated/state.json', 'utf8'))
homePageProjects = JSON.parse(await fs.readFile('./generated/homepage.json', 'utf8'))
} catch {
// File doesn't exist, create folder
await fs.mkdir('./generated', { recursive: true })
}

const API_URL = getApiUrl()

if (
// Skip regeneration if within TTL...
state.lastGenerated &&
new Date(state.lastGenerated).getTime() + TTL > new Date().getTime() &&
// ...but only if the API URL is the same
state.apiUrl === API_URL &&
homePageProjects.length !== 0
) {
return
}

state.lastGenerated = new Date().toISOString()

state.apiUrl = API_URL

const headers = {
headers: {
'user-agent': 'Knossos generator ([email protected])',
},
}

const [categories, loaders, gameVersions, donationPlatforms, reportTypes, projects] =
await Promise.all([
$fetch(`${API_URL}tag/category`, headers),
$fetch(`${API_URL}tag/loader`, headers),
$fetch(`${API_URL}tag/game_version`, headers),
$fetch(`${API_URL}tag/donation_platform`, headers),
$fetch(`${API_URL}tag/report_type`, headers),
$fetch(`${API_URL}projects_random?count=40`, headers),
])

state.categories = categories
state.loaders = loaders
state.gameVersions = gameVersions
state.donationPlatforms = donationPlatforms
state.reportTypes = reportTypes
homePageProjects = projects

await fs.writeFile('./generated/state.json', JSON.stringify(state))
await fs.writeFile('./generated/homepage.json', JSON.stringify(homePageProjects))

console.log('Tags generated!')
},
'pages:extend'(routes) {
routes.splice(
routes.findIndex((x) => x.name === 'search-searchProjectType'),
Expand Down
40 changes: 21 additions & 19 deletions pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -525,31 +525,33 @@ import PrismLauncherLogo from '~/assets/images/external/prism.svg'
import ATLauncherLogo from '~/assets/images/external/atlauncher.svg'
import Avatar from '~/components/ui/Avatar.vue'
import ProjectCard from '~/components/ui/ProjectCard.vue'
import homepageProjects from '~/generated/homepage.json'
const searchQuery = ref('better')
const sortType = ref('relevance')
const [{ data: searchProjects, refresh: updateSearchProjects }, { data: notifications }] =
await Promise.all([
useAsyncData(
'demoSearchProjects',
() => useBaseFetch(`search?limit=3&query=${searchQuery.value}&index=${sortType.value}`),
{
transform: (result) => result.hits,
}
),
useAsyncData('updatedProjects', () => useBaseFetch(`search?limit=3&query=&index=updated`), {
const [
$homepageProjects,
{ data: searchProjects, refresh: updateSearchProjects },
{ data: notifications },
] = await Promise.all([
useHomepageProjects(),
useAsyncData(
'demoSearchProjects',
() => useBaseFetch(`search?limit=3&query=${searchQuery.value}&index=${sortType.value}`),
{
transform: (result) => result.hits,
}),
])
const val = Math.ceil(homepageProjects.length / 3)
const rows = shallowRef([
homepageProjects.slice(0, val),
homepageProjects.slice(val, val * 2),
homepageProjects.slice(val * 2, val * 3),
}
),
useAsyncData('updatedProjects', () => useBaseFetch(`search?limit=3&query=&index=updated`), {
transform: (result) => result.hits,
}),
])
const rows = computed(() => {
const projects = $homepageProjects.value
const val = Math.ceil(projects.length / 3)
return [projects.slice(0, val), projects.slice(val, val * 2), projects.slice(val * 2, val * 3)]
})
</script>

<style lang="scss" scoped>
Expand Down
18 changes: 12 additions & 6 deletions plugins/2.state.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
export default defineNuxtPlugin(async (nuxtApp) => {
export default defineNuxtPlugin(async () => {
const authStore = await useAuth()
await useUser()
const cosmeticsStore = useCosmetics()
const tagsStore = useTags()
const tagsStore = await useTags()

nuxtApp.provide('auth', authStore.value)
nuxtApp.provide('cosmetics', cosmeticsStore.value)
nuxtApp.provide('tag', tagsStore.value)
nuxtApp.provide('notify', (notif) => addNotification(notif))
return {
provide: {
auth: authStore.value,
cosmetics: cosmeticsStore.value,
tag: tagsStore.data.value,
notify(value) {
addNotification(value)
},
},
}
})
9 changes: 9 additions & 0 deletions plugins/homepage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Provider for the `useHomepageProjects` composable.
export default defineNuxtPlugin(() => ({
provide: {
homepageState: {
projects: useFetch('/api/homepage'),
lastFetchedAt: useState('homepageLastFetchedAt', () => Date.now() / 1000),
},
},
}))
16 changes: 16 additions & 0 deletions server/api/homepage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default defineCachedEventHandler(
() => {
const { apiBaseUrl } = useRuntimeConfig()

return $fetch(`/projects_random?count=40`, {
baseURL: apiBaseUrl,
headers: {
'user-agent': 'Nuxt Knossos ([email protected])',
},
})
},
{
maxAge: 30 * 60,
name: 'homepage',
}
)
33 changes: 33 additions & 0 deletions server/api/tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export default defineCachedEventHandler(
async () => {
const { apiBaseUrl } = useRuntimeConfig()

const opts = {
baseURL: apiBaseUrl,
headers: {
'user-agent': 'Nuxt Knossos ([email protected])',
},
}

const [categories, loaders, gameVersions, donationPlatforms, reportTypes] = await Promise.all([
$fetch(`tag/category`, opts),
$fetch(`tag/loader`, opts),
$fetch(`tag/game_version`, opts),
$fetch(`tag/donation_platform`, opts),
$fetch(`tag/report_type`, opts),
])

return {
categories,
loaders,
gameVersions,
donationPlatforms,
reportTypes,
}
},
{
// 30 min in seconds
maxAge: 30 * 60,
name: 'tags',
}
)

0 comments on commit 107d5b7

Please sign in to comment.