diff --git a/backend/api.py b/backend/api.py index 42da48b8..84cce089 100644 --- a/backend/api.py +++ b/backend/api.py @@ -11,7 +11,7 @@ import uuid # Import the agent API module from agent import api as agent_api -from sandbox.api import router as sandbox_router +from sandbox import api as sandbox_api # Load environment variables load_dotenv() @@ -36,6 +36,9 @@ async def lifespan(app: FastAPI): instance_id # Pass the instance_id to agent_api ) + # Initialize the sandbox API with shared resources + sandbox_api.initialize(db) + # Initialize Redis before restoring agent runs from services import redis await redis.initialize_async() @@ -56,17 +59,17 @@ app = FastAPI(lifespan=lifespan) app.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=["https://www.suna.so", "https://suna.so", "http://localhost:3000"], allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], + allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allow_headers=["Content-Type", "Authorization"], ) # Include the agent router with a prefix app.include_router(agent_api.router, prefix="/api") # Include the sandbox router with a prefix -app.include_router(sandbox_router, prefix="/api") +app.include_router(sandbox_api.router, prefix="/api") @app.get("/api/health-check") async def health_check(): diff --git a/backend/fly.toml b/backend/fly.toml new file mode 100644 index 00000000..23cb16ed --- /dev/null +++ b/backend/fly.toml @@ -0,0 +1,23 @@ +# fly.toml app configuration file generated for backend-sparkling-sea-8773 on 2025-04-18T12:56:23+01:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = 'backend-sparkling-sea-8773' +primary_region = 'cdg' + +[build] + dockerfile = 'docker/Dockerfile' + +[http_service] + internal_port = 8000 + force_https = true + auto_stop_machines = 'stop' + auto_start_machines = true + min_machines_running = 0 + processes = ['app'] + +[[vm]] + memory = '1gb' + cpu_kind = 'shared' + cpus = 1 diff --git a/backend/sandbox/api.py b/backend/sandbox/api.py index 2cdd3386..de4d2e47 100644 --- a/backend/sandbox/api.py +++ b/backend/sandbox/api.py @@ -1,15 +1,27 @@ import os from typing import List, Optional -from fastapi import FastAPI, UploadFile, File, HTTPException, APIRouter, Form +from fastapi import FastAPI, UploadFile, File, HTTPException, APIRouter, Form, Depends, Request from fastapi.responses import Response, JSONResponse from pydantic import BaseModel from utils.logger import logger +from utils.auth_utils import get_current_user_id, get_user_id_from_stream_auth from sandbox.sandbox import get_or_start_sandbox +from services.supabase import DBConnection # TODO: ADD AUTHORIZATION TO ONLY HAVE ACCESS TO SANDBOXES OF PROJECTS U HAVE ACCESS TO +# Initialize shared resources +router = APIRouter(tags=["sandbox"]) +db = None + +def initialize(_db: DBConnection): + """Initialize the sandbox API with resources from the main API.""" + global db + db = _db + logger.info("Initialized sandbox API with database connection") + class FileInfo(BaseModel): """Model for file information""" name: str @@ -19,16 +31,51 @@ class FileInfo(BaseModel): mod_time: str permissions: Optional[str] = None -# Create a router for the Sandbox API -router = APIRouter(tags=["sandbox"]) +async def verify_sandbox_access(client, sandbox_id: str, user_id: str): + """ + Verify that a user has access to a specific sandbox based on account membership. + + Args: + client: The Supabase client + sandbox_id: The sandbox ID to check access for + user_id: The user ID to check permissions for + + Returns: + dict: Project data containing sandbox information + + Raises: + HTTPException: If the user doesn't have access to the sandbox or sandbox doesn't exist + """ + # Find the project that owns this sandbox + project_result = await client.table('projects').select('*').filter('sandbox->>id', 'eq', sandbox_id).execute() + + if not project_result.data or len(project_result.data) == 0: + raise HTTPException(status_code=404, detail="Sandbox not found") + + project_data = project_result.data[0] + account_id = project_data.get('account_id') + + # Verify account membership + if account_id: + account_user_result = await client.schema('basejump').from_('account_user').select('account_role').eq('user_id', user_id).eq('account_id', account_id).execute() + if account_user_result.data and len(account_user_result.data) > 0: + return project_data + + raise HTTPException(status_code=403, detail="Not authorized to access this sandbox") @router.post("/sandboxes/{sandbox_id}/files") async def create_file( sandbox_id: str, path: str = Form(...), - file: UploadFile = File(...) + file: UploadFile = File(...), + user_id: str = Depends(get_current_user_id) ): """Create a file in the sandbox using direct file upload""" + client = await db.client + + # Verify the user has access to this sandbox + await verify_sandbox_access(client, sandbox_id, user_id) + try: # Get or start sandbox instance sandbox = await get_or_start_sandbox(sandbox_id) @@ -47,8 +94,17 @@ async def create_file( # For backward compatibility, keep the JSON version too @router.post("/sandboxes/{sandbox_id}/files/json") -async def create_file_json(sandbox_id: str, file_request: dict): +async def create_file_json( + sandbox_id: str, + file_request: dict, + user_id: str = Depends(get_current_user_id) +): """Create a file in the sandbox using JSON (legacy support)""" + client = await db.client + + # Verify the user has access to this sandbox + await verify_sandbox_access(client, sandbox_id, user_id) + try: # Get or start sandbox instance sandbox = await get_or_start_sandbox(sandbox_id) @@ -74,8 +130,17 @@ async def create_file_json(sandbox_id: str, file_request: dict): raise HTTPException(status_code=500, detail=str(e)) @router.get("/sandboxes/{sandbox_id}/files") -async def list_files(sandbox_id: str, path: str): +async def list_files( + sandbox_id: str, + path: str, + user_id: str = Depends(get_current_user_id) +): """List files and directories at the specified path""" + client = await db.client + + # Verify the user has access to this sandbox + await verify_sandbox_access(client, sandbox_id, user_id) + try: # Get or start sandbox instance using the async function sandbox = await get_or_start_sandbox(sandbox_id) @@ -102,8 +167,17 @@ async def list_files(sandbox_id: str, path: str): raise HTTPException(status_code=500, detail=str(e)) @router.get("/sandboxes/{sandbox_id}/files/content") -async def read_file(sandbox_id: str, path: str): +async def read_file( + sandbox_id: str, + path: str, + user_id: str = Depends(get_current_user_id) +): """Read a file from the sandbox""" + client = await db.client + + # Verify the user has access to this sandbox + await verify_sandbox_access(client, sandbox_id, user_id) + try: # Get or start sandbox instance using the async function sandbox = await get_or_start_sandbox(sandbox_id) @@ -121,3 +195,48 @@ async def read_file(sandbox_id: str, path: str): except Exception as e: logger.error(f"Error reading file in sandbox {sandbox_id}: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) + +@router.post("/project/{project_id}/sandbox/ensure-active") +async def ensure_project_sandbox_active( + project_id: str, + user_id: str = Depends(get_current_user_id) +): + """ + Ensure that a project's sandbox is active and running. + Checks the sandbox status and starts it if it's not running. + """ + client = await db.client + + # Find the project and sandbox information + project_result = await client.table('projects').select('*').eq('project_id', project_id).execute() + + if not project_result.data or len(project_result.data) == 0: + raise HTTPException(status_code=404, detail="Project not found") + + project_data = project_result.data[0] + account_id = project_data.get('account_id') + + # Verify account membership + if account_id: + account_user_result = await client.schema('basejump').from_('account_user').select('account_role').eq('user_id', user_id).eq('account_id', account_id).execute() + if not (account_user_result.data and len(account_user_result.data) > 0): + raise HTTPException(status_code=403, detail="Not authorized to access this project") + + # Check if project has a sandbox + sandbox_id = project_data.get('sandbox', {}).get('id') + if not sandbox_id: + raise HTTPException(status_code=404, detail="No sandbox found for this project") + + try: + # Get or start sandbox instance + logger.info(f"Ensuring sandbox {sandbox_id} is active for project {project_id}") + sandbox = await get_or_start_sandbox(sandbox_id) + + return { + "status": "success", + "sandbox_id": sandbox_id, + "message": "Sandbox is active" + } + except Exception as e: + logger.error(f"Error ensuring sandbox is active for project {project_id}: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/backend/sandbox/sandbox.py b/backend/sandbox/sandbox.py index 9b96e66a..602e10e0 100644 --- a/backend/sandbox/sandbox.py +++ b/backend/sandbox/sandbox.py @@ -1,8 +1,6 @@ import os -import requests -from time import sleep -from daytona_sdk import Daytona, DaytonaConfig, CreateSandboxParams, SessionExecuteRequest, Sandbox +from daytona_sdk import Daytona, DaytonaConfig, CreateSandboxParams, Sandbox from daytona_api_client.models.workspace_state import WorkspaceState from dotenv import load_dotenv diff --git a/backend/supabase/email-template.html b/backend/supabase/email-template.html new file mode 100644 index 00000000..9fb77b22 --- /dev/null +++ b/backend/supabase/email-template.html @@ -0,0 +1,74 @@ + + + + + + Confirm your signup to Kortix Suna + + + + + + + + + + + + +
+ +
+ +
+ Kortix / Suna +
+
+
+

+ Confirm your signup to Suna +

+ +

+ Thank you for signing up! Suna, your AI Employee, is ready to assist you. Please confirm your email to get started. +

+ +
+ + Confirm your email + +
+ +

+ If you didn't sign up for Kortix Suna, you can safely ignore this email. +

+
+
+ + + + + + + + + + +
+

+ © 2024 Kortix. All rights reserved. +

+
+ + + + + + +
+

+ Kortix AI — Suna, your AI Employee +

+
+ + \ No newline at end of file diff --git a/frontend/src/app/legal/page.tsx b/frontend/src/app/legal/page.tsx new file mode 100644 index 00000000..da18f87e --- /dev/null +++ b/frontend/src/app/legal/page.tsx @@ -0,0 +1,362 @@ +"use client"; + +import { useState, useEffect, Suspense } from "react"; +import Link from "next/link"; +import { FlickeringGrid } from "@/components/home/ui/flickering-grid"; +import { useMediaQuery } from "@/hooks/use-media-query"; +import { ArrowLeft } from "lucide-react"; +import { useSearchParams, useRouter, usePathname } from "next/navigation"; + +function LegalContent() { + const searchParams = useSearchParams(); + const router = useRouter(); + const pathname = usePathname(); + + // Get tab from URL or default to "terms" + const tabParam = searchParams.get("tab"); + const [activeTab, setActiveTab] = useState<"terms" | "privacy">( + (tabParam === "terms" || tabParam === "privacy") ? tabParam : "terms" + ); + + const tablet = useMediaQuery("(max-width: 1024px)"); + const [mounted, setMounted] = useState(false); + const [isScrolling, setIsScrolling] = useState(false); + + useEffect(() => { + setMounted(true); + + // Update the URL if it doesn't match the active tab + if (tabParam !== activeTab) { + updateUrl(activeTab); + } + }, []); + + // Update the URL when the tab changes + useEffect(() => { + updateUrl(activeTab); + }, [activeTab]); + + // Update the active tab when URL changes + useEffect(() => { + if (tabParam === "terms" || tabParam === "privacy") { + setActiveTab(tabParam); + } + }, [tabParam]); + + // Function to update URL without refreshing the page + const updateUrl = (tab: string) => { + const params = new URLSearchParams(searchParams); + params.set("tab", tab); + router.replace(`${pathname}?${params.toString()}`, { scroll: false }); + }; + + // Handle tab change + const handleTabChange = (tab: "terms" | "privacy") => { + setActiveTab(tab); + }; + + return ( +
+
+
+ {/* Left side flickering grid with gradient fades - similar to hero section */} +
+
+
+
+ + +
+ + {/* Right side flickering grid with gradient fades */} +
+
+
+
+ + +
+ + {/* Center content background with rounded bottom */} +
+ +
+
+ + + Back + + +

+ Legal Information +

+
+ +
+
+ + +
+
+ +
+
+ {activeTab === "terms" ? ( +
+

Terms of Service

+

Last updated: {new Date().toLocaleDateString()}

+ +

Terms of Service & Privacy Policy

+

Last updated and effective date: 13 August 2024

+ +

PLEASE READ THESE TERMS OF USE ("AGREEMENT" OR "TERMS OF USE" or "TERMS OF SERVICE" or "TERMS AND CONDITIONS") CAREFULLY BEFORE USING THE SERVICES OFFERED BY Kortix AI Corp (701 Tillery Street Unit 12-2521 Austin, Texas 78702, United States). THIS AGREEMENT SETS FORTH THE LEGALLY BINDING TERMS AND CONDITIONS FOR YOUR USE OF THE SUNA WEBSITE AND ALL RELATED SERVICES.

+ +

Definitions

+
    +
  • "Company" refers to Kortix AI Corp (701 Tillery Street Unit 12-2521 Austin, Texas 78702, United States).
  • +
  • "Site" refers to the Suna website, including any related features, content, or applications offered from time to time by the Company.
  • +
  • "Service" refers to the Suna website and all related services provided by the Company, including the AI-powered agent that helps you accomplish real-world tasks.
  • +
  • "User" refers to any individual or entity using the Site or Service.
  • +
  • "Content" refers to any text, images, code, or other material uploaded to or generated by the Site or Service by Users.
  • +
  • "Assets" refers to the results and outputs generated by the AI models provided by the Service, including any code, applications, or reports.
  • +
  • "Terms of Use" refers to these terms and conditions governing the use of the Site and Service.
  • +
  • "License" refers to the permissions granted to Users to use the Site and Service as outlined in these Terms of Use.
  • +
  • "DMCA" refers to the Digital Millennium Copyright Act.
  • +
  • "Fees" refers to the subscription or other payments made by Users for access to certain features or levels of the Service.
  • +
  • "Notice Address" refers to the contact address for the Company, specifically legal@kortix.ai
  • +
  • "Privacy Policy" refers to the document outlining how the Company collects, uses, and protects User data.
  • +
  • "Third Party" refers to any person or entity other than the Company or the User.
  • +
  • "AAA Rules" refers to the American Arbitration Association's Consumer Arbitration Rules.
  • +
  • "Claim" refers to any dispute, claim, demand, or cause of action that arises between the User and the Company.
  • +
+ +

Acceptance of Terms of Use

+

The Service is offered subject to acceptance without modification of all of these Terms of Use and all other operating rules, policies, and procedures that may be published from time to time in connection with the Services by the Company. In addition, some services offered through the Service may be subject to additional terms and conditions promulgated by the Company from time to time; your use of such services is subject to those additional terms and conditions, which are incorporated into these Terms of Use by this reference.

+ +

The Company may, in its sole discretion, refuse to offer the Service to any person or entity and change its eligibility criteria at any time. This provision is void where prohibited by law and the right to access the Service is revoked in such jurisdictions.

+ +

Rules and Conduct

+

By using the Service, you agree that it is intended solely for the purpose of using an AI assistant to help accomplish real-world tasks through natural conversation. The Service's capabilities include browser automation, file management, web crawling, search capabilities, command-line execution, website deployment, and integration with various APIs and services. You acknowledge and agree that when using the Service, you must have the necessary rights and permissions for any content or data you incorporate. You are solely responsible for ensuring that your use of the Service is legal and that you have the necessary rights for any tasks you perform. The Company is not responsible for any content created or actions taken through the Service and disclaims all liability for any issues arising from the created content or performed actions, including but not limited to copyright infringement, illegal content, or any other legal matters.

+ +

As a condition of use, you promise not to use the Service for any purpose that is prohibited by the Terms of Use. By way of example, and not as a limitation, you shall not (and shall not permit any third party to) take any action (including making use of the Site, any Assets, or our models or derivatives of our models) that:

+ +
    +
  • would constitute a violation of any applicable law, rule, or regulation;
  • +
  • infringes upon any intellectual property or other right of any other person or entity;
  • +
  • is threatening, abusive, harassing, defamatory, libelous, deceptive, fraudulent, invasive of another's privacy, tortious, obscene, offensive, furthering of self-harm, or profane;
  • +
  • creates Assets that exploit or abuse children;
  • +
  • generates or disseminates verifiably false information with the purpose of harming others;
  • +
  • impersonates or attempts to impersonate others;
  • +
  • generates or disseminates personally identifying or identifiable information;
  • +
  • creates Assets that imply or promote support of a terrorist organization;
  • +
  • creates Assets that condone or promote violence against people based on any protected legal category.
  • +
+ +

You agree not to use the Service for the purpose of generating illegal or harmful applications or content.

+ +

User Responsibility for Created Content

+

You agree not to create any content or perform any actions that are illegal, infringe on the rights of any third party, or violate any applicable law, regulation, or these Terms of Use. The Company reserves the right to remove any content or disable any action that it deems to be in violation of these Terms of Use, at its sole discretion, and without notice. You are solely responsible for any content you create or actions you perform, and you agree to indemnify and hold harmless the Company from any claims, losses, damages, or expenses arising out of or related to your created content or performed actions.

+ +

Open Source License

+

Suna is licensed under the Apache License, Version 2.0. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

+ +

Accuracy Disclaimer

+

The Service is provided for general assistance purposes. The analysis and results generated by the AI are not guaranteed to be error-free and should be thoroughly verified before relying on them. Users assume full responsibility for any content created or actions performed using the Service.

+ +

DMCA and Takedowns Policy

+

The Company utilizes artificial intelligence systems to generate content and perform actions. Such generation may unintentionally involve copyrighted material or trademarks held by others. We respect rights holders internationally, and we ask our users to do the same. If you believe your copyright or trademark is being infringed by the Service, please write to legal@kortixai.com and we will process and investigate your request and take appropriate actions under the Digital Millennium Copyright Act and other applicable intellectual property laws with respect to any alleged or actual infringement.

+ +

Fees and Payments

+

The Company may offer paid Services. You can learn more about our pricing after signing up. You may sign up for a subscription, payable in U.S. dollars, that will automatically renew. You can stop using the Service and cancel your subscription at any time through the website or by emailing us at legal@kortixai.com. If you cancel your subscription, you may not receive a refund or credit for any amounts that have already been billed or paid. The Company reserves the right to change its prices at any time. If you are on a subscription plan, changes to pricing will not apply until your next renewal.

+ +

Unless otherwise stated, your subscription fees ("Fees") do not include federal, state, local, and foreign taxes, duties, and other similar assessments ("Taxes"). You are responsible for all Taxes associated with your purchase and we may invoice you for such Taxes. You agree to timely pay such Taxes and provide us with documentation showing the payment or additional evidence that we may reasonably require. If any amount of your Fees is past due, we may suspend your access to the Services after we provide you with written notice of late payment.

+ +

Termination

+

The Company may terminate your access to all or any part of the Service at any time if you fail to comply with these Terms of Use, which may result in the forfeiture and destruction of all information associated with your account. Further, either party may terminate the Services for any reason and at any time upon written notice. If you wish to terminate your account, you may do so by following the instructions on the Service. Any fees paid hereunder are non-refundable. Upon any termination, all rights and licenses granted to you in this Agreement shall immediately terminate, but all provisions hereof which by their nature should survive termination shall survive termination, including, without limitation, warranty disclaimers, indemnity, and limitations of liability.

+ +

Dispute Resolution by Binding Arbitration

+

PLEASE READ THIS SECTION CAREFULLY, AS IT AFFECTS YOUR RIGHTS.

+ +

Agreement to Arbitrate. You and the Company agree that any and all disputes, claims, demands, or causes of action ("Claims") that have arisen or may arise between you and us, whether arising out of or relating to these Terms, the Site, or any aspect of the relationship or transactions between us, will be resolved exclusively through final and binding arbitration before a neutral arbitrator, rather than in a court by a judge or jury, in accordance with the terms of this Arbitration Agreement, except that you or we may (but are not required to) assert individual Claims in small claims court if such Claims are within the scope of such court's jurisdiction.

+ +

Prohibition of Class and Representative Actions. YOU AND WE AGREE THAT EACH OF US MAY BRING CLAIMS AGAINST THE OTHER ONLY ON AN INDIVIDUAL BASIS AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE ACTION OR PROCEEDING.

+ +

Pre-Arbitration Dispute Resolution. Before commencing any arbitration, you agree to provide the Company with a written notice of Claim, and the Company agrees to provide you with a written notice of Claim to the extent reasonably possible based on the availability of your contact information to the Company. The Notice must describe the nature and basis of the Claim in sufficient detail and set forth the specific relief sought.

+ +

Both parties agree that they will attempt to resolve a Claim through informal negotiation within sixty (60) calendar days from the date the Notice is received. If the Claim is not resolved within sixty (60) calendar days after the Notice is received, you or we may commence an arbitration proceeding.

+ +

Choice of Law

+

Any and all Claims shall be governed by the Federal Arbitration Act and the internal substantive laws of Singapore in all respects, without regard for the jurisdiction or forum in which the user is domiciled, resides, or is located at the time of such access or use. Except as provided in the Arbitration Agreement, all Claims will be brought in the federal or state courts in Singapore, and you and the Company each unconditionally, voluntarily, and irrevocably consent to the exclusive personal jurisdiction and venue of those courts.

+ +

Links to and From Other Websites

+

You may gain access to other websites via links on the Site. These Terms apply to the Site only and do not apply to other parties' websites. Similarly, you may have come to the Site via a link from another website. The terms of use of other websites do not apply to the Site. The Company assumes no responsibility for any terms of use or material outside of the Site accessed via any link.

+ +

Modification of Terms of Use

+

At its sole discretion, the Company may modify or replace any of the Terms of Use, or change, suspend, or discontinue the Service (including without limitation, the availability of any feature, database, or content) at any time by posting a notice on the Site or by sending you an email. The Company may also impose limits on certain features and services or restrict your access to parts or all of the Service without notice or liability. It is your responsibility to check the Terms of Use periodically for changes. Your continued use of the Service following the posting of any changes to the Terms of Use constitutes acceptance of those changes.

+ +

Trademarks and Patents

+

All Suna logos, marks, and designations are trademarks or registered trademarks of the Company. All other trademarks mentioned on this website are the property of their respective owners. The trademarks and logos displayed on this website may not be used without the prior written consent of the Company or their respective owners. Portions, features, and/or functionality of the Company's products may be protected under the Company's patent applications or patents.

+ +

Licensing Terms

+

Subject to your compliance with this Agreement, the conditions herein, and any limitations applicable to the Company or by law:

+
    +
  • you are granted a non-exclusive, limited, non-transferable, non-sublicensable, non-assignable, freely revocable license to access and use the Service for business or personal use;
  • +
  • you own all Assets you create with the Services, and
  • +
  • we hereby assign to you all rights, title, and interest in and to such Assets for your personal or commercial use.
  • +
+

Otherwise, the Company reserves all rights not expressly granted under these Terms of Use. Each person must have a unique account, and you are responsible for any activity conducted on your account. A breach or violation of any of our Terms of Use may result in an immediate termination of your right to use our Service.

+ +

Indemnification

+

You shall defend, indemnify, and hold harmless the Company, its affiliates, and each of its, and its affiliates employees, contractors, directors, suppliers, and representatives from all liabilities, losses, claims, and expenses, including reasonable attorneys' fees, that arise from or relate to (i) your use or misuse of, or access to, the Service, or (ii) your violation of the Terms of Use or any applicable law, contract, policy, regulation, or other obligation. The Company reserves the right to assume the exclusive defense and control of any matter otherwise subject to indemnification by you, in which event you will assist and cooperate with the Company in connection therewith.

+ +

Limitation of Liability

+

IN NO EVENT SHALL THE COMPANY OR ITS DIRECTORS, EMPLOYEES, AGENTS, PARTNERS, SUPPLIERS, OR CONTENT PROVIDERS, BE LIABLE UNDER CONTRACT, TORT, STRICT LIABILITY, NEGLIGENCE, OR ANY OTHER LEGAL OR EQUITABLE THEORY WITH RESPECT TO THE SERVICE (I) FOR ANY LOST PROFITS, DATA LOSS, COST OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR SPECIAL, INDIRECT, INCIDENTAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES OF ANY KIND WHATSOVER, OR SUBSTITUTE GOODS OR SERVICES, (II) FOR YOUR RELIANCE ON THE SERVICE, INCLUDING ANY APPLICATIONS CREATED USING THE AI, OR (III) FOR ANY DIRECT DAMAGES IN EXCESS (IN THE AGGREGATE) OF THE FEES PAID BY YOU FOR THE SERVICE OR, IF GREATER, $100. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATIONS AND EXCLUSIONS MAY NOT APPLY TO YOU.

+ +

Disclaimer

+

ALL USE OF THE SERVICE AND ANY CONTENT IS UNDERTAKEN ENTIRELY AT YOUR OWN RISK. THE SERVICE (INCLUDING, WITHOUT LIMITATION, THE SUNA WEB APP AND ANY CONTENT) IS PROVIDED "AS IS" AND "AS AVAILABLE" AND IS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE, AND ANY WARRANTIES IMPLIED BY ANY COURSE OF PERFORMANCE OR USAGE OF TRADE, ALL OF WHICH ARE EXPRESSLY DISCLAIMED. SUNA DOES NOT GUARANTEE THE ACCURACY, COMPLETENESS, OR RELIABILITY OF THE AI-GENERATED CONTENT, AND USERS ASSUME FULL RESPONSIBILITY FOR ANY APPLICATIONS CREATED USING THE SERVICE. SOME STATES DO NOT ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY LASTS, SO THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.

+ +

Age Requirements

+

By accessing the Services, you confirm that you're at least 18 years old and meet the minimum age of digital consent in your country. If you are not old enough to consent to our Terms of Use in your country, your parent or guardian must agree to this Agreement on your behalf.

+ +

Please ask your parent or guardian to read these terms with you. If you're a parent or legal guardian, and you allow your teenager to use the Services, then these terms also apply to you and you're responsible for your teenager's activity on the Services. No assurances are made as to the suitability of the Assets for you.

+ +

Contact Us

+

For questions regarding the Service, you can get in touch by emailing us at legal@kortixai.com.

+
+ ) : ( +
+

Privacy Policy

+

Last updated: {new Date().toLocaleDateString()}

+ +

Privacy

+

Our commitment to privacy and data protection is reflected in this Privacy Statement which describes how we collect and process "personal information" that identifies you, like your name or email address. Any other information besides this is "non-personal information." If we store personal information with non-personal information, we'll consider that combination to be personal information.

+ +

References to our "Services" at Suna in this statement include our website, apps, and other products and services. This statement applies to our Services that display or reference this Privacy Statement. Third-party services that we integrate with are governed under their own privacy policies.

+ +

Suna does not collect biometric or identifying information. All data is processed securely and any data is deleted upon account removal.

+ +

Information Gathering

+

We learn information about you when:

+ +

You directly provide it to us.

+

For example, we collect:

+
    +
  • Name and contact information. We collect details such as name and email address.
  • +
  • Payment information. If you make a purchase, we collect credit card numbers, financial account information, and other payment details.
  • +
  • Content and files. We collect and retain the videos, documents, or other files you send to us in connection with delivering our Services, including via email or chat.
  • +
+ +

We collect it automatically through our products and services.

+

For instance, we collect:

+
    +
  • Identifiers and device information. When you visit our websites, our web servers log your Internet Protocol (IP) address and information about your device, including device identifiers, device type, operating system, browser, and other software including type, version, language, settings, and configuration.
  • +
  • Geolocation data. Depending on your device and app settings, we collect geolocation data when you use our Services.
  • +
  • Usage data. We log your activity on our website, including the URL of the website from which you came to our site, pages you viewed on our website, how long you spent on a page, access times, and other details about your use of and actions on our website. We also collect information about which web-elements or objects you interact with on our Service, metadata about your activity on the Service, changes in your user state, and the duration of your use of our Service.
  • +
+ +

Someone else tells us information about you.

+

Third-party sources include, for example:

+
    +
  • Third-party partners. Third-party applications and services, including social networks you choose to connect with or interact with through our services.
  • +
  • Service providers. Third parties that collect or provide data in connection with work they do on our behalf, for example, companies that determine your device's location based on its IP address.
  • +
+ +

When we try and understand more about you based on information you've given to us.

+

We infer new information from other data we collect, including using automated means to generate information about your likely preferences or other characteristics ("inferences"). For example, we infer your general geographic location based on your IP address.

+ +

Information Use

+

We use each category of personal information about you:

+
    +
  • To provide you with our Services
  • +
  • To improve and develop our Services
  • +
  • To communicate with you
  • +
  • To provide customer support
  • +
+ +

Information Sharing

+

We share information about you:

+
    +
  • When we've asked & received your consent to share it.
  • +
  • As needed, including to third-party service providers, to process or provide Services or products to you, but only if those entities agree to provide at least the same level of privacy protection we're committed to under this Privacy Statement.
  • +
  • To comply with laws or to respond to lawful requests and legal processes, provided that we'll notify you unless we're legally prohibited from doing so. We'll only release personal information if we believe in good faith that it's legally required.
  • +
  • Only if we reasonably believe it's necessary to prevent harm to the rights, property, or safety of you or others.
  • +
  • In the event of a corporate restructuring or change in our organizational structure or status to a successor or affiliate.
  • +
+ +

Please note that some of our Services include integrations, references, or links to services provided by third parties whose privacy practices differ from ours. If you provide personal information to any of those third parties, or allow us to share personal information with them, that data is governed by their privacy statements.

+ +

Finally, we may share non-personal information in accordance with applicable law.

+ +

Information Protection

+

We implement physical, business, and technical security measures to safeguard your personal information. In the event of a security breach, we'll notify you so that you can take appropriate protective steps. We only keep your personal information for as long as is needed to do what we collected it for. After that, we destroy it unless required by law.

+ +

Contact Us

+

You can get in touch by emailing us at legal@kortixai.com.

+
+ )} +
+
+ +
+ + Return to Home + + + + + + +
+
+
+
+
+ ); +} + +// Wrap the LegalContent component with Suspense to handle useSearchParams() +export default function LegalPage() { + return ( + Loading...}> + + + ); +} diff --git a/frontend/src/components/GoogleSignIn.tsx b/frontend/src/components/GoogleSignIn.tsx index 0946a7f4..452298f5 100644 --- a/frontend/src/components/GoogleSignIn.tsx +++ b/frontend/src/components/GoogleSignIn.tsx @@ -43,6 +43,8 @@ interface GoogleButtonOptions { size?: string; text?: string; shape?: string; + logoAlignment?: string; + width?: number; } interface GoogleNotification { @@ -60,7 +62,6 @@ interface GoogleSignInProps { export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) { const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID; - const buttonRef = useRef(null); const [isLoading, setIsLoading] = useState(false); const handleGoogleSignIn = useCallback(async (response: GoogleSignInResponse) => { @@ -80,12 +81,6 @@ export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) { } }, [returnUrl]); - const handleCustomButtonClick = useCallback(() => { - if (window.google) { - window.google.accounts.id.prompt(); - } - }, []); - useEffect(() => { // Assign the callback to window object so it can be called from the Google button window.handleGoogleSignIn = handleGoogleSignIn; @@ -128,7 +123,7 @@ export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) { return ( <> - {/* Hidden div for One Tap popup */} + {/* Google One Tap container */}
- {/* Custom Google button that matches site style */} - - - {/* Hidden original Google button with no styles */} -
+ {/* Google Sign-In button container styled to match site design */} +
+ {/* The Google button will be rendered here */} +