Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/autofix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: autofix.ci # needed to securely identify the workflow

on:
pull_request:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
autofix:
name: autofix
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/[email protected]
with:
fetch-depth: 0
- name: Setup Tools
uses: tanstack/config/.github/setup@main
- name: Fix formatting
run: pnpm format
- name: Apply fixes
uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27
with:
commit-message: 'ci: apply automated fixes'
15 changes: 5 additions & 10 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@ jobs:
name: PR
runs-on: ubuntu-latest
steps:
- name: Git Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: Checkout
uses: actions/[email protected]
with:
node-version-file: .nvmrc
cache: pnpm
- name: Install Packages
run: pnpm install --frozen-lockfile
fetch-depth: 0
- name: Setup Tools
uses: tanstack/config/.github/setup@main
- name: Run Lint
run: pnpm lint
- name: Run Build
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v20.15.0
v20.19.6
15 changes: 11 additions & 4 deletions netlify/functions/scheduled/refresh-stats-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ export const handler: Handler = async (
// Invoke the background function
const functionUrl = `${process.env.URL}/.netlify/functions/refresh-stats-cache-background`

console.log('[refresh-stats-cache] Invoking background function:', functionUrl)
console.log(
'[refresh-stats-cache] Invoking background function:',
functionUrl
)

// Fire and forget - background function will run independently
const response = await fetch(functionUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cronSecret}`,
Authorization: `Bearer ${cronSecret}`,
},
body: JSON.stringify({
source: 'scheduled-function',
Expand All @@ -46,10 +49,14 @@ export const handler: Handler = async (

if (!response.ok) {
const errorText = await response.text()
throw new Error(`Background function invocation failed: ${response.status} - ${errorText}`)
throw new Error(
`Background function invocation failed: ${response.status} - ${errorText}`
)
}

console.log('[refresh-stats-cache] Background function invoked successfully')
console.log(
'[refresh-stats-cache] Background function invoked successfully'
)

return {
statusCode: 202, // Accepted - processing in background
Expand Down
19 changes: 14 additions & 5 deletions netlify/functions/scheduled/sync-github-releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const handler: Handler = async (
event: HandlerEvent,
context: HandlerContext
) => {
console.log('[sync-github-releases] Triggering background GitHub release sync...')
console.log(
'[sync-github-releases] Triggering background GitHub release sync...'
)

try {
const cronSecret = process.env.CRON_SECRET
Expand All @@ -29,14 +31,17 @@ export const handler: Handler = async (
// Invoke the background function
const functionUrl = `${process.env.URL}/.netlify/functions/sync-github-releases-background`

console.log('[sync-github-releases] Invoking background function:', functionUrl)
console.log(
'[sync-github-releases] Invoking background function:',
functionUrl
)

// Fire and forget - background function will run independently
const response = await fetch(functionUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cronSecret}`,
Authorization: `Bearer ${cronSecret}`,
},
body: JSON.stringify({
source: 'scheduled-function',
Expand All @@ -46,10 +51,14 @@ export const handler: Handler = async (

if (!response.ok) {
const errorText = await response.text()
throw new Error(`Background function invocation failed: ${response.status} - ${errorText}`)
throw new Error(
`Background function invocation failed: ${response.status} - ${errorText}`
)
}

console.log('[sync-github-releases] Background function invoked successfully')
console.log(
'[sync-github-releases] Background function invoked successfully'
)

return {
statusCode: 202, // Accepted - processing in background
Expand Down
8 changes: 6 additions & 2 deletions netlify/functions/sync-github-releases-background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ export const handler: Handler = async (
event: HandlerEvent,
context: HandlerContext
) => {
console.log('[sync-github-releases-background] Starting GitHub release sync...')
console.log(
'[sync-github-releases-background] Starting GitHub release sync...'
)

// Check authentication
const cronSecret = process.env.CRON_SECRET
const authHeader = event.headers.authorization || event.headers.Authorization

if (!cronSecret) {
console.error('[sync-github-releases-background] CRON_SECRET not configured')
console.error(
'[sync-github-releases-background] CRON_SECRET not configured'
)
return {
statusCode: 500,
body: JSON.stringify({
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dev:frontend": "pnpm run with-env vite dev",
"build": "vite build",
"start": "vite start",
"lint": "prettier --check '**/*' --ignore-unknown && eslint --ext .ts,.tsx ./src",
"lint": "eslint --ext .ts,.tsx ./src",
"format": "prettier --write '**/*' --ignore-unknown",
"linkAll": "node scripts/link.js",
"db:generate": "drizzle-kit generate",
Expand Down
13 changes: 9 additions & 4 deletions scripts/migrate-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This guide covers migrating data from Convex to PostgreSQL (Neon DB).
## Prerequisites

1. **Export Convex Data**

- Use Convex dashboard or CLI to export all tables
- Export format: JSON or CSV
- Tables to export:
Expand All @@ -31,15 +32,18 @@ This guide covers migrating data from Convex to PostgreSQL (Neon DB).
Create a transformation script that:

1. **Convert Convex IDs to UUIDs**

- Generate new UUIDs for each record
- Maintain mapping for foreign key relationships

2. **Transform Timestamps**

- Convex: milliseconds (number)
- PostgreSQL: `timestamp with time zone` (Date object)
- Convert: `new Date(timestamp)` for milliseconds

3. **Transform Arrays**

- Convex: stored as arrays
- PostgreSQL: array columns (already compatible)
- Ensure proper formatting
Expand Down Expand Up @@ -101,17 +105,19 @@ WHERE u.id IS NULL;
### Step 4: Post-Migration Tasks

1. **Set up indexes** (if not created by Drizzle):

```sql
-- GIN indexes for array columns
CREATE INDEX IF NOT EXISTS feed_entries_library_ids_gin_idx
CREATE INDEX IF NOT EXISTS feed_entries_library_ids_gin_idx
ON feed_entries USING GIN (library_ids);
CREATE INDEX IF NOT EXISTS feed_entries_tags_gin_idx
CREATE INDEX IF NOT EXISTS feed_entries_tags_gin_idx
ON feed_entries USING GIN (tags);

-- Full-text search (see drizzle/migrations/README.md)
```

2. **Verify functionality**:

- Test authentication flow
- Test feed queries
- Test admin interfaces
Expand All @@ -138,4 +144,3 @@ If migration fails:
- **entryId field**: The `feed_entries` table uses `entryId` (string) as the unique identifier, not `id` (UUID). Make sure to map Convex feed entry IDs to `entryId` field.
- **Metadata**: JSONB fields should be preserved as-is from Convex
- **Capabilities**: Array fields should be preserved as arrays

13 changes: 9 additions & 4 deletions scripts/test-db-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ async function testConnection() {

// Test 3: Count records
console.log('3. Counting records...')
const userCount = await db.select({ count: sql<number>`count(*)` }).from(users)
const roleCount = await db.select({ count: sql<number>`count(*)` }).from(roles)
const feedCount = await db.select({ count: sql<number>`count(*)` }).from(feedEntries)
const userCount = await db
.select({ count: sql<number>`count(*)` })
.from(users)
const roleCount = await db
.select({ count: sql<number>`count(*)` })
.from(roles)
const feedCount = await db
.select({ count: sql<number>`count(*)` })
.from(feedEntries)

console.log(`✓ Users: ${userCount[0]?.count ?? 0}`)
console.log(`✓ Roles: ${roleCount[0]?.count ?? 0}`)
Expand All @@ -57,4 +63,3 @@ async function testConnection() {
}

testConnection()

2 changes: 1 addition & 1 deletion src/components/FeedEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
}
}

export function FeedEntry({

Check warning on line 42 in src/components/FeedEntry.tsx

View workflow job for this annotation

GitHub Actions / PR

'FeedEntry' is already defined
entry,
showFullContent = false,
expanded: expandedProp,
Expand Down Expand Up @@ -253,7 +253,7 @@
className="text-xs font-semibold text-gray-900 dark:text-gray-100 truncate hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
>
{entry.title}
</Link>
</Link>
</TableCell>

{/* Excerpt */}
Expand Down
31 changes: 14 additions & 17 deletions src/components/FeedFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
// Update filters when debounced search changes
React.useEffect(() => {
onFiltersChange({ search: debouncedSearch || undefined })
}, [debouncedSearch])

Check warning on line 157 in src/components/FeedFilters.tsx

View workflow job for this annotation

GitHub Actions / PR

React Hook React.useEffect has a missing dependency: 'onFiltersChange'. Either include it or remove the dependency array. If 'onFiltersChange' changes too often, find the parent component that defines it and wrap that definition in useCallback

// Sync local state with prop when search prop changes externally
React.useEffect(() => {
Expand Down Expand Up @@ -206,7 +206,6 @@
releaseLevelsDiffer ||
(includePrerelease !== undefined && includePrerelease !== true)


// Render filter content (shared between mobile and desktop)
const renderFilterContent = () => (
<>
Expand All @@ -216,9 +215,7 @@
<FilterCheckbox
label="Featured"
checked={featured ?? false}
onChange={() =>
handleFeaturedChange(featured ? undefined : true)
}
onChange={() => handleFeaturedChange(featured ? undefined : true)}
count={facetCounts?.featured}
/>
</div>
Expand All @@ -234,9 +231,7 @@
onSelectNone={() => {
onFiltersChange({ releaseLevels: undefined })
}}
isAllSelected={
selectedReleaseLevels?.length === RELEASE_LEVELS.length
}
isAllSelected={selectedReleaseLevels?.length === RELEASE_LEVELS.length}
isSomeSelected={
selectedReleaseLevels !== undefined &&
selectedReleaseLevels.length > 0 &&
Expand Down Expand Up @@ -452,10 +447,7 @@
compact
/>
)}
<div
onClick={(e) => e.stopPropagation()}
className="flex-shrink-0"
>
<div onClick={(e) => e.stopPropagation()} className="flex-shrink-0">
<FilterSearch
value={searchInput}
onChange={handleSearchChange}
Expand All @@ -482,10 +474,7 @@
>
<LuHelpCircle className="w-4 h-4 text-gray-400 dark:text-gray-500 cursor-help" />
</Tooltip>
<ViewModeToggle
viewMode={viewMode}
onViewModeChange={onViewModeChange}
/>
<ViewModeToggle viewMode={viewMode} onViewModeChange={onViewModeChange} />
</div>
)

Expand All @@ -498,8 +487,16 @@
desktopHeader={desktopHeader}
>
{/* Search - Desktop */}
<div className={`mb-2 lg:block hidden ${viewMode === 'table' ? 'max-w-xs lg:max-w-none lg:w-full' : 'w-full'}`}>
<FilterSearch value={searchInput} onChange={handleSearchChange} className="w-full" />
<div
className={`mb-2 lg:block hidden ${
viewMode === 'table' ? 'max-w-xs lg:max-w-none lg:w-full' : 'w-full'
}`}
>
<FilterSearch
value={searchInput}
onChange={handleSearchChange}
className="w-full"
/>
</div>

{renderFilterContent()}
Expand Down
6 changes: 1 addition & 5 deletions src/components/FrameworkSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import * as React from 'react'
import { create } from 'zustand'
import { useNavigate, useParams } from '@tanstack/react-router'
import { Select } from './Select'
import {
Framework,
getLibrary,
LibraryId,
} from '~/libraries'
import { Framework, getLibrary, LibraryId } from '~/libraries'
import { getFrameworkOptions } from '~/libraries/frameworks'

export function FrameworkSelect({ libraryId }: { libraryId: LibraryId }) {
Expand Down
Loading
Loading