> [!NOTE] > This article is only applicable to single server deployments for both front-end and back-end > > and both front-end and back-end use the same subdomain > > If you are using SaaS/server panels, please refer to [other community tutorials](https://mx-space.js.org/docs/core/community)Mix Space is a small personal space station program. Unlike traditional blogging programs, it adopts a front-end and back-end separation design, suitable for those who enjoy writing in different styles or types.
Preparation#
-
Server * 1
- Kernel > 4.18
- RAM > 1G (recommended 2G or more, Next compilation is memory-intensive, too low may cause OOM)
- SWAP > 2G (optional, can be ignored if memory is large enough)
-
Domain name * 1
-
SSL certificate * 1
Note
For CDN users:
If you are using a self-signed certificate on the source server, you need to add the CA of the self-signed certificate to the system CA library or manually trust the certificate; otherwise, it may cause front-end requests to fail to reach the back-end.
Back-end#
-
Pull the Docker Compose configuration file template
cd && mkdir -p mx-space/core && cd $_ curl https://https://raw.githubusercontent.com/mx-space/core/refs/heads/master/docker-compose.yml -o docker-compose.yml
-
Edit the downloaded
docker-compose.yml
services: app: container_name: mx-server image: innei/mx-server:latest environment: - TZ=Asia/Shanghai - NODE_ENV=production - DB_HOST=mongo - REDIS_HOST=redis - ALLOWED_ORIGINS=localhost # Add your domain here, separated by commas - JWT_SECRET=YOUR_SUPER_SECURED_JWT_SECRET_STRING # Generate one using openssl - ENCRYPT_ENABLE=false # Whether to enable encryption, generally do not change it - ENCRYPT_KEY=069d51c5e6f09e1d806e4dedb1c7bc620c29a67c91a984547911be8a846cf6cb # Example value volumes: - ./data/mx-space:/root/.mx-space ports: - '2333:2333' depends_on: - mongo - redis networks: - mx-space restart: unless-stopped healthcheck: test: ['CMD', 'curl', '-f', 'http://127.0.0.1:2333/api/v2/ping'] interval: 1m30s timeout: 30s retries: 5 start_period: 30s mongo: container_name: mongo image: mongo volumes: - ./data/db:/data/db networks: - mx-space restart: unless-stopped redis: image: redis:alpine container_name: redis volumes: - ./data/redis:/data healthcheck: test: ['CMD-SHELL', 'redis-cli ping | grep PONG'] start_period: 20s interval: 30s retries: 5 timeout: 3s networks: - mx-space restart: unless-stopped networks: mx-space: driver: bridge
Important
If encryption is enabled, note that the key length must be 64 bits and consist only of lowercase letters and numbers; otherwise, an error will occur during initialization. Note that this is irreversible, so be sure to save your key. Therefore, it is not highly recommended to use it unless you really need to encrypt the API Key.
The key can be generated using the command openssl rand -hex 32
. Please remember it.
-
Start the back-end
docker compose up -d
-
Configure reverse proxy
Below is an example file
server { listen 80; listen 443 ssl; listen 443 quic; # Only available for nginx version 1.25.0 and above, please remove this line for other versions server_name YOUR_SERVER_NAME; # Change to your domain ssl_certificate path/to/certificate; # SSL certificate path ssl_certificate_key path/to/key; # SSL certificate key path # Reverse proxy # This is an example for nginx configuration if you host mx-space manually location / { proxy_pass http://127.0.0.1:2323; proxy_set_header Host $host; proxy_intercept_errors on; } location ^~ /api/v2 { proxy_pass http://127.0.0.1:2333/api/v2; proxy_set_header Host $host; proxy_redirect off; proxy_buffering off; } location /proxy/qaqdmin { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/proxy/qaqdmin; } location ^~ /proxy/ { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/proxy/; } location ^~ /render/ { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/render/; } location ^~ /socket.io { proxy_pass http://127.0.0.1:2333/socket.io; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_buffering off; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
}
Reference this file in `nginx.conf`
```nginx filename="/etc/nginx/nginx.conf"
http {
...
include /etc/nginx/sites-enabled/*;
...
}
- Initialize the back-end
- Set up domain name resolution
- Open
https://<YOUR_DOMAIN>/proxy/qaqdmin
- Complete initialization according to the prompts
- Go to Settings > Site Settings and set as follows
Item | Value |
---|---|
Front-end Address | https://<YOUR_DOMAIN> |
Admin Backend Address | https://<YOUR_DOMAIN>/proxy/qaqdmin |
API Address | https://<YOUR_DOMAIN>/api/v2 |
Gateway Address | https://<YOUR_DOMAIN> |
Thus, the back-end is complete.
Front-end (Shiro)#
- Initialize the environment
cd ~/mx-space
git clone https://github.com/Innei/Shiro.git
cd Shiro
git submodule init && git submodule update
pnpm i
- Set theme configuration
Enter the Mix Space backend, go to the "Configuration and Cloud Functions" page, click the add button in the upper right corner, and fill in the following settings:
Important
Please note that you need to modify this configuration to meet your needs. Directly using the configuration below may cause your blog to not function as expected.
The following configuration may not be complete; for more configuration item information, please refer to Configuration Items.
Additionally, the configuration can also be written in YAML format, in which case the data type should be selected as YAML
.
For details on the "My Dynamics" feature, see the official documentation.
- Name: `shiro`
- Reference: `theme`
- Data Type: `JSON`
- Data: (click the button below to copy)
```json
{
"footer": {
"otherInfo": {
"date": "2020-{{now}}",
"icp": {
"text": "Meng ICP Record No. 20236136",
"link": "https://icp.gov.moe/?keyword=20236136"
}
},
"linkSections": [
{
"name": "About",
"links": [
{
"name": "About this site",
"href": "/about-site"
},
{
"name": "About me",
"href": "/about"
},
{
"name": "About this project",
"href": "https://github.com/innei/Shiro",
"external": true
}
]
},
{
"name": "More",
"links": [
{
"name": "Timeline",
"href": "/timeline"
},
{
"name": "Friend Links",
"href": "/friends"
},
{
"name": "Monitoring",
"href": "https://status.innei.in/status/main",
"external": true
}
]
},
{
"name": "Contact",
"links": [
{
"name": "Leave a message",
"href": "/message"
},
{
"name": "Send an email",
"href": "mailto:[email protected]",
"external": true
},
{
"name": "GitHub",
"href": "https://github.com/innei",
"external": true
}
]
}
]
},
"config": {
"color": {
"light": [
"#33A6B8",
"#FF6666",
"#26A69A",
"#fb7287",
"#69a6cc",
"#F11A7B",
"#78C1F3",
"#FF6666",
"#7ACDF6"
],
"dark": [
"#F596AA",
"#A0A7D4",
"#ff7b7b",
"#99D8CF",
"#838BC6",
"#FFE5AD",
"#9BE8D8",
"#A1CCD1",
"#EAAEBA"
]
},
"bg": [
"https://github.com/Innei/static/blob/master/images/F0q8mwwaIAEtird.jpeg?raw=true",
"https://github.com/Innei/static/blob/master/images/IMG_2111.jpeg.webp.jpg?raw=true"
],
"custom": {
"css": [],
"styles": [],
"js": [],
"scripts": []
},
"site": {
"favicon": "/innei.svg",
"faviconDark": "/innei-dark.svg"
},
"hero": {
"title": {
"template": [
{
"type": "h1",
"text": "Hi, I'm ",
"class": "font-light text-4xl"
},
{
"type": "h1",
"text": "Innei",
"class": "font-medium mx-2 text-4xl"
},
{
"type": "h1",
"text": "👋。",
"class": "font-light text-4xl"
},
{
"type": "br"
},
{
"type": "h1",
"text": "A NodeJS Full Stack ",
"class": "font-light text-4xl"
},
{
"type": "code",
"text": "<Developer />",
"class": "font-medium mx-2 text-3xl rounded p-1 bg-gray-200 dark:bg-gray-800/0 hover:dark:bg-gray-800/100 bg-opacity-0 hover:bg-opacity-100 transition-background duration-200"
},
{
"type": "span",
"class": "inline-block w-[1px] h-8 -bottom-2 relative bg-gray-800/80 dark:bg-gray-200/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 group-hover:animation-blink"
}
]
},
"description": "An independent developer coding with love."
},
"module": {
"activity": {
"enable": true,
"endpoint": "/fn/ps/update"
},
"donate": {
"enable": true,
"link": "https://afdian.net/@Innei",
"qrcode": [
"https://cdn.jsdelivr.net/gh/Innei/img-bed@master/20191211132347.png",
"https://cdn.innei.ren/bed/2023/0424213144.png"
]
},
"bilibili": {
"liveId": 1434499
}
}
}
}
```
Save changes
4. Modify environment variables
nano .env
Example:
NEXT_PUBLIC_API_URL=http://localhost:2333
NEXT_PUBLIC_GATEWAY_URL=http://localhost:2333
NEXT_PUBLIC_API_URL=https://<YOUR_DOMAIN>/api/v2
NEXT_PUBLIC_GATEWAY_URL=https://<YOUR_DOMAIN>
# The following are optional
TMDB_API_KEY=
GH_TOKEN=
-
Compile
pnpm run build
-
Start
pnpm prod:pm2
Thus, the front-end and back-end are all complete.
Some Tips#
Speeding up the connection between front-end and back-end#
Background#
For CDN users, by default, the front-end requests the back-end interface through the CDN, which increases latency and complicates the writing of WAF rules, causing inconvenience.
Solution#
Since our front-end and back-end are on the same server, we can speed up the process by resolving the domain name to localhost
or 127.0.0.1
on the server side, allowing requests to go directly to the back-end without going through the CDN.
echo -e "127.0.0.1 <YOUR_DOMAIN>" | sudo tee -a /etc/hosts
Important
Please carefully check if your hosts file is managed by cloud-init
.
If so, you need to change the cloud-init
template instead of directly modifying /etc/hosts
.
About favicon#
Background#
Although the favicon can be modified through the configuration file, in some cases (such as Cloudflare challenges), the favicon obtained is the default favicon of Shiro, which is somewhat frustrating.
Solution#
I decided to use git alias
, git submodule
, and git hooks
to solve this. First, create a Git repository to store the favicon.
Example
Delete the original public
folder and add the submodule.
rm -rf {SHIRO_PATH}/public && git rm -rfq public
git submodule --quiet add --force {YOUR_REPO} public
git submodule --quiet update --init --remote
git submodule --quiet sync
Set git alias
git config --global alias.pull-clean '!git fetch && test $(git rev-list --count HEAD..@{upstream}) -gt 0 && git reset --hard && git clean -fd && git pull'
Add post-merge
Git Hook
#!/bin/sh
. git-sh-setup
echo "Replacing public folder"
rm -rf public && git rm -rfq public
git submodule --quiet add --force https://github.com/nyaruta/Shiro-Assets.git public
echo "Syncing submodules"
git submodule --quiet update --init --remote
git submodule --quiet sync
Add execution permissions
chmod +x .git/hooks/post-merge
This way, every time you pull the repository, just replace git pull
with git pull-clean
to execute our changes to the favicon without causing conflicts.
Important
After adding Git Hooks, you need to add the --ignore-scripts
parameter when executing pnpm install
, otherwise, the prepare
script will automatically execute and overwrite our hook.
Alright, I can't think of any new tips for now, so let's leave it at that.
This article is synchronized and updated to xLog by Mix Space. The original link is https://lar.moe/posts/deploy/mx-space