mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa0044e821 | |||
| 536588dd19 | |||
| a10dfffe0f | |||
| aaaad02042 | |||
| 17d55eab72 | |||
| bf9f953bea |
Generated
+14
@@ -44,6 +44,7 @@
|
||||
"react-router": "^7.5.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-toastify": "^11.0.5",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
@@ -15444,6 +15445,19 @@
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-toastify": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
||||
"integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19",
|
||||
"react-dom": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"react-router": "^7.5.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-toastify": "^11.0.5",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sirv-cli": "^3.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
|
||||
@@ -4,8 +4,6 @@ import { I18nKey } from "#/i18n/declaration";
|
||||
import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { TOSCheckbox } from "./tos-checkbox";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
import { BrandButton } from "../settings/brand-button";
|
||||
import GitHubLogo from "#/assets/branding/github-logo.svg?react";
|
||||
import GitLabLogo from "#/assets/branding/gitlab-logo.svg?react";
|
||||
@@ -19,7 +17,6 @@ interface AuthModalProps {
|
||||
|
||||
export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isTosAccepted, setIsTosAccepted] = React.useState(false);
|
||||
|
||||
const gitlabAuthUrl = useAuthUrl({
|
||||
appMode: appMode || null,
|
||||
@@ -28,14 +25,14 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
|
||||
const handleGitHubAuth = () => {
|
||||
if (githubAuthUrl) {
|
||||
handleCaptureConsent(true);
|
||||
// Always start the OIDC flow, let the backend handle TOS check
|
||||
window.location.href = githubAuthUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const handleGitLabAuth = () => {
|
||||
if (gitlabAuthUrl) {
|
||||
handleCaptureConsent(true);
|
||||
// Always start the OIDC flow, let the backend handle TOS check
|
||||
window.location.href = gitlabAuthUrl;
|
||||
}
|
||||
};
|
||||
@@ -50,11 +47,8 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<TOSCheckbox onChange={() => setIsTosAccepted((prev) => !prev)} />
|
||||
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<BrandButton
|
||||
isDisabled={!isTosAccepted}
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitHubAuth}
|
||||
@@ -65,7 +59,6 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
</BrandButton>
|
||||
|
||||
<BrandButton
|
||||
isDisabled={!isTosAccepted}
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleGitLabAuth}
|
||||
|
||||
@@ -458,4 +458,8 @@ export enum I18nKey {
|
||||
SYSTEM_MESSAGE_MODAL$TOOLS_TAB = "SYSTEM_MESSAGE_MODAL$TOOLS_TAB",
|
||||
SYSTEM_MESSAGE_MODAL$PARAMETERS = "SYSTEM_MESSAGE_MODAL$PARAMETERS",
|
||||
SYSTEM_MESSAGE_MODAL$NO_TOOLS = "SYSTEM_MESSAGE_MODAL$NO_TOOLS",
|
||||
TOS$ACCEPT_TERMS_OF_SERVICE = "TOS$ACCEPT_TERMS_OF_SERVICE",
|
||||
TOS$ACCEPT_TERMS_DESCRIPTION = "TOS$ACCEPT_TERMS_DESCRIPTION",
|
||||
TOS$CONTINUE = "TOS$CONTINUE",
|
||||
TOS$ERROR_ACCEPTING = "TOS$ERROR_ACCEPTING",
|
||||
}
|
||||
|
||||
@@ -6586,4 +6586,53 @@
|
||||
"es": "No hay herramientas disponibles para este agente",
|
||||
"tr": "Bu ajan için kullanılabilir araç yok"
|
||||
}
|
||||
,
|
||||
"TOS$ACCEPT_TERMS_OF_SERVICE": {
|
||||
"en": "Accept Terms of Service",
|
||||
"ja": "利用規約に同意する",
|
||||
"zh-CN": "接受服务条款",
|
||||
"zh-TW": "接受服務條款",
|
||||
"ko-KR": "서비스 약관 동의",
|
||||
"fr": "Accepter les conditions d'utilisation",
|
||||
"es": "Aceptar términos de servicio",
|
||||
"de": "Nutzungsbedingungen akzeptieren",
|
||||
"it": "Accetta i termini di servizio",
|
||||
"pt": "Aceitar termos de serviço"
|
||||
},
|
||||
"TOS$ACCEPT_TERMS_DESCRIPTION": {
|
||||
"en": "Please review and accept our terms of service before continuing",
|
||||
"ja": "続行する前に利用規約を確認して同意してください",
|
||||
"zh-CN": "请在继续之前查看并接受我们的服务条款",
|
||||
"zh-TW": "請在繼續之前查看並接受我們的服務條款",
|
||||
"ko-KR": "계속하기 전에 서비스 약관을 검토하고 동의해 주세요",
|
||||
"fr": "Veuillez examiner et accepter nos conditions d'utilisation avant de continuer",
|
||||
"es": "Por favor, revise y acepte nuestros términos de servicio antes de continuar",
|
||||
"de": "Bitte überprüfen und akzeptieren Sie unsere Nutzungsbedingungen, bevor Sie fortfahren",
|
||||
"it": "Si prega di rivedere e accettare i nostri termini di servizio prima di continuare",
|
||||
"pt": "Por favor, revise e aceite nossos termos de serviço antes de continuar"
|
||||
},
|
||||
"TOS$CONTINUE": {
|
||||
"en": "Continue",
|
||||
"ja": "続行",
|
||||
"zh-CN": "继续",
|
||||
"zh-TW": "繼續",
|
||||
"ko-KR": "계속",
|
||||
"fr": "Continuer",
|
||||
"es": "Continuar",
|
||||
"de": "Fortfahren",
|
||||
"it": "Continua",
|
||||
"pt": "Continuar"
|
||||
},
|
||||
"TOS$ERROR_ACCEPTING": {
|
||||
"en": "Error accepting Terms of Service",
|
||||
"ja": "利用規約の承諾中にエラーが発生しました",
|
||||
"zh-CN": "接受服务条款时出错",
|
||||
"zh-TW": "接受服務條款時出錯",
|
||||
"ko-KR": "서비스 약관 수락 중 오류 발생",
|
||||
"fr": "Erreur lors de l'acceptation des conditions d'utilisation",
|
||||
"es": "Error al aceptar los Términos de Servicio",
|
||||
"de": "Fehler beim Akzeptieren der Nutzungsbedingungen",
|
||||
"it": "Errore nell'accettazione dei Termini di Servizio",
|
||||
"pt": "Erro ao aceitar os Termos de Serviço"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
export default [
|
||||
layout("routes/root-layout.tsx", [
|
||||
index("routes/home.tsx"),
|
||||
route("accept-tos", "routes/accept-tos.tsx"),
|
||||
route("settings", "routes/settings.tsx", [
|
||||
index("routes/account-settings.tsx"),
|
||||
route("billing", "routes/billing.tsx"),
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { toast } from "react-toastify";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react";
|
||||
import { TOSCheckbox } from "#/components/features/waitlist/tos-checkbox";
|
||||
import { BrandButton } from "#/components/features/settings/brand-button";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
import { openHands } from "#/api/open-hands-axios";
|
||||
|
||||
export default function AcceptTOS() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [isTosAccepted, setIsTosAccepted] = React.useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
|
||||
// Get the redirect URL from the query parameters
|
||||
const redirectUrl = searchParams.get("redirect_url") || "/";
|
||||
|
||||
const handleAcceptTOS = async () => {
|
||||
if (isTosAccepted && !isSubmitting) {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Set consent for analytics
|
||||
handleCaptureConsent(true);
|
||||
|
||||
// Call the API to record TOS acceptance in the database
|
||||
const response = await openHands.post("/api/accept_tos", {
|
||||
redirect_url: redirectUrl,
|
||||
});
|
||||
|
||||
// Get the redirect URL from the response
|
||||
const finalRedirectUrl = response.data.redirect_url || redirectUrl;
|
||||
|
||||
// Check if the redirect URL is an external URL (starts with http or https)
|
||||
if (
|
||||
finalRedirectUrl.startsWith("http://") ||
|
||||
finalRedirectUrl.startsWith("https://")
|
||||
) {
|
||||
// For external URLs, redirect using window.location
|
||||
window.location.href = finalRedirectUrl;
|
||||
} else {
|
||||
// For internal routes, use navigate
|
||||
navigate(finalRedirectUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(t(I18nKey.TOS$ERROR_ACCEPTING), error);
|
||||
toast.error(t(I18nKey.ERROR$GENERIC));
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full">
|
||||
<div className="border border-tertiary p-8 rounded-lg max-w-md w-full flex flex-col gap-6 items-center">
|
||||
<AllHandsLogo width={68} height={46} />
|
||||
|
||||
<div className="flex flex-col gap-2 w-full items-center text-center">
|
||||
<h1 className="text-2xl font-bold">
|
||||
{t(I18nKey.TOS$ACCEPT_TERMS_OF_SERVICE)}
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500">
|
||||
{t(I18nKey.TOS$ACCEPT_TERMS_DESCRIPTION)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<TOSCheckbox onChange={() => setIsTosAccepted((prev) => !prev)} />
|
||||
|
||||
<BrandButton
|
||||
isDisabled={!isTosAccepted || isSubmitting}
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleAcceptTOS}
|
||||
className="w-full"
|
||||
>
|
||||
{isSubmitting ? t(I18nKey.HOME$LOADING) : t(I18nKey.TOS$CONTINUE)}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -112,8 +112,17 @@ export default function MainApp() {
|
||||
}, [error?.status, pathname, isFetching]);
|
||||
|
||||
const userIsAuthed = !!isAuthed && !authError;
|
||||
const isOnTosPage = pathname === "/accept-tos";
|
||||
|
||||
// Only show the auth modal if:
|
||||
// 1. User is not authenticated
|
||||
// 2. We're not currently on the TOS page
|
||||
// 3. We're in SaaS mode
|
||||
const renderAuthModal =
|
||||
!isFetchingAuth && !userIsAuthed && config.data?.APP_MODE === "saas";
|
||||
!isFetchingAuth &&
|
||||
!userIsAuthed &&
|
||||
!isOnTosPage &&
|
||||
config.data?.APP_MODE === "saas";
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user