Compare commits
1 Commits
master
...
vue-plus-p
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e5317d471 |
@@ -15,7 +15,6 @@ export const highlighter = await createHighlighter({
|
|||||||
'html',
|
'html',
|
||||||
'markdown',
|
'markdown',
|
||||||
'mdx',
|
'mdx',
|
||||||
'nginx',
|
|
||||||
'bash',
|
'bash',
|
||||||
'svelte',
|
'svelte',
|
||||||
'yaml',
|
'yaml',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"about": {
|
"about": {
|
||||||
"h": "About me",
|
"h": "About me",
|
||||||
"text": "<p>Fullstack developer from St. Petersburg.</p> <p>I like to explore new technologies, find unusual approaches to solving problems and work at the intersection of fields.</p>",
|
"text": "<p>Fullstack developer from St. Petersburg.</p> <p>I like to explore new technologies, find unusual approaches to solving problems and work at the intersection of fields.</p>",
|
||||||
"career_value": "Yandex",
|
"career_value": "Selectel",
|
||||||
"masters_value": "ITMO"
|
"masters_value": "ITMO"
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"about": {
|
"about": {
|
||||||
"h": "Обо мне",
|
"h": "Обо мне",
|
||||||
"text": "<p>Fullstack разработчик из Санкт-Петербурга.</p> <p>Люблю разбираться в новых технологиях, находить необычные подходы к решению проблем и работать на пересечении областей.</p>",
|
"text": "<p>Fullstack разработчик из Санкт-Петербурга.</p> <p>Люблю разбираться в новых технологиях, находить необычные подходы к решению проблем и работать на пересечении областей.</p>",
|
||||||
"career_value": "Яндекс",
|
"career_value": "Selectel",
|
||||||
"masters_value": "ИТМО"
|
"masters_value": "ИТМО"
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {loadTranslations} from '$lib/translations';
|
import {loadTranslations} from '$lib/translations';
|
||||||
import type {LayoutLoad} from "./$types";
|
import type {LayoutLoad} from "./$types";
|
||||||
|
|
||||||
export const load: LayoutLoad = async ({ url, data }) => {
|
export const load: LayoutLoad = async ({ data }) => {
|
||||||
const { locale } = data.i18n
|
const { locale } = data.i18n
|
||||||
|
|
||||||
await loadTranslations(locale);
|
await loadTranslations(locale);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const projects = [
|
|||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
href: 'https://pure-city.kopyl.dev',
|
href: 'https://pure-city.ru',
|
||||||
textSlug: 'projects.link_types.website',
|
textSlug: 'projects.link_types.website',
|
||||||
icon: MaterialSymbolsGlobe
|
icon: MaterialSymbolsGlobe
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ export const projects = [
|
|||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
href: 'https://tpk-artel.kopyl.dev',
|
href: 'https://tpk-artel.ru',
|
||||||
textSlug: 'projects.link_types.website',
|
textSlug: 'projects.link_types.website',
|
||||||
icon: MaterialSymbolsGlobe
|
icon: MaterialSymbolsGlobe
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx"
|
import {metadata as htmlInCssMetadata} from "./html-in-css/+page.svx"
|
||||||
import {metadata as thisBlogMetadata} from "./this-blog/+page.svx"
|
import {metadata as thisBlogMetadata} from "./this-blog/+page.svx"
|
||||||
import {metadata as shortsMetadata} from "./shorts/+page.svx"
|
import {metadata as shortsMetadata} from "./shorts/+page.svx"
|
||||||
import {metadata as minecraftServerMetadata} from "./minecraft-server/+page.svx"
|
import {metadata as vuePlusPayloadMetadata} from "./vue-plus-payload/+page.svx"
|
||||||
|
|
||||||
const posts = [
|
const posts = [
|
||||||
{
|
{
|
||||||
href: '/blog/minecraft-server',
|
href: '/blog/vue-plus-payload',
|
||||||
...minecraftServerMetadata
|
...vuePlusPayloadMetadata
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/blog/shorts',
|
href: '/blog/shorts',
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Мой овердофига усложненный Майнкрафт сервер"
|
|
||||||
date: "2026-03-13"
|
|
||||||
---
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ArticleTitle from "$lib/components/ArticleTitle.svelte"
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ArticleTitle metadata={metadata}/>
|
|
||||||
|
|
||||||
Каждый год в моей компании друзей наступает «неделя Майнкрафта» — время, когда мы все собираемся вместе поиграть. И, как обычно, мне выпала участь поднять сервер для всей компашки.
|
|
||||||
|
|
||||||
Идею поиграть с модами обсуждали давно, но модифицированный сервер всегда оказывался слишком большой морокой. В первую очередь из-за необходимости объяснять каждому участнику, как установить все нужные моды. Была и другая проблема — постоянный риск гриферства со стороны случайных игроков. Большинство моих друзей пираты, поэтому использовать белый список не было вариантом.
|
|
||||||
|
|
||||||
К счастью, я наткнулся на [Drasl](https://github.com/unmojang/drasl)! Это открытие затянуло меня в кроличью нору и в итоге привело к той серверной инфраструктуре, которой мы пользуемся сейчас.
|
|
||||||
|
|
||||||
Некоторые из вас возможно знают, что система аутентификации, которую Mojang использовала до перехода в Microsoft, называлась Yggdrasil. Drasl — это open-source реализация этого алгоритма на Go. Проще говоря, он позволяет поднять собственный сервер аутентификации, на котором мои друзья могут зарегистрироваться и через который будет происходить авторизация.
|
|
||||||
|
|
||||||
Здесь стоит упомянуть, что я и так уже хощу несколько сервисов для своей компании друзей. В основном это VPN сервис, написанный на Go поверх [Pocketbase](https://pocketbase.io/). Поэтому все пользователи уже зарегистрированы в системе, с которой они знакомы и которой пользуются регулярно.
|
|
||||||
|
|
||||||
### Синхронизация пользователей
|
|
||||||
|
|
||||||
В Drasl есть административный API для управления пользователями. Я решил написать модуль для своего существующего Golang-монолита, который через этот API будет синхронизировать пользователей Minecraft с пользователями моей основной системы. Drasl я развернул во внутренней сети и сделал доступ к нему через Nginx с ограничением:
|
|
||||||
|
|
||||||
- публично доступны только Yggdrasil-эндпоинты,
|
|
||||||
- административный API может вызывать только сервер с Pocketbase.
|
|
||||||
|
|
||||||
Примерная конфигурация:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
# /etc/nginx/conf.d/mc-auth.my-domain.conf
|
|
||||||
|
|
||||||
location ^~ /drasl/ {
|
|
||||||
allow 192.168.1.0/24;
|
|
||||||
allow <pocketbase-ip>;
|
|
||||||
deny all;
|
|
||||||
|
|
||||||
proxy_pass http://192.168.1.132:25585;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://192.168.1.132:25585;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Впоследствии, домен, на котором размещён этот сервис, можно указать как параметр запуска для сервера и клиента Minecraft — тогда вместо официального сервера аутентификации Mojang будет использоваться кастомный.
|
|
||||||
|
|
||||||
### Что делать с клиентом?
|
|
||||||
|
|
||||||
С сервером всё просто передали параметр и ладно, но возникает вопрос: как заставить клиента использовать мой auth-сервер? Если установка модов уже была проблемой для пользователей, то эта часть выглядит ещё сложнее. Поэтому я решил форкнуть [Helios Launcher](https://github.com/dscalzi/HeliosLauncher) (ранее известный как Electron Launcher).
|
|
||||||
|
|
||||||
Я зарабатываю на хлеб фронтенд-разработкой, поэтому я обновил визуальный стиль лаунчера, привёл его к дизайну, который уже используется в веб-панели управления VPN и заменил модуль аутентификации в лаунчере на собственный.
|
|
||||||
|
|
||||||
Теперь логин проходит через мой API, который выдаёт клиенту Yggdrasil-токен, используемый для входа на сервер.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Автоматическая установка модов
|
|
||||||
|
|
||||||
У Helios Launcher есть ещё одна полезная возможность — он умеет автоматически скачивать и устанавливать необходимые для сервера моды, если в нем захардкодить ссылку на .json с определённой структурой. Но создание такого JSON-файла — довольно утомительный процесс, особенно если вы не используете готовый модпак с CurseForge.
|
|
||||||
|
|
||||||
Поэтому я написал небольшой хелпер-скрипт get-nebula-obj, который генерирует нужный объект для любого .jar файла с модом.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
После того как я перебрал примерно 200 модов и собрал модпак, который меня устраивает, проект можно было почти считать завершённым.
|
|
||||||
|
|
||||||
### Панель управления Minecraft
|
|
||||||
|
|
||||||
Поскольку у меня уже была веб-панель для сервисов, я решил докинуть туда страницу управления Minecraft-аккаунтом. Там игроки могут: менять скин, добавлять плащ и менять свой никнейм.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Теперь игроку нужно сделать всего одну вещь: скачать мой лаунчер. После этого всё настраивается автоматически: моды, сервер в списке multiplayer, голосовой чат, шейдеры, текстурпаки.
|
|
||||||
|
|
||||||
Любой клиент, который попытается подключиться к серверу не через мой лаунчер, получит ошибку аутентификации. Судя по логам, это уже помогло отфильтровать большое количество ботов.
|
|
||||||
|
|
||||||
На разработку ушло около месяца (действие происходило до всеобъемлющего перехода на вайбкодинг). Так что неделя Майнкрафта давно позади. Следующая задача — уговорить друзей поиграть с собой в Майнкрафт.
|
|
||||||
15
src/routes/blog/vue-plus-payload/+page.svx
Normal file
15
src/routes/blog/vue-plus-payload/+page.svx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "Vue + Payload"
|
||||||
|
date: "2025-xx-xx"
|
||||||
|
---
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ArticleTitle from "$lib/components/ArticleTitle.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ArticleTitle metadata={metadata}/>
|
||||||
|
|
||||||
|
Я разработал интеграцию между Vue и Payload CMS, превращающую последнюю в конструктор сайтов использующий готовые компоненты
|
||||||
|
Vue, которые есть в существующем проекте.
|
||||||
|
|
||||||
|
Допустим вы уже построили сайт, на котором сверстаны компоненты, они используются
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"*.config.ts",
|
"*.config.ts",
|
||||||
"src/blog/**/*.svx",
|
"src/routes/blog/**/*.svx",
|
||||||
"src/**/*.svelte",
|
"src/**/*.svelte",
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"rehype/**/*.ts"
|
"rehype/**/*.ts"
|
||||||
|
|||||||
Reference in New Issue
Block a user