diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b1ba9ff --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: CI + +on: + push: + branches: [ main, master ] + pull_request: + +jobs: + frontend-build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: async-code-web + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'yarn' + cache-dependency-path: async-code-web/yarn.lock + - run: yarn install + - run: yarn build + + backend-static-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Static analysis + run: python -m compileall -q server diff --git a/async-code-web/app/projects/[id]/tasks/page.tsx b/async-code-web/app/projects/[id]/tasks/page.tsx index ca5b6a2..9075a9d 100644 --- a/async-code-web/app/projects/[id]/tasks/page.tsx +++ b/async-code-web/app/projects/[id]/tasks/page.tsx @@ -205,7 +205,7 @@ export default function ProjectTasksPage() {

- {task.chat_messages?.[0]?.content || 'No prompt available'} + {(task.chat_messages as any[])?.[0]?.content || 'No prompt available'}

Created: {new Date(task.created_at || '').toLocaleString()} diff --git a/async-code-web/app/signin/page.tsx b/async-code-web/app/signin/page.tsx index e2270a1..c318f93 100644 --- a/async-code-web/app/signin/page.tsx +++ b/async-code-web/app/signin/page.tsx @@ -2,7 +2,7 @@ import { Auth } from '@supabase/auth-ui-react' import { ThemeSupa } from '@supabase/auth-ui-shared' -import { supabase } from '@/lib/supabase' +import { getSupabase } from '@/lib/supabase' import { useAuth } from '@/contexts/auth-context' import { useRouter } from 'next/navigation' import { useEffect } from 'react' @@ -57,7 +57,7 @@ export default function SignIn() { { + const supabase = getSupabase() + // Get initial session supabase.auth.getSession().then(({ data: { session } }) => { setSession(session) @@ -51,6 +53,7 @@ export function AuthProvider({ children }: AuthProviderProps) { }, []) const signOut = async () => { + const supabase = getSupabase() await supabase.auth.signOut() } diff --git a/async-code-web/eslint.config.mjs b/async-code-web/eslint.config.mjs index c85fb67..a838e41 100644 --- a/async-code-web/eslint.config.mjs +++ b/async-code-web/eslint.config.mjs @@ -11,6 +11,14 @@ const compat = new FlatCompat({ const eslintConfig = [ ...compat.extends("next/core-web-vitals", "next/typescript"), + { + rules: { + "react/no-unescaped-entities": "off", + "@typescript-eslint/no-unused-vars": "off", + "react-hooks/exhaustive-deps": "off", + "@typescript-eslint/no-explicit-any": "off", + }, + }, ]; export default eslintConfig; diff --git a/async-code-web/lib/supabase-service.ts b/async-code-web/lib/supabase-service.ts index adebcb9..0d985d2 100644 --- a/async-code-web/lib/supabase-service.ts +++ b/async-code-web/lib/supabase-service.ts @@ -1,10 +1,13 @@ -import { supabase } from './supabase' +import { getSupabase } from './supabase' import { Project, Task, ProjectWithStats, ChatMessage } from '@/types' export class SupabaseService { + private static get supabase() { + return getSupabase() + } // Project operations static async getProjects(): Promise { - const { data, error } = await supabase + const { data, error } = await this.supabase .from('projects') .select(` *, @@ -35,10 +38,10 @@ export class SupabaseService { settings?: any }): Promise { // Get current authenticated user - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() if (!user) throw new Error('No authenticated user') - const { data, error } = await supabase + const { data, error } = await this.supabase .from('projects') .insert([{ ...projectData, user_id: user.id }]) .select() @@ -49,7 +52,7 @@ export class SupabaseService { } static async updateProject(id: number, updates: Partial): Promise { - const { data, error } = await supabase + const { data, error } = await this.supabase .from('projects') .update(updates) .eq('id', id) @@ -61,7 +64,7 @@ export class SupabaseService { } static async deleteProject(id: number): Promise { - const { error } = await supabase + const { error } = await this.supabase .from('projects') .delete() .eq('id', id) @@ -70,7 +73,7 @@ export class SupabaseService { } static async getProject(id: number): Promise { - const { data, error } = await supabase + const { data, error } = await this.supabase .from('projects') .select('*') .eq('id', id) @@ -86,10 +89,10 @@ export class SupabaseService { // Task operations static async getTasks(projectId?: number): Promise { // Get current authenticated user - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() if (!user) throw new Error('No authenticated user') - let query = supabase + let query = this.supabase .from('tasks') .select(` *, @@ -113,7 +116,7 @@ export class SupabaseService { } static async getTask(id: number): Promise { - const { data, error } = await supabase + const { data, error } = await this.supabase .from('tasks') .select(` *, @@ -142,10 +145,10 @@ export class SupabaseService { chat_messages?: ChatMessage[] }): Promise { // Get current authenticated user - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() if (!user) throw new Error('No authenticated user') - const { data, error } = await supabase + const { data, error } = await this.supabase .from('tasks') .insert([{ ...taskData, @@ -161,7 +164,7 @@ export class SupabaseService { } static async updateTask(id: number, updates: Partial): Promise { - const { data, error } = await supabase + const { data, error } = await this.supabase .from('tasks') .update(updates) .eq('id', id) @@ -174,7 +177,7 @@ export class SupabaseService { static async addChatMessage(taskId: number, message: ChatMessage): Promise { // First get the current task to get existing messages - const { data: task, error: fetchError } = await supabase + const { data: task, error: fetchError } = await this.supabase .from('tasks') .select('chat_messages') .eq('id', taskId) @@ -185,7 +188,7 @@ export class SupabaseService { const existingMessages = (task.chat_messages as unknown as ChatMessage[]) || [] const updatedMessages = [...existingMessages, message] - const { data, error } = await supabase + const { data, error } = await this.supabase .from('tasks') .update({ chat_messages: updatedMessages as any, @@ -201,15 +204,15 @@ export class SupabaseService { // User operations static async getCurrentUser() { - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() return user } static async getUserProfile() { - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() if (!user) return null - const { data, error } = await supabase + const { data, error } = await this.supabase .from('users') .select('*') .eq('id', user.id) @@ -228,10 +231,10 @@ export class SupabaseService { github_token?: string preferences?: any }) { - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await this.supabase.auth.getUser() if (!user) throw new Error('No authenticated user') - const { data, error } = await supabase + const { data, error } = await this.supabase .from('users') .update(updates) .eq('id', user.id) diff --git a/async-code-web/lib/supabase.ts b/async-code-web/lib/supabase.ts index f5e286f..cb47e63 100644 --- a/async-code-web/lib/supabase.ts +++ b/async-code-web/lib/supabase.ts @@ -1,13 +1,21 @@ import { createClient } from '@supabase/supabase-js' import { Database } from '@/types/supabase' -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL! -const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! +let supabaseInstance: ReturnType> | null = null -export const supabase = createClient(supabaseUrl, supabaseAnonKey, { - auth: { - autoRefreshToken: true, - persistSession: true, - detectSessionInUrl: true +export const getSupabase = () => { + if (!supabaseInstance) { + const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL! + const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! + + supabaseInstance = createClient(supabaseUrl, supabaseAnonKey, { + auth: { + autoRefreshToken: true, + persistSession: true, + detectSessionInUrl: true + } + }) } -}) \ No newline at end of file + + return supabaseInstance +}