Skip to content

feat: video in iframe#255

Merged
hmbanan666 merged 1 commit intomainfrom
hub-video
Oct 31, 2025
Merged

feat: video in iframe#255
hmbanan666 merged 1 commit intomainfrom
hub-video

Conversation

@hmbanan666
Copy link
Copy Markdown
Collaborator

@hmbanan666 hmbanan666 commented Oct 31, 2025

Summary by CodeRabbit

  • New Features
    • Launched new Flow section displaying hub posts and items
    • Added comment functionality for flow items with full discussion threads
    • Redesigned bottom navigation menu for easier access to main sections
    • Introduced iframe support for embedded content within flow items
    • Implemented view tracking to highlight unread items in your flow

@hmbanan666 hmbanan666 self-assigned this Oct 31, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 31, 2025

Walkthrough

This PR introduces a flow item management system across multiple applications. It adds a Pinia store for hub-telegram to fetch and track flow items, implements UI components for displaying flow items and comments, extends the database schema with an iframe field and new flow item types (hub_post, hub_iframe), and provides server API endpoints for retrieving items, recording views, and managing comments.

Changes

Cohort / File(s) Summary
Icon name helper centralization
apps/atrium-telegram/shared/utils/helpers.ts, apps/hub-telegram/shared/utils/helpers.ts
Added new exported function getIconNameForFlowItem() that maps FlowItemWithData types to Lucide icon names via switch statement.
Atrium-telegram flow item UI
apps/atrium-telegram/app/components/flow/ItemCard.vue, apps/atrium-telegram/app/pages/flow/[itemId]/index.vue
Updated icon bindings to use centralized getIconNameForFlowItem(), replaced avatar with dynamic icon rendering, added iframe rendering block, adjusted text wrapping classes from break-words to wrap-break-word.
Hub-telegram flow store
apps/hub-telegram/app/stores/flow.ts
New Pinia store with reactive items list, nowViewedItemsCount computed property, update() method to fetch from API, and addView(itemId) method to track item views.
Hub-telegram flow item components
apps/hub-telegram/app/components/flow/ItemCard.vue, apps/hub-telegram/app/components/flow/ItemComment.vue, apps/hub-telegram/app/components/form/CreateFlowItemComment.vue
New components for displaying flow items with metadata, rendering comments with author info and actions, and form for composing new comments.
Hub-telegram generic UI components
apps/hub-telegram/app/components/ActiveCard.vue, apps/hub-telegram/app/components/CounterBadge.vue, apps/hub-telegram/app/components/Navigation.vue, apps/hub-telegram/app/components/NavigationButton.vue, apps/hub-telegram/app/components/Section.vue
New reusable components: button card with click feedback, conditional counter badge, sticky navigation bar with responsive grid, navigation button with scroll/redirect logic, and generic section wrapper.
Hub-telegram layout and pages
apps/hub-telegram/app/layouts/default.vue, apps/hub-telegram/app/pages/index.vue, apps/hub-telegram/app/pages/flow/[itemId]/index.vue
New default layout with confetti overlay support; homepage updated to display user greeting and flow item list; new detail page for viewing individual flow items with comments section and view tracking.
Hub-telegram routing
apps/hub-telegram/app/composables/useNavigation.ts
Replaced functional routes (/task/my, /ticket, /navigation) with placeholder routes (path #, title Позже, icon lock) to lock features behind future availability.
Hub-telegram app initialization
apps/hub-telegram/app/app.vue
Integrated useFlowStore() with initial and periodic 20-second update() calls alongside existing user store updates.
Server API endpoints
apps/hub-telegram/server/api/flow/hub-list.get.ts, apps/hub-telegram/server/api/flow/id/[itemId]/comment.post.ts, apps/hub-telegram/server/api/flow/id/[itemId]/view.post.ts
New GET endpoint to list hub flow items, POST endpoint to add comments with validation, and POST endpoint to record item views with duplicate prevention.
Database schema and repository
packages/database/src/tables.ts, packages/database/src/types/entities.ts, packages/database/src/repository/flow.ts
Added iframe varchar column to flow_items table; extended FlowItemType union with hub_post and hub_iframe; added Flow.listHubItems() method querying hub-type items with nested comments and views.
Type definitions
apps/hub-telegram/shared/types/index.ts
New exported type FlowItemWithData combining FlowItem with comments array (including user data) and views array.
Localization
apps/hub-telegram/i18n/locales/ru-RU.json
Added app.flow entry with value "Поток".

Sequence Diagram

sequenceDiagram
    participant User
    participant HubApp as Hub Telegram App
    participant FlowStore as Flow Store
    participant API as API Server
    participant DB as Database

    User->>HubApp: Load app
    HubApp->>FlowStore: useFlowStore()
    FlowStore->>API: GET /api/flow/hub-list
    API->>DB: listHubItems()
    DB-->>API: FlowItemWithData[]
    API-->>FlowStore: items
    FlowStore->>FlowStore: items = response

    User->>HubApp: View flow list on index page
    HubApp->>User: Render FlowItemCard components

    User->>HubApp: Click flow item
    HubApp->>FlowStore: Fetch item from store
    FlowStore-->>HubApp: FlowItemWithData
    HubApp->>User: Render detail page with comments

    rect rgba(100, 200, 150, 0.2)
    Note over User,DB: First view tracking
    HubApp->>FlowStore: addView(itemId)
    FlowStore->>API: POST /api/flow/id/{itemId}/view
    API->>DB: createItemView(itemId, userId)
    DB-->>API: ok
    API-->>FlowStore: { ok: true }
    FlowStore->>API: GET /api/flow/hub-list (refresh)
    end

    User->>HubApp: Type comment
    HubApp->>HubApp: Validate with schema
    User->>HubApp: Submit comment
    HubApp->>API: POST /api/flow/id/{itemId}/comment
    API->>DB: createItemComment(itemId, userId, text)
    DB-->>API: ok
    API-->>HubApp: { ok: true }
    HubApp->>FlowStore: update() (refresh)
    HubApp->>User: Show new comment in list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • Store integration & async flow: The flow store's update() and addView() methods perform multiple sequential API calls and store mutations; verify error handling and race condition prevention between concurrent requests
  • Comment submission & side effects: CreateFlowItemComment.vue performs a POST, then triggers update() and user.update() in sequence; ensure proper error states and loading indicators
  • View tracking logic: apps/hub-telegram/app/pages/flow/[itemId]/index.vue watches for view status and calls addView() on mount; verify the watch condition and that duplicate view attempts are prevented server-side
  • Database query with nested relations: Flow.listHubItems() includes nested comments with user data and views; confirm the query is performant and properly ordered (especially comments ordering by createdAt)
  • Type safety for flow items: New FlowItemWithData type extends database entities; cross-reference usage in components and store to ensure type consistency
  • Navigation route replacement: useNavigation.ts replaces routes with locked placeholders; verify this doesn't break existing navigation or cause routing errors

Possibly related PRs

  • feat: add flow item comment action #205: Modifies flow item UI and icon-mapping logic in ItemCard and detail pages—this PR centralizes icon name resolution and adds comment-related UI that mirrors changes from the related PR.
  • chore: navigation rework #152: Updates apps/atrium-telegram/app/components/flow/ItemCard.vue heading size and description styling—overlaps with this PR's wrapping class adjustments.
  • feat: create flow item action #179: Modifies the same flow UI files (ItemCard.vue and detail page) with avatar/icon rendering logic changes—similar scope and potential conflicts with this PR's icon rendering refactor.

Poem

🐰 A rabbit hopped through the code with glee,
Flow items and cards, what wonders to see!
With icons and comments, the hub came alive,
Now views are tracked—let the flow truly thrive!
From database seeds to the user's delight, 🌸

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title "feat: video in iframe" refers to a real aspect of the changeset—iframe support is indeed added via a new database column, type variants (hub_iframe), and iframe rendering logic in the flow detail page. However, this represents only a small portion of the actual changes. The primary objective of this PR is to implement a complete flow/feed management system with multiple features including: a new flow store with data fetching, a full flow item detail page with comments, view tracking, new UI components (ActiveCard, CounterBadge, Navigation, ItemCard, ItemComment), API routes for comments and views, and integration into the main hub page. The title captures only the iframe rendering capability without reflecting the broader flow/feed system that is the main point of these changes. Consider revising the title to reflect the primary objective. A more accurate title would be "feat: implement flow/feed system with comments and views" or "feat: add hub flow system" to better communicate the scope of this changeset, which encompasses flow item display, commenting, view tracking, and navigation—with iframe support being one component among many.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hub-video

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
32.5% Duplication on New Code (required ≤ 3%)
B Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@hmbanan666 hmbanan666 merged commit ed1d5a1 into main Oct 31, 2025
6 of 8 checks passed
@hmbanan666 hmbanan666 deleted the hub-video branch October 31, 2025 10:45
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (12)
apps/hub-telegram/app/components/Section.vue (1)

2-2: Consider using Vue's automatic class merging.

The explicit class prop with manual binding works but may be redundant. Vue 3 automatically merges class attributes to the root element by default.

You can either:

  • Keep the explicit prop if you need custom control over class handling
  • Remove the prop definition and let Vue handle class merging automatically

If you choose to remove the explicit prop:

-  <div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg" :class="props.class">
+  <div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg">
     <slot />
   </div>
 </template>

 <script setup lang="ts">
-const props = defineProps<{ class?: string }>()
 </script>

Also applies to: 8-8

apps/hub-telegram/app/components/Navigation.vue (1)

1-21: Consider accessibility improvements for navigation.

The navigation component looks functional, but consider these enhancements:

  1. ARIA labels: Add role="navigation" and aria-label="Main navigation" to the <nav> element for screen reader users.

  2. Grid column logic: The ternary condition mainRoutes.length > 3 ? 'grid-cols-4' : 'grid-cols-3' might not handle edge cases well (e.g., 1-2 routes). Consider whether the grid should adapt more granularly.

  3. Type safety: The script setup doesn't explicitly type the composable returns. Consider adding type annotations.

Apply this diff to improve accessibility:

     <nav
       v-if="isNavigationShown"
+      role="navigation"
+      aria-label="Main navigation"
       class="w-full h-24 tg-bg-bottom-bar border-t border-default rounded-t-lg motion-preset-slide-up"
     >
apps/atrium-telegram/app/components/flow/ItemCard.vue (1)

16-24: Consider localizing the hub indicator text.

The hardcoded Russian text "В Хабе" should be moved to the localization file for maintainability and consistency with the rest of the application.

Apply this diff to use localized text:

-          <p class="max-w-22 text-sm/4 font-bold">
-            В Хабе
-          </p>
+          <p class="max-w-22 text-sm/4 font-bold">
+            {{ $t('app.hub') }}
+          </p>

And add the localization key to the i18n file.

apps/hub-telegram/app/components/ActiveCard.vue (1)

2-8: Consider adding accessibility attributes and disabled state support.

The button lacks accessibility attributes and disabled state handling. Consider:

  1. Adding an optional disabled prop to prevent interaction when needed.
  2. Providing a way to pass aria-label or other ARIA attributes for better accessibility.
  3. Adding :disabled="disabled" to the button element.

Apply this diff to enhance the component:

 <template>
   <button
     class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0"
-    :class="props.class"
+    :class="[props.class, { 'opacity-50 pointer-events-none': disabled }]"
+    :disabled="disabled"
+    :aria-label="ariaLabel"
     @click="vibrate()"
   >
     <slot />
   </button>
 </template>

 <script setup lang="ts">
-const props = defineProps<{ class?: string }>()
+const props = defineProps<{ 
+  class?: string
+  disabled?: boolean
+  ariaLabel?: string
+}>()

 const { vibrate } = useFeedback()
 </script>
apps/hub-telegram/app/components/CounterBadge.vue (1)

3-3: Simplify the condition or make it more explicit.

The condition value != null && value !== '0' && value !== 0 is checking multiple cases, but the loose equality != combined with strict inequality checks (!==) may be confusing. Consider:

  1. Using strict equality throughout for consistency: value !== null && value !== undefined
  2. Converting to number and checking: value && Number(value) !== 0
  3. Adding a comment explaining why both string '0' and number 0 need separate checks.

Consider this more explicit approach:

   <UBadge
-    v-if="value != null && value !== '0' && value !== 0"
+    v-if="value !== null && value !== undefined && value !== '0' && value !== 0"
     size="lg"

Or a more concise approach if you want to treat falsy values uniformly:

   <UBadge
-    v-if="value != null && value !== '0' && value !== 0"
+    v-if="value && value !== '0' && Number(value) !== 0"
     size="lg"
apps/hub-telegram/app/pages/index.vue (2)

15-29: Add loading and empty states for flow items.

The flow items section doesn't handle loading or empty states. Users won't see feedback while items are loading or if there are no items available.

Consider adding loading and empty state handling:

     <div class="flex flex-col gap-2.5">
       <div class="flex flex-row justify-between items-center">
         <SectionTitle :title="$t('app.flow')" />
       </div>
-      <div class="flex flex-col gap-4">
+      <div v-if="flowStore.loading" class="flex justify-center p-8">
+        <UIcon name="i-lucide-loader-2" class="animate-spin" />
+      </div>
+      <div v-else-if="flowStore.items.length === 0" class="text-center p-8 text-gray-500">
+        {{ $t('app.noFlowItems') }}
+      </div>
+      <div v-else class="flex flex-col gap-4">
         <NuxtLink
           v-for="item in flowStore.items"
           :key="item.id"

Note: This assumes flowStore has a loading property. If not, you may need to add state management for loading.


41-41: Consider extracting avatar fallback logic to a composable or helper.

The avatar fallback logic uses a chained ternary with three fallbacks, which could be reused across components. Consider extracting it to a reusable utility.

Create a helper in shared/utils/helpers.ts:

export function getUserAvatar(user: User | null | undefined): string {
  return user?.initDataState?.user?.photo_url ?? user?.avatarUrl ?? ''
}

Then use it:

-const userAvatar = computed(() => userStore.initDataState?.user?.photo_url ?? userStore.avatarUrl ?? '')
+const userAvatar = computed(() => getUserAvatar(userStore))
apps/hub-telegram/app/components/form/CreateFlowItemComment.vue (1)

61-64: Consider handling partial failures in store updates.

If one of the store updates fails, the error is caught but the user still gets an error vibration, even though the comment was successfully created. Consider handling this more gracefully.

     await Promise.all([
-      flowStore.update(),
-      userStore.update(),
+      flowStore.update().catch(err => console.warn('Failed to refresh flow store:', err)),
+      userStore.update().catch(err => console.warn('Failed to refresh user store:', err)),
     ])

     vibrate('success')
     emit('success')

This way, the comment creation success is still communicated to the user even if the store refresh fails.

apps/hub-telegram/app/components/NavigationButton.vue (2)

2-5: Consider extracting the click handler logic to a method.

The ternary operator in the click handler is readable but could be clearer as a dedicated method.

Apply this diff to extract the logic:

   <button
     class="flex flex-col items-center justify-center gap-1 px-4 cursor-pointer tg-text-subtitle"
-    @click="(isMainPage && canScrollToTop && isThisRoute) ? handleScrollToTop() : handleRedirect(route.path)"
+    @click="handleClick"
   >

Add the method in the script section:

+function handleClick() {
+  if (isMainPage && canScrollToTop && isThisRoute) {
+    handleScrollToTop()
+  } else {
+    handleRedirect(route.path)
+  }
+}
+
 function handleScrollToTop() {
   vibrate()
   window.scrollTo({ top: 0, behavior: 'smooth' })
 }

2-46: Add accessibility attributes for better screen reader support.

The button lacks semantic attributes to indicate its navigation role and active state to assistive technologies.

Apply this diff to add accessibility attributes:

   <button
+    :aria-label="route.title"
+    :aria-current="(isThisRoute || isThisName) ? 'page' : undefined"
     class="flex flex-col items-center justify-center gap-1 px-4 cursor-pointer tg-text-subtitle"
     @click="(isMainPage && canScrollToTop && isThisRoute) ? handleScrollToTop() : handleRedirect(route.path)"
   >
apps/hub-telegram/app/stores/flow.ts (2)

6-6: Optimize reactivity by extracting user store reference.

Calling useUserStore().id directly within the computed callback may impact reactivity tracking and performance. Extract the store reference at the module level.

Apply this diff:

+const userStore = useUserStore()
+
 export const useFlowStore = defineStore('flow', () => {
   const items = ref<FlowItemWithData[]>([])
 
-  const nowViewedItemsCount = computed(() => items.value.filter((item) => !item.views.some((view) => view.userId === useUserStore().id)).length)
+  const nowViewedItemsCount = computed(() => items.value.filter((item) => !item.views.some((view) => view.userId === userStore.id)).length)

3-7: Consider adding loading states for better UX.

The store doesn't expose loading indicators, which would help UI components show loading spinners or disable interactions during API calls.

Apply this diff to add loading states:

 export const useFlowStore = defineStore('flow', () => {
   const items = ref<FlowItemWithData[]>([])
+  const isLoading = ref(false)
 
   const nowViewedItemsCount = computed(() => items.value.filter((item) => !item.views.some((view) => view.userId === useUserStore().id)).length)

Then set isLoading.value = true at the start of update() and addView(), and false in the finally block.

Expose it in the return statement:

   return {
     items,
+    isLoading,
 
     nowViewedItemsCount,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1991d53 and ae29fcd.

📒 Files selected for processing (26)
  • apps/atrium-telegram/app/components/flow/ItemCard.vue (4 hunks)
  • apps/atrium-telegram/app/pages/flow/[itemId]/index.vue (2 hunks)
  • apps/atrium-telegram/shared/utils/helpers.ts (2 hunks)
  • apps/hub-telegram/app/app.vue (2 hunks)
  • apps/hub-telegram/app/components/ActiveCard.vue (1 hunks)
  • apps/hub-telegram/app/components/CounterBadge.vue (1 hunks)
  • apps/hub-telegram/app/components/Navigation.vue (1 hunks)
  • apps/hub-telegram/app/components/NavigationButton.vue (1 hunks)
  • apps/hub-telegram/app/components/Section.vue (1 hunks)
  • apps/hub-telegram/app/components/flow/ItemCard.vue (1 hunks)
  • apps/hub-telegram/app/components/flow/ItemComment.vue (1 hunks)
  • apps/hub-telegram/app/components/form/CreateFlowItemComment.vue (1 hunks)
  • apps/hub-telegram/app/composables/useNavigation.ts (1 hunks)
  • apps/hub-telegram/app/layouts/default.vue (1 hunks)
  • apps/hub-telegram/app/pages/flow/[itemId]/index.vue (1 hunks)
  • apps/hub-telegram/app/pages/index.vue (2 hunks)
  • apps/hub-telegram/app/stores/flow.ts (1 hunks)
  • apps/hub-telegram/i18n/locales/ru-RU.json (1 hunks)
  • apps/hub-telegram/server/api/flow/hub-list.get.ts (1 hunks)
  • apps/hub-telegram/server/api/flow/id/[itemId]/comment.post.ts (1 hunks)
  • apps/hub-telegram/server/api/flow/id/[itemId]/view.post.ts (1 hunks)
  • apps/hub-telegram/shared/types/index.ts (2 hunks)
  • apps/hub-telegram/shared/utils/helpers.ts (1 hunks)
  • packages/database/src/repository/flow.ts (1 hunks)
  • packages/database/src/tables.ts (1 hunks)
  • packages/database/src/types/entities.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
apps/hub-telegram/shared/utils/helpers.ts (2)
apps/atrium-telegram/shared/utils/helpers.ts (1)
  • getIconNameForFlowItem (88-104)
apps/hub-telegram/shared/types/index.ts (1)
  • FlowItemWithData (16-19)
apps/hub-telegram/server/api/flow/hub-list.get.ts (1)
apps/web-app/server/services/db.ts (1)
  • db (164-164)
apps/hub-telegram/server/api/flow/id/[itemId]/view.post.ts (1)
apps/web-app/server/services/db.ts (1)
  • db (164-164)
apps/atrium-telegram/shared/utils/helpers.ts (2)
apps/hub-telegram/shared/utils/helpers.ts (1)
  • getIconNameForFlowItem (3-19)
apps/hub-telegram/shared/types/index.ts (1)
  • FlowItemWithData (16-19)
apps/hub-telegram/shared/types/index.ts (1)
packages/database/src/types/tables.ts (3)
  • FlowItemComment (169-169)
  • FlowItem (166-166)
  • FlowItemView (172-172)
apps/hub-telegram/app/stores/flow.ts (1)
apps/hub-telegram/shared/types/index.ts (1)
  • FlowItemWithData (16-19)
apps/hub-telegram/server/api/flow/id/[itemId]/comment.post.ts (2)
packages/schema/src/services/flow.ts (1)
  • createFlowItemCommentSchema (12-14)
apps/web-app/server/services/db.ts (1)
  • db (164-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (20)
apps/hub-telegram/app/components/Section.vue (1)

1-9: Clean and functional wrapper component.

The overall structure is well-organized and appropriate for a simple section wrapper component. The use of Tailwind utilities is consistent, and the slot pattern allows flexible content composition.

packages/database/src/types/entities.ts (1)

38-39: LGTM! Type extension looks good.

The addition of 'hub_post' and 'hub_iframe' to the FlowItemType union follows the existing naming convention and integrates well with the related changes across the PR (database schema, repository methods, and UI components).

apps/hub-telegram/i18n/locales/ru-RU.json (1)

24-24: LGTM! Localization addition is appropriate.

The Russian translation "Поток" for "flow" is accurate and follows the existing localization structure.

apps/hub-telegram/app/app.vue (2)

62-62: LGTM! Flow store initialization follows existing patterns.

The flow store is initialized consistently with the user store pattern.


75-79: No changes needed—error handling is already in place.

Both flow.update() and user.update() implement identical error handling patterns. Each method wraps its API call in a try-catch block that silently handles errors without rethrowing, so Promise.all() will never reject due to failures in either method. This is intentional and consistent across both store methods.

apps/atrium-telegram/shared/utils/helpers.ts (2)

1-1: LGTM! Type import is appropriate.

The import of FlowItemWithData provides type safety for the new helper function.


88-104: LGTM! Icon mapping function is well-implemented.

The function provides a clean, type-safe way to map flow item types to icons with:

  • Exhaustive case coverage for all FlowItemType values
  • Sensible default fallback
  • Consistent icon naming convention
  • Proper fall-through for related types (daily/weekly task reports)
apps/atrium-telegram/app/components/flow/ItemCard.vue (2)

12-12: LGTM! Icon helper usage is correct.

The component now uses the shared getIconNameForFlowItem helper, promoting consistency across the application.


76-76: LGTM! Hub detection logic is correct.

The computed property correctly identifies hub-related flow items by checking for both hub_iframe and hub_post types.

packages/database/src/repository/flow.ts (1)

32-50: <!-- [scratchpad_start]
[task verification review_comment_flow.ts continued]

[observations]

  • Full flow.ts now visible; imports from '../tables' which is the schema file location
  • Related methods: findItem (no limit), listItems (limit: 500), listHubItems (limit: 100)
  • No pagination parameters in any method - all hardcoded limits
  • Drizzle ORM is being used
  • Need to check tables definition file to see if index on type column exists

[pending]

  • Locate and inspect tables definition file (likely packages/database/src/tables.ts or similar)
  • Verify if index on type column exists in schema
  • Verify pagination hardcoding across methods (it appears intentional but inconsistent: 500 vs 100)
  • Determine if concerns in review comment are valid

[actions]

  1. Find and inspect tables.ts file
  2. Check flowItems table schema for indexes
  3. Verify pagination patterns to assess if this is a real concern or expected behavior
packages/database/src/tables.ts (1)

748-748: Verify iframe data flow and apply recommended constraints.

The iframe field on line 748 is defined as an unbounded varchar with no constraints. While the public creation schema (createFlowItemSchema) does not currently include this field, the concerns raised are valid:

  1. No visible validation/sanitization in the codebase for iframe content
  2. Unbounded storage poses efficiency and security risks if iframe data is ever populated
  3. Type discrimination shows 'hub_iframe' exists in the system but isn't creatable via standard API flow

The original suggestion to add validation, length constraints, and documentation remains sound. Confirm:

  • How iframe data enters the system (internal APIs, admin operations, etc.)
  • Whether iframe content needs HTML sanitization or URL validation
  • Apply reasonable length constraints and document the expected format
apps/hub-telegram/app/layouts/default.vue (1)

1-26: LGTM! Layout structure is well organized.

The layout properly:

  1. Wraps confetti in ClientOnly to avoid SSR issues
  2. Conditionally renders navigation based on user authentication
  3. Uses ref to dynamically calculate confetti stage dimensions
  4. Applies appropriate Telegram-specific classes (tg-text, tg-safe-area)
apps/hub-telegram/server/api/flow/hub-list.get.ts (1)

3-5: Error handling is missing; authentication is already handled by middleware.

The endpoint lacks try-catch for database errors, but authentication is already applied globally via apps/hub-telegram/server/middleware/01.auth.ts. Add error handling (without the redundant auth check):

 export default defineEventHandler(async () => {
+  try {
     return db.flow.listHubItems()
+  } catch (error) {
+    throw errorResolver(error)
+  }
 })

Explicit authentication checks in the endpoint are unnecessary—the middleware sets event.context.user for all requests and enforces auth before reaching API handlers. Focus on wrapping the database call with try-catch, consistent with other endpoints like api/auth/online.post.ts.

Likely an incorrect or invalid review comment.

apps/hub-telegram/server/api/flow/id/[itemId]/view.post.ts (1)

30-33: Incorrect review comment - authentication is guaranteed by middleware.

The authentication check is already enforced by the middleware (apps/hub-telegram/server/middleware/01.auth.ts). The middleware runs on all /api/* routes and throws a 401 error if the user is not authenticated, only setting event.context.user if authentication succeeds. By the time the route handler executes, event.context.user is guaranteed to exist.

The referenced comment endpoint (comment.post.ts) also accesses event.context.user.id without explicit checks on the same endpoint structure, confirming this is the intended architectural pattern. All other endpoints in the application follow the same approach.

Adding the suggested explicit check would be redundant and inconsistent with the codebase's established pattern of relying on middleware for authentication guarantees.

Likely an incorrect or invalid review comment.

apps/hub-telegram/server/api/flow/id/[itemId]/comment.post.ts (1)

31-35: The review comment is incorrect—authentication is already enforced by middleware.

The middleware at apps/hub-telegram/server/middleware/01.auth.ts protects all /api/** routes. It throws a 401 error if the user is not authenticated, and only sets event.context.user upon successful authentication. Since Nitro middleware always runs before the route handler, event.context.user.id is guaranteed to be defined and safe to access in comment.post.ts and view.post.ts. The suggested authentication check is redundant because the framework's middleware pattern already provides this guarantee.

Likely an incorrect or invalid review comment.

apps/hub-telegram/shared/utils/helpers.ts (1)

3-18: Icon mapping keeps hub items aligned.

This mirrors the atrium helper and adds hub_iframe, so all flow types render consistent icons across apps. Nicely done.

apps/hub-telegram/shared/types/index.ts (1)

12-19: Typed payload matches UI expectations.

Extending FlowItem with comment/view payloads gives the components the structure they expect without extra casting. Looks good.

apps/hub-telegram/app/components/NavigationButton.vue (2)

56-57: Ensure route.names is always defined as an array.

Line 57 accesses route.names.includes() without a null check. Verify that the NavigationRoute type guarantees names is always a non-null array to prevent runtime errors.

The script from the previous comment will also help verify the structure of the NavigationRoute type, specifically whether names is guaranteed to be an array.


50-50: NavigationRoute type is properly accessible via Nuxt's auto-import configuration.

The NavigationRoute type is used without explicit imports in both NavigationButton.vue and useNavigation.ts composable, indicating it's auto-imported by Nuxt. This is configured through the parent config (@roll-stack/ui extended in nuxt.config), which provides type auto-import capabilities. No action is required.

apps/hub-telegram/app/stores/flow.ts (1)

10-16: Verify that initDataRaw is always defined before making API calls.

The Authorization header uses initDataRaw.value without checking if it's defined. If the TMA SDK hasn't initialized, this could send invalid headers.

Consider adding a guard:

 async function update() {
+  if (!initDataRaw.value) {
+    console.warn('TMA init data not available')
+    return
+  }
+
   try {
     const data = await $fetch('/api/flow/hub-list', {

Apply the same check in addView() at line 34.

</h3>

<div class="w-full text-base/5 font-normal whitespace-pre-wrap break-words line-clamp-8">
<div class="w-full text-base/5 font-normal whitespace-pre-wrap wrap-break-word line-clamp-8">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the custom CSS class.

The class name changed from the standard Tailwind class break-words to wrap-break-word. Ensure that wrap-break-word is defined in your custom CSS, as it's not a standard Tailwind utility.


🏁 Script executed:

#!/bin/bash
# Search for wrap-break-word class definition in CSS files
rg -nP 'wrap-break-word' --type=css --type=vue --type=scss

Length of output: 27


I'll generate a corrected shell script to search for the wrap-break-word class definition with proper file type handling.


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for wrap-break-word class definition without type restrictions
# Check CSS and SCSS files
rg -nP 'wrap-break-word' --type=css --type=scss

# Also search in all files to catch definitions in Vue files, global configs, etc.
echo "---"
rg -nP 'wrap-break-word' .

Length of output: 2418


I need to check where this class is actually defined. Let me search for it in the component's style block and look for global CSS configuration files.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check the ItemCard.vue file content to see if it has a style block defining wrap-break-word
cat ./apps/atrium-telegram/app/components/flow/ItemCard.vue

echo "---"
# Search for CSS class definitions (look for dot followed by class name or similar patterns)
rg -nP '\bwrap-break-word\s*[{:]' .

echo "---"
# Look for Tailwind config files
fd -name 'tailwind.config.*' .

echo "---"
# Search in CSS files for any @apply or similar that might define this
rg -n '@apply.*wrap-break-word' .

echo "---"
# Look for global CSS or styles directory
fd -name '*.css' . | head -20

Length of output: 2855


Let me fix the syntax errors and search for Tailwind configuration and global CSS files:


---

🏁 Script executed:

```shell
#!/bin/bash
# Find tailwind config files
echo "=== Tailwind config files ==="
fd tailwind.config .

echo "=== Checking app root for style files ==="
ls -la ./apps/atrium-telegram/app/ | grep -i style

echo "=== Searching in nuxt.config for Tailwind config ==="
find ./apps/atrium-telegram -name 'nuxt.config.*' -o -name 'tailwind.config.*'

echo "=== Looking for app.vue ==="
find ./apps/atrium-telegram -name 'app.vue'

echo "=== Search for any CSS that defines wrap-break-word ==="
rg 'wrap-break-word' --type=css .

echo "=== Search for wrap-break-word in config files ==="
rg 'wrap-break-word' . -g '*.config.*' -g '*.json'

Length of output: 332


Let me check the Nuxt config and app.vue files for the class definition:


---

🏁 Script executed:

```shell
#!/bin/bash
# Check nuxt.config.ts for Tailwind configuration
echo "=== nuxt.config.ts content ==="
cat ./apps/atrium-telegram/nuxt.config.ts

echo ""
echo "=== app.vue content ==="
cat ./apps/atrium-telegram/app/app.vue

echo ""
echo "=== Search for wrap-break-word anywhere in config or app files ==="
rg 'wrap-break-word' ./apps/atrium-telegram/nuxt.config.ts ./apps/atrium-telegram/app/app.vue

Length of output: 3697


Now I found the global CSS file reference. Let me check the styles.css file:


---

🏁 Script executed:

```shell
#!/bin/bash
# Check the global styles.css file
echo "=== styles.css content ==="
cat ./apps/atrium-telegram/app/assets/css/styles.css

echo ""
echo "=== Check if wrap-break-word is defined ==="
rg 'wrap-break-word' ./apps/atrium-telegram/app/assets/css/

echo ""
echo "=== Look for other CSS files in assets ==="
find ./apps/atrium-telegram/app/assets -name '*.css' -o -name '*.scss'

Length of output: 3605


Add wrap-break-word class definition to your custom CSS.

The class wrap-break-word is used in 15+ locations across the codebase (ItemCard.vue:41, TaskInfoCard.vue, PartnerAgreementCard.vue, KitchenCard.vue, Card.vue, and several page files) but is not defined anywhere. It's not in your global styles.css, component style blocks, or Tailwind config. Either define this class in your CSS or revert to the standard Tailwind break-words class.

🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/flow/ItemCard.vue around line 41, the
class wrap-break-word is used but not defined anywhere; either add a CSS
definition for .wrap-break-word in a global stylesheet (or component scoped
style) that maps to the expected behavior (e.g., breaking long words and
preserving wrapping), or replace all usages with the standard Tailwind utility
break-words (or break-all if stricter breaking is required); update the 15+
occurrences across the codebase consistently and run a quick visual check to
confirm line-wrapping behaves as intended.

Comment on lines +11 to +21
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Block potential XSS via unchecked iframe src.

Line 13 binds item.iframe directly into the iframe src. If that field ever contains javascript:/data: or an attacker-controlled URL, the iframe executes in our origin, which is a stored XSS vector. Please normalize the value to trusted protocols or whitelist hosts before rendering.

-      <iframe
-        v-if="item?.iframe"
-        :src="item.iframe"
+      <iframe
+        v-if="iframeSrc"
+        :src="iframeSrc"
 const item = computed(() => flowStore.items.find((item) => item.id === params.itemId))
 
+const iframeSrc = computed(() => {
+  const src = item.value?.iframe
+  if (!src)
+    return undefined
+  try {
+    const parsed = new URL(src, 'https://example.invalid')
+    return parsed.protocol === 'https:' ? src : undefined
+  }
+  catch {
+    return undefined
+  }
+})
+
 const isViewed = computed(() => item.value?.views.some((view) => view.userId === userStore?.id))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
<iframe
v-if="iframeSrc"
:src="iframeSrc"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
Suggested change
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
const item = computed(() => flowStore.items.find((item) => item.id === params.itemId))
const iframeSrc = computed(() => {
const src = item.value?.iframe
if (!src)
return undefined
try {
const parsed = new URL(src, 'https://example.invalid')
return parsed.protocol === 'https:' ? src : undefined
}
catch {
return undefined
}
})
const isViewed = computed(() => item.value?.views.some((view) => view.userId === userStore?.id))
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/pages/flow/[itemId]/index.vue around lines 11 to 21,
the iframe src is bound directly to item.iframe which allows javascript:, data:,
or attacker-controlled URLs leading to stored XSS; validate and sanitize the
value before rendering by parsing the URL and only allowing safe protocols
(https: and optionally http:) and an explicit whitelist of trusted hostnames
(reject or null-out any url with protocol javascript:, data:, blob:, or an
unrecognized host), and if the value is not allowed do not render the iframe or
render a safe fallback; additionally add a restrictive iframe sandbox attribute
(e.g., sandbox="allow-scripts" avoided unless needed, prefer no
allow-same-origin/allow-top-navigation) and ensure allowfullscreen/allow
attributes are removed or minimized so that even if an attacker supplies a URL
it cannot execute in our origin.

Comment on lines +47 to +58
<script setup lang="ts">
import { getIconNameForFlowItem } from '#shared/utils/helpers'
import { format } from 'date-fns'
import { ru } from 'date-fns/locale/ru'

const { item } = defineProps<{
item: FlowItemWithData
}>()

const userStore = useUserStore()
const isViewed = computed(() => item.views.some((view) => view.userId === userStore?.id))
</script>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Import FlowItemWithData so the file type-checks.

Line 53 references FlowItemWithData, but the type isn’t imported, so vue-tsc will fail. Please add the missing type-only import.

 <script setup lang="ts">
-import { getIconNameForFlowItem } from '#shared/utils/helpers'
+import type { FlowItemWithData } from '#shared/types'
+import { getIconNameForFlowItem } from '#shared/utils/helpers'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script setup lang="ts">
import { getIconNameForFlowItem } from '#shared/utils/helpers'
import { format } from 'date-fns'
import { ru } from 'date-fns/locale/ru'
const { item } = defineProps<{
item: FlowItemWithData
}>()
const userStore = useUserStore()
const isViewed = computed(() => item.views.some((view) => view.userId === userStore?.id))
</script>
<script setup lang="ts">
import type { FlowItemWithData } from '#shared/types'
import { getIconNameForFlowItem } from '#shared/utils/helpers'
import { format } from 'date-fns'
import { ru } from 'date-fns/locale/ru'
const { item } = defineProps<{
item: FlowItemWithData
}>()
const userStore = useUserStore()
const isViewed = computed(() => item.views.some((view) => view.userId === userStore?.id))
</script>
🤖 Prompt for AI Agents
In apps/hub-telegram/app/components/flow/ItemCard.vue around lines 47 to 58, the
file references FlowItemWithData in the defineProps type but never imports it;
add a type-only import at the top of the script section (for example: import
type { FlowItemWithData } from '#shared/types') so TypeScript/vue-tsc can
resolve the type without pulling runtime code.

Comment on lines +49 to +72
async function onSubmit(event: FormSubmitEvent<CreateFlowItemComment>) {
emit('submitted')

try {
await $fetch(`/api/flow/id/${itemId}/comment`, {
method: 'POST',
headers: {
Authorization: `tma ${userStore.initDataRaw}`,
},
body: event.data,
})

await Promise.all([
flowStore.update(),
userStore.update(),
])

vibrate('success')
emit('success')
} catch (error) {
console.error(error)
vibrate('error')
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add loading state to prevent double submissions and improve UX.

The form doesn't track submission state, which can lead to:

  1. Double submissions if the user clicks submit multiple times
  2. No visual feedback during the API call
  3. The text field not being cleared after successful submission

Apply this diff to add loading state and clear the form:

 const state = ref<Partial<CreateFlowItemComment>>({
   text: undefined,
 })
+const isSubmitting = ref(false)

 async function onSubmit(event: FormSubmitEvent<CreateFlowItemComment>) {
+  if (isSubmitting.value) return
+  
   emit('submitted')
+  isSubmitting.value = true

   try {
     await $fetch(`/api/flow/id/${itemId}/comment`, {
       method: 'POST',
       headers: {
         Authorization: `tma ${userStore.initDataRaw}`,
       },
       body: event.data,
     })

     await Promise.all([
       flowStore.update(),
       userStore.update(),
     ])

+    state.value.text = undefined
     vibrate('success')
     emit('success')
   } catch (error) {
     console.error(error)
     vibrate('error')
+  } finally {
+    isSubmitting.value = false
   }
 }

Then update the button:

     <UButton
       type="submit"
       variant="solid"
       color="secondary"
       size="xl"
       icon="i-lucide-send"
       block
       class="mt-3"
-      :disabled="!state.text"
+      :disabled="!state.text || isSubmitting"
+      :loading="isSubmitting"
       :label="$t('common.send')"
     />

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/hub-telegram/app/components/form/CreateFlowItemComment.vue around lines
49 to 72, the submit handler lacks a loading state and does not clear the form,
allowing double submissions and no visual feedback; add a reactive loading
boolean (set true at start of submit and false in finally), early-return if
loading to prevent re-entry, disable the submit button via that loading state
and show a loading indicator on the button, clear the form model (or call the
form reset) after a successful POST, and ensure vibrate/error handling and
emit('success') happen only after success while keeping emit('submitted') at
submit start.

@@ -0,0 +1,9 @@
<template>
<div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg" :class="props.class">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the group/section utility is being used.

The group/section class suggests hover interactions with child elements, but it's unclear from this component alone whether slotted content actually uses the corresponding group-hover/section:* modifiers. If unused, it can be removed.

Run the following script to check if the group variant is referenced in the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Check if group-hover/section or similar modifiers are used

# Search for group-hover/section usage in Vue files
rg -n --type=vue 'group-hover/section' apps/hub-telegram/

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Description: Check if group-hover/section modifiers are used in the codebase

# Search for group-hover/section in all files (not restricting to vue type)
rg -n 'group-hover/section' apps/hub-telegram/

echo "---"

# Also search more broadly for group-hover with /section variant
rg -n 'group-hover.*section' apps/hub-telegram/

echo "---"

# Check where Section.vue component is imported/used
rg -n 'Section\.vue|<Section' apps/hub-telegram/ -A 3 -B 1

Length of output: 3738


🏁 Script executed:

#!/bin/bash
# View the full content of Section.vue to check for any internal group-hover usage
cat apps/hub-telegram/app/components/Section.vue

Length of output: 229


Remove the unused group/section utility class.

The group/section class in Section.vue enables group-hover/section:* modifiers for child elements, but a comprehensive search of the codebase confirms no such modifiers are used anywhere. The class can be safely removed.

- <div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg" :class="props.class">
+ <div class="relative p-4 tg-bg-section space-y-3.5 rounded-lg" :class="props.class">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg" :class="props.class">
<div class="relative p-4 tg-bg-section space-y-3.5 rounded-lg" :class="props.class">
🤖 Prompt for AI Agents
In apps/hub-telegram/app/components/Section.vue around line 2, remove the unused
Tailwind utility class "group/section" from the root div's class list; replace
the class attribute so it no longer includes "group/section" while preserving
the other classes and the dynamic :class binding, then run a quick grep for
"group-hover/section" to confirm no usages remain.

Comment on lines +11 to +21
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard the iframe src against unsafe protocols.

Line 13 pipes item.iframe straight into the iframe src. An attacker who stores javascript: (or a hostile URL) there gains script execution in the hub app. Please validate/whitelist the URL before rendering.

-      <iframe
-        v-if="item?.iframe"
-        :src="item.iframe"
+      <iframe
+        v-if="iframeSrc"
+        :src="iframeSrc"
 const item = computed(() => flowStore.items.find((item) => item.id === params.itemId))
 
+const iframeSrc = computed(() => {
+  const src = item.value?.iframe
+  if (!src)
+    return undefined
+  try {
+    const parsed = new URL(src, 'https://example.invalid')
+    return parsed.protocol === 'https:' ? src : undefined
+  }
+  catch {
+    return undefined
+  }
+})
+
 const isViewed = computed(() => item.value?.views.some((view) => view.userId === userStore?.id))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
<iframe
v-if="iframeSrc"
:src="iframeSrc"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
Suggested change
<iframe
v-if="item?.iframe"
:src="item.iframe"
width="100%"
height="240"
style="background-color: #000"
allow="autoplay; encrypted-media; fullscreen; screen-wake-lock;"
frameborder="0"
allowfullscreen
class="rounded-lg"
/>
const iframeSrc = computed(() => {
const src = item.value?.iframe
if (!src)
return undefined
try {
const parsed = new URL(src, 'https://example.invalid')
return parsed.protocol === 'https:' ? src : undefined
}
catch {
return undefined
}
})
🤖 Prompt for AI Agents
In apps/hub-telegram/app/pages/flow/[itemId]/index.vue around lines 11 to 21,
the iframe src is assigned directly from item.iframe which allows unsafe
protocols like javascript:; validate and whitelist the URL before rendering by
parsing item.iframe with the URL constructor (or a safe URL parser), ensure the
protocol is only http: or https: (or allowlist specific hosts/origins), and only
then bind it to src; additionally, harden the iframe by adding a restrictive
sandbox attribute (e.g., sandbox="allow-scripts" only if necessary) and
remove/limit permissive allow values to minimize exposure.

import { initData, useSignal } from '@tma.js/sdk-vue'

export const useFlowStore = defineStore('flow', () => {
const items = ref<FlowItemWithData[]>([])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that FlowItemWithData type is properly imported.

The type is used but not explicitly imported. Ensure it's auto-imported from the shared types module.

Run this script to verify the type is accessible:


🏁 Script executed:

#!/bin/bash
# Description: Verify FlowItemWithData type definition and imports

# Find type definition
echo "=== Type Definition ==="
rg -n --type=ts -A5 'type FlowItemWithData|interface FlowItemWithData'

# Check auto-imports configuration
echo -e "\n=== Auto-imports Config ==="
fd 'nuxt.config|auto-imports' --exec cat {}

Length of output: 10241


🏁 Script executed:

#!/bin/bash
# Check imports in hub-telegram flow.ts and auto-imports setup

echo "=== Full flow.ts file (hub-telegram) ==="
cat apps/hub-telegram/app/stores/flow.ts | head -30

echo -e "\n=== Looking for auto-imports.d.ts ==="
fd 'auto-imports.d.ts' --type f

echo -e "\n=== Checking for .nuxtrc files ==="
fd '.nuxtrc' --type f

echo -e "\n=== Hub-telegram nuxt.config content ==="
cat apps/hub-telegram/nuxt.config.ts

Length of output: 291


🏁 Script executed:

#!/bin/bash
# Search for flow.ts files and check hub-telegram structure

echo "=== All flow.ts files in repository ==="
fd 'flow.ts' --type f

echo -e "\n=== Hub-telegram directory structure ==="
fd . apps/hub-telegram -type d | head -20

echo -e "\n=== Files in apps/hub-telegram/app if it exists ==="
fd . apps/hub-telegram/app 2>/dev/null | head -20 || echo "Directory does not exist"

Length of output: 1426


🏁 Script executed:

#!/bin/bash
# Check the actual imports and first 40 lines of flow.ts

echo "=== First 40 lines of apps/hub-telegram/app/stores/flow.ts ==="
head -40 apps/hub-telegram/app/stores/flow.ts

echo -e "\n=== Search for FlowItemWithData import or reference ==="
rg -n 'FlowItemWithData|from.*types' apps/hub-telegram/app/stores/flow.ts -A2 -B2

Length of output: 1417


Add missing import for FlowItemWithData type.

The type is used on line 4 but not imported. Add:

import { FlowItemWithData } from '../shared/types'
🤖 Prompt for AI Agents
In apps/hub-telegram/app/stores/flow.ts around line 4 the FlowItemWithData type
is used but not imported; add the missing import statement: import {
FlowItemWithData } from '../shared/types' at the top of the file so the type
reference resolves.

Comment on lines +22 to +31
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('401')) {
// No
}
if (error.message.includes('404')) {
// Not found
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement proper error handling instead of empty catch blocks.

The error handling is inadequate:

  1. Empty catch blocks provide no feedback or logging
  2. Checking error.message.includes('401') is fragile - use error.statusCode instead
  3. Users have no indication when the fetch fails

Apply this diff to improve error handling:

     } catch (error) {
-      if (error instanceof Error) {
-        if (error.message.includes('401')) {
-          // No
-        }
-        if (error.message.includes('404')) {
-          // Not found
-        }
+      if (error && typeof error === 'object' && 'statusCode' in error) {
+        const statusCode = (error as { statusCode?: number }).statusCode
+        if (statusCode === 401) {
+          console.error('Unauthorized: Invalid TMA credentials')
+        } else if (statusCode === 404) {
+          console.error('Flow items endpoint not found')
+        } else {
+          console.error('Failed to fetch flow items:', error)
+        }
+      } else {
+        console.error('Failed to fetch flow items:', error)
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('401')) {
// No
}
if (error.message.includes('404')) {
// Not found
}
}
}
} catch (error) {
if (error && typeof error === 'object' && 'statusCode' in error) {
const statusCode = (error as { statusCode?: number }).statusCode
if (statusCode === 401) {
console.error('Unauthorized: Invalid TMA credentials')
} else if (statusCode === 404) {
console.error('Flow items endpoint not found')
} else {
console.error('Failed to fetch flow items:', error)
}
} else {
console.error('Failed to fetch flow items:', error)
}
}
🤖 Prompt for AI Agents
In apps/hub-telegram/app/stores/flow.ts around lines 22 to 31, the catch block
currently swallows errors and uses fragile message.includes checks; replace it
with concrete error handling: detect HTTP errors by checking a status code
property (e.g. error.statusCode or error.response?.status) instead of
error.message, log the full error (message and stack) to your logger, map 401
and 404 to explicit handling paths (auth failure and not-found) and for other
errors emit or return a user-visible error (throw a new Error or set an
observable/error state) so callers/users are notified of failures; ensure
non-HTTP errors are also logged and propagated.

Comment on lines +44 to +53
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('401')) {
// No
}
if (error.message.includes('404')) {
// Not found
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement proper error handling (same issues as update()).

This function has the same error handling problems as the update() function reviewed above:

  1. Empty catch blocks with no feedback
  2. Fragile error detection via message.includes()
  3. No logging or user notification

Apply similar improvements as suggested for the update() function.

🤖 Prompt for AI Agents
In apps/hub-telegram/app/stores/flow.ts around lines 44 to 53, the catch block
silently swallows errors and uses fragile string-matching on error.message;
replace this with robust error handling: detect HTTP/axios errors by checking
error.response?.status (or the library-specific status property) instead of
message.includes, log the full error and context via the module logger, and
either throw a wrapped error with a clear message or return a rejected
Promise/meaningful result so callers can react; also handle non-HTTP errors with
a generic log+rethrow path and include user-friendly messages for 401/404 cases
rather than empty branches.

Comment on lines +23 to +28
if (item.views.some((view) => view.userId === event.context.user.id)) {
throw createError({
statusCode: 400,
message: 'Already viewed',
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential N+1 performance issue: fetching all views to check user view.

The code loads the entire item with all views (Line 16) just to check if the current user has already viewed it (Line 23). This is inefficient, especially as the number of views grows.

Consider adding a dedicated method to check view existence:

// In database layer
hasUserViewedItem(itemId: string, userId: string): Promise<boolean>

Then refactor the handler:

-    const item = await db.flow.findItem(itemId)
-    if (!item) {
+    const [item, hasViewed] = await Promise.all([
+      db.flow.findItem(itemId),
+      db.flow.hasUserViewedItem(itemId, event.context.user.id),
+    ])
+    
+    if (!item) {
       throw createError({
         statusCode: 404,
         message: 'Item not found',
       })
     }
-    if (item.views.some((view) => view.userId === event.context.user.id)) {
+    if (hasViewed) {
       throw createError({

This avoids loading all view records just to check for existence.

🤖 Prompt for AI Agents
In apps/hub-telegram/server/api/flow/id/[itemId]/view.post.ts around lines 23 to
28, avoid loading the whole item.views array to check if the current user
already viewed the item; add a database-layer method such as
hasUserViewedItem(itemId: string, userId: string): Promise<boolean> that
performs an efficient existence check (e.g., SELECT 1 WHERE item_id = ? AND
user_id = ? LIMIT 1) and replace the item.views.some(...) check with a call to
that method, preserving the current behavior of throwing createError({
statusCode: 400, message: 'Already viewed' }) when true.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant