feat: VK OAuth flow, catalog sync improvements, and expanded test suite

- Add VK OAuth implicit flow: /vk-auth redirect, /vk-callback JS page,
  /vk-callback/save endpoint with state validation
- Add VK_CLIENT_ID/VK_CLIENT_SECRET to config
- Add refresh_token/token_expires_at columns to vk_connections (migration 0006)
- Fix vk_catalog task: handle price/thumb_photo as string or dict (VK API v5.199)
- Fix connections/vk/test: use groups.getById instead of market.getAlbums
  (works with both user and group tokens)
- Add orphan deletion to mirror_to_vk: VK products not in Evotor are removed
- Handle ungrouped Evotor products: push to "Без категории" VK album
- Respect SyncConfig.is_enabled in mirror_to_vk
- Add product count column to catalog groups page
- Add group name column to catalog products page
- Expand test suite: 73 new tests covering connections routes, catalog routes,
  vk_sync task logic, and catalog task helpers (138 total, all passing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-05-12 15:09:47 +03:00
parent 4f4081c54c
commit 7b4f52b005
16 changed files with 1624 additions and 32 deletions

View File

@@ -35,6 +35,8 @@ class VkConnection(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
access_token = Column(Text, nullable=False)
refresh_token = Column(Text, nullable=True)
token_expires_at = Column(DateTime, nullable=True)
vk_user_id = Column(String(50), nullable=True)
first_name = Column(String(255), nullable=True)
last_name = Column(String(255), nullable=True)
@@ -167,6 +169,7 @@ class CachedProduct(Base):
allow_to_sell = Column(Boolean, nullable=True)
fetched_at = Column(DateTime, nullable=False)
synced_at = Column(DateTime, nullable=True)
vk_product_id = Column(String(50), nullable=True) # VK market item ID after first push
__table_args__ = (
UniqueConstraint("user_id", "evotor_id", name="uq_cached_products_user_evotor"),