Add Russian inline comments

This commit is contained in:
“Naeel” 2026-02-23 20:09:41 +04:00
parent d7e584067c
commit edffcc5e64
5 changed files with 17 additions and 6 deletions

View File

@ -2,3 +2,5 @@
Simple Node.js CRUD app that writes to `nubes_test_table` and renders a small HTML UI. Simple Node.js CRUD app that writes to `nubes_test_table` and renders a small HTML UI.
If the input is empty, it inserts "Node did it". If the input is empty, it inserts "Node did it".
Примечание: таблица создается автоматически, а дубликаты подряд отсекаются по таймауту.

View File

@ -2,6 +2,7 @@ const http = require("http");
const { URL } = require("url"); const { URL } = require("url");
const { Pool } = require("pg"); const { Pool } = require("pg");
// Конфигурация Postgres, DATABASE_URL имеет приоритет.
function buildPgConfig() { function buildPgConfig() {
if (process.env.DATABASE_URL) { if (process.env.DATABASE_URL) {
return { return {
@ -20,14 +21,17 @@ function buildPgConfig() {
}; };
} }
// Общий пул подключений.
const pool = new Pool(buildPgConfig()); const pool = new Pool(buildPgConfig());
// Таблица для демо создается автоматически.
async function ensureTable() { async function ensureTable() {
await pool.query( await pool.query(
"CREATE TABLE IF NOT EXISTS nubes_test_table (id SERIAL PRIMARY KEY, test_data TEXT, created_at TIMESTAMP DEFAULT NOW())" "CREATE TABLE IF NOT EXISTS nubes_test_table (id SERIAL PRIMARY KEY, test_data TEXT, created_at TIMESTAMP DEFAULT NOW())"
); );
} }
// Простой парсер application/x-www-form-urlencoded.
function parseForm(body) { function parseForm(body) {
return body return body
.split("&") .split("&")
@ -38,6 +42,7 @@ function parseForm(body) {
}, {}); }, {});
} }
// Рендер HTML-страницы с CRUD-формами.
function renderPage(rows, error) { function renderPage(rows, error) {
const errorHtml = error ? `<p style="color:red">${error}</p>` : ""; const errorHtml = error ? `<p style="color:red">${error}</p>` : "";
const rowsHtml = rows const rowsHtml = rows
@ -118,6 +123,7 @@ function renderPage(rows, error) {
</html>`; </html>`;
} }
// Маршруты: /, /add, /update, /delete, /healthz.
async function handleRequest(req, res) { async function handleRequest(req, res) {
const url = new URL(req.url, `http://${req.headers.host}`); const url = new URL(req.url, `http://${req.headers.host}`);
@ -153,6 +159,7 @@ async function handleRequest(req, res) {
await ensureTable(); await ensureTable();
if (url.pathname === "/add") { if (url.pathname === "/add") {
const content = form.txt_content || "Node did it"; const content = form.txt_content || "Node did it";
// Защита от быстрых дублей подряд.
const { rows: lastRows } = await pool.query( const { rows: lastRows } = await pool.query(
"SELECT test_data, created_at FROM nubes_test_table ORDER BY id DESC LIMIT 1" "SELECT test_data, created_at FROM nubes_test_table ORDER BY id DESC LIMIT 1"
); );

View File

@ -18,23 +18,23 @@ export class Loopback4ExampleGithubApplication extends BootMixin(
constructor(options: ApplicationConfig = {}) { constructor(options: ApplicationConfig = {}) {
super(options); super(options);
// Set up the custom sequence // Подключаем кастомную sequence для обработки запросов.
this.sequence(MySequence); this.sequence(MySequence);
// Set up default home page // Главная страница из папки public.
this.static('/', path.join(__dirname, '../public')); this.static('/', path.join(__dirname, '../public'));
// Customize @loopback/rest-explorer configuration here // Включаем REST Explorer.
this.configure(RestExplorerBindings.COMPONENT).to({ this.configure(RestExplorerBindings.COMPONENT).to({
path: '/explorer', path: '/explorer',
}); });
this.component(RestExplorerComponent); this.component(RestExplorerComponent);
this.projectRoot = __dirname; this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here // Настройки автозагрузки контроллеров.
this.bootOptions = { this.bootOptions = {
controllers: { controllers: {
// Customize ControllerBooter Conventions here // Ищем контроллеры в папке controllers.
dirs: ['controllers'], dirs: ['controllers'],
extensions: ['.controller.js'], extensions: ['.controller.js'],
nested: true, nested: true,

View File

@ -3,6 +3,7 @@ import {ApplicationConfig, Loopback4ExampleGithubApplication} from './applicatio
export * from './application'; export * from './application';
export async function main(options: ApplicationConfig = {}) { export async function main(options: ApplicationConfig = {}) {
// Создаем и запускаем LoopBack приложение.
const app = new Loopback4ExampleGithubApplication(options); const app = new Loopback4ExampleGithubApplication(options);
await app.boot(); await app.boot();
await app.start(); await app.start();
@ -15,7 +16,7 @@ export async function main(options: ApplicationConfig = {}) {
} }
if (require.main === module) { if (require.main === module) {
// Run the application // Локальный запуск с базовой конфигурацией REST.
const config = { const config = {
rest: { rest: {
port: +(process.env.PORT ?? 3000), port: +(process.env.PORT ?? 3000),

View File

@ -1,3 +1,4 @@
import {MiddlewareSequence} from '@loopback/rest'; import {MiddlewareSequence} from '@loopback/rest';
// Стандартная sequence без кастомной логики.
export class MySequence extends MiddlewareSequence {} export class MySequence extends MiddlewareSequence {}