Заметки и сниппеты
Личная коллекция команд и запросов
PostgreSQL / Supabase
Колонки таблицы одной строкой
Список колонок через запятую — для копирования в SELECT, INSERT или быстрой инспекции схемы.
SELECT string_agg(column_name, ', ' ORDER BY ordinal_position) AS columns
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'workflow_costs';
Перед кликом «Copy» скопируйте имя нужной таблицы — оно подставится автоматически.
Полная структура таблицы
Имя, тип, nullable и default для каждой колонки. Используется при дебаге миграций и проверке после ALTER TABLE.
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'llm_generated_articles'
ORDER BY ordinal_position;
Атомарная архивация статьи
Удаление из основной таблицы с одновременной вставкой в архив. Атомарно — без промежуточных состояний.
WITH deleted AS (
DELETE FROM llm_generated_articles
WHERE article_id = 'ARTICLE_UUID'
AND batch_id = 'BATCH_ID'
RETURNING *
)
INSERT INTO llm_generated_articles_archive
SELECT * FROM deleted;
Перед копированием положите в буфер UUID статьи — он подставится в article_id. batch_id отредактируйте после вставки в SQL Editor.
Восстановление статьи из архива
Обратная операция — переносит запись из _archive назад в основную таблицу.
WITH restored AS (
DELETE FROM llm_generated_articles_archive
WHERE article_id = 'ARTICLE_UUID'
AND batch_id = 'BATCH_ID'
RETURNING *
)
INSERT INTO llm_generated_articles
SELECT * FROM restored;
Стоимость и длительность последнего запуска
Агрегат по последнему run_id в llm_usage_log: длительность, число вызовов, токены, USD.
SELECT
run_id,
MIN(created_at) AS начало,
MAX(created_at) AS конец,
MAX(created_at)::timestamp - MIN(created_at)::timestamp AS длительность,
COUNT(*) AS вызовов_llm,
SUM(input_tokens) AS input_tok,
SUM(output_tokens) AS output_tok,
ROUND(SUM(estimated_cost_usd)::numeric, 4) AS стоимость_usd
FROM llm_usage_log
WHERE run_id = (
SELECT run_id FROM llm_usage_log
ORDER BY created_at DESC
LIMIT 1
)
GROUP BY run_id;
Полная сумма за сегодня
Сводка по всем запускам с начала суток — завершённым и незавершённым: общий cost, токены, число уникальных run_id и LLM-вызовов.
SELECT
ROUND(SUM(estimated_cost_usd)::numeric, 4) AS total_cost_usd,
SUM(input_tokens) AS total_in_tok,
SUM(output_tokens) AS total_out_tok,
COUNT(DISTINCT run_id) AS distinct_runs,
COUNT(*) AS total_llm_calls
FROM llm_usage_log
WHERE created_at >= CURRENT_DATE;
CURRENT_DATE — полночь по таймзоне Postgres-сессии. Если БД в UTC, «сегодня» отсчитывается с 00:00 UTC.
YTK — YouTube Knowledge Pipeline
Universal Collector (1U) → Universal Scorer (2U) → ytk_channels. Канал = данные, не код.
Сводка по всем каналам
Что собрано и отскорено по каждому каналу: видео, с метриками, с субтитрами, scraped/skipped, уже со score, диапазон дат.
SELECT
channel_handle,
COUNT(*) AS videos,
COUNT(*) FILTER (WHERE view_count IS NOT NULL) AS with_metrics,
COUNT(*) FILTER (WHERE raw_subtitles IS NOT NULL) AS with_subs,
COUNT(*) FILTER (WHERE processing_status = 'scraped') AS scraped,
COUNT(*) FILTER (WHERE processing_status = 'skipped') AS skipped,
COUNT(*) FILTER (WHERE score IS NOT NULL) AS scored,
MIN(published_at)::date AS oldest,
MAX(published_at)::date AS newest
FROM youtube_knowledge
GROUP BY channel_handle
ORDER BY videos DESC;
Проверка целостности канала
Битые строки и рассинхрон: видео в статусе scraped, но без субтитров (должно быть 0). Замените handle.
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE title IS NULL OR title = '') AS no_title,
COUNT(*) FILTER (WHERE view_count IS NULL) AS no_views,
COUNT(*) FILTER (WHERE raw_subtitles IS NULL
AND processing_status = 'scraped') AS scraped_but_no_subs
FROM youtube_knowledge
WHERE channel_handle = 'elton_labs';
Лучшие видео (score ≥ 7)
Топовые видео по оценке с датой публикации. Можно убрать фильтр канала для всех сразу.
SELECT video_id, score, published_at::date AS published, title
FROM youtube_knowledge
WHERE score >= 7
AND channel_handle = 'elton_labs'
ORDER BY score DESC, published_at DESC;
video_id одной строкой (экспорт)
Все ID лучших видео через запятую — одна ячейка для копирования (например в поле video_ids Collector).
SELECT string_agg(video_id, ',') AS ids
FROM youtube_knowledge
WHERE score >= 7
AND channel_handle = 'elton_labs';
Распределение оценок по каналу
Сколько видео на каждый балл и средняя оценка — для калибровки scorer-промпта.
SELECT score, COUNT(*) AS videos
FROM youtube_knowledge
WHERE channel_handle = 'elton_labs'
AND score IS NOT NULL
GROUP BY score
ORDER BY score DESC;
Пере-скоринг: сброс оценок
Обнуляет score канала, чтобы Scorer прогнал заново (берёт scraped + score IS NULL). Промпт не меняется.
UPDATE youtube_knowledge
SET score = NULL,
score_reason = NULL,
score_weaknesses = NULL,
processing_status = 'scraped'
WHERE channel_handle = 'elton_labs';
Пере-сбор: удаление для повторного Collector
Collector дедуплицирует и НЕ обновляет существующие строки. Для пересбора (после фикса субтитров) — удалить пустые.
DELETE FROM youtube_knowledge
WHERE channel_handle = 'elton_labs'
AND raw_subtitles IS NULL;
Список каналов в ytk_channels
Конфиг каналов: handle, ниша, длина scorer-промпта, статус. prompt_len > 0 = промпт на месте.
SELECT channel_handle, channel_id, niche,
scorer_skill_name,
length(scorer_system_prompt) AS prompt_len,
is_active, scrape_status
FROM ytk_channels
ORDER BY channel_handle;
Добавить новый канал
Шаблон INSERT. $prompt$…$prompt$ — dollar-quoting для кириллицы/спецсимволов в промпте. Промпт ниша-специфичный.
INSERT INTO ytk_channels (
channel_id, channel_handle, channel_title,
scorer_skill_name, niche, scorer_system_prompt
) VALUES (
'UC...',
'channel_handle',
'Channel Title',
'youtube-scorer-NAME-v1',
'niche-slug',
$prompt$Ты — асессор видео канала ...
ШКАЛА ОЦЕНОК: ...
Выдай JSON: {"score": 1-10, "weaknesses": "...", "reason": "..."}$prompt$
);
Стоимость последних прогонов Scorer
Сводный cost по запускам скоринга. Должен быть ненулевым (агрегируется из llm_usage_log по run_id).
SELECT workflow_name, total_tokens, total_cost_usd, status, created_at
FROM workflow_costs
WHERE category = 'youtube-knowledge-scoring'
ORDER BY created_at DESC
LIMIT 10;
PowerShell
Функции Push-Site и Edit-Site в профиль
Запись в $PROFILE, чтобы любой статический сайт из ~/Sites/<name>/ заливался одной командой Push-Site <name>.
function Push-Site {
param([Parameter(Mandatory)][string]$Name)
$local = "$HOME\Sites\$Name\index.html"
if (-not (Test-Path $local)) {
Write-Host "Файл не найден: $local" -ForegroundColor Red
return
}
scp $local "n8n:/opt/static/$Name/index.html"
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ https://$Name.peniaze-ai.work" -ForegroundColor Green
}
}
function Edit-Site {
param([Parameter(Mandatory)][string]$Name)
notepad "$HOME\Sites\$Name\index.html"
}
Цикл правок: Edit-Site notes → правите → сохраняете → Push-Site notes → Ctrl+F5 в браузере.
Сервер (n8n / Caddy)
Перезагрузка Caddy после правки конфига
Без рестарта контейнера. Выполнять после редактирования /opt/n8n/Caddyfile.
docker exec n8n-caddy-1 caddy reload --config /etc/caddy/Caddyfile
Поиск в выводе Windows Terminal
Поиск по буферу в Windows Terminal
Ctrl + Shift + F — открывает строку поиска. Вводите UA='Mozilla (или любую уникальную часть), Enter — терминал перематывает к найденному месту и подсвечивает совпадение. Стрелки ↑/↓ — следующее/предыдущее вхождение.
Дальше выделяете мышью с того места, где курсор остановился, до конца нужного фрагмента, и Ctrl + Shift + C — копирует выделенное.
Если используете старый conhost — там Ctrl + F в Mark mode, аналогично.
Запись сессии в файл с поиском по контексту
Для длинных диагностических сессий — пишем всё в файл, потом ищем с захватом N строк после маркера.
# Записать всю сессию в файл
Start-Transcript -Path "$HOME\Downloads\diag-$(Get-Date -Format 'yyyyMMdd-HHmm').log"
# ... ваши ssh-команды ...
Stop-Transcript
# Затем искать в файле:
Select-String -Path "$HOME\Downloads\diag-*.log" -Pattern "UA='Mozilla" -Context 0,200
-Context 0,200 — совпадение + 200 строк после.
Разовый SSH с записью вывода в файл
Команды из bash-скрипта пайпятся через ssh, вывод одновременно идёт на экран и в лог.
ssh n8n 'bash -s' < commands.sh `
| Tee-Object -FilePath "$HOME\Downloads\out.log"