Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
name: Build Jekyll Site
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v6
Expand All @@ -26,13 +29,16 @@ jobs:
-e JEKYLL_ENV=production \
jekyll/jekyll:4.2.2@sha256:400b8d1569f118bca8a3a09a25f32803b00a55d1ea241feaf5f904d66ca9c625 \
sh -c "jekyll build --trace && \
mkdir -p _site/en/css _site/ru/css && \
mkdir -p _site/en/css _site/ru/css _site/es/css && \
cp css/*.css _site/en/css/ && \
cp css/*.css _site/ru/css/ && \
cp css/*.css _site/es/css/ && \
find _site/en -name '*.html' -exec sed -i 's|href=\"/en/|href=\"/|g' {} \; && \
find _site/en -name '*.html' -exec sed -i 's|src=\"/en/|src=\"/|g' {} \; && \
find _site/ru -name '*.html' -exec sed -i 's|href=\"/ru/|href=\"/|g' {} \; && \
find _site/ru -name '*.html' -exec sed -i 's|src=\"/ru/|src=\"/|g' {} \;"
find _site/ru -name '*.html' -exec sed -i 's|src=\"/ru/|src=\"/|g' {} \; && \
find _site/es -name '*.html' -exec sed -i 's|href=\"/es/|href=\"/|g' {} \; && \
find _site/es -name '*.html' -exec sed -i 's|src=\"/es/|src=\"/|g' {} \;"

- name: Upload site artifact
uses: actions/upload-artifact@v6
Expand All @@ -47,6 +53,10 @@ jobs:
runs-on: ubuntu-latest
needs: build

# The artifact actions authenticate against the Actions service, not the
# repository, so the job needs no GITHUB_TOKEN scopes at all.
permissions: {}

strategy:
fail-fast: false
matrix:
Expand All @@ -57,6 +67,8 @@ jobs:
index: 2
- name: tertiary
index: 3
- name: quaternary
index: 4
Comment thread
coderabbitai[bot] marked this conversation as resolved.

steps:
- name: Download site artifact
Expand Down Expand Up @@ -99,12 +111,24 @@ jobs:
echo "region=${{ secrets.S3_ENDPOINT_3_REGION }}" >> $GITHUB_OUTPUT
echo "locale=${{ secrets.S3_ENDPOINT_3_LOCALE }}" >> $GITHUB_OUTPUT
;;
4)
echo "url=${{ secrets.S3_ENDPOINT_4_URL }}" >> $GITHUB_OUTPUT
echo "bucket=${{ secrets.S3_ENDPOINT_4_BUCKET }}" >> $GITHUB_OUTPUT
echo "access_key=${{ secrets.S3_ENDPOINT_4_ACCESS_KEY }}" >> $GITHUB_OUTPUT
echo "secret_key=${{ secrets.S3_ENDPOINT_4_SECRET_KEY }}" >> $GITHUB_OUTPUT
echo "region=${{ secrets.S3_ENDPOINT_4_REGION }}" >> $GITHUB_OUTPUT
echo "locale=${{ secrets.S3_ENDPOINT_4_LOCALE }}" >> $GITHUB_OUTPUT
;;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
esac

- name: Check if endpoint is configured
id: check
run: |
if [ -z "${{ steps.creds.outputs.url }}" ] || [ -z "${{ steps.creds.outputs.bucket }}" ] || [ -z "${{ steps.creds.outputs.locale }}" ]; then
if [ -z "${{ steps.creds.outputs.url }}" ] \
|| [ -z "${{ steps.creds.outputs.bucket }}" ] \
|| [ -z "${{ steps.creds.outputs.locale }}" ] \
|| [ -z "${{ steps.creds.outputs.access_key }}" ] \
|| [ -z "${{ steps.creds.outputs.secret_key }}" ]; then
echo "configured=false" >> $GITHUB_OUTPUT
echo "::warning::Endpoint ${{ matrix.endpoint.name }} is not configured, skipping..."
else
Expand Down
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ COPY --chown=1000:1000 . /site
WORKDIR /site

RUN jekyll build --trace && \
mkdir -p _site/en/css _site/ru/css && \
mkdir -p _site/en/css _site/ru/css _site/es/css && \
cp css/*.css _site/en/css/ && \
cp css/*.css _site/ru/css/ && \
cp css/*.css _site/es/css/ && \
find _site/en -name "*.html" -exec sed -i 's|href="/en/|href="/|g' {} \; && \
find _site/en -name "*.html" -exec sed -i 's|src="/en/|src="/|g' {} \; && \
find _site/ru -name "*.html" -exec sed -i 's|href="/ru/|href="/|g' {} \; && \
find _site/ru -name "*.html" -exec sed -i 's|src="/ru/|src="/|g' {} \;
find _site/ru -name "*.html" -exec sed -i 's|src="/ru/|src="/|g' {} \; && \
find _site/es -name "*.html" -exec sed -i 's|href="/es/|href="/|g' {} \; && \
find _site/es -name "*.html" -exec sed -i 's|src="/es/|src="/|g' {} \;


FROM golang:1.25-alpine as gobuilder
Expand Down
4 changes: 3 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ title: 'GameAP Documentation'
domains:
en: "https://docs.gameap.com"
ru: "https://docs.gameap.ru"
es: "https://docs.gameap.com/es"

# if you wish to integrate disqus on pages set your shortname here
disqus_shortname: ''
Expand All @@ -24,7 +25,8 @@ navigation: 1
# Default categories (in order) to appear in the navigation
sections: [
['ru', 'Russian'],
['en', 'English']
['en', 'English'],
['es', 'Spanish']
]

# Use the kramdown Markdown renderer
Expand Down
10 changes: 10 additions & 0 deletions _data/lang/es.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
title: GameAP Documentación
tagline: panel de control de servidores de juego
docs: Documentación
documentation: Documentación
download: Descargar
on_github: Proyecto en GitHub
forum: Foro
site: Sitio
site_url: https://gameap.com
edit_page: Editar esta página
30 changes: 20 additions & 10 deletions _includes/header.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
{% assign lang = site.data.lang[page.lang] %}
{% assign other_lang = 'ru' %}
{% assign other_domain = site.domains.ru %}
{% if page.lang == 'ru' %}
{% assign other_lang = 'en' %}
{% assign other_domain = site.domains.en %}
{% endif %}
{% assign clean_path = page.url | replace: '/en/', '/' | replace: '/ru/', '/' %}
{% assign clean_path = page.url | replace: '/en/', '/' | replace: '/ru/', '/' | replace: '/es/', '/' %}
{% assign lang_labels = 'en:English,ru:Русский,es:Español' | split: ',' %}

<div class="container">
<header id="header">
Expand All @@ -16,9 +11,24 @@
<nav class="header-nav">
<a href="{{ lang.site_url }}">{{ lang.site }}</a>
<a href="https://github.com/gameap">GitHub</a>
<a href="{{ other_domain }}{{ clean_path }}" class="lang-switch">
{% if page.lang == 'en' %}RU{% else %}EN{% endif %}
</a>
<details class="lang-menu">
<summary>
<span>{{ page.lang | upcase }}</span>
<svg class="chevron" width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M2 3.5 5 6.5 8 3.5"/></svg>
</summary>
<ul>
{% for item in lang_labels %}
{% assign pair = item | split: ':' %}
{% assign code = pair[0] %}
{% assign label = pair[1] %}
{% if code == page.lang %}
<li><span class="lang-item is-current" aria-current="true" lang="{{ code }}">{{ label }}</span></li>
{% else %}
<li><a class="lang-item" href="{{ site.domains[code] }}{{ clean_path }}" hreflang="{{ code }}" lang="{{ code }}">{{ label }}</a></li>
{% endif %}
{% endfor %}
</ul>
</details>
</nav>
</header>
</div>
15 changes: 12 additions & 3 deletions _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">

{% assign lang = site.data.lang[page.lang] %}
{% assign clean_path = page.url | replace: '/ru/', '/' | replace: '/en/', '/' %}
{% assign clean_path = page.url | replace: '/ru/', '/' | replace: '/en/', '/' | replace: '/es/', '/' %}
{% assign page_url = site.domains[page.lang] | append: clean_path %}

{%- comment -%}
Expand All @@ -32,8 +32,16 @@
{% assign page_description = content | strip_html | normalize_whitespace | strip | truncate: 160 %}
{% endif %}

{% if page.lang == 'ru' %}{% assign og_locale = 'ru_RU' %}{% else %}{% assign og_locale = 'en_US' %}{% endif %}
{% assign og_image = site.domains[page.lang] | append: '/images/gameap.png' %}
{% if page.lang == 'ru' %}{% assign og_locale = 'ru_RU' %}{% elsif page.lang == 'es' %}{% assign og_locale = 'es_ES' %}{% else %}{% assign og_locale = 'en_US' %}{% endif %}

{%- comment -%}
Images are served from the host root, not from under the language path.
The Spanish site lives in a subdirectory of the English domain, so its
assets resolve against that domain — `domains.es` would point the crawler
at /es/images/, where nothing is published.
{%- endcomment -%}
{% if page.lang == 'es' %}{% assign image_domain = site.domains.en %}{% else %}{% assign image_domain = site.domains[page.lang] %}{% endif %}
{% assign og_image = image_domain | append: '/images/gameap.png' %}

<title>{{ page_title }}</title>
<meta name="description" content="{{ page_description | escape }}">
Expand All @@ -47,6 +55,7 @@
<!-- Hreflang tags for language alternatives -->
<link rel="alternate" hreflang="en" href="{{ site.domains.en }}{{ clean_path }}">
<link rel="alternate" hreflang="ru" href="{{ site.domains.ru }}{{ clean_path }}">
<link rel="alternate" hreflang="es" href="{{ site.domains.es }}{{ clean_path }}">
<link rel="alternate" hreflang="x-default" href="{{ site.domains.en }}{{ clean_path }}">

<!-- Open Graph -->
Expand Down
75 changes: 75 additions & 0 deletions css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,81 @@ pre:hover .code-copy-btn {
color: #0366d6;
}

.lang-menu {
position: relative;
}

.lang-menu summary {
list-style: none;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: #24292e;
border: 1px solid #ddd;
border-radius: 6px;
padding: 4px 8px;
cursor: pointer;
user-select: none;
}

.lang-menu summary::-webkit-details-marker {
display: none;
}

.lang-menu summary:hover,
.lang-menu[open] summary {
color: #0366d6;
border-color: #0366d6;
}

.lang-menu[open] .chevron {
transform: rotate(180deg);
}

.lang-menu ul {
position: absolute;
right: 0;
top: calc(100% + 6px);
margin: 0;
padding: 4px;
list-style: none;
min-width: 140px;
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
z-index: 1030;
}

.lang-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 6px 10px;
border-radius: 6px;
font-size: 14px;
color: #24292e;
text-decoration: none;
white-space: nowrap;
}

a.lang-item:hover {
background: #f6f8fa;
color: #24292e;
text-decoration: none;
}

.lang-item.is-current {
color: #999;
}

.lang-item.is-current::after {
content: "✓";
color: #1a7f37;
}

#footer {
margin: 20px 0;
font-size: 0.85em;
Expand Down
39 changes: 37 additions & 2 deletions en/daemon/process_managers.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ process_manager:
```

**Available managers:**
- Linux: `systemd` (default), `docker`, `podman`, `tmux`
- Windows: `shawl` (default), `winsw`
- Linux: `systemd` (default), `docker`, `podman`, `tmux`, `simple`
- Windows: `shawl` (default), `winsw`, `simple`

## Linux

Expand Down Expand Up @@ -216,3 +216,38 @@ WinSW does not require additional configuration.
process_manager:
name: winsw
```

## Linux and Windows

### Simple

Simple does not delegate to any system service. It starts the game server through the command
templates configured for the dedicated server and knows nothing about the process beyond what those
commands report.

On Linux the daemon falls back to it automatically when neither systemd nor tmux is available.
On Windows it can be selected explicitly.

| Feature | |
|----------------------------------------|----|
| Start, stop, restart servers | ✅ |
| Statistics | ❌ |
| Resource limits (CPU / RAM) | ❌ |
| Console reading | ✅ |
| Sending commands to console | ✅ |
| Isolation | ❌ |

The daemon does not track the process ID, so only the "server is running" flag reaches the panel —
there are no CPU, memory or network charts, and resource limits cannot be applied. Choose Simple
only when nothing else is available on the system.

#### Simple Configuration

Simple does not require additional configuration.

##### Configuration Example

```yaml
process_manager:
name: simple
```
4 changes: 4 additions & 0 deletions en/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ The directory with the database file must be writable by the user the panel runs

```dotenv
DATABASE_DRIVER=inmemory
DATABASE_URL=inmemory
```

Data is kept in RAM only and is lost on restart. Meant for tests; not suitable for a production
installation.

`DATABASE_URL` has to be set even here: the panel checks it for emptiness before it learns the
driver, and will not start without it. The value itself is not used.

## Migrations

The panel applies migrations itself at startup — there is no separate command. The schema
Expand Down
2 changes: 1 addition & 1 deletion en/gameap_configure/games.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Example game server working directory `/srv/gameap/servers/example-server`
Each game can have many mods, each has its own features, settings,
startup parameters, configuration files, etc.

To add a new game, go to **"Administration"** → **"Games"**, then select
To add a new mod, go to **"Administration"** → **"Games"**, then select
**"Add Mod"**

### Fields
Expand Down
2 changes: 1 addition & 1 deletion en/get_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ bash <(curl -s https://gameap.com/install.sh) --with-daemon
## Adding a Dedicated Server

Add a new dedicated server (VDS) on which you will then install game servers.
After installing the panel, log in and select **"Administration"** **"Dedicated servers"** → **"Create"** from the menu. After
After installing the panel, log in and select **"Administration"** **"Dedicated servers"** → **"Create"** from the menu. After
that, a window with instructions will open, follow them.

![Adding a dedicated server in the GameAP panel](/images/en/get_started/add_dedicated_server.gif)
Expand Down
5 changes: 5 additions & 0 deletions en/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ Certificates, the ACME account key, and housekeeping data are stored in the `ACM
directory inside the panel's file storage. With `FILES_DRIVER=s3` they end up in S3 — this is
what lets several panel instances share one certificate.

Shared storage alone is not enough for several instances: the lock that keeps them from requesting
a certificate at the same time works through Redis and is active only with `CACHE_DRIVER=redis`.
With the in-memory cache the lock is local to each instance, and they will get in each other's way.
See [Multiple Panel Instances](/en/multi_instance.html).

## Certificate status

The current status is available to an administrator at `GET /api/admin/letsencrypt/status`:
Expand Down
4 changes: 2 additions & 2 deletions en/install/install_docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ docker run -d \
-p 31718:31718 \
-e DATABASE_DRIVER=sqlite \
-e 'DATABASE_URL=file:/var/lib/gameap/db.sqlite?_busy_timeout=5000&_journal_mode=WAL&cache=shared' \
-e AUTH_SECRET=$(openssl rand -hex 16) \
-e ENCRYPTION_KEY=$(openssl rand -hex 16) \
-e AUTH_SECRET=$(openssl rand -base64 24) \
-e ENCRYPTION_KEY=$(openssl rand -hex 32) \
-e GRPC_EXTERNAL_HOST=panel.example.com \
-v gameap-data:/var/lib/gameap \
gameap/gameap:latest
Expand Down
Loading