diff --git a/.do/app.yaml b/.do/app.yaml index 3f28965..f22be37 100644 --- a/.do/app.yaml +++ b/.do/app.yaml @@ -68,3 +68,4 @@ static_sites: routes: - path: / + diff --git a/.gitignore b/.gitignore index b0a135b..ba398a1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ coverage/ # Misc .turso/ + diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 5819025..6e36a09 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -240,3 +240,4 @@ Available sizes: **Ready to deploy?** Follow the steps above and you'll be live in minutes! 🚀 + diff --git a/QUICKSTART.md b/QUICKSTART.md index 61706b9..94933e1 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -79,3 +79,4 @@ See [DEPLOYMENT.md](./DEPLOYMENT.md) for detailed instructions and troubleshooti **Total: $5/month** + diff --git a/backend/package.json b/backend/package.json index de63f9f..af822b5 100644 --- a/backend/package.json +++ b/backend/package.json @@ -34,3 +34,4 @@ } } + diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts index 746f57c..cb721ad 100644 --- a/backend/src/config/database.ts +++ b/backend/src/config/database.ts @@ -29,3 +29,4 @@ export async function setSetting(key: string, value: string): Promise { }); } + diff --git a/backend/src/config/env.ts b/backend/src/config/env.ts index 1ef7a69..1763964 100644 --- a/backend/src/config/env.ts +++ b/backend/src/config/env.ts @@ -58,3 +58,4 @@ export const env = { initialAdminPassword: process.env.INITIAL_ADMIN_PASSWORD }; + diff --git a/backend/src/controllers/auth.controller.ts b/backend/src/controllers/auth.controller.ts index 50810b3..d10063b 100644 --- a/backend/src/controllers/auth.controller.ts +++ b/backend/src/controllers/auth.controller.ts @@ -151,6 +151,16 @@ export async function logout(req: AuthRequest, res: Response) { export async function getCurrentUser(req: AuthRequest, res: Response) { try { + if (!req.userId) { + return res.status(401).json({ + success: false, + error: { + code: 'UNAUTHORIZED', + message: 'User not authenticated' + } + }); + } + const result = await db.execute({ sql: 'SELECT id, username, last_login FROM users WHERE id = ?', args: [req.userId] @@ -188,3 +198,4 @@ export async function getCurrentUser(req: AuthRequest, res: Response) { } } + diff --git a/backend/src/controllers/channels.controller.ts b/backend/src/controllers/channels.controller.ts index 8f7397a..3a1f104 100644 --- a/backend/src/controllers/channels.controller.ts +++ b/backend/src/controllers/channels.controller.ts @@ -271,3 +271,4 @@ export async function refreshChannel(req: AuthRequest, res: Response) { } } + diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 9b24b19..9a2e9a7 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -42,3 +42,4 @@ export function authMiddleware( } } + diff --git a/backend/src/middleware/errorHandler.ts b/backend/src/middleware/errorHandler.ts index 51687d1..bb5c1b5 100644 --- a/backend/src/middleware/errorHandler.ts +++ b/backend/src/middleware/errorHandler.ts @@ -22,3 +22,4 @@ export function errorHandler( }); } + diff --git a/backend/src/middleware/rateLimiter.ts b/backend/src/middleware/rateLimiter.ts index 9ccd2b9..7d704d7 100644 --- a/backend/src/middleware/rateLimiter.ts +++ b/backend/src/middleware/rateLimiter.ts @@ -28,3 +28,4 @@ export const apiLimiter = rateLimit({ legacyHeaders: false }); + diff --git a/backend/src/routes/auth.routes.ts b/backend/src/routes/auth.routes.ts index 2ba83da..78c1392 100644 --- a/backend/src/routes/auth.routes.ts +++ b/backend/src/routes/auth.routes.ts @@ -13,3 +13,4 @@ router.get('/me', authMiddleware, getCurrentUser); export default router; + diff --git a/backend/src/routes/channels.routes.ts b/backend/src/routes/channels.routes.ts index 93fc1b2..c11fa86 100644 --- a/backend/src/routes/channels.routes.ts +++ b/backend/src/routes/channels.routes.ts @@ -15,3 +15,4 @@ router.put('/:id/refresh', authMiddleware, refreshChannel); export default router; + diff --git a/backend/src/routes/videos.routes.ts b/backend/src/routes/videos.routes.ts index c3ae1bc..b68b358 100644 --- a/backend/src/routes/videos.routes.ts +++ b/backend/src/routes/videos.routes.ts @@ -13,3 +13,4 @@ router.post('/refresh', authMiddleware, refreshVideos); export default router; + diff --git a/backend/src/services/auth.service.ts b/backend/src/services/auth.service.ts index 4cd4416..3e3e804 100644 --- a/backend/src/services/auth.service.ts +++ b/backend/src/services/auth.service.ts @@ -9,7 +9,7 @@ export async function createTokens(userId: number, username: string) { const accessToken = jwt.sign( { userId, username, type: 'access' }, env.jwtSecret, - { expiresIn: env.accessTokenExpiry } + { expiresIn: env.accessTokenExpiry as string } ); // Refresh token (long-lived) @@ -17,7 +17,7 @@ export async function createTokens(userId: number, username: string) { const refreshToken = jwt.sign( { token: refreshTokenValue, userId, type: 'refresh' }, env.jwtRefreshSecret, - { expiresIn: env.refreshTokenExpiry } + { expiresIn: env.refreshTokenExpiry as string } ); // Store refresh token in database @@ -59,7 +59,7 @@ export async function refreshAccessToken(refreshToken: string) { const accessToken = jwt.sign( { userId: tokenData.user_id, username: tokenData.username, type: 'access' }, env.jwtSecret, - { expiresIn: env.accessTokenExpiry } + { expiresIn: env.accessTokenExpiry as string } ); return { accessToken }; @@ -90,3 +90,4 @@ export async function hashPassword(password: string): Promise { return bcrypt.hash(password, 10); } + diff --git a/backend/src/services/youtube.service.ts b/backend/src/services/youtube.service.ts index 60c305d..2e08881 100644 --- a/backend/src/services/youtube.service.ts +++ b/backend/src/services/youtube.service.ts @@ -146,3 +146,4 @@ export function formatDuration(isoDuration: string): string { return `${minutes || '0'}:${seconds.padStart(2, '0')}`; } + diff --git a/backend/src/setup/initialSetup.ts b/backend/src/setup/initialSetup.ts index f9d5063..94d8c96 100644 --- a/backend/src/setup/initialSetup.ts +++ b/backend/src/setup/initialSetup.ts @@ -27,3 +27,4 @@ export async function createInitialAdmin() { } } + diff --git a/backend/src/types/index.ts b/backend/src/types/index.ts index 57b7955..11f2a90 100644 --- a/backend/src/types/index.ts +++ b/backend/src/types/index.ts @@ -59,3 +59,4 @@ export interface ApiResponse { }; } + diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 86c99ca..3986ae2 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -17,3 +17,4 @@ "exclude": ["node_modules", "dist"] } + diff --git a/frontend/index.html b/frontend/index.html index 517df89..fd7e8b0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -12,3 +12,4 @@ + diff --git a/frontend/package.json b/frontend/package.json index 3756cf5..901ed1f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,3 +23,4 @@ } } + diff --git a/frontend/src/App.css b/frontend/src/App.css index 3a7161c..0d5dc57 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -59,3 +59,4 @@ body { background-color: #0556c4; } + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3ef7201..11de22c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -38,3 +38,4 @@ function App() { export default App; + diff --git a/frontend/src/components/ChannelManager/ChannelManager.css b/frontend/src/components/ChannelManager/ChannelManager.css index 5c1fe9b..3e739ce 100644 --- a/frontend/src/components/ChannelManager/ChannelManager.css +++ b/frontend/src/components/ChannelManager/ChannelManager.css @@ -172,3 +172,4 @@ } } + diff --git a/frontend/src/components/ChannelManager/ChannelManager.tsx b/frontend/src/components/ChannelManager/ChannelManager.tsx index 7900e02..0bc9cb3 100644 --- a/frontend/src/components/ChannelManager/ChannelManager.tsx +++ b/frontend/src/components/ChannelManager/ChannelManager.tsx @@ -112,3 +112,4 @@ export function ChannelManager() { ); } + diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx index 543e524..bc562d3 100644 --- a/frontend/src/components/ErrorBoundary.tsx +++ b/frontend/src/components/ErrorBoundary.tsx @@ -40,3 +40,4 @@ export class ErrorBoundary extends React.Component { } } + diff --git a/frontend/src/components/ProtectedRoute.tsx b/frontend/src/components/ProtectedRoute.tsx index a56cb65..4d23923 100644 --- a/frontend/src/components/ProtectedRoute.tsx +++ b/frontend/src/components/ProtectedRoute.tsx @@ -19,3 +19,4 @@ export function ProtectedRoute({ children }: ProtectedRouteProps) { return <>{children}; } + diff --git a/frontend/src/components/SearchFilter/SearchFilter.css b/frontend/src/components/SearchFilter/SearchFilter.css index 823be56..f8e2c1f 100644 --- a/frontend/src/components/SearchFilter/SearchFilter.css +++ b/frontend/src/components/SearchFilter/SearchFilter.css @@ -108,3 +108,4 @@ } } + diff --git a/frontend/src/components/SearchFilter/SearchFilter.tsx b/frontend/src/components/SearchFilter/SearchFilter.tsx index 296017f..6301a24 100644 --- a/frontend/src/components/SearchFilter/SearchFilter.tsx +++ b/frontend/src/components/SearchFilter/SearchFilter.tsx @@ -80,3 +80,4 @@ export function SearchFilter({ ); } + diff --git a/frontend/src/components/VideoCard/VideoCard.css b/frontend/src/components/VideoCard/VideoCard.css index 6295fd0..6b37148 100644 --- a/frontend/src/components/VideoCard/VideoCard.css +++ b/frontend/src/components/VideoCard/VideoCard.css @@ -101,3 +101,4 @@ } } + diff --git a/frontend/src/components/VideoCard/VideoCard.tsx b/frontend/src/components/VideoCard/VideoCard.tsx index 51779da..67d5dfc 100644 --- a/frontend/src/components/VideoCard/VideoCard.tsx +++ b/frontend/src/components/VideoCard/VideoCard.tsx @@ -61,3 +61,4 @@ export function VideoCard({ video, onClick }: VideoCardProps) { ); } + diff --git a/frontend/src/components/VideoGrid/VideoGrid.css b/frontend/src/components/VideoGrid/VideoGrid.css index 90c8773..7c07baf 100644 --- a/frontend/src/components/VideoGrid/VideoGrid.css +++ b/frontend/src/components/VideoGrid/VideoGrid.css @@ -153,3 +153,4 @@ } } + diff --git a/frontend/src/components/VideoGrid/VideoGrid.tsx b/frontend/src/components/VideoGrid/VideoGrid.tsx index 4c1511f..359962a 100644 --- a/frontend/src/components/VideoGrid/VideoGrid.tsx +++ b/frontend/src/components/VideoGrid/VideoGrid.tsx @@ -117,3 +117,4 @@ export function VideoGrid({ ); } + diff --git a/frontend/src/components/VideoPlayer/VideoPlayer.css b/frontend/src/components/VideoPlayer/VideoPlayer.css index 2043623..435ea32 100644 --- a/frontend/src/components/VideoPlayer/VideoPlayer.css +++ b/frontend/src/components/VideoPlayer/VideoPlayer.css @@ -79,3 +79,4 @@ } } + diff --git a/frontend/src/components/VideoPlayer/VideoPlayer.tsx b/frontend/src/components/VideoPlayer/VideoPlayer.tsx index 9e180e4..242a527 100644 --- a/frontend/src/components/VideoPlayer/VideoPlayer.tsx +++ b/frontend/src/components/VideoPlayer/VideoPlayer.tsx @@ -42,3 +42,4 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { ); } + diff --git a/frontend/src/hooks/useAuth.tsx b/frontend/src/hooks/useAuth.tsx index c93ac69..4279a83 100644 --- a/frontend/src/hooks/useAuth.tsx +++ b/frontend/src/hooks/useAuth.tsx @@ -69,3 +69,4 @@ export const useAuth = () => { return context; }; + diff --git a/frontend/src/hooks/useChannels.ts b/frontend/src/hooks/useChannels.ts index 1d34415..b740fb5 100644 --- a/frontend/src/hooks/useChannels.ts +++ b/frontend/src/hooks/useChannels.ts @@ -53,3 +53,4 @@ export function useChannels() { }; } + diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 9b446b0..bdd6477 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -8,3 +8,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render( , ) + diff --git a/frontend/src/pages/AdminPage.css b/frontend/src/pages/AdminPage.css index 5467a58..e682e67 100644 --- a/frontend/src/pages/AdminPage.css +++ b/frontend/src/pages/AdminPage.css @@ -23,3 +23,4 @@ color: #606060; } + diff --git a/frontend/src/pages/AdminPage.tsx b/frontend/src/pages/AdminPage.tsx index 5fd4870..302f381 100644 --- a/frontend/src/pages/AdminPage.tsx +++ b/frontend/src/pages/AdminPage.tsx @@ -14,3 +14,4 @@ export function AdminPage() { ); } + diff --git a/frontend/src/pages/LoginPage.css b/frontend/src/pages/LoginPage.css index f72dec9..89f0695 100644 --- a/frontend/src/pages/LoginPage.css +++ b/frontend/src/pages/LoginPage.css @@ -97,3 +97,4 @@ cursor: not-allowed; } + diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 3f315bd..58e5dd4 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -72,3 +72,4 @@ export function LoginPage() { ); } + diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index 82095f3..5234af4 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -119,3 +119,4 @@ export const videosApi = { api.post('/videos/refresh', { channelIds }) }; + diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 7844ad4..6d04e4b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -20,3 +20,4 @@ "references": [{ "path": "./tsconfig.node.json" }] } + diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index e428d50..b72368a 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -9,3 +9,4 @@ "include": ["vite.config.ts"] } + diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 60c65b3..158335c 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -8,3 +8,4 @@ export default defineConfig({ } }) + diff --git a/navbar-refactor-plan.md b/navbar-refactor-plan.md index 763afcd..7c5b36d 100644 --- a/navbar-refactor-plan.md +++ b/navbar-refactor-plan.md @@ -226,3 +226,4 @@ But this requires lifting state to App level and prop drilling. **Estimated time:** 30-45 minutes **Complexity:** Medium (URL params + responsive styling) + diff --git a/package.json b/package.json index 6ddbc9e..1065b91 100644 --- a/package.json +++ b/package.json @@ -34,3 +34,4 @@ "tsx": "^4.7.0" } } +