diff --git a/db/migrations/046_insta_parser_settings.sql b/db/migrations/046_insta_parser_settings.sql index e6562de..af1322c 100644 --- a/db/migrations/046_insta_parser_settings.sql +++ b/db/migrations/046_insta_parser_settings.sql @@ -8,6 +8,7 @@ VALUES ('insta_password', '""'::jsonb, 'secret', 'Instagram password', 'Password for the Instagram account used by instagrapi.', 'Instagram Parser'), ('insta_proxy_url', '""'::jsonb, 'str', 'Instagram proxy URL', 'Optional stable proxy, for example http://user:pass@host:port.', 'Instagram Parser'), ('insta_session_path', '"insta_session.json"'::jsonb, 'str', 'Instagram session path', 'Path to the persisted instagrapi session settings file.', 'Instagram Parser'), + ('insta_auth_status', '""'::jsonb, 'str', 'Instagram auth status', 'Last manual Instagram login result.', 'Instagram Parser'), ('insta_fetch_count', '5'::jsonb, 'int', 'Posts to inspect', 'How many latest posts to inspect per account visit.', 'Instagram Parser'), ('insta_delay_base_minutes', '35'::jsonb, 'int', 'Account visit delay, minutes', 'Base pause after checking one Instagram account.', 'Instagram Parser'), ('insta_delay_random_minutes', '5'::jsonb, 'int', 'Delay random spread, minutes', 'Random +/- spread added to the base account visit delay.', 'Instagram Parser'), diff --git a/db/migrations/047_insta_manual_auth.sql b/db/migrations/047_insta_manual_auth.sql new file mode 100644 index 0000000..f80d5b5 --- /dev/null +++ b/db/migrations/047_insta_manual_auth.sql @@ -0,0 +1,12 @@ +INSERT INTO app_settings(key, value_json, value_type, title, description, category) +VALUES + ('insta_auth_status', '""'::jsonb, 'str', 'Instagram auth status', 'Last manual Instagram login result.', 'Instagram Parser') +ON CONFLICT (key) DO NOTHING; + +UPDATE app_settings +SET description='Used only by the manual Instagram login button. The parser reads the saved session file and does not auto-login.' +WHERE key='insta_login'; + +UPDATE app_settings +SET description='Used only by the manual Instagram login button. The parser reads the saved session file and does not auto-login.' +WHERE key='insta_password'; diff --git a/src/vk_parser_app/admin.py b/src/vk_parser_app/admin.py index 53acc71..cd346ec 100644 --- a/src/vk_parser_app/admin.py +++ b/src/vk_parser_app/admin.py @@ -37,7 +37,7 @@ from .workers.tg_poster import TelegramPoster from .workers.tg_reactor import TelegramReactor from .workers.vk_poster import VKPoster from .workers.vk_storage_uploader import TelegramStorageUploader -from .workers.insta_parser import InstaParserWorker +from .workers.insta_parser import InstaParserWorker, instagram_login COOKIE_NAME = "vk_parser_admin" VK_OAUTH_VERIFIER_COOKIE = "vk_oauth_verifier" @@ -196,6 +196,7 @@ CATEGORY_TITLES = { "MAX Poster": "MAX-постер", "Publishing": "Публикации", "Daily Report": "Ежедневный отчет", + "Instagram Parser": "Instagram-парсер", "Parser": "Парсер", "Uploader": "Аплоадер", "VK": "VK API", @@ -212,6 +213,7 @@ CATEGORY_ORDER = { "Site Poster": 52, "Publishing": 53, "Daily Report": 55, + "Instagram Parser": 56, "Parser": 60, "VK": 70, "Uploader": 80, @@ -3949,6 +3951,51 @@ async def worker_toggle(request: Request, worker_name: str, csrf_token: str = Fo return redirect("/workers") +@app.post("/instagram-auth/login") +async def instagram_auth_login(request: Request, csrf_token: str = Form(...)): + user = await get_current_user(request) + if not user: + return redirect("/login") + require_csrf(user, csrf_token) + login = str(await fetch_setting("insta_login", "") or "").strip() + password = str(await fetch_setting("insta_password", "") or "").strip() + proxy = str(await fetch_setting("insta_proxy_url", "") or "").strip() + session_path = str(await fetch_setting("insta_session_path", "insta_session.json") or "").strip() + pool = await get_pool() + if not login or not password: + status_text = "missing login or password" + else: + try: + await asyncio.to_thread(instagram_login, login, password, proxy, session_path) + status_text = f"ok: session saved to {session_path or 'insta_session.json'} at {datetime.now(timezone.utc).isoformat()}" + await pool.execute( + """ + INSERT INTO app_settings(key, value_json, value_type, title, description, category) + VALUES('insta_cooldown_until', '""'::jsonb, 'str', 'Cooldown until', '', 'Instagram Parser') + ON CONFLICT (key) DO UPDATE + SET value_json='""'::jsonb, + description='', + updated_at=NOW() + """ + ) + except Exception as exc: + status_text = f"failed: {str(exc)[:1000]}" + await pool.execute( + """ + INSERT INTO app_settings(key, value_json, value_type, title, description, category, updated_by) + VALUES('insta_auth_status', $1::jsonb, 'str', 'Instagram auth status', 'Last manual Instagram login result.', 'Instagram Parser', $2) + ON CONFLICT (key) DO UPDATE + SET value_json=$1::jsonb, + updated_by=$2, + updated_at=NOW() + """, + json.dumps(status_text), + user["id"], + ) + await audit(user["id"], "instagram.login", "setting", None, {"status": status_text}) + return redirect("/workers") + + @app.post("/settings/save") async def settings_save(request: Request, csrf_token: str = Form(...), key: str = Form(...), value: str = Form(...)): user = await get_current_user(request) diff --git a/src/vk_parser_app/templates/workers.html b/src/vk_parser_app/templates/workers.html index 93c0952..933df82 100644 --- a/src/vk_parser_app/templates/workers.html +++ b/src/vk_parser_app/templates/workers.html @@ -8,6 +8,25 @@