npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

spwmini

v1.0.5

Published

SPWorlds Miniapp creation toolkit

Downloads

61

Readme

SPWMini

NPM Version NPM
downloads

SPWMini - Библиотека для создания мини приложений для сайта SPWorlds.

Она состоит из клиентской части, серверной части и набора типов.

Установка

Ты умеешь устанавливать пакеты своим любимым менеджером пакетов. Наш называется spwmini.

npm install spwmini

yarn add spwmini

pnpm install spwmini

Клиентская часть

Пакет предоставляет импорт класса SPWMini из spwmini/client

import SPWMini from 'spwmini/client';

Инициализация

Инициализация класса требует обязательный аргумент - ID приложения, который можно взять со страницы /[server]/apps/[id-приложения].

const spm = new SPWMini('123e4567-e89b-12d3-a456-426655440000');

При работе с фреймворками, опционально, можно отключить автоматическую инициализацию, чтобы предупредить работу с window до появления возможности его использования.

Метод initialize позволит произвести инициализацию в нужный момент, а метод dispose - удалить все хендлеры по необходимости.

const spm = new SPWMini('123e4567-e89b-12d3-a456-426655440000', {
  autoinit: false
});

onMounted(() => spm.initialize());
onUnmounted(() => spm.dispose());

Так как для вализации данных о пользователе требуется запрос к серверу, отдельной опцией может быть кастомизирован и метод fetch.

const spm = new SPWMini('123e4567-e89b-12d3-a456-426655440000', {
  customFetch: ofetch
});

Общение с SPWorlds

Во время инициализации сразу же на SPWorlds отправляется запрос init с id приложения.

Если id запущенного приложения и вашего совпадут, в ответ будет отправлено сообщение initResponse с данными о пользователе. Иначе - initError с данными об ошибке.

Все сообщения прослушиваются с помощью метода on или addEventListener.

spm.on('initResponse', user => {
  console.log(`Logged in as ${user.username} / ${user.minecraftUUID}`);
});

spm.on('initError', message => console.error(`Log in error: ${message}`));

Сразу после инициализации и события ready данные о пользователе доступны как параметр user.

spm.on('ready', () => {
  console.log('App is ready!');
  console.log('Current user:', spm.user);
})

Все описанные далее методы будут работать только после инициализации приложения (после события initResponse или ready).

Валидация пользователя

Так как эти данные может легко подделать любой пользователь, предоставлен и метод валидации информации, отправляющий запрос к API и тесно связанный с серверной частью этого пакета.

Метод validateUser принимает URL и опциональные параметры fetch. Он делает запрос к бекенду, передавая информацию о пользователе.

Бэкенд должен вернуть простой тектовый ответ, содержащий число 1 в случае, если всё хорошо и 0 в случае, если пользователь подделан. Подробнее в секции про серверную часть.

const isUserValid = await spm.validateUser('/validate');

const isUserValid = await spm.validateUser('/validate', { credentials: 'include' });

Открытие URL в новом окне

Для открытия ссылки в новом окне используется метод openURL. Он принимает только ссылки с протоколом https://. Вызов метода с валидной ссылкой открывает диалоговое окно, предлагающее пользователю перейти по ссылке.

spm.openURL('https://google.com');

В случае успеха будет отправлен ответ openURLResponse со строкой success. Любую ошибку открытия можно поймать событием openURLError и получить строку с информацией о ней.

spm.on('openURLResponse', () => console.log('Окно открытия URL успешно открыто'));
spm.on('openURLError', err => console.error(`Ошибка запроса окна открытия URL: ${err}`));

Окно оплаты

Открытие окна оплаты требует создание заранее транзакции и получение кода от API SPWorlds. Затем, достаточно передать этот код в метод openPayment и у пользователя откроется окно оплаты.

const payment = await fetch('/api/buyPremium').then(r => r.json());
spm.openPayment(payment.code);

Да, нужно на сервере самостоятельно создать транзакцию. Для этого можно воспользоваться либо эндпоинтом API SPWorlds напрямую, либо одной из библиотек, созданных коммьюнити.

Оплата состоит из двух этапов, так что и пар событий две: открытие окна оплаты и сама оплата.

Событие openPaymentReponse со строкой success отправляется при удачном открытии окна, иначе - openPaymentError со строкой, содержащей информацию об ошибке. Например, транзакция по коду уже оплачена или срок оплаты истёк.

spm.on('openPaymentReponse', () => console.log('Окно успешно открыто, ждём оплату'));
spm.on('openPaymentError', err => console.error(`Не удалось открыть окно оплаты: ${err}`));

Событие paymentResponse со строкой success отправляется при удачной оплате пользователем товара. Это не значит, что нужно верить, что товар оплачен, стоит проверить это и на бэкенде, но это позволит фронтенду знать, что проверить уже пора. (Хотя, наверное, перед проверкой стоит подождать секунду-другую, пока хук до твоего бекенда дойдёт и обработается)

spm.on('paymentReponse', async () => {
  console.log('Оплата успешно произведена');

  const premiumStatus = await fetch('/api/premium').then(r => r.json());
  if (premiumStatus.active)
    user.showShinyBadge = true;
});

В случае же ошибки оплаты, например, если средств недостаточно, это будет отправлено событием paymentError в виде строки, как и в случае любого другого события ошибки.

spm.on('openPaymentError', err => console.error(`Оплатить не удалось! Ошибка: ${err}`));

В случае ошибки новую транзакцию создавать не нужно. Стоит предложить пользователю попробовать оплатить ещё раз и запросить открытие окна оплаты с тем же кодом.

Серверная часть

Из spwmini/middleware могут быть импортированы функции, помогающие с валидацией пользователя.

Действительно middleware

Функция spwmValidate принимает как обязательный аргумент токен приложения и как опциональный - настройки. Возвращает она другую функцию, уже используемую как посредник.

Токен выдаётся сразу после создания приложения и показывается только один раз. Чтобы получить новый, потребуется удалить приложение, создать его заново и придётся заново проходить модерацию.

Возвращаемая функция-посредник принимает http2 запрос и http2 ответ аргументами, а потому может быть встроена посредником как в http2 сервер, так в express.js сервер и иные.

Для корректной работы в случае с express.js, она должна быть встроена как можно раньше, до применения трансформации тела запроса в json, так как она обрабатывает его самостоятельно.

Наверное это нужно переделать, сделать лучше, но неизвестно как. Контрибуторство приветствуется.

import express from 'express';
import { validate } from 'spwmini/middleware';

const app = express();
app.use('/validate', validate('SECRET_TOKEN'));
app.use(express.json());

После подобного применения посредника, его URL можно указать в клиентской части для проверки пользователя.

Единственной настройкой этой функции является отключение проверки метода. По умолчанию функция принимает только POST запросы, отклоняя любые другие методы. Указав для checkPostMethod значение false, можно позволить посреднику принимать вообще любые методы.

// Принимает совершенно любые методы
app.use('/validate', validate('SECRET_TOKEN', { checkPostMethod: false }));

// Принимает только метод PUT
app.put('/validate', validate('SECRET_TOKEN', { checkPostMethod: false }));

// Отклоняет PUT запросы с ошибкой, другие методы не принимает
app.put('/validate', validate('SECRET_TOKEN'));

Только проверка

Функция checkUser принимает пользователя, имеющегося на клиенте после инициализации, и секретный токен приложения. Возвращается логическое значение, означающее валидность информации о пользователе, что ни одна часть структуры не подменена.

import express from 'express';
import { checkUser } from 'spwmini/middleware';

const app = express();
app.use(express.json());

app.use('/validate-user', (req, res) => {
  if (!req.body.user || typeof req.body.user !== 'object')
    return res.status(400).send({ message: "Invalid user provided" });

  res.send({ valid: checkUser(req.body.user) })
});

Набор типов

Все типы импортируются из spwmini/types.

«Это немного, но это честная работа».

Уголок безопасности

Чтобы ваш сайт невозможно было использовать внутри других сайтов, кроме spworlds, нужно наприсать в <head> элементе:

<meta http-equiv="Content-Security-Policy" content="frame-ancestors https://spworlds.ru;">

Благодаря одной лишь этой строчке вы легко сделаете свой сайт безопаснее.

После https://spworlds.ru через пробел можно указать и свой сайт, если это где-то требуется для тестирования:

<meta http-equiv="Content-Security-Policy" content="frame-ancestors https://spworlds.ru https://example.com;">

А если есть возможность модифицировать заголовки запросов, можно для фронтенда сайта указать

X-Frame-Options: ALLOW-FROM https://spworlds.ru https://example.com