mirror of
https://github.com/modrinth/code.git
synced 2026-08-29 11:04:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcf469ef91 | ||
|
|
1bee2f83d8 | ||
|
|
7823f83d04 | ||
|
|
a6a9139019 | ||
|
|
b9c3b363e8 | ||
|
|
0ab9100c46 | ||
|
|
a1b73d089e | ||
|
|
c2cbf16434 | ||
|
|
ff4c9be910 | ||
|
|
98f8bbcad9 | ||
|
|
a9b774005b |
@@ -73,7 +73,7 @@ jobs:
|
||||
- name: Build frontend
|
||||
run: pnpm web:build
|
||||
env:
|
||||
NITRO_PRESET: node-server
|
||||
NITRO_PRESET: node_cluster
|
||||
BUILD_ENV: ${{ steps.meta.outputs.env }}
|
||||
CF_PAGES_BRANCH: ${{ github.ref_name }}
|
||||
CF_PAGES_COMMIT_SHA: ${{ github.sha }}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@sentry/vue": "^8.27.0",
|
||||
"@sfirew/minecraft-motd-parser": "^1.1.6",
|
||||
"@tanstack/vue-query": "5.90.7",
|
||||
"@tanstack/vue-query": "5.101.4",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.4.5",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM node:24-slim
|
||||
FROM docker.io/tailscale/tailscale:latest AS tailscale
|
||||
|
||||
FROM node:24-trixie-slim
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/modrinth/code
|
||||
LABEL org.opencontainers.image.title=frontend
|
||||
@@ -11,6 +13,67 @@ RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends dumb-init \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Debug
|
||||
COPY --from=tailscale /usr/local/bin/tailscale /usr/local/bin/tailscaled /usr/local/bin/
|
||||
|
||||
COPY --chmod=0755 <<'EOF' /usr/local/bin/entrypoint.sh
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
TAILSCALE_DIR=/tmp/tailscale
|
||||
TAILSCALE_SOCKET="$TAILSCALE_DIR/tailscaled.sock"
|
||||
|
||||
start_tailscale() {
|
||||
echo "entrypoint: starting tailscale as euid=$(id -u) groups=$(id -G)" >&2
|
||||
mkdir -p "$TAILSCALE_DIR"
|
||||
|
||||
# Userspace networking needs no TUN device or extra capabilities.
|
||||
# --state=mem: registers an ephemeral node; --statedir gives the SSH host keys
|
||||
# somewhere writable to live, which mem: on its own does not.
|
||||
tailscaled \
|
||||
--tun=userspace-networking \
|
||||
--state=mem: \
|
||||
--statedir="$TAILSCALE_DIR" \
|
||||
--socket="$TAILSCALE_SOCKET" &
|
||||
|
||||
waited=0
|
||||
while [ ! -S "$TAILSCALE_SOCKET" ]; do
|
||||
if [ "$waited" -ge 50 ]; then
|
||||
echo "entrypoint: tailscaled socket never appeared" >&2
|
||||
return 1
|
||||
fi
|
||||
waited=$((waited + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
if [ -n "${TAILSCALE_HOSTNAME:-}" ]; then
|
||||
node_hostname="$TAILSCALE_HOSTNAME"
|
||||
elif [ -n "${BUNNYNET_MC_PODID:-}" ] && [ -n "${BUNNYNET_MC_REGION:-}" ]; then
|
||||
node_hostname="frontend-$BUNNYNET_MC_PODID-$BUNNYNET_MC_REGION"
|
||||
else
|
||||
node_hostname="frontend-$(hostname)"
|
||||
fi
|
||||
|
||||
tailscale --socket="$TAILSCALE_SOCKET" up \
|
||||
--authkey="$TAILSCALE_AUTH_KEY" \
|
||||
--hostname="$node_hostname" \
|
||||
--accept-dns=false \
|
||||
--timeout=30s \
|
||||
--ssh
|
||||
}
|
||||
|
||||
if [ -n "${TAILSCALE_AUTH_KEY:-}" ]; then
|
||||
start_tailscale || echo "entrypoint: tailscale setup failed, serving without SSH" >&2
|
||||
fi
|
||||
|
||||
# Only drop if we actually started as root; some runtimes pin their own uid.
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
exec setpriv --reuid=node --regid=node --init-groups -- "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
EOF
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=3000
|
||||
@@ -20,11 +83,9 @@ WORKDIR /app
|
||||
# Nitro bundles every runtime dependency into .output, so no node_modules is needed.
|
||||
COPY --chown=node:node .output ./.output
|
||||
|
||||
USER node
|
||||
# Stays root so tailscaled can setgroups/setuid for SSH sessions; the entrypoint
|
||||
# drops the server itself to the node user.
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
||||
CMD node -e "fetch('http://127.0.0.1:'+process.env.PORT+'/robots.txt').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"
|
||||
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
ENTRYPOINT ["dumb-init", "--", "/usr/local/bin/entrypoint.sh"]
|
||||
CMD ["node", "/app/.output/server/index.mjs"]
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@sentry/nuxt": "^10.33.0",
|
||||
"@tanstack/vue-query": "5.90.7",
|
||||
"@tanstack/vue-query": "5.101.4",
|
||||
"@types/three": "^0.172.0",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue-email/components": "^0.0.21",
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
import { useAuth } from './composables/auth'
|
||||
|
||||
const auth = await useAuth()
|
||||
const userPreferences = setupProviders(auth)
|
||||
const { userPreferences } = setupProviders(auth)
|
||||
const cosmetics = useCosmetics()
|
||||
const theme = useTheme()
|
||||
const { locale, setLocale } = injectI18n()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ISO3166, Labrinth } from '@modrinth/api-client'
|
||||
import type { DisplayProjectType } from '@modrinth/utils'
|
||||
import type { ShallowRef } from 'vue'
|
||||
|
||||
import {
|
||||
apiUrl,
|
||||
@@ -155,11 +156,23 @@ const generatedState = shallowRef<GeneratedState>(
|
||||
}) as GeneratedState,
|
||||
)
|
||||
|
||||
/**
|
||||
* Non-reactive server-side view of the state above.
|
||||
*
|
||||
* `generatedState` lives for the lifetime of the isolate, so every per-request `computed()` that
|
||||
* reads it registers a subscriber link on its dep. SSR never unmounts, so those links are never
|
||||
* released, and each one pins the whole request graph that created it. The data is frozen and
|
||||
* `setGameVersions` is client-only, so the server has nothing to react to.
|
||||
*/
|
||||
const serverGeneratedState = {
|
||||
value: generatedState.value,
|
||||
} as unknown as ShallowRef<GeneratedState>
|
||||
|
||||
/**
|
||||
* Composable for accessing the globally used generated state.
|
||||
* This includes both fetched data and runtime-defined constants.
|
||||
*/
|
||||
export const useGeneratedState = () => generatedState
|
||||
export const useGeneratedState = () => (import.meta.server ? serverGeneratedState : generatedState)
|
||||
|
||||
/**
|
||||
* Replaces the build-time game versions with a freshly fetched list. Client-only: mutating this
|
||||
|
||||
@@ -110,7 +110,6 @@ import {
|
||||
commonMessages,
|
||||
defineMessage,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
LoadingBar,
|
||||
normalizeChildren,
|
||||
@@ -131,10 +130,10 @@ import { getSignInRouteObj } from '~/composables/auth.js'
|
||||
import { setupProviders } from '~/providers/setup.ts'
|
||||
|
||||
const auth = await useAuth()
|
||||
setupProviders(auth)
|
||||
const { notificationManager } = setupProviders(auth)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { addNotification } = notificationManager
|
||||
const isSwitchingAccount = useIsSwitchingAccount()
|
||||
|
||||
const props = defineProps({
|
||||
|
||||
@@ -970,13 +970,10 @@ const showTinMismatchBanner = computed(() => {
|
||||
|
||||
const PRIDE_COLLECTION_ID = 'M4c3ITvd'
|
||||
const PRIDE_ARTICLE_SLUGS = ['pride-campaign-2025', 'pride-campaign-2026', 'proud-of-you-2026']
|
||||
const PRIDE_CACHE_TIME = 1000 * 60 * 60 * 24
|
||||
|
||||
const { data: prideCollection } = useQuery({
|
||||
queryKey: computed(() => ['collection', PRIDE_COLLECTION_ID]),
|
||||
queryFn: () => client.labrinth.collections.get(PRIDE_COLLECTION_ID),
|
||||
staleTime: PRIDE_CACHE_TIME,
|
||||
gcTime: PRIDE_CACHE_TIME,
|
||||
})
|
||||
|
||||
const prideProjectIds = computed(() => new Set(prideCollection.value?.projects ?? []))
|
||||
|
||||
@@ -29,6 +29,9 @@ export default defineNuxtPlugin((nuxt) => {
|
||||
if (import.meta.server) {
|
||||
nuxt.hooks.hook('app:rendered', () => {
|
||||
vueQueryState.value = dehydrate(queryClient)
|
||||
|
||||
// Hack to prevent memory leak when gcTime is being set on the server side.
|
||||
queryClient.clear()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ export function setupProviders(auth: Awaited<ReturnType<typeof useAuth>>) {
|
||||
setupLoadingStateProvider()
|
||||
setupUserCountryProvider()
|
||||
|
||||
return userPreferences
|
||||
return { userPreferences, notificationManager }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export default defineNitroPlugin((nitroApp) => {
|
||||
nitroApp.hooks.hook('error', async (error, { event }) => {
|
||||
const statusCode = (error as { statusCode?: number }).statusCode ?? 500
|
||||
if (statusCode < 500) return
|
||||
|
||||
console.error(`[Context Error] at ${event?.path}:`, error)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"@modrinth/assets": "workspace:*",
|
||||
"@modrinth/blog": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@tanstack/vue-query": "5.90.7",
|
||||
"@tanstack/vue-query": "5.101.4",
|
||||
"@tresjs/cientos": "^4.3.0",
|
||||
"@tresjs/core": "^4.3.4",
|
||||
"@tresjs/post-processing": "^2.4.0",
|
||||
|
||||
Generated
+20
-20
@@ -102,8 +102,8 @@ importers:
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6
|
||||
'@tanstack/vue-query':
|
||||
specifier: 5.90.7
|
||||
version: 5.90.7(vue@3.5.27(typescript@5.9.3))
|
||||
specifier: 5.101.4
|
||||
version: 5.101.4(vue@3.5.27(typescript@5.9.3))
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2.5.0
|
||||
version: 2.10.1
|
||||
@@ -300,8 +300,8 @@ importers:
|
||||
specifier: ^10.33.0
|
||||
version: 10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)(magicast@0.5.1)(nuxt@3.20.2(@parcel/watcher@2.5.6)(@types/node@24.12.2)(@vue/compiler-sfc@3.5.27)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.2(jiti@2.6.1))(ioredis@5.9.2)(lightningcss@1.32.0)(magicast@0.5.1)(optionator@0.9.4)(rolldown@1.0.0-rc.12)(rollup@4.57.1)(sass@1.97.3)(terser@5.46.0)(typescript@5.9.3)(vite@8.0.3(@types/node@24.12.2)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue-tsc@2.2.12(typescript@5.9.3))(xml2js@0.6.2)(yaml@2.8.2))(rollup@4.57.1)(vue@3.5.27(typescript@5.9.3))
|
||||
'@tanstack/vue-query':
|
||||
specifier: 5.90.7
|
||||
version: 5.90.7(vue@3.5.27(typescript@5.9.3))
|
||||
specifier: 5.101.4
|
||||
version: 5.101.4(vue@3.5.27(typescript@5.9.3))
|
||||
'@types/three':
|
||||
specifier: ^0.172.0
|
||||
version: 0.172.0
|
||||
@@ -671,8 +671,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../utils
|
||||
'@tanstack/vue-query':
|
||||
specifier: 5.90.7
|
||||
version: 5.90.7(vue@3.5.27(typescript@5.9.3))
|
||||
specifier: 5.101.4
|
||||
version: 5.101.4(vue@3.5.27(typescript@5.9.3))
|
||||
'@tresjs/cientos':
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.1(@tresjs/core@4.3.6(three@0.172.0)(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3)))(@types/three@0.172.0)(react@19.2.8)(three@0.172.0)(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3))
|
||||
@@ -721,12 +721,12 @@ importers:
|
||||
es-toolkit:
|
||||
specifier: ^1.44.0
|
||||
version: 1.44.0
|
||||
flatpickr:
|
||||
specifier: ^4.6.13
|
||||
version: 4.6.13
|
||||
fabric:
|
||||
specifier: ^7.4.0
|
||||
version: 7.4.0
|
||||
flatpickr:
|
||||
specifier: ^4.6.13
|
||||
version: 4.6.13
|
||||
floating-vue:
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2(@nuxt/kit@3.21.0(magicast@0.5.1))(vue@3.5.27(typescript@5.9.3))
|
||||
@@ -4453,11 +4453,11 @@ packages:
|
||||
resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/query-core@5.90.7':
|
||||
resolution: {integrity: sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==}
|
||||
'@tanstack/query-core@5.101.4':
|
||||
resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==}
|
||||
|
||||
'@tanstack/vue-query@5.90.7':
|
||||
resolution: {integrity: sha512-2h0esebc2qVRVDge3gFArhss5+qn/Wb4abXuaEliSy+/xPWQMrEX7Ny0UKkCy1HcMZZjvfTtNRUfpYrx+vpipw==}
|
||||
'@tanstack/vue-query@5.101.4':
|
||||
resolution: {integrity: sha512-UYjkUZhnWQIFGNb7SdgjCitAftNyYQfOIVUh6vBUQAG4SQKNMSBKeQERFDubpxLAMpIBJTaOrE8fM4c1kZcIGQ==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.1.2
|
||||
vue: ^2.6.0 || ^3.3.0
|
||||
@@ -10381,8 +10381,8 @@ packages:
|
||||
vue-component-type-helpers@3.2.4:
|
||||
resolution: {integrity: sha512-05lR16HeZDcDpB23ku5b5f1fBOoHqFnMiKRr2CiEvbG5Ux4Yi0McmQBOET0dR0nxDXosxyVqv67q6CzS3AK8rw==}
|
||||
|
||||
vue-component-type-helpers@3.3.10:
|
||||
resolution: {integrity: sha512-t7IQivQ3oD4D01b7s7a9AWzHAcr3DGBIa/1jZREsFQJcFgSL92gqUkqNiHTYgzTt7QDuJa0I9wCeDwEtZICoBQ==}
|
||||
vue-component-type-helpers@3.3.11:
|
||||
resolution: {integrity: sha512-LwcxzeliO9fkQcpJG0PoX8X5kmAhKmH9wkpDLxNabwzkQ9Zeib2YVHwFV4pcWmMLfXVfjr/dSV+DaJ3cIPgSNA==}
|
||||
|
||||
vue-confetti-explosion@1.0.2:
|
||||
resolution: {integrity: sha512-80OboM3/6BItIoZ6DpNcZFqGpF607kjIVc5af56oKgtFmt5yWehvJeoYhkzYlqxrqdBe0Ko4Ie3bWrmLau+dJw==}
|
||||
@@ -14128,7 +14128,7 @@ snapshots:
|
||||
storybook: 10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||
type-fest: 2.19.0
|
||||
vue: 3.5.27(typescript@5.9.3)
|
||||
vue-component-type-helpers: 3.3.10
|
||||
vue-component-type-helpers: 3.3.11
|
||||
|
||||
'@stripe/stripe-js@7.9.0': {}
|
||||
|
||||
@@ -14255,12 +14255,12 @@ snapshots:
|
||||
dependencies:
|
||||
remove-accents: 0.5.0
|
||||
|
||||
'@tanstack/query-core@5.90.7': {}
|
||||
'@tanstack/query-core@5.101.4': {}
|
||||
|
||||
'@tanstack/vue-query@5.90.7(vue@3.5.27(typescript@5.9.3))':
|
||||
'@tanstack/vue-query@5.101.4(vue@3.5.27(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@tanstack/match-sorter-utils': 8.19.4
|
||||
'@tanstack/query-core': 5.90.7
|
||||
'@tanstack/query-core': 5.101.4
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.27(typescript@5.9.3)
|
||||
vue-demi: 0.14.10(vue@3.5.27(typescript@5.9.3))
|
||||
@@ -21551,7 +21551,7 @@ snapshots:
|
||||
|
||||
vue-component-type-helpers@3.2.4: {}
|
||||
|
||||
vue-component-type-helpers@3.3.10: {}
|
||||
vue-component-type-helpers@3.3.11: {}
|
||||
|
||||
vue-confetti-explosion@1.0.2(vue@3.5.27(typescript@5.9.3)):
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user