INSERT INTO app_settings(key, value_json, value_type, title, description, category) VALUES ( 'ai_qualifier_contract', '"OUTPUT SCHEMA (return array matching input order):\n{\"results\":[{\"id\":123,\"score\":8,\"decision\":\"accepted\",\"reason\":\"до 10 слов на русском\",\"reject_tag\":null}]}\n\nRules:\n- Input is a JSON array of posts.\n- Return JSON only. No markdown. No text outside JSON.\n- score must be integer 1..10.\n- decision must be exactly one of: \"accepted\", \"rejected\", \"maybe\".\n- reject_tag (rejected/maybe only) must be one of: \"meme\", \"no_product\", \"politics\", \"discount_only\", \"off_topic\", \"vacancy\", \"low_content\", \"wrong_language\", \"injection\", \"weapon\", null.\n- Return one result for every input post id."'::jsonb, 'text', 'Технический контракт квалификатора', 'JSON-схема и строгие правила ответа. Обычно не меняется при правке смыслового промпта.', 'AI Qualifier' ), ( 'ai_writer_contract', '"OUTPUT SCHEMA (return array matching input order):\n{\"rewrites\":[{\"id\":123,\"category\":\"разгрузка\",\"text\":\"готовый текст без хэштегов\",\"notes\":\"короткая заметка для редактора или null\"}]}\n\nRules:\n- Input is a JSON object with key \"posts\" containing accepted posts.\n- Each post includes producer_name and producer_tag. Use producer_name when it helps, but do not invent facts.\n- If post includes editor_regeneration_prompt, follow it as an additional editor instruction while still obeying facts and output schema.\n- Do not add hashtags to rewritten text. Pick exactly one category from the categories list in input.\n- Return JSON only. No markdown. No text outside JSON.\n- Return one rewrite for every input post id."'::jsonb, 'text', 'Технический контракт райтера', 'JSON-схема, выбор категории и запрет хэштегов в тексте. Обычно не меняется при правке стиля.', 'AI Writer' ) ON CONFLICT (key) DO NOTHING; UPDATE app_settings SET title='Свободная инструкция квалификатора', description='Роль, тон, критерии и примеры оценки. Технический JSON-контракт вынесен отдельно.' WHERE key='ai_qualifier_prompt'; UPDATE app_settings SET title='Свободная инструкция райтера', description='Стиль, голос канала, правила фактов и примеры текста. Технический JSON-контракт вынесен отдельно.' WHERE key='ai_writer_prompt'; CREATE TABLE IF NOT EXISTS admin_login_attempts ( id BIGSERIAL PRIMARY KEY, ip TEXT NOT NULL, login TEXT NOT NULL DEFAULT '', success BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_admin_login_attempts_ip_time ON admin_login_attempts(ip, created_at DESC); CREATE INDEX IF NOT EXISTS idx_admin_login_attempts_login_time ON admin_login_attempts(login, created_at DESC);