ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Curso de ruso para hispanohablantes: alfabeto, gramática, cuentos con audio y vídeo. Empieza gratis, sin tarjeta.
Translation pending
EducationTypeScript · JavaScriptGitHub @justicloudforme-cryptoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
RusoFácilapp — сайт для обучения русскому языку Сайт для испаноговорящих студентов из Мексики: курсы русского языка от уровня A1 до B2, интерфейс на трёх языках (испанский, русский, английский), платная подписка (Stripe) и личный кабинет студента с прогрессом по урокам. • Раздел 1–2 — как запустить сайт на своём компьютере (нужен один раз). • Раздел 3 — как редактировать тексты, уроки и цены без программирования (для преподавателя/контент-менеджера). • Раздел 4–6 — команды проверки, переменные окружения и деплой (для разработчика).Read the full README ↗
Сайт для испаноговорящих студентов из Мексики: курсы русского языка от уровня A1 до B2, интерфейс на трёх языках (испанский, русский, английский), платная подписка (Stripe) и личный кабинет студента с прогрессом по урокам.
Это руководство написано для двух аудиторий:
Node.js версии 20 или новее. Проверить, установлен ли он:
node -v
Если команда не найдена — самый простой способ поставить Node.js через nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# закройте и снова откройте терминал, затем:
nvm install --lts
Больше ничего устанавливать вручную не нужно — все остальные зависимости (Next.js, база данных, Stripe SDK) ставятся одной командой на следующем шаге.
# 1. Установить зависимости (один раз, займёт 1–2 минуты)
npm install
# 2. Скопировать файл с переменными окружения
cp .env.example .env
# 3. Сгенерировать секрет для входа и вписать его в .env
# (без этого шага вход и подписка не заработают — см. раздел 5)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# скопируйте, что выведет команда, и вставьте в .env вместо SESSION_SECRET=""
# 4. Создать локальную базу данных (SQLite-файл dev.db)
npx prisma db push
# 5. Назначить себя владельцем сайта (доступ к панели администратора)
# подставьте свою почту — той же почтой нужно будет войти на сайте
npm run db:set-owner -- [email protected]
# 6. Запустить сайт
npm run dev
После этого сайт открывается на http://localhost:3000 (Next.js сам подставит другой порт, например 3001, если 3000 занят — смотрите, что написано в терминале после запуска).
По умолчанию .env работает в «демо-режиме»: платежи не уходят в настоящий
Stripe, а подписка активируется сразу же после нажатия кнопки — это удобно
для тестирования и для локальной демонстрации сайта. Как подключить настоящий
Stripe — см. раздел 5. Переменные окружения.
Каждый следующий раз для запуска достаточно одной команды:
npm run dev
(Шаг 1 нужно повторить, только если вы удалили папку node_modules; шаги
4–5 — если вы удалили файл dev.db, то есть база данных стала пустой.)
Все тексты, которые видит студент, вынесены в отдельные JSON-файлы —
их можно открыть и отредактировать в любом текстовом редакторе (даже в
Блокноте), не трогая код сайта. Главное правило работы с JSON:
Сохраняйте кавычки
"..."и запятые между строками как есть — если случайно удалить кавычку или запятую, сайт перестанет собираться. Если сомневаетесь — сделайте копию файла перед правкой.
После любого изменения в JSON-файлах сайт обновляется автоматически,
если он запущен через npm run dev — просто сохраните файл и обновите
страницу в браузере.
Файлы: src/dictionaries/es.json, src/dictionaries/ru.json,
src/dictionaries/en.json — по одному на каждый язык интерфейса
(испанский / русский / английский).
Внутри каждого файла есть, среди прочего:
"nav", "home", "footer" — тексты шапки, главной страницы, подвала;
"courses"."levels"."a1" (и a2, b1, b2) — заголовок, описание и
список названий уроков для каждого уровня. Чтобы переименовать урок
или изменить его описание — меняйте текст здесь;
"pricing" — тексты и цены тарифов:
"monthly": { "name": "Mensual", "price": "$249", "period": "/ mes", "cta": "Suscribirme" },
"annual": { "name": "Anual", "price": "$1,990", "period": "/ año", "cta": "Suscribirme", "badge": "Ahorra 33%" }
Чтобы поменять цену — отредактируйте значение "price". Отображаемая
цена — это просто текст (не связана напрямую с реальным биллингом), но
не забудьте поменять её во всех трёх файлах (es.json, ru.json,
en.json), иначе на разных языках сайта будут показаны разные цифры.
(Реальная сумма, которую спишет Stripe, настраивается отдельно — в самом
Stripe, через STRIPE_PRICE_MONTHLY / STRIPE_PRICE_ANNUAL, см. раздел 5.)
Структура одинаковая во всех трёх файлах — если добавляете новый уровень
вложенности текста в es.json, добавьте такой же ключ в ru.json и
en.json, иначе сайт покажет ошибку на этом языке.
Файл: src/lib/lessons/content.json.
Здесь хранится подробное содержание уроков — то, что студент видит на вкладках «Gramática / Vocabulario / Ejercicios» на странице урока. Весь этот текст всегда пишется на испанском (курс объясняет русский язык через сравнение с испанским), независимо от того, какой язык выбран в переключателе сайта.
Каждый урок — это один блок в файле, с ключом вида "a1-1" (уровень a1,
урок 1), "a1-2", "a2-1" и так далее — цифра соответствует номеру урока
в списке "lessons" того же уровня в файлах из раздела 3.1.
Пока что заполнены только уроки a1-1 и a1-2 — это готовый пример
формата. Для всех остальных уроков (которые уже видны в списке курса)
страница автоматически показывает заглушку «содержание в разработке», пока
для них не добавлен блок в этом файле.
Структура одного урока:
"a1-1": {
"grammar": {
"title": "El alfabeto cirílico y los sonidos del ruso",
"paragraphs": [
"Первый абзац объяснения грамматики.",
"Второй абзац — можно добавлять сколько угодно строк в этот список."
]
},
"vocabulary": [
{ "word": "привет", "transcription": "privét", "translation": "hola (informal)" }
],
"exercises": [ /* см. ниже */ ]
}
grammar.paragraphs — список абзацев текста (обычный текст, без
markdown-разметки — **жирный** или *курсив* отображены не будут).vocabulary — список слов: word (по-русски), transcription
(произношение латиницей), translation (перевод на испанский).Внутри "exercises" — список упражнений. У каждого упражнения обязательно
своё уникальное "id" (просто строка, использована как ключ, не показывается
студенту) и "type", определяющий тип упражнения:
1. Выбор варианта (multiple-choice)
{
"id": "a1-1-mc-3",
"type": "multiple-choice",
"prompt": "¿Qué significa 'спасибо'?",
"options": ["hola", "gracias", "sí", "no"],
"correctIndex": 1
}
correctIndex — номер правильного варианта в списке options, считая от
нуля (0 — первый вариант, 1 — второй, и так далее).
2. Вставка пропущенной буквы/слова (fill-blank)
{
"id": "a1-1-fill-3",
"type": "fill-blank",
"before": "вод",
"after": "",
"answers": ["а"]
}
Студент видит before + поле для ввода + after (например, «вод[___]» →
нужно вписать «а»). В answers можно перечислить несколько вариантов
правильного ответа — регистр букв (заглавные/строчные) не важен.
3. Сопоставление пар (matching)
{
"id": "a1-1-matching-2",
"type": "matching",
"pairs": [
{ "left": "дом", "right": "casa" },
{ "left": "вода", "right": "agua" }
]
}
Студент видит список слов слева (left) и должен выбрать для каждого
правильный перевод из выпадающего списка (right).
src/lib/lessons/content.json."a1-2": { ... })
вместе с запятой перед ним."a1-3" —
номер должен совпадать с позицией названия урока в списке "lessons"
уровня a1 в src/dictionaries/es.json (и ru.json, en.json).Если после сохранения файла сайт показывает ошибку — скорее всего, в JSON пропущена запятая или кавычка. Хорошо помогает бесплатный онлайн-валидатор JSON (например, jsonlint.com) — можно вставить туда содержимое файла и проверить, где ошибка.
Важно: этот файл — только «встроенный пример по умолчанию». Если урок отредактирован через панель администратора (
/admin/lessons, см. раздел 3.4), сайт показывает версию из базы данных, а не из этого файла — правки вcontent.jsonдля такого урока перестанут быть видны, пока редактор не удалит правку через кнопку «Eliminar edición» / «Удалить правку». Для урока, который никто не редактировал через панель, всё работает как описано выше.
Всё остальное в папке src/ — это код сайта (файлы .ts и .tsx):
оформление страниц, логика подписки, защита уроков и т.д. Их менять без
знания программирования не стоит.
У сайта три роли:
| Роль | Права |
|---|---|
| owner (владелец) | Всё, что может admin, плюс раздел «Пользователи и роли»: назначение и снятие ролей у кого угодно, включая других администраторов. Владелец не может случайно снять роль владельца сама с себя, если она последняя в системе — сайт это запрещает. |
| admin (администратор) | Добавление/редактирование/удаление уроков, просмотр списка пользователей и подписок. Не видит раздел «Пользователи и роли» и не может ни выдавать/отменять подписки вручную, ни менять чьи-либо роли. |
| student (студент) | Обычный пользователь: личный кабинет и оплаченный контент. Роль по умолчанию для всех новых регистраций. |
Панель администратора находится на /admin (ссылка «Panel de administración»
появляется в шапке сайта у тех, кто вошёл как admin или owner). В ней три
раздела:
/admin/lessons) — список всех уроков всех уровней со статусом
(«Отредактировано» / «Есть пример» / «Нет содержания») и редактор с полем
JSON для грамматики/словаря/упражнений (тот же формат, что в разделе 3.2,
но правки здесь сохраняются в базу данных, а не в файл — это единственный
способ редактирования уроков, который переживает деплой на Vercel)./admin/subscriptions) — статус подписки каждого
пользователя; у владельца есть кнопки «Активировать на 30 дней» и
«Отменить» для ручного управления доступом без Stripe./admin/users, только для владельца) — смена
роли любого пользователя через выпадающий список.Как назначить владельца. Первый владелец назначается вручную — через скрипт, а не через интерфейс (иначе некому было бы выдать первую роль):
npm run db:set-owner -- [email protected]
Команда идемпотентна — её можно запускать повторно (например, на новой базе
данных после деплоя) или для смены владельца на другой email. Дальше этот
человек может назначать администраторов через /admin/users.
Как загрузить содержание уроков в базу данных массово. Если правите
src/lib/lessons/content.json напрямую (раздел 3.2) и хотите, чтобы эти же
версии оказались в БД (а не только в статическом файле-фолбэке), выполните:
npm run db:seed-lessons
Важно: команда перезаписывает в БД любой урок, чей ключ уже есть в
content.json — включая все текущие 32 урока. Если вы уже отредактировали
какой-то из этих 32 уроков через /admin/lessons и хотите сохранить эту
правку, не запускайте команду повторно для этого урока (либо сначала
обновите content.json тем же содержанием, прежде чем запускать).
Защита маршрутов. Доступ к /admin и ко всем API-маршрутам
/api/admin/* проверяется дважды независимо друг от друга: в
src/proxy.ts (до отрисовки страницы) и внутри каждого маршрута/страницы
(src/lib/admin-auth.ts). Это сделано специально, чтобы ошибка в одном
месте не открывала доступ посторонним — тот же принцип, что и у защиты
платных уроков.
Перед тем как выложить изменения на Vercel (или любой другой хостинг), запустите одну команду — она проверяет код, типы и то, что сайт вообще собирается:
npm run verify
Это эквивалент трёх шагов подряд:
| Команда | Что делает |
|---|---|
npm run lint | Проверяет код на ошибки и плохие практики (ESLint) |
npm run typecheck | Проверяет типы TypeScript без сборки (быстрее, чем полный build) |
npm run build | Собирает продакшн-версию сайта — если она падает, упадёт и деплой на Vercel |
Если вы правили только JSON-файлы из раздела 3 (тексты, уроки, цены),
запускать npm run verify не обязательно — эти файлы не участвуют в
проверке типов и почти никогда не ломают сборку (разве что из-за опечатки
в структуре JSON — в этом случае npm run build покажет, в какой строке
файла ошибка).
Если вы правили код (.ts / .tsx) — обязательно прогоните npm run verify
перед тем, как отправлять изменения в git / деплоить.
Файл .env (создаётся копированием .env.example, см. раздел 2) содержит:
| Переменная | Обязательна? | Назначение |
|---|---|---|
DATABASE_URL | да | Путь к локальной базе SQLite (file:./dev.db) |
SESSION_SECRET | да | Секрет для подписи cookie-сессии входа. Сгенерировать: openssl rand -hex 32 |
STRIPE_SECRET_KEY | нет | Секретный ключ Stripe. Пусто = демо-режим (см. ниже) |
STRIPE_WEBHOOK_SECRET | нет | Секрет для проверки подписи вебхуков Stripe |
STRIPE_PRICE_MONTHLY / STRIPE_PRICE_ANNUAL | нет | ID цен (Price ID) месячного/годового тарифа в Stripe |
Демо-режим (по умолчанию): если STRIPE_SECRET_KEY не задан, кнопка
«Suscribirme» на странице тарифов сразу активирует подписку в базе данных,
без обращения к настоящему Stripe. Это позволяет полностью протестировать
сайт (личный кабинет, доступ к урокам, отмену подписки) без создания
Stripe-аккаунта.
Реальный Stripe: чтобы принимать настоящие платежи, создайте аккаунт на
stripe.com, создайте два тарифа (Price) в разделе
Products, впишите их ID и секретные ключи в .env, а также настройте
вебхук в Stripe Dashboard на адрес <ваш-домен>/api/webhooks/stripe с
событиями checkout.session.completed, customer.subscription.updated,
customer.subscription.deleted, invoice.payment_failed.
.env (раздел 5).Важно про базу данных. Локально сайт использует SQLite — файл dev.db
на диске. На Vercel (как и на большинстве serverless-хостингов) файловая
система эфемерна: файл базы данных не сохраняется между запросами, поэтому
SQLite не подходит для продакшн-деплоя на Vercel как есть. Перед боевым
запуском на Vercel нужно:
DATABASE_URL в переменных окружения Vercel;src/lib/db.ts (сейчас там
@prisma/adapter-better-sqlite3) на адаптер, соответствующий выбранной
базе (например, @prisma/adapter-pg для Postgres) — это уже требует
участия разработчика.Пока этот шаг не сделан, деплой на Vercel будет работать для демонстрации интерфейса, но данные о пользователях/подписках/прогрессе не будут надёжно сохраняться между запросами.
src/
app/[lang]/ # страницы сайта (испанский/русский/английский)
courses/[level]/[lesson]/ # страница урока с вкладками и упражнениями
profile/ # личный кабинет студента
admin/ # панель администратора (уроки/подписки/роли, см. раздел 3.4)
pricing/, login/ # тарифы и вход
app/api/ # серверные обработчики (авторизация, checkout, вебхук Stripe)
api/admin/ # ручки панели администратора
components/ # переиспользуемые UI-компоненты
dictionaries/ # ← тексты интерфейса и цены (JSON, см. раздел 3.1)
lib/lessons/content.json # ← содержание уроков по умолчанию (JSON, см. раздел 3.2)
lib/roles.ts # роли (owner/admin/student) и проверки прав
lib/ # логика подписки, прогресса, авторизации
i18n/ # список поддерживаемых языков
proxy.ts # защита /courses/.../[lesson] и /admin на уровне запроса
prisma/schema.prisma # модели базы данных (пользователи, роли, подписки, уроки, прогресс)
prisma/set-owner.ts # скрипт назначения владельца (npm run db:set-owner)