Compare commits

..

64 Commits

Author SHA1 Message Date
Starystars67 017c8708d8 Update logo URL for Connect Hosting 2026-05-27 16:59:56 +01:00
Starystars67 d2ec932153 Bump package version to 2.4.22 & address vulns 2026-05-16 13:45:14 -05:00
Starystars67 38213e1a50 Remove Horizon Hosting and Vertuo Hosting entries
Removed entries for Horizon Hosting and Vertuo Hosting from partners.json.
2026-05-16 18:12:51 +01:00
Mackenzie d879a2995f fix typos in partners config 2026-05-02 11:11:06 +01:00
Starystars67 c2e37eea5a fix flags for languages 2026-04-14 16:45:18 +01:00
Starystars67 13e36d5bc3 fix 2 high sev vulns & bump version 2026-04-14 16:19:34 +01:00
Starystars67 b2cbc7239b move flags to public 2026-04-14 16:18:22 +01:00
Starystars67 f203dfcbf5 Bump version 2026-04-14 16:18:22 +01:00
Starystars67 58540763d2 Add i18n support for zh 2026-04-14 16:18:22 +01:00
Tangzixy 1b9d2a0b87 Translate zh.json via GitLocalize 2026-04-14 16:18:22 +01:00
Ace06_10010 fa3059c09e Translate zh.json via GitLocalize 2026-04-14 16:18:22 +01:00
trebor8820 7a840ac233 Update partners.json
Updated link for host havoc
2026-04-14 16:17:26 +01:00
Starystars67 0322221529 Move back to local served installer 2026-04-13 09:52:01 +01:00
Starystars67 9066e31c55 Use nginx:stable, add nginx config, non-root
Switch the runtime stage to nginx:stable (replacing alpine), remove the default static assets, and copy the built app into /usr/share/nginx/html. Add a custom nginx main config (nginx.main.conf → /etc/nginx/nginx.conf) and keep the default site config. Run the container as the non-root nginx user and expose port 80. These changes ensure the image serves the intended build, uses a stable nginx variant, and improves container security.
2026-04-13 00:54:51 +01:00
Starystars67 d19a10c08b correct actions workflows 2026-04-13 00:27:00 +01:00
Starystars67 a90e60ef32 Merge pull request #25 from BeamMP/dev 2026-04-13 00:19:08 +01:00
Starystars67 070fb38709 chang to node lts and correct json structure 2026-04-13 00:17:41 +01:00
Starystars67 ed1cb0e2a8 Cleanup 2026-04-13 00:08:22 +01:00
Starystars67 bedcd0cb50 Merge remote-tracking branch 'origin/main' into dev 2026-04-13 00:05:35 +01:00
Starystars67 2987de52ec 2.4.16 2026-04-12 23:55:03 +01:00
Starystars67 aaf888f182 Update release workflow by removing CA cert step
Removed installation of registry CA certificate and updated image tag from 'prod' to 'latest'.
2026-04-12 23:37:22 +01:00
Starystars67 052da9316c deperation.... 2026-04-02 03:35:42 +01:00
Starystars67 842e229e55 add zip to test download 2026-04-02 03:22:38 +01:00
Starystars67 b2742428d0 Test 2026-04-02 03:09:16 +01:00
Starystars67 48717eeabd Update nginx.conf 2026-04-02 02:53:16 +01:00
Starystars67 5f14414055 trying things at this point 2026-04-02 02:48:57 +01:00
Starystars67 a253919575 server hardening + deperation to tget the installer served 2026-04-02 02:42:55 +01:00
Starystars67 4626a69185 Serve .msi as download and update installer link
Add nginx rule to serve .msi files as application/octet-stream with Content-Disposition: attachment and X-Content-Type-Options: nosniff, returning 404 when missing. Update Home.vue to build the installer download URL from import.meta.env.BASE_URL and bind it to the CTA link (using :href) instead of a hardcoded path, ensuring the installer link respects the app base URL.
2026-04-02 02:13:11 +01:00
Starystars67 44268d076d Use imported images and URL helper
Replace hardcoded asset paths with bundler-friendly imports and a URL helper. Added getFlagUrl in LanguageSelector.vue to resolve flag assets via import.meta.url and replaced string-based src attributes. In Home.vue, import landingLq and landingHq and use them for initial and high-quality hero image loading to avoid direct '/src/...' paths and ensure proper asset resolution.
2026-04-02 01:34:31 +01:00
Starystars67 c2e460b408 Move landing images to src/assets
Rename public/landing-lq.jpg and public/landing_hq.png to src/assets and update Home.vue to use the new asset paths for loading the high-quality hero image (img.src and heroImageSrc). This ensures the images are managed by the app's asset pipeline.
2026-04-02 00:07:19 +01:00
Starystars67 c827c81d28 Fix metrics 2026-04-02 00:02:23 +01:00
Starystars67 5f9aad80ac mv flags 2026-04-01 23:55:16 +01:00
Starystars67 be9bae93de Use backend address. 2026-04-01 23:46:03 +01:00
Starystars67 5295ec664b Add docker-compose and MSI installer; fix paths
Add docker-compose.yml to run the website container (binds 127.0.0.1:3000 -> 80). Add BeamMP_Installer.msi to public/installer. Update src/views/Home.vue to use /landing-lq.jpg for the hero image (instead of /src/assets/landing-lq.jpg) and change the installer download link from BeamMP_Installer.zip to BeamMP_Installer.msi.
2026-04-01 22:28:32 +01:00
Starystars67 f59b5df887 remove mobile details plus try fix image 2026-04-01 22:00:48 +01:00
Starystars67 cf3c3790b5 changes for temp launch 2026-04-01 21:30:37 +01:00
Starystars67 d24ec58185 bumped versions 2025-11-30 12:15:19 +00:00
Starystars67 a3f47c5b12 Update versions of launcher 2025-09-14 14:56:01 +01:00
Starystars67 d5c33e56e5 update launcher to 2.5.1 2025-09-13 17:35:10 +01:00
Starystars67 266c1470ba Update package.json 2025-02-10 10:15:53 +00:00
Starystars67 6fcf1d1f6f added build pipelines 2025-02-08 20:58:57 +00:00
Starystars67 30eb6b96e0 add dockerignore 2025-02-08 20:56:42 +00:00
Starystars67 ebb7611a92 update to include installer :( 2025-02-08 20:44:03 +00:00
Starystars67 77c71a501f update served version of launcher 2025-02-08 20:43:14 +00:00
Starystars67 c5988f94b0 Bump version of launcher and update some packages to solve vulnerabilities 2024-11-12 16:35:20 +00:00
alex.ita d1150ed84d Update index.ejs
404 fix
2024-01-17 19:58:07 +00:00
Starystars67 5dfb7b79d7 bump build version 2024-01-17 19:42:09 +00:00
Starystars67 19abd94069 Merge pull request #18 from AlexITA1100/resize-fix
Resize fix
2024-01-17 19:39:58 +00:00
alex.ita c0f96b35de Merge branch 'BeamMP:main' into resize-fix 2024-01-17 20:38:36 +01:00
Starystars67 dedc16fb73 Merge pull request #16 from AlexITA1100/main
Made the website work when Javascript is disabled.
2024-01-17 19:36:38 +00:00
alex.ita f8d4478d35 Merge branch 'main' into main 2024-01-17 20:30:29 +01:00
alex.ita 738721119e Merge branch 'main' into resize-fix 2024-01-17 18:53:17 +01:00
Starystars67 b8a5db6d48 Merge pull request #19 from AlexITA1100/update-docs-link
Updated links to redirect to the docs instead of the wiki.
2024-01-17 12:03:35 +00:00
alex.ita cc48fe2ffc Update nav-nojs.ejs
Keeping the no-JS version in line with the resize fix.
2024-01-16 21:03:06 +01:00
alex.ita a307a3cd9f Updated links to docs. 2024-01-16 20:48:18 +01:00
alex.ita 9a2a10499d Updated links to redirect to the docs instead of the wiki. 2024-01-16 20:43:12 +01:00
alex.ita 3daa559a67 Updated for release.
Updated:
- Updated for v3.2.2 naming scheme changes.
- Made the links in `linux-builds.ejs` comply with [WCAG AAA standards](https://www.w3.org/WAI/WCAG22/quickref/#contrast-enhanced).
2024-01-16 20:31:58 +01:00
alex.ita e459538f18 Removed unused code 2024-01-11 13:50:06 +01:00
alex.ita 0065093ca3 Added route to new page.
Added route to Linux builds download page for JS-free users **ONLY**.
2024-01-09 11:14:16 +01:00
alex.ita 391f634d65 Changed Linux distro download option.
Moved from drop down menu to a new page.
This **will** need a new servlet in the backend.
2024-01-08 23:32:30 +01:00
alex.ita 838a33b4d5 Resize fix
Fixes the resize and zoom issues.
2024-01-06 12:07:56 +01:00
alex.ita 647ab8bda9 Updated to be future ready
Specified the architecture for Linux builds
2024-01-03 10:25:27 +01:00
alex.ita 8b37e854d8 CSS fix
Missed a comma
2023-12-18 10:16:17 +01:00
alex.ita 23ec17460e Made the website work when Javascript is disabled
Added:
- The website can now serve Javascript-free pages for user that have JS disabled or a browser that does not support it. The pages "/stats" and "/servers" cannot work without JS so the JS-fee index does not link to them;
- stats.ejs: if Javascript is disabled, won't serve any data but will prompt the user to enable Javascript.
Fixed:
- stats.ejs: copyright in the footer has been updated from "2021" to "2019 - present";
2023-12-16 19:26:02 +01:00
19 changed files with 745 additions and 305 deletions
+4 -11
View File
@@ -1,14 +1,12 @@
name: Build Docker image and push to release name: Build Docker image and push to release
on: on:
push: release:
# Sequence of patterns matched against refs/tags types: [published]
tags:
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
jobs: jobs:
docker: docker:
if: 'github.event.release' if: "!github.event.release.prerelease"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Connect to Tailscale - name: Connect to Tailscale
@@ -17,11 +15,6 @@ jobs:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci tags: tag:ci
- name: Install registry CA certificate
run: |
sudo mkdir -p /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}
echo "${{ secrets.REGISTRY_CA_CERT }}" | base64 -d | sudo tee /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}/ca.crt
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
@@ -38,4 +31,4 @@ jobs:
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
push: true push: true
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:prod tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest, ${{ secrets.REGISTRY_URL }}/beammp/website:production
+5 -12
View File
@@ -1,14 +1,12 @@
name: Build Docker image and push to release name: Build Docker image and push to prerelease
on: on:
push: release:
# Sequence of patterns matched against refs/tags types: [published]
tags:
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
jobs: jobs:
docker: docker:
if: 'github.event.prerelease' if: github.event.release.prerelease
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Connect to Tailscale - name: Connect to Tailscale
@@ -17,11 +15,6 @@ jobs:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci tags: tag:ci
- name: Install registry CA certificate
run: |
sudo mkdir -p /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}
echo "${{ secrets.REGISTRY_CA_CERT }}" | base64 -d | sudo tee /etc/docker/certs.d/${{ secrets.REGISTRY_URL }}/ca.crt
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
@@ -38,4 +31,4 @@ jobs:
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
push: true push: true
tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:dev tags: ${{ secrets.REGISTRY_URL }}/beammp/website:${{ github.REF_NAME }}, ${{ secrets.REGISTRY_URL }}/beammp/website:latest
+1 -1
View File
@@ -102,7 +102,6 @@ dist
# TernJS port file # TernJS port file
.tern-port .tern-port
*.exe
*.zip *.zip
# Logs # Logs
@@ -129,3 +128,4 @@ frontend/*.ntvs*
frontend/*.njsproj frontend/*.njsproj
frontend/*.sln frontend/*.sln
frontend/*.sw? frontend/*.sw?
.DS_Store
+11 -4
View File
@@ -1,22 +1,29 @@
# Step 1: Build stage # Step 1: Build stage
FROM node:22-alpine3.21 AS build FROM node:lts-alpine AS build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
RUN npm ci RUN npm ci
COPY . . COPY . .
ENV NODE_ENV=development ENV NODE_ENV=production
RUN npm run build RUN npm run build
# Step 2: Serve stage # Step 2: Serve stage
FROM nginx:alpine FROM nginx:stable
# Remove default nginx static assets
RUN rm -rf /usr/share/nginx/html/*
# Copy built files from the previous stage # Copy built files from the previous stage
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
# Add a custom Nginx configuration # Copy secure nginx configs
COPY nginx.main.conf /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
# Use non-root user for security
USER nginx
# Expose port 80 # Expose port 80
EXPOSE 80 EXPOSE 80
+8
View File
@@ -0,0 +1,8 @@
services:
website:
build:
dockerfile: Dockerfile
container_name: website
restart: unless-stopped
ports:
- "127.0.0.1:3000:80"
+33 -8
View File
@@ -2,21 +2,46 @@ server {
listen 80; listen 80;
server_name _; server_name _;
server_tokens off;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location / { # Baseline security hardening for a static SPA.
# SPA fallback: serve index.html for non-file routes so Vue Router can render NotFound # NOTE: add_header directives are NOT inherited by child location blocks that
try_files $uri $uri/ /index.html; # define their own add_header. To avoid silently dropping security headers,
# use the `expires` directive (not add_header Cache-Control) in location blocks.
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self' https://backend.beammp.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
# Installer directory: serve files directly; return 404 for anything missing.
location ^~ /installer/ {
try_files $uri =404;
} }
# Let real 404s for assets return 404s; Vue handles route-level 404 via the SPA fallback above. # Vite-hashed assets (JS, CSS, fonts, images): serve directly with a long cache.
# These files have content hashes in their names so stale cache is never an issue.
location /static/ { # Return 404 if a file is genuinely missing rather than silently serving index.html.
# Serve static files directly location ~* \.(js|css|woff2?|ttf|eot|svg|webp|avif|png|jpg|jpeg|gif|ico|map)$ {
expires max; try_files $uri =404;
expires 1y;
access_log off; access_log off;
} }
# Data / download files: serve directly, no client-side cache, 404 if missing.
location ~* \.(json|txt|exe|zip)$ {
try_files $uri =404;
expires -1;
access_log off;
}
# SPA fallback: all other paths go to index.html so Vue Router can render the
# correct view (including the NotFound page for unknown routes).
location / {
try_files $uri /index.html;
}
} }
+27
View File
@@ -0,0 +1,27 @@
worker_processes auto;
pid /tmp/nginx.pid;
error_log /tmp/error.log warn;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /tmp/access.log;
sendfile on;
keepalive_timeout 65;
server_tokens off;
# Redefine temp paths to writable tmpfs locations
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
include /etc/nginx/conf.d/*.conf;
}
+326 -226
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "beammp-website", "name": "beammp-website",
"private": true, "private": true,
"version": "2.4.15", "version": "2.4.22",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -35,4 +35,4 @@
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"vite": "^7.2.4" "vite": "^7.2.4"
} }
} }
Binary file not shown.
+4 -16
View File
@@ -1,10 +1,4 @@
[ [
{
"name": "Horizon Hosting",
"website": "https://hrzn.link/beammp",
"from": "$3.34/mo",
"logo": "https://hrznhosting.com/assets/logo.svg"
},
{ {
"name": "RackGenius", "name": "RackGenius",
"website": "https://rackgeni.us/beammp-plans", "website": "https://rackgeni.us/beammp-plans",
@@ -15,7 +9,7 @@
"name": "Connect Hosting", "name": "Connect Hosting",
"website": "https://connecthosting.net/beammp", "website": "https://connecthosting.net/beammp",
"from": "$1.49/mo", "from": "$1.49/mo",
"logo": "https://connecthosting.net/img/logo.webp" "logo": "https://connecthosting.net/logo.png"
}, },
{ {
"name": "Assetto Hosting", "name": "Assetto Hosting",
@@ -31,7 +25,7 @@
}, },
{ {
"name": "HostHavoc", "name": "HostHavoc",
"website": "https://hosthavoc.com", "website": "https://hosthavoc.com/game-servers/beammp",
"from": "$3.75/mo", "from": "$3.75/mo",
"logo": "https://hosthavoc.com/images/logo.svg" "logo": "https://hosthavoc.com/images/logo.svg"
}, },
@@ -48,23 +42,17 @@
"logo": "https://vyperhosting.com/assets/logo.png" "logo": "https://vyperhosting.com/assets/logo.png"
}, },
{ {
"name": "BisecHosting", "name": "BisectHosting",
"website": "https://bisecthosting.com/beammp-server-hosting", "website": "https://bisecthosting.com/beammp-server-hosting",
"from": "$5.99/mo", "from": "$5.99/mo",
"logo": "https://www.bisecthosting.com/_ipx/q_100&s_140x46/images/logo-dark-theme.svg" "logo": "https://www.bisecthosting.com/_ipx/q_100&s_140x46/images/logo-dark-theme.svg"
}, },
{ {
"name": "Four Season Hosting", "name": "Four Seasons Hosting",
"website": "https://fourseasonshosting.com", "website": "https://fourseasonshosting.com",
"from": "$3.00/mo", "from": "$3.00/mo",
"logo": "https://fourseasonshosting.com/_next/image?url=%2Flogo.png&w=256&q=75" "logo": "https://fourseasonshosting.com/_next/image?url=%2Flogo.png&w=256&q=75"
}, },
{
"name": "Vertuo Hosting",
"website": "https://vertuohosting.com",
"from": "$1.99/mo",
"logo": "https://vertuohosting.com/assets/img/logo.png"
},
{ {
"name": "Winheberg", "name": "Winheberg",
"website": "https://winheberg.fr/offres/gaming/beammp", "website": "https://winheberg.fr/offres/gaming/beammp",

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Before

Width:  |  Height:  |  Size: 16 MiB

After

Width:  |  Height:  |  Size: 16 MiB

+14 -14
View File
@@ -60,7 +60,7 @@ function localRoute(path) {
<div class="hidden xl:flex items-center gap-4"> <div class="hidden xl:flex items-center gap-4">
<NavigationMenu> <NavigationMenu>
<NavigationMenuList> <NavigationMenuList>
<NavigationMenuItem> <!--<NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<RouterLink <RouterLink
:to="localRoute('communities')" :to="localRoute('communities')"
@@ -74,7 +74,7 @@ function localRoute(path) {
{{ $t('message.nav.communities') }} {{ $t('message.nav.communities') }}
</RouterLink> </RouterLink>
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>-->
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<RouterLink <RouterLink
@@ -90,7 +90,7 @@ function localRoute(path) {
</RouterLink> </RouterLink>
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>
<NavigationMenuItem> <!--<NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<RouterLink <RouterLink
:to="localRoute('servers')" :to="localRoute('servers')"
@@ -104,8 +104,8 @@ function localRoute(path) {
{{ $t('message.nav.servers') }} {{ $t('message.nav.servers') }}
</RouterLink> </RouterLink>
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>-->
<NavigationMenuItem> <!--<NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<RouterLink <RouterLink
:to="localRoute('stats')" :to="localRoute('stats')"
@@ -119,8 +119,8 @@ function localRoute(path) {
{{ $t('message.nav.statistics') }} {{ $t('message.nav.statistics') }}
</RouterLink> </RouterLink>
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>-->
<NavigationMenuItem> <!--<NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<a <a
href="https://forum.beammp.com" href="https://forum.beammp.com"
@@ -137,7 +137,7 @@ function localRoute(path) {
{{ $t('message.nav.forums') }} {{ $t('message.nav.forums') }}
</a> </a>
</NavigationMenuLink> </NavigationMenuLink>
</NavigationMenuItem> </NavigationMenuItem>-->
<NavigationMenuItem> <NavigationMenuItem>
<NavigationMenuLink as-child> <NavigationMenuLink as-child>
<a <a
@@ -247,13 +247,13 @@ function localRoute(path) {
class="xl:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900" class="xl:hidden border-t border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900"
> >
<div class="px-4 py-3 space-y-1"> <div class="px-4 py-3 space-y-1">
<RouterLink <!--<RouterLink
:to="localRoute('communities')" :to="localRoute('communities')"
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors" class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
@click="closeMobileMenu" @click="closeMobileMenu"
> >
{{ $t('message.nav.communities') }} {{ $t('message.nav.communities') }}
</RouterLink> </RouterLink>-->
<RouterLink <RouterLink
:to="localRoute('partners')" :to="localRoute('partners')"
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors" class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
@@ -268,14 +268,14 @@ function localRoute(path) {
> >
{{ $t('message.nav.servers') }} {{ $t('message.nav.servers') }}
</RouterLink> </RouterLink>
<RouterLink <!--<RouterLink
:to="localRoute('stats')" :to="localRoute('stats')"
class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors" class="block px-4 py-3 text-neutral-900 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-md transition-colors"
@click="closeMobileMenu" @click="closeMobileMenu"
> >
{{ $t('message.nav.statistics') }} {{ $t('message.nav.statistics') }}
</RouterLink> </RouterLink>-->
<a <!--<a
href="https://forum.beammp.com" href="https://forum.beammp.com"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@@ -283,7 +283,7 @@ function localRoute(path) {
@click="closeMobileMenu" @click="closeMobileMenu"
> >
{{ $t('message.nav.forums') }} {{ $t('message.nav.forums') }}
</a> </a>-->
<a <a
href="https://docs.beammp.com" href="https://docs.beammp.com"
target="_blank" target="_blank"
+7 -2
View File
@@ -37,6 +37,11 @@ const toggleDropdown = () => {
isOpen.value = !isOpen.value isOpen.value = !isOpen.value
} }
const getFlagUrl = (flagName) => {
//return new URL(`../assets/flags/${flagName}.png`, import.meta.url).href
return `/flags/${flagName}.png`
}
onMounted(async () => { onMounted(async () => {
const saved = localStorage.getItem('lang') || getCurrentLocale() const saved = localStorage.getItem('lang') || getCurrentLocale()
if (saved && saved !== locale.value) { if (saved && saved !== locale.value) {
@@ -60,7 +65,7 @@ onMounted(async () => {
@click="toggleDropdown" @click="toggleDropdown"
> >
<img <img
:src="`/flags/${currentLanguage().flag}.png`" :src="getFlagUrl(currentLanguage().flag)"
:alt="currentLanguage().name" :alt="currentLanguage().name"
class="w-5 h-4 rounded-sm" class="w-5 h-4 rounded-sm"
/> />
@@ -85,7 +90,7 @@ onMounted(async () => {
]" ]"
@click="selectLanguage(code)" @click="selectLanguage(code)"
> >
<img :src="`/flags/${lang.flag}.png`" :alt="lang.name" class="w-5 h-4 rounded-sm" /> <img :src="getFlagUrl(lang.flag)" :alt="lang.name" class="w-5 h-4 rounded-sm" />
<span>{{ lang.name }}</span> <span>{{ lang.name }}</span>
<span v-if="locale === code" class="ml-auto text-neutral-500"></span> <span v-if="locale === code" class="ml-auto text-neutral-500"></span>
</button> </button>
+2 -1
View File
@@ -1,7 +1,7 @@
import { nextTick } from 'vue' import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
export const SUPPORT_LOCALES = ['en', 'es', 'fr', 'de', 'it', 'ru'] export const SUPPORT_LOCALES = ['en', 'es', 'fr', 'de', 'it', 'ru', 'zh']
export const LANGUAGES = { export const LANGUAGES = {
en: { name: 'English', code: 'en', flag: 'gb' }, en: { name: 'English', code: 'en', flag: 'gb' },
@@ -10,6 +10,7 @@ export const LANGUAGES = {
de: { name: 'Deutsch', code: 'de', flag: 'de' }, de: { name: 'Deutsch', code: 'de', flag: 'de' },
it: { name: 'Italiano', code: 'it', flag: 'it' }, it: { name: 'Italiano', code: 'it', flag: 'it' },
ru: { name: 'Русский', code: 'ru', flag: 'ru' }, ru: { name: 'Русский', code: 'ru', flag: 'ru' },
zh: { name: '中文', code: 'zh', flag: 'cn' },
} }
export function setupI18n(options = { locale: 'en' }) { export function setupI18n(options = { locale: 'en' }) {
+278
View File
@@ -0,0 +1,278 @@
{
"message": {
"theme": {
"light": "明亮模式",
"dark": "黑暗模式",
"system": "跟随系统"
},
"nav": {
"home": "主页",
"forums": "论坛",
"docs": "文档",
"communities": "社区",
"partners": "托管合作伙伴",
"servers": "服务器",
"statistics": "统计",
"store": "周边商店",
"github": "GitHub",
"patreon": "在 Patreon 上为我们捐献",
"language": "选择语言",
"theme": {
"light": "明亮主题",
"dark": "黑暗主题",
"system": "跟随系统"
}
},
"footer": {
"support_on_patreon": "Patreon",
"about": "关于我们",
"privacy_policy": "隐私政策",
"terms_conditions": "条款与细则"
},
"404": {
"title": "找不到页面",
"description": "此页面不存在,\n此页面可能不存在或已经被删除",
"return_home": "返回首页"
},
"home": {
"hero": {
"title": "为BeamNG.Drive 提供多人游戏",
"subtitle": "联机体验软体物理引擎\n和朋友一起赛车 角色扮演或闲逛",
"download_now": "现在下载",
"browse_servers": "查看服务器"
},
"metrics": {
"active_players": "活跃玩家",
"players_online": "在线玩家",
"public_servers": "公开服务器",
"all_servers": "所有服务器"
},
"why_beammp": "为什么选择BeamMP",
"features": {
"stable_servers": {
"title": "稳定",
"description": "BeamMP在全球开设服务器 提供稳定体验"
},
"beamng": {
"title": "易用",
"description": "BeamMP与BeamNG使用相同的地图 车辆和模组 无需学习新东西"
},
"standalone": {
"title": "独立",
"description": "BeamMP不会修改你的BeamNG 你可以无缝使用单机和联机"
},
"sync": {
"title": "流畅",
"description": "BeamMP 每秒更新~50次同步 带来流畅的联机体验"
}
},
"communities": {
"join": "加入这个充满活力的社区",
"description": "探索数千个独特服务器上的多样化游戏体验",
"racing": {
"name": "赛车",
"description": "为全球玩家提供联机赛车与计时赛"
},
"roleplay": {
"name": "角色扮演",
"description": "从警匪追逐到货运模拟都能获得沉浸的体验"
},
"crash": {
"name": "破坏",
"description": "以德比和破坏为玩法的主题玩法"
},
"freeroam": {
"name": "自由漫游与自定义服务器",
"description": "探索开放世界服务器,或使用自定义脚本、地图和模组构建你自己的服务器。"
}
},
"find": {
"title": "寻找你的完美服务器",
"description": "查看数百个活跃服务器 拥有不同的游戏模式 模组和社区 从竞速到休闲 每个人都能在这里找到适合自己的服务器",
"points": {
"custom": "独特体验",
"custom_desc": "每个玩法都由社区创造",
"moderation": "环境友好",
"moderation_desc": "安全友好的游戏环境",
"global": "极低延迟",
"global_desc": "全球服务器实现低延迟"
},
"browse_all_servers": "浏览全部服务器"
},
"devFeatures": {
"title": "为开发者而生",
"description": "打造独特游戏规则 开设自己的服务器 为项目做出贡献",
"lua": {
"title": "Lua API",
"description": "强大的服务器端Lua脚本支持,用于实现自定义游戏模式与功能。"
},
"docs": {
"title": "文档",
"description": "面向服务器开发的详尽指南与API参考文档"
},
"openSource": {
"title": "开源",
"description": "采用社区驱动开发模式,并在GitHub上开源"
},
"learn_more": "了解更多",
"host": {
"title": "准备搭建您的专属服务器了吗?",
"description": "下载服务器文件,即可打造您独特的BeamMP游戏体验。",
"windows": "Windows 服务器端",
"linux": "Linux 服务器端",
"partners": {
"description": "通过我们可信赖的托管合作伙伴快速上手,享受无忧设置、24/7技术支持和优化性能。",
"view_partners": "查看托管合作伙伴"
},
"self_host": {
"title": "或者自托管"
}
}
},
"faq": {
"title": "常见问题回答",
"0": {
"question": "服务器列表没有显示!",
"answer": "如果遇到此问题,可尝试重启BeamMP客户端。若仍未解决,请在官方论坛发帖或加入Discord支持频道寻求帮助。"
},
"1": {
"question": "若遇到问题或有疑问,如何提交工单?",
"answer": "请查看Discord及官方论坛中的#how-to-use频道。详细描述您已尝试的操作步骤,以便支持团队能够快速有效地为您提供帮助。"
},
"2": {
"question": "求助!我遇到错误代码了",
"answer": "请前往我们的论坛查看是否有人遇到过相同问题。很可能已有其他用户遇到过类似情况,回复中通常也会附有解决方案。或者可以访问我们的Discord频道,那里是目前更活跃的社区,大家都很热心帮助。"
},
"3": {
"question": "我可以在盗版的BeamNG.Drive上玩吗?",
"answer": "我们尚不确定其是否兼容BeamNG.drive的盗版版本,但对于非正版游戏副本,我们将不提供任何技术支持,也不会兼容它"
},
"4": {
"question": "如何托管服务器?",
"answer": "托管服务器所需的文件可在本页顶部客户端下载下方找到。同时您需要在Keymaster获取认证密钥。更多设置相关信息请查阅我们的官方文档。"
},
"5": {
"question": "我可以使用模组吗?",
"answer": "模组是支持的,这些安装在服务器上。请查看我们的文档以获取更多信息。"
}
},
"stats": {
"active_players": "活跃玩家",
"global_servers": "全球服务器",
"total_downloads": "总下载量"
}
},
"communities": {
"title": "社区",
"description": "发现围绕BeamMP建立的繁荣社区。从竞技赛车联盟到休闲自由漫游会话,找到您完美的玩伴群组。",
"starting": {
"title": "想要创建您自己的社区吗?",
"description": "托管您自己的BeamMP服务器,并围绕您钟爱的游戏模式建立社区。",
"setup_guide": "服务器设置指南",
"join_discord": "加入我们的Discord"
}
},
"partners": {
"title": "托管合作伙伴",
"description": "选择我们值得信赖的托管合作伙伴。简单的设置、优化的性能和支持,以确保您的BeamMP服务器运行顺畅。",
"from_price": "从 {price}",
"visit_website": "访问网站",
"error_loading": "无法获取合作伙伴信息"
},
"servers": {
"title": "服务器",
"description": "探索并加入来自全球的BeamMP服务器。在此一站式发现您钟爱的游戏模式、活跃社群与丰富玩法。",
"show_only": "仅显示:",
"loading_servers": "加载服务器中...",
"server_count": "0 个发现的服务器 | 1 个发现的服务器 | {count} 发现的服务器",
"players_found": "0 个玩家 | 1 个玩家 | {count} 个玩家",
"filters": {
"hide_empty": "隐藏空服务器",
"hide_full": "隐藏爆满服务器",
"search": "搜索服务器中",
"hide_password": "隐藏受密码保护的服务器",
"show_official": "官方服务器",
"show_partner": "合作伙伴服务器",
"show_featured": "推荐服务器"
},
"table_headers": {
"location": "位置",
"name": "服务器名称",
"map": "地图",
"players": "玩家",
"mods": "模组",
"status": "状态"
}
},
"statistics": {
"title": "统计数据",
"description": "提供活跃服务器的实时快照指标与玩家数量时间线。支持在图表上标注版本发布日期。",
"loading": "加载中...",
"peak": "峰值: {count}",
"current_players": "当前玩家",
"current_servers": "当前服务器",
"avg_players_per_server": "服务器平均玩家数量",
"official_servers": "官方服务器",
"partner_servers": "合作伙伴服务器",
"player_volume": "在线人数时间轴",
"hint": "版本发布标记显示为橙色垂直虚线(例如 v3.0.0、v4.0.0)。悬停鼠标至数据点可查看详细信息。",
"server_volume": "服务器数量时间轴",
"server_hint": "服务器数量呈现相似趋势。发布标记指示主要版本发布。"
},
"about": {
"title": "关于BeamMP",
"description": "BeamMP可为BeamNG.drive带来多人游戏体验。它由社区共建、为社区服务,注重稳定性与性能,致力于让您与朋友共享真实的驾驶体验。",
"note": {
"title": "开发者的话",
"content": "BeamMP 始于一个简单的想法:我想和我的兄弟们一起玩BeamNG.drive。这个最初作为实验的项目,迅速成长为一个社区驱动的倡议,专注于实现多人游戏体验与乐趣。我们深切关注于提供尽可能最佳的用户体验,并让玩家能够轻松加入,享受共同驾驶的快乐。该项目向所有技能水平的贡献者开放——无论您是编写代码、管理服务器、设计场景,还是帮助他人完成设置,您的努力都是 BeamMP 蓬勃发展的关键部分。感谢您的参与,帮助我们共同构建这个非凡的项目。"
},
"provides": {
"title": "BeamMP的功能与服务",
"points": {
"0": "提供BeamNG.drive的多人联机会话,支持服务器浏览器与筛选功能。",
"1": "服务器端管理工具与配置选项",
"2": "模组支持与付费内容保护机制",
"3": "活跃的社区(论坛、Discord),可以提供帮助与协作支持。"
}
},
"project": {
"title": "项目价值观",
"points": {
"0": "社区优先:决策由真实玩家/服务器需求引导",
"1": "可靠性:我们始终把游戏稳定性、数据同步精度和性能表现放在首位,能优化到的地方都全力以赴。",
"2": "开放:欢迎贡献、反馈与透明",
"3": "安全:促进公平游戏和尊重互动"
}
},
"get_involved": {
"title": "参与进来",
"description": "参与的方式有很多种——比如加入讨论、反馈问题、贡献代码,或者通过捐赠等途径给项目提供财务支持。",
"patreon": "Patreon",
"forum": "论坛",
"docs": "文档",
"github": "GitHub",
"discord": "Discord"
},
"funding": {
"title": "资金与可持续发展",
"description": "BeamMP的发展离不开社区的支持。捐款将用于支付服务器等基础设施、带宽、工具及开发时间的成本。如果您认可本项目并愿意支持我们持续成长,请考虑为我们提供捐助。",
"patreon": "在Patreon上支持我们",
"learn": "了解更多内容请访问GitHub"
},
"credits": {
"title": "致谢与贡献者",
"description": "BeamMP项目由模组团队及一众优秀的社区贡献者共同维护。我们同时感谢所有服务器所有者、模组创作者、测试人员以及积极反馈问题的用户——你们的时间与热情是项目持续前进的动力。",
"desc_2": "衷心感谢BeamNG.drive的开发团队,他们打造的卓越平台为多人游戏功能的实现奠定了坚实基础。",
"desc_3": "同时衷心感谢以下现任与曾经的社区成员,感谢他们多年来对BeamMP项目作出的重大贡献:",
"mentions": {
"0": "Jojos38(联合创始人)—感谢他早期在设计各方面的工作,帮助将想法变为现实。",
"1": "Jettajetta.cat)—负责标志的设计与制作。",
"2": "Anonymous275 与 Lionkor — 在项目重构期间负责使用 C++ 重写代码的工作。",
"3": "Tixx — 感谢他对整个项目代码库作出的重大贡献。",
"4": "以及更多未列名的贡献者—衷心感谢每一位以各种方式支持过本项目的朋友!"
},
"thank_you": "我们正在共同打造一个独特的作品—真诚感谢您成为BeamMP社区的一员!"
}
}
}
}
+21 -6
View File
@@ -14,13 +14,25 @@ import {
Rocket, Rocket,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import landingLq from '@/assets/landing-lq.jpg'
import landingHq from '@/assets/landing_hq.png'
const { t } = useI18n() const { t } = useI18n()
const onlinePlayers = ref('...') const onlinePlayers = ref('...')
const onlineServers = ref('...') const onlineServers = ref('...')
const totalServers = ref('...')
const isLoading = ref(true) const isLoading = ref(true)
const heroImageLoaded = ref(false) const heroImageLoaded = ref(false)
const heroImageSrc = ref('/landing-lq.jpg') const heroImageSrc = ref(landingLq)
const installerDownloadUrl = computed(() => {
const base = import.meta.env.BASE_URL || '/'
if (base === './') {
return '/installer/BeamMP_Installer.exe'
}
return `${base.replace(/\/$/, '')}/installer/BeamMP_Installer.exe`
//return 'https://github.com/BeamMP/BeamMP-Launcher/releases/download/v2.8.0/BeamMP_Installer.exe'
})
onMounted(async () => { onMounted(async () => {
try { try {
@@ -31,19 +43,21 @@ onMounted(async () => {
if (values.length >= 2) { if (values.length >= 2) {
onlinePlayers.value = values[1] onlinePlayers.value = values[1]
onlineServers.value = values[3] onlineServers.value = values[3]
totalServers.value = values[5]
} }
} catch { } catch {
onlinePlayers.value = 'N/A' onlinePlayers.value = 'N/A'
onlineServers.value = 'N/A' onlineServers.value = 'N/A'
totalServers.value = 'N/A'
} finally { } finally {
isLoading.value = false isLoading.value = false
} }
// Load high-quality hero image // Load high-quality hero image
const img = new Image() const img = new Image()
img.src = '/landing_hq.png' img.src = landingHq
img.onload = () => { img.onload = () => {
heroImageSrc.value = '/landing_hq.png' heroImageSrc.value = landingHq
heroImageLoaded.value = true heroImageLoaded.value = true
} }
}) })
@@ -126,8 +140,8 @@ const devFeatures = computed(() => [
const stats = computed(() => [ const stats = computed(() => [
{ label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' }, { label: t('message.home.metrics.active_players'), value: onlinePlayers, suffix: '+' },
{ label: t('message.home.metrics.public_servers'), value: '500', suffix: '+' }, { label: t('message.home.metrics.public_servers'), value: onlineServers, suffix: '+' },
{ label: t('message.home.metrics.all_servers'), value: '2M', suffix: '+' }, { label: t('message.home.metrics.all_servers'), value: totalServers, suffix: '+' },
]) ])
const faqs = computed(() => [ const faqs = computed(() => [
{ {
@@ -188,7 +202,7 @@ const faqs = computed(() => [
<!-- CTA Buttons --> <!-- CTA Buttons -->
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center pt-4"> <div class="flex flex-col sm:flex-row gap-4 justify-center items-center pt-4">
<a <a
href="/installer/BeamMP_Installer.zip" :href="installerDownloadUrl"
download download
class="group flex items-center gap-3 bg-linear-to-r from-beammp-orange to-red-600 hover:from-red-600 hover:to-beammp-orange px-8 py-4 rounded-lg font-semibold text-lg transition-all transform hover:scale-105 shadow-lg hover:shadow-beammp-orange/50 text-white" class="group flex items-center gap-3 bg-linear-to-r from-beammp-orange to-red-600 hover:from-red-600 hover:to-beammp-orange px-8 py-4 rounded-lg font-semibold text-lg transition-all transform hover:scale-105 shadow-lg hover:shadow-beammp-orange/50 text-white"
> >
@@ -279,6 +293,7 @@ const faqs = computed(() => [
</div> </div>
<div class="text-center mt-8"> <div class="text-center mt-8">
<RouterLink <RouterLink
style="display: none;"
:to="`/${$i18n.locale}/communities`" :to="`/${$i18n.locale}/communities`"
class="inline-flex items-center gap-2 bg-beammp-orange hover:bg-beammp-orange/90 text-white px-6 py-3 rounded-lg font-semibold transition-colors" class="inline-flex items-center gap-2 bg-beammp-orange hover:bg-beammp-orange/90 text-white px-6 py-3 rounded-lg font-semibold transition-colors"
> >
+2 -2
View File
@@ -244,8 +244,8 @@ const regionDisplay = computed(() => {
onMounted(async () => { onMounted(async () => {
try { try {
const response = await fetch('/servers.json') //const response = await fetch('/servers.json')
//const response = await fetch('https://backend.beammp.com/servers-info') const response = await fetch('https://backend.beammp.com/servers-info')
console.log(response) console.log(response)
if (!response.ok) throw new Error('Failed to fetch servers') if (!response.ok) throw new Error('Failed to fetch servers')
const data = await response.json() const data = await response.json()