diff --git a/components/settings/components/tool-selection-form.tsx b/components/settings/components/tool-selection-form.tsx index 2fcf440e..3d7dd299 100644 --- a/components/settings/components/tool-selection-form.tsx +++ b/components/settings/components/tool-selection-form.tsx @@ -54,6 +54,7 @@ export function ToolSelectionForm({ form }: ToolSelectionFormProps) { const { toast } = useToast(); const [skyfiConnected, setSkyfiConnected] = useState(false); const [skyfiBudget, setSkyfiBudget] = useState(null); + const [skyfiExpired, setSkyfiExpired] = useState(false); const [loadingStatus, setLoadingStatus] = useState(true); const [isConnecting, setIsConnecting] = useState(false); const [isDisconnecting, setIsDisconnecting] = useState(false); @@ -66,6 +67,7 @@ export function ToolSelectionForm({ form }: ToolSelectionFormProps) { const status = await getSkyfiConnectionStatus(); setSkyfiConnected(status.connected); setSkyfiBudget(status.budget || null); + setSkyfiExpired(!!status.expired); } catch (err) { console.error("Failed to load SkyFi connection status:", err); } finally { @@ -167,6 +169,104 @@ export function ToolSelectionForm({ form }: ToolSelectionFormProps) { Checking SkyFi connection status... + ) : skyfiExpired ? ( +
+
+ +
+

SkyFi Connection Expired/Unauthorized

+

Your prior SkyFi authentication session is invalid, expired, or was revoked. Please reconnect to restore planetary intelligence access.

+
+
+ + +
+
+
) : skyfiConnected ? (
@@ -193,6 +293,7 @@ export function ToolSelectionForm({ form }: ToolSelectionFormProps) { if (res.success) { setSkyfiConnected(false); setSkyfiBudget(null); + setSkyfiExpired(false); toast({ title: "SkyFi Disconnected", description: "Your SkyFi account has been disconnected.", @@ -240,10 +341,18 @@ export function ToolSelectionForm({ form }: ToolSelectionFormProps) { return; } if (res.error) { - throw new Error(res.error); + toast({ + title: "Connection Error", + description: res.error, + variant: "destructive", + }); + setIsConnecting(false); + return; } if (res.url) { window.location.href = res.url; + } else { + throw new Error("No authorization URL returned from the connection action."); } } catch (err: any) { toast({ diff --git a/drizzle/migrations/0011_motionless_banshee.sql b/drizzle/migrations/0011_motionless_banshee.sql new file mode 100644 index 00000000..550f3267 --- /dev/null +++ b/drizzle/migrations/0011_motionless_banshee.sql @@ -0,0 +1 @@ +ALTER TABLE "skyfi_oauth_tokens" ADD COLUMN "redirect_uri" text; \ No newline at end of file diff --git a/drizzle/migrations/meta/0010_snapshot.json b/drizzle/migrations/meta/0010_snapshot.json new file mode 100644 index 00000000..6b773dac --- /dev/null +++ b/drizzle/migrations/meta/0010_snapshot.json @@ -0,0 +1,1093 @@ +{ + "id": "c3b5b40c-c48b-4396-9cbc-a6643abe7a5d", + "prevId": "cb88b9e7-0bb2-4a61-b151-a12cf1071b91", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.calendar_notes": { + "name": "calendar_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location_tags": { + "name": "location_tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "user_tags": { + "name": "user_tags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "map_feature_id": { + "name": "map_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "calendar_notes_user_id_users_id_fk": { + "name": "calendar_notes_user_id_users_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "calendar_notes_chat_id_chats_id_fk": { + "name": "calendar_notes_chat_id_chats_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_participants": { + "name": "chat_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'collaborator'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_participants_chat_id_chats_id_fk": { + "name": "chat_participants_chat_id_chats_id_fk", + "tableFrom": "chat_participants", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_participants_user_id_users_id_fk": { + "name": "chat_participants_user_id_users_id_fk", + "tableFrom": "chat_participants", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_participants_chat_user_unique": { + "name": "chat_participants_chat_user_unique", + "nullsNotDistinct": false, + "columns": [ + "chat_id", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Untitled Chat'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'private'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_path": { + "name": "share_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shareable_link_id": { + "name": "shareable_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chats_user_id_users_id_fk": { + "name": "chats_user_id_users_id_fk", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chats_shareable_link_id_unique": { + "name": "chats_shareable_link_id_unique", + "nullsNotDistinct": false, + "columns": [ + "shareable_link_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_chunks": { + "name": "document_chunks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_chunks_document_id_documents_id_fk": { + "name": "document_chunks_document_id_documents_id_fk", + "tableFrom": "document_chunks", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documents": { + "name": "documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime": { + "name": "mime", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "documents_user_id_users_id_fk": { + "name": "documents_user_id_users_id_fk", + "tableFrom": "documents", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documents_chat_id_chats_id_fk": { + "name": "documents_chat_id_chats_id_fk", + "tableFrom": "documents", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.locations": { + "name": "locations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "geojson": { + "name": "geojson", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_user_id_users_id_fk": { + "name": "locations_user_id_users_id_fk", + "tableFrom": "locations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "locations_chat_id_chats_id_fk": { + "name": "locations_chat_id_chats_id_fk", + "tableFrom": "locations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "location_id": { + "name": "location_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "messages_chat_id_chats_id_fk": { + "name": "messages_chat_id_chats_id_fk", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_user_id_users_id_fk": { + "name": "messages_user_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_location_id_locations_id_fk": { + "name": "messages_location_id_locations_id_fk", + "tableFrom": "messages", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prompt_generation_jobs": { + "name": "prompt_generation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result_prompt": { + "name": "result_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "prompt_generation_jobs_user_id_users_id_fk": { + "name": "prompt_generation_jobs_user_id_users_id_fk", + "tableFrom": "prompt_generation_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skyfi_oauth_tokens": { + "name": "skyfi_oauth_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_expiry": { + "name": "token_expiry", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_client_uri": { + "name": "registration_client_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_access_token": { + "name": "registration_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "skyfi_oauth_tokens_user_id_users_id_fk": { + "name": "skyfi_oauth_tokens_user_id_users_id_fk", + "tableFrom": "skyfi_oauth_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "skyfi_oauth_tokens_user_id_unique": { + "name": "skyfi_oauth_tokens_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompts": { + "name": "system_prompts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_prompts_user_id_users_id_fk": { + "name": "system_prompts_user_id_users_id_fk", + "tableFrom": "system_prompts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'viewer'" + }, + "selected_model": { + "name": "selected_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clerk_user_id": { + "name": "clerk_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone_number": { + "name": "phone_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_clerk_user_id_unique": { + "name": "users_clerk_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.visualizations": { + "name": "visualizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'map_layer'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "visualizations_user_id_users_id_fk": { + "name": "visualizations_user_id_users_id_fk", + "tableFrom": "visualizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "visualizations_chat_id_chats_id_fk": { + "name": "visualizations_chat_id_chats_id_fk", + "tableFrom": "visualizations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/migrations/meta/0011_snapshot.json b/drizzle/migrations/meta/0011_snapshot.json new file mode 100644 index 00000000..00caa834 --- /dev/null +++ b/drizzle/migrations/meta/0011_snapshot.json @@ -0,0 +1,1099 @@ +{ + "id": "ee79be91-b7f3-458c-a335-b235e6dee978", + "prevId": "c3b5b40c-c48b-4396-9cbc-a6643abe7a5d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.calendar_notes": { + "name": "calendar_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location_tags": { + "name": "location_tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "user_tags": { + "name": "user_tags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "map_feature_id": { + "name": "map_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "calendar_notes_user_id_users_id_fk": { + "name": "calendar_notes_user_id_users_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "calendar_notes_chat_id_chats_id_fk": { + "name": "calendar_notes_chat_id_chats_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_participants": { + "name": "chat_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'collaborator'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_participants_chat_id_chats_id_fk": { + "name": "chat_participants_chat_id_chats_id_fk", + "tableFrom": "chat_participants", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_participants_user_id_users_id_fk": { + "name": "chat_participants_user_id_users_id_fk", + "tableFrom": "chat_participants", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_participants_chat_user_unique": { + "name": "chat_participants_chat_user_unique", + "nullsNotDistinct": false, + "columns": [ + "chat_id", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Untitled Chat'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'private'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_path": { + "name": "share_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shareable_link_id": { + "name": "shareable_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chats_user_id_users_id_fk": { + "name": "chats_user_id_users_id_fk", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chats_shareable_link_id_unique": { + "name": "chats_shareable_link_id_unique", + "nullsNotDistinct": false, + "columns": [ + "shareable_link_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.document_chunks": { + "name": "document_chunks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "document_id": { + "name": "document_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_chunks_document_id_documents_id_fk": { + "name": "document_chunks_document_id_documents_id_fk", + "tableFrom": "document_chunks", + "tableTo": "documents", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documents": { + "name": "documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime": { + "name": "mime", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "documents_user_id_users_id_fk": { + "name": "documents_user_id_users_id_fk", + "tableFrom": "documents", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "documents_chat_id_chats_id_fk": { + "name": "documents_chat_id_chats_id_fk", + "tableFrom": "documents", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.locations": { + "name": "locations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "geojson": { + "name": "geojson", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_user_id_users_id_fk": { + "name": "locations_user_id_users_id_fk", + "tableFrom": "locations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "locations_chat_id_chats_id_fk": { + "name": "locations_chat_id_chats_id_fk", + "tableFrom": "locations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "location_id": { + "name": "location_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "messages_chat_id_chats_id_fk": { + "name": "messages_chat_id_chats_id_fk", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_user_id_users_id_fk": { + "name": "messages_user_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_location_id_locations_id_fk": { + "name": "messages_location_id_locations_id_fk", + "tableFrom": "messages", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prompt_generation_jobs": { + "name": "prompt_generation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result_prompt": { + "name": "result_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "prompt_generation_jobs_user_id_users_id_fk": { + "name": "prompt_generation_jobs_user_id_users_id_fk", + "tableFrom": "prompt_generation_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skyfi_oauth_tokens": { + "name": "skyfi_oauth_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_expiry": { + "name": "token_expiry", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_client_uri": { + "name": "registration_client_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_access_token": { + "name": "registration_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "skyfi_oauth_tokens_user_id_users_id_fk": { + "name": "skyfi_oauth_tokens_user_id_users_id_fk", + "tableFrom": "skyfi_oauth_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "skyfi_oauth_tokens_user_id_unique": { + "name": "skyfi_oauth_tokens_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompts": { + "name": "system_prompts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_prompts_user_id_users_id_fk": { + "name": "system_prompts_user_id_users_id_fk", + "tableFrom": "system_prompts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'viewer'" + }, + "selected_model": { + "name": "selected_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clerk_user_id": { + "name": "clerk_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone_number": { + "name": "phone_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_clerk_user_id_unique": { + "name": "users_clerk_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.visualizations": { + "name": "visualizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'map_layer'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "visualizations_user_id_users_id_fk": { + "name": "visualizations_user_id_users_id_fk", + "tableFrom": "visualizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "visualizations_chat_id_chats_id_fk": { + "name": "visualizations_chat_id_chats_id_fk", + "tableFrom": "visualizations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index d416fd22..5188f9fd 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -71,6 +71,20 @@ "when": 1784838292039, "tag": "0009_add_skyfi_oauth_tokens", "breakpoints": true + }, + { + "idx": 10, + "version": "7", + "when": 1784897748431, + "tag": "0010_tough_the_professor", + "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1784900080766, + "tag": "0011_motionless_banshee", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/lib/actions/skyfi.ts b/lib/actions/skyfi.ts index 8bc5cd3c..0665bec5 100644 --- a/lib/actions/skyfi.ts +++ b/lib/actions/skyfi.ts @@ -10,19 +10,31 @@ import crypto from 'crypto'; import { headers } from 'next/headers'; +import { getClerkUserIdOnServer, resolveClerkUserToDbUser } from '@/lib/auth/get-current-user'; + export async function getRedirectUri(): Promise { + const baseUrl = process.env.NEXT_PUBLIC_APP_URL; + if (baseUrl) { + return `${baseUrl}/api/skyfi/callback`; + } try { const headersList = await headers(); + const forwardedProto = headersList.get('x-forwarded-proto'); + const forwardedHost = headersList.get('x-forwarded-host'); const host = headersList.get('host'); - if (host) { - const protocol = host.startsWith('localhost') || host.startsWith('127.0.0.1') ? 'http' : 'https'; - return `${protocol}://${host}/api/skyfi/callback`; + + const finalHost = forwardedHost || host; + if (finalHost) { + let finalProto = forwardedProto; + if (!finalProto) { + finalProto = finalHost.startsWith('localhost') || finalHost.startsWith('127.0.0.1') || finalHost.startsWith('3000') || finalHost.startsWith('3001') ? 'http' : 'https'; + } + return `${finalProto}://${finalHost}/api/skyfi/callback`; } } catch (e) { - console.warn('[Skyfi] Failed to get host from headers, falling back to ENV:', e); + console.warn('[Skyfi] Failed to get host from headers:', e); } - const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'; - return `${baseUrl}/api/skyfi/callback`; + return 'http://localhost:3000/api/skyfi/callback'; } /** @@ -30,11 +42,11 @@ export async function getRedirectUri(): Promise { * Uses an AbortController with a 10s timeout to prevent hanging. */ async function ensureClientRegistered(provider: SkyfiOAuthProvider, forceRegister: boolean = false): Promise { - if (!forceRegister) { - const currentInfo = await provider.clientInformation(); - if (currentInfo?.client_id) { - return currentInfo.client_id; - } + const currentInfo = await provider.clientInformation(); + const currentRedirectUri = provider.redirectUrl?.toString(); + + if (!forceRegister && currentInfo?.client_id && currentInfo?.redirect_uri === currentRedirectUri) { + return currentInfo.client_id; } console.log('[SkyFiAction] Client not registered or force-register active. Registering dynamically...'); @@ -48,7 +60,7 @@ async function ensureClientRegistered(provider: SkyfiOAuthProvider, forceRegiste headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_name: 'QCX Planetary Copilot', - redirect_uris: [provider.redirectUrl?.toString()], + redirect_uris: [currentRedirectUri], token_endpoint_auth_method: 'none', grant_types: ['authorization_code', 'refresh_token'], response_types: ['code'], @@ -63,11 +75,16 @@ async function ensureClientRegistered(provider: SkyfiOAuthProvider, forceRegiste } const data = await res.json(); + if (!data || !data.client_id) { + throw new Error('Dynamic Client Registration response did not contain a valid client_id.'); + } + await provider.saveClientInformation({ client_id: data.client_id, client_secret: data.client_secret || null, registration_client_uri: data.registration_client_uri || null, registration_access_token: data.registration_access_token || null, + redirect_uri: currentRedirectUri, } as any); return data.client_id; @@ -82,16 +99,26 @@ async function ensureClientRegistered(provider: SkyfiOAuthProvider, forceRegiste * Performs DCR, generates PKCE, and returns the authorization URL. */ export async function startSkyfiConnection(): Promise<{ url?: string; error?: string; authRequired?: boolean }> { - const userId = await getCurrentUserIdOnServer(); - if (!userId) { - return { authRequired: true, error: 'Please sign in to connect your SkyFi account.' }; + let userId: string | null = null; + try { + const clerkUserId = await getClerkUserIdOnServer(); + if (!clerkUserId) { + return { authRequired: true, error: 'Please sign in to connect your SkyFi account.' }; + } + userId = await resolveClerkUserToDbUser(clerkUserId); + if (!userId) { + return { error: 'Authentication service error: Database resolution failed to return a valid user ID.' }; + } + } catch (authError: any) { + console.error('[SkyFiAction: startSkyfiConnection] Auth error:', authError); + return { error: `Authentication service error: ${authError.message}` }; } try { const redirectUri = await getRedirectUri(); const provider = new SkyfiOAuthProvider(userId, redirectUri); - const clientId = await ensureClientRegistered(provider, true); + const clientId = await ensureClientRegistered(provider, false); const verifier = generateCodeVerifier(); const challenge = generateCodeChallenge(verifier); @@ -112,7 +139,15 @@ export async function startSkyfiConnection(): Promise<{ url?: string; error?: st return { url: authorizeUrl.toString() }; } catch (error: any) { console.error('[SkyFiAction: startSkyfiConnection] Error:', error.message); - return { error: error.message || 'Failed to start SkyFi connection.' }; + const isTimeout = error.name === 'AbortError' || error.message?.toLowerCase().includes('timeout') || error.message?.toLowerCase().includes('aborted'); + const isNetwork = error.message?.toLowerCase().includes('fetch') || error.message?.toLowerCase().includes('network') || error.message?.toLowerCase().includes('failed to fetch'); + + if (isTimeout) { + return { error: 'SkyFi Connection Error: Dynamic client registration request timed out.' }; + } else if (isNetwork) { + return { error: 'SkyFi Connection Error: Network failure. Please check your connection to SkyFi services.' }; + } + return { error: `SkyFi Registration/Connection failed: ${error.message}` }; } } @@ -120,19 +155,34 @@ export async function startSkyfiConnection(): Promise<{ url?: string; error?: st * Returns whether a valid SkyFi connection exists for the current user. * Uses an AbortController with a 10s timeout to prevent hanging. */ -export async function getSkyfiConnectionStatus(): Promise<{ connected: boolean; email?: string; budget?: string }> { +export async function getSkyfiConnectionStatus(): Promise<{ connected: boolean; email?: string; budget?: string; expired?: boolean; missing?: boolean; error?: string }> { try { const userId = await getCurrentUserIdOnServer(); if (!userId) { - return { connected: false }; + return { connected: false, missing: true }; + } + + const [row] = await db.select() + .from(skyfiOAuthTokens) + .where(eq(skyfiOAuthTokens.userId, userId)) + .limit(1); + + if (!row || !row.clientId) { + return { connected: false, missing: true }; } const redirectUri = await getRedirectUri(); const provider = new SkyfiOAuthProvider(userId, redirectUri); - const tokens = await provider.tokens(); + let tokens; + try { + tokens = await provider.tokens(); + } catch (tokenErr: any) { + console.warn('[SkyFiAction: getSkyfiConnectionStatus] Failed to retrieve tokens:', tokenErr); + return { connected: false, expired: true, error: tokenErr.message }; + } if (!tokens || !tokens.access_token) { - return { connected: false }; + return { connected: false, expired: true }; } const controller = new AbortController(); @@ -144,7 +194,7 @@ export async function getSkyfiConnectionStatus(): Promise<{ connected: boolean; method: 'POST', headers: { 'Content-Type': 'application/json', - 'X-Skyfi-Api-Key': tokens.access_token, + 'Authorization': `Bearer ${tokens.access_token}`, }, body: JSON.stringify({ jsonrpc: '2.0', @@ -160,6 +210,10 @@ export async function getSkyfiConnectionStatus(): Promise<{ connected: boolean; clearTimeout(timeoutId); + if (res.status === 401) { + return { connected: false, expired: true }; + } + if (res.ok) { const data = await res.json(); const content = data?.result?.content?.[0]?.text || ''; @@ -173,7 +227,7 @@ export async function getSkyfiConnectionStatus(): Promise<{ connected: boolean; return { connected: true }; } catch (error: any) { console.error('[SkyFiAction: getSkyfiConnectionStatus] Error:', error.message); - return { connected: false }; + return { connected: false, error: error.message }; } } diff --git a/lib/agents/tools/skyfi.tsx b/lib/agents/tools/skyfi.tsx index c8264b34..ecde7027 100644 --- a/lib/agents/tools/skyfi.tsx +++ b/lib/agents/tools/skyfi.tsx @@ -63,7 +63,6 @@ async function getConnectedSkyfiMcpClient(accessToken: string, signal?: AbortSig requestInit: { headers: { 'Authorization': `Bearer ${accessToken}`, - 'X-Skyfi-Api-Key': accessToken, }, signal, } @@ -179,11 +178,11 @@ export const skyfiTool = ({ const mcpClient = await getConnectedSkyfiMcpClient(tokens.access_token, controller.signal); if (!mcpClient) { clearTimeout(timeoutId); - feedbackMessage = 'Failed to connect to the SkyFi MCP server. Your login may have expired, please try reconnecting in Settings.'; + feedbackMessage = 'Failed to connect to the SkyFi MCP server. Your SkyFi connection may have expired or is unauthorized. Please reconnect your SkyFi account in Settings.'; uiFeedbackStream.update(feedbackMessage); uiFeedbackStream.done(); uiStream.update(); - return { type: 'SKYFI_QUERY', success: false, error: 'Connection failed' }; + return { type: 'SKYFI_QUERY', success: false, error: 'Unauthenticated', message: feedbackMessage }; } let dryRunValidateOnly = false; @@ -337,9 +336,13 @@ export const skyfiTool = ({ clearTimeout(timeoutId); const isAbortError = error.name === 'AbortError' || error.message?.includes('aborted') || error.message?.includes('timeout'); + const isUnauthorized = error.message?.includes('401') || error.message?.toLowerCase().includes('unauthorized') || error.message?.toLowerCase().includes('expired') || error.message?.toLowerCase().includes('token'); + let toolError = `SkyFi execution failed: ${error.message}`; - if (isAbortError) { + if (isUnauthorized) { + toolError = `SkyFi execution failed: Your SkyFi connection is unauthorized or expired. Please reconnect your SkyFi account in Settings.`; + } else if (isAbortError) { toolError = `SkyFi execution failed: Request timed out after 30 seconds.`; if (queryType === 'place_order' && !dryRunValidateOnly) { toolError += ` The order request was aborted to prevent duplicate placements. Use the idempotency key: ${stableIdempotencyKey} if you attempt to retry.`; diff --git a/lib/auth/get-current-user.ts b/lib/auth/get-current-user.ts index 4b529adf..1e2d3334 100644 --- a/lib/auth/get-current-user.ts +++ b/lib/auth/get-current-user.ts @@ -50,7 +50,9 @@ export async function resolveClerkUserToDbUser(clerkUserId: string): Promise { + async clientInformation(): Promise { const [row] = await db.select() .from(skyfiOAuthTokens) .where(eq(skyfiOAuthTokens.userId, this.userId)) @@ -76,12 +76,13 @@ export class SkyfiOAuthProvider implements OAuthClientProvider { client_secret: decrypt(row.clientSecret) || undefined, registration_client_uri: row.registrationClientUri || undefined, registration_access_token: decrypt(row.registrationAccessToken) || undefined, + redirect_uri: row.redirectUri || undefined, }; } return undefined; } - async saveClientInformation(clientInformation: OAuthClientInformationMixed): Promise { + async saveClientInformation(clientInformation: OAuthClientInformationMixed & { redirect_uri?: string }): Promise { await db.insert(skyfiOAuthTokens) .values({ userId: this.userId, @@ -89,6 +90,7 @@ export class SkyfiOAuthProvider implements OAuthClientProvider { clientSecret: encrypt(clientInformation.client_secret || null), registrationClientUri: clientInformation.registration_client_uri || null, registrationAccessToken: encrypt(clientInformation.registration_access_token || null), + redirectUri: clientInformation.redirect_uri || null, }) .onConflictDoUpdate({ target: skyfiOAuthTokens.userId, @@ -97,6 +99,7 @@ export class SkyfiOAuthProvider implements OAuthClientProvider { clientSecret: encrypt(clientInformation.client_secret || null), registrationClientUri: clientInformation.registration_client_uri || null, registrationAccessToken: encrypt(clientInformation.registration_access_token || null), + redirectUri: clientInformation.redirect_uri || null, updatedAt: new Date(), } }); @@ -122,9 +125,11 @@ export class SkyfiOAuthProvider implements OAuthClientProvider { const nowSeconds = Math.floor(Date.now() / 1000); const expiresAt = row.tokenExpiry ? Math.floor(row.tokenExpiry.getTime() / 1000) : null; - // Check if token is expired or expiring in less than 60 seconds - if (expiresAt && expiresAt < nowSeconds + 60 && decryptedRefreshToken && row.clientId) { - console.log('[SkyfiProvider] Token expired or expiring soon. Refreshing token...'); + // Check if token is expired, expiring in less than 60 seconds, or missing expiry entirely (conservative treatment) + const isExpiredOrMissingExpiry = !expiresAt || (expiresAt < nowSeconds + 60); + + if (isExpiredOrMissingExpiry && decryptedRefreshToken && row.clientId) { + console.log('[SkyfiProvider] Token expired, expiring soon, or missing expiry. Refreshing token...'); try { const response = await fetch('https://mcp.skyfi.com/oauth/token', { method: 'POST', diff --git a/server.log b/server.log index 1bc7314c..0e81c8b4 100644 --- a/server.log +++ b/server.log @@ -11,83 +11,16 @@ You can learn more, including how to opt-out if you'd not like to participate in https://nextjs.org/telemetry ○ Compiling middleware ... - ✓ Compiled middleware in 601ms - ✓ Ready in 3s + ✓ Compiled middleware in 877ms + ✓ Ready in 4.6s ⚠ Webpack is configured while Turbopack is not, which may cause problems. ⚠ See instructions if you need to configure Turbopack: https://nextjs.org/docs/app/api-reference/next-config-js/turbo ○ Compiling / ... - ✓ Compiled / in 36.4s + ✓ Compiled / in 41s Chat DB actions loaded. Ensure getCurrentUserId() is correctly implemented for server-side usage if applicable. - - -[Clerk]: You are running in keyless mode. -You can claim your keys by visiting https://dashboard.clerk.com/apps/claim?framework=nextjs&token=u8in4njl99cq019fx15giva3ajotgj8n6grsclbj&return_url=http%3A%2F%2Flocalhost%3A3000%2F - - - -[Clerk]: You are running in keyless mode. -You can claim your keys by visiting https://dashboard.clerk.com/apps/claim?framework=nextjs&token=63h2o0pwcd8onoj31hbd3bc1dky3cu7yjhphokyg&return_url=http%3A%2F%2Flocalhost%3A3000%2F - - - -[Clerk]: You are running in keyless mode. -You can claim your keys by visiting https://dashboard.clerk.com/apps/claim?framework=nextjs&token=ggk1j0tedensjvwglwsepaxeo2myduop3jvqb4pd&return_url=http%3A%2F%2Flocalhost%3A3000%2F - - - -[Clerk]: You are running in keyless mode. -You can claim your keys by visiting https://dashboard.clerk.com/apps/claim?framework=nextjs&token=97p1p0i41tl8qc2yr1fcynojpxg23xq0i3q5kudn&return_url=http%3A%2F%2Flocalhost%3A3000%2F - - GET / 200 in 6706ms - GET / 200 in 1626ms - ○ Compiling /favicon.ico ... - ✓ Compiled /favicon.ico in 4.4s - POST / 200 in 3482ms - POST / 200 in 1580ms - GET /favicon.ico?favicon.1eb660de.ico 200 in 5774ms -[Error: aborted] { code: 'ECONNRESET' } - ⨯ uncaughtException: [Error: aborted] { code: 'ECONNRESET' } - ⨯ uncaughtException: [Error: aborted] { code: 'ECONNRESET' } - GET / 200 in 531ms - GET /favicon.ico?favicon.1eb660de.ico 200 in 315ms - POST / 200 in 605ms - GET / 200 in 1025ms - GET /favicon.ico?favicon.1eb660de.ico 200 in 271ms - GET / 200 in 964ms - POST / 200 in 1430ms - ○ Compiling /manifest.webmanifest ... - POST / 200 in 1016ms - ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: -- Your middleware file exists at ./middleware.(ts|js) -- clerkMiddleware() is used in your Next.js middleware file. -- Your middleware matcher is configured to match this route or page. -- If you are using the src directory, make sure the middleware file is inside of it. - -If you've verified your configuration and are still seeing this error, there may be a runtime issue or a problem communicating with Clerk. - -For more details, see https://clerk.com/err/auth-middleware - - at async getClerkUserIdOnServer (lib/auth/get-current-user.ts:20:21) - at async getCurrentUserIdOnServer (lib/auth/get-current-user.ts:135:22) - at async getNotes (lib/actions/calendar.ts:17:17) - 18 | return MOCK_USER_ID; - 19 | } -> 20 | const { userId } = await auth(); - | ^ - 21 | return userId; - 22 | } - 23 | { - digest: '67936875' -} - ✓ Compiled /manifest.webmanifest in 1852ms - GET /manifest.webmanifest 200 in 1976ms - POST / 200 in 764ms - GET / 200 in 990ms - GET /manifest.webmanifest 200 in 143ms - POST / 200 in 1462ms - GET / 200 in 632ms + GET / 200 in 780ms getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not set. Ensure POSTGRES_URL is configured in Vercel environment variables. at Proxy. (lib/db/index.ts:61:16) at getSystemPrompt (lib/actions/chat.ts:314:28) @@ -98,7 +31,7 @@ getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not s 62 | `Cannot execute ${String(prop)}() — POSTGRES_URL is not set. ` + 63 | 'Ensure POSTGRES_URL is configured in Vercel environment variables.' 64 | ); - POST / 200 in 1657ms + POST / 200 in 1282ms ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: - Your middleware file exists at ./middleware.(ts|js) - clerkMiddleware() is used in your Next.js middleware file. @@ -119,58 +52,9 @@ For more details, see https://clerk.com/err/auth-middleware 21 | return userId; 22 | } 23 | { - digest: '1604531766' + digest: '2094540635' } - POST / 500 in 1869ms - GET /manifest.webmanifest 200 in 1758ms - POST / 200 in 568ms - GET / 200 in 893ms -[Error: aborted] { code: 'ECONNRESET' } - ⨯ uncaughtException: [Error: aborted] { code: 'ECONNRESET' } - ⨯ uncaughtException: [Error: aborted] { code: 'ECONNRESET' } - POST / 200 in 1340ms - GET / 200 in 455ms - POST / 200 in 613ms - GET / 200 in 463ms - POST / 200 in 731ms - GET / 200 in 500ms - GET /manifest.webmanifest 200 in 47ms - POST / 200 in 1043ms - GET / 200 in 568ms - GET /manifest.webmanifest 200 in 42ms - POST / 200 in 1237ms - GET / 200 in 570ms - GET /manifest.webmanifest 200 in 147ms - POST / 200 in 951ms - GET / 200 in 1203ms - GET / 200 in 1276ms - POST / 200 in 1324ms - POST / 200 in 1704ms - ⨯ [Error: aborted] { code: 'ECONNRESET', digest: '1052313065' } - GET / 200 in 903ms - POST / 200 in 701ms - POST / 200 in 88ms - ⨯ [Error: aborted] { code: 'ECONNRESET', digest: '1052313065' } - GET / 200 in 1232ms - GET / 200 in 1151ms - GET /favicon.ico?favicon.1eb660de.ico 200 in 621ms - GET /favicon.ico?favicon.1eb660de.ico 200 in 472ms - POST / 200 in 1661ms - POST / 200 in 2138ms - GET / 200 in 563ms - ⨯ [Error: aborted] { code: 'ECONNRESET', digest: '1052313065' } - GET /favicon.ico?favicon.1eb660de.ico 200 in 268ms - POST / 200 in 716ms - POST / 200 in 86ms - ⨯ [Error: aborted] { code: 'ECONNRESET', digest: '1052313065' } - GET / 200 in 1005ms - GET / 200 in 1000ms - GET /manifest.webmanifest 200 in 547ms - GET /manifest.webmanifest 200 in 515ms - POST / 200 in 2237ms - POST / 200 in 2242ms - GET / 200 in 626ms - GET /manifest.webmanifest 200 in 245ms + POST / 500 in 936ms getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not set. Ensure POSTGRES_URL is configured in Vercel environment variables. at Proxy. (lib/db/index.ts:61:16) at getSystemPrompt (lib/actions/chat.ts:314:28) @@ -181,7 +65,7 @@ getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not s 62 | `Cannot execute ${String(prop)}() — POSTGRES_URL is not set. ` + 63 | 'Ensure POSTGRES_URL is configured in Vercel environment variables.' 64 | ); - POST / 200 in 1462ms + POST / 200 in 1032ms ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: - Your middleware file exists at ./middleware.(ts|js) - clerkMiddleware() is used in your Next.js middleware file. @@ -202,9 +86,10 @@ For more details, see https://clerk.com/err/auth-middleware 21 | return userId; 22 | } 23 | { - digest: '1604531766' + digest: '2094540635' } - POST / 500 in 1323ms + POST / 500 in 863ms + GET / 200 in 602ms getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not set. Ensure POSTGRES_URL is configured in Vercel environment variables. at Proxy. (lib/db/index.ts:61:16) at getSystemPrompt (lib/actions/chat.ts:314:28) @@ -215,8 +100,7 @@ getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not s 62 | `Cannot execute ${String(prop)}() — POSTGRES_URL is not set. ` + 63 | 'Ensure POSTGRES_URL is configured in Vercel environment variables.' 64 | ); - POST / 200 in 2102ms - POST / 200 in 2176ms + POST / 200 in 846ms ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: - Your middleware file exists at ./middleware.(ts|js) - clerkMiddleware() is used in your Next.js middleware file. @@ -237,14 +121,9 @@ For more details, see https://clerk.com/err/auth-middleware 21 | return userId; 22 | } 23 | { - digest: '1604531766' + digest: '2094540635' } - POST / 500 in 1484ms - ⨯ [Error: aborted] - ○ Compiling /_error ... - ✓ Compiled /_error in 2.7s - GET / 200 in 597ms - POST / 200 in 784ms + POST / 500 in 848ms getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not set. Ensure POSTGRES_URL is configured in Vercel environment variables. at Proxy. (lib/db/index.ts:61:16) at getSystemPrompt (lib/actions/chat.ts:314:28) @@ -255,7 +134,7 @@ getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not s 62 | `Cannot execute ${String(prop)}() — POSTGRES_URL is not set. ` + 63 | 'Ensure POSTGRES_URL is configured in Vercel environment variables.' 64 | ); - POST / 200 in 943ms + POST / 200 in 999ms ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: - Your middleware file exists at ./middleware.(ts|js) - clerkMiddleware() is used in your Next.js middleware file. @@ -276,21 +155,10 @@ For more details, see https://clerk.com/err/auth-middleware 21 | return userId; 22 | } 23 | { - digest: '1604531766' + digest: '2094540635' } - POST / 500 in 784ms -getSystemPrompt: Error: Error: Cannot execute select() — POSTGRES_URL is not set. Ensure POSTGRES_URL is configured in Vercel environment variables. - at Proxy. (lib/db/index.ts:61:16) - at getSystemPrompt (lib/actions/chat.ts:314:28) - 59 | } - 60 | return function (...args: any[]) { -> 61 | throw new Error( - | ^ - 62 | `Cannot execute ${String(prop)}() — POSTGRES_URL is not set. ` + - 63 | 'Ensure POSTGRES_URL is configured in Vercel environment variables.' - 64 | ); - POST / 200 in 723ms - ⨯ Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: + POST / 500 in 1326ms +[SkyFiAction: getSkyfiConnectionStatus] Error: Clerk: auth() was called but Clerk can't detect usage of clerkMiddleware(). Please ensure the following: - Your middleware file exists at ./middleware.(ts|js) - clerkMiddleware() is used in your Next.js middleware file. - Your middleware matcher is configured to match this route or page. @@ -300,16 +168,9 @@ If you've verified your configuration and are still seeing this error, there may For more details, see https://clerk.com/err/auth-middleware - at async getClerkUserIdOnServer (lib/auth/get-current-user.ts:20:21) - at async getCurrentUserIdOnServer (lib/auth/get-current-user.ts:135:22) - at async getSelectedModel (lib/actions/users.ts:148:17) - 18 | return MOCK_USER_ID; - 19 | } -> 20 | const { userId } = await auth(); - | ^ - 21 | return userId; - 22 | } - 23 | { - digest: '1604531766' -} - POST / 500 in 794ms + POST / 200 in 703ms + ✓ Compiled in 52ms + ✓ Compiled in 36ms + ✓ Compiled in 1344ms + ✓ Compiled in 39ms + ✓ Compiled in 40ms diff --git a/verification/screenshots/verification.png b/verification/screenshots/verification.png new file mode 100644 index 00000000..83863fb1 Binary files /dev/null and b/verification/screenshots/verification.png differ diff --git a/verification/videos/4b3c21fc8a1eaf98287b63d128862a1a.webm b/verification/videos/4b3c21fc8a1eaf98287b63d128862a1a.webm new file mode 100644 index 00000000..538fd100 Binary files /dev/null and b/verification/videos/4b3c21fc8a1eaf98287b63d128862a1a.webm differ diff --git a/verification/videos/6c291f358382cf59561e02b7ca4e017b.webm b/verification/videos/6c291f358382cf59561e02b7ca4e017b.webm new file mode 100644 index 00000000..19e30ca1 Binary files /dev/null and b/verification/videos/6c291f358382cf59561e02b7ca4e017b.webm differ diff --git a/verification/videos/a2dc584b8ae1e9c7c7a7976bef136e61.webm b/verification/videos/a2dc584b8ae1e9c7c7a7976bef136e61.webm new file mode 100644 index 00000000..84c5bfcb Binary files /dev/null and b/verification/videos/a2dc584b8ae1e9c7c7a7976bef136e61.webm differ diff --git a/verification/videos/d201cea3d13586ac718a0107d918d876.webm b/verification/videos/d201cea3d13586ac718a0107d918d876.webm new file mode 100644 index 00000000..858e9c88 Binary files /dev/null and b/verification/videos/d201cea3d13586ac718a0107d918d876.webm differ diff --git a/verify_settings.py b/verify_settings.py new file mode 100644 index 00000000..a2f6b2d8 --- /dev/null +++ b/verify_settings.py @@ -0,0 +1,64 @@ +import os +from playwright.sync_api import sync_playwright + +def run_cuj(page): + print("Navigating to home page...") + page.goto("http://localhost:3000/") + page.wait_for_timeout(2000) + + # Click on "Later" button if credit limit modal is blocking the view + try: + later_btn = page.locator('button:has-text("Later")') + if later_btn.is_visible(): + print("Credit limit modal is visible. Clicking 'Later' to dismiss...") + later_btn.click() + page.wait_for_timeout(1000) + except Exception as e: + print("Later button not found or already dismissed:", e) + + print("Opening the profile menu...") + # Click on the profile toggle button + profile_toggle = page.locator('[data-testid="profile-toggle"]') + profile_toggle.click() + page.wait_for_timeout(1000) + + print("Clicking 'Settings' option...") + # Click the Settings menu item + settings_item = page.locator('[data-testid="profile-settings"]') + settings_item.click() + page.wait_for_timeout(1000) + + print("Clicking the Tools tab trigger...") + # Find and click the Tools tab trigger + tools_tab = page.locator('button[role="tab"]:has-text("Tools")') + tools_tab.click() + page.wait_for_timeout(1000) + + print("Selecting SkyFi planetary tool...") + # Select SkyFi option + skyfi_option = page.locator('label[for="SkyFi"]') + skyfi_option.click() + page.wait_for_timeout(3000) # wait for status loading indicator to complete check + + print("Taking screenshot...") + os.makedirs("/app/verification/screenshots", exist_ok=True) + page.screenshot(path="/app/verification/screenshots/verification.png") + page.wait_for_timeout(1000) + print("Verification journey complete!") + +if __name__ == "__main__": + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + # Create context recording video + os.makedirs("/app/verification/videos", exist_ok=True) + context = browser.new_context( + record_video_dir="/app/verification/videos" + ) + page = context.new_page() + try: + run_cuj(page) + except Exception as e: + print("Error running CUJ:", e) + finally: + context.close() + browser.close()