diff --git a/apps/backend/package.json b/apps/backend/package.json index 31b6394ad..1b69452dd 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -13,7 +13,7 @@ "db": "vitnode-backend db" }, "dependencies": { - "@ai-sdk/google": "^1.0.10", + "@ai-sdk/google": "^1.0.12", "@nestjs/cache-manager": "3.0.0-next.0", "@nestjs/common": "^10.4.15", "@nestjs/core": "^10.4.15", @@ -24,7 +24,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "drizzle-kit": "^0.30.1", - "drizzle-orm": "^0.38.2", + "drizzle-orm": "^0.38.3", "react": "^19.0.0", "react-dom": "^19.0.0", "reflect-metadata": "^0.2.2", diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 7dadb05f2..08c1e513c 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -20,7 +20,7 @@ const google = createGoogleGenerativeAI({ config: DATABASE_ENVS, schemaDatabase, }, - ai: google('gemini-1.5-pro'), + ai: google('gemini-2.0-flash-exp'), // captcha: { // type: 'cloudflare_turnstile', // secret_key: '', diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 6bc1bb20a..275d15987 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -16,12 +16,12 @@ "dependencies": { "@hookform/resolvers": "^3.9.1", "geist": "^1.3.1", - "lucide-react": "^0.468.0", + "lucide-react": "^0.469.0", "next": "^15.1.2", "next-intl": "4.0.0-beta-ddd5ae5", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-hook-form": "^7.54.1", + "react-hook-form": "^7.54.2", "recharts": "^2.15.0", "sonner": "^1.7.1", "vitnode-frontend": "workspace:*", diff --git a/apps/frontend/src/plugins/admin/langs/en.json b/apps/frontend/src/plugins/admin/langs/en.json index 837aff4a1..53251064f 100644 --- a/apps/frontend/src/plugins/admin/langs/en.json +++ b/apps/frontend/src/plugins/admin/langs/en.json @@ -62,12 +62,28 @@ "diagnostic": { "title": "Diagnostic Tools", "desc": "Use these tools to diagnose and fix common issues with your website.", + "get_support": "Get Support", "clear_cache": { "title": "Clear Cache", "desc": "This action clean up the cache of your website. This can help fix issues with your website but may slow down your website temporarily.", "confirm": "Yes, clear cache", "success": "Cache has been cleared." - } + }, + "captcha": { + "title": "Captcha", + "desc": "Protect your website from spam." + }, + "email": { + "title": "Email", + "desc": "Access to send emails." + }, + "ai": { + "title": "Artificial Intelligence (AI)", + "desc": "Use AI to improve your website." + }, + "enable": "Enable", + "disable": "Disable", + "how_to_enable": "How to enable?" }, "email": { "hello": "Hello" diff --git a/packages/backend/package.json b/packages/backend/package.json index 5a6be2636..6f2ed2583 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -77,7 +77,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "drizzle-kit": "^0.30.1", - "drizzle-orm": "^0.38.2", + "drizzle-orm": "^0.38.3", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -101,7 +101,7 @@ "class-validator": "^0.14.1", "concurrently": "^9.1.0", "drizzle-kit": "^0.30.1", - "drizzle-orm": "^0.38.2", + "drizzle-orm": "^0.38.3", "eslint-config-typescript-vitnode": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -115,7 +115,7 @@ "@nestjs/swagger": "^8.1.0", "@nestjs/throttler": "^6.3.0", "@react-email/render": "^1.0.3", - "ai": "^4.0.17", + "ai": "^4.0.22", "cache-manager": "^6.3.1", "cookie-parser": "^1.4.7", "dotenv": "^16.4.7", diff --git a/packages/backend/src/core/admin/languages/services/translate-ai.service.ts b/packages/backend/src/core/admin/languages/services/translate-ai.service.ts index 14edf87e1..ca2213aa4 100644 --- a/packages/backend/src/core/admin/languages/services/translate-ai.service.ts +++ b/packages/backend/src/core/admin/languages/services/translate-ai.service.ts @@ -77,14 +77,13 @@ export class TranslateAiLanguagesAdminService { prompt: `You are a professional translator in JSON format. Task: Translate the content below from en to ${code}. - Only translate the untranslated parts. If the content is already translated, leave it as is. Translation Requirements: + - If the content is already translated and it is correct, do not change it, - Do not translate the code, - Maintain exact file structure, indentation, and formatting, - Preserve all object/property keys, syntax characters, and punctuation marks exactly, - Keep consistent capitalization, spacing, and line breaks, - - Provide natural, culturally-adapted translations that sound native, - Match source file's JSON/object structure precisely, - Wrap return JSON in \`\`\`json code block. @@ -97,7 +96,7 @@ export class TranslateAiLanguagesAdminService { const cleanedText = text .replace(/^```json\n?/, '') - .replace(/\n?```$/, ''); + .replace(/\n?```\s*$/, ''); await writeFile( join(pluginLangPath, `${code}.json`), JSON.stringify(JSON.parse(cleanedText), null, 2), diff --git a/packages/backend/src/core/middleware/services/show.service.ts b/packages/backend/src/core/middleware/services/show.service.ts index 94dcb6ebe..205c5f4dc 100644 --- a/packages/backend/src/core/middleware/services/show.service.ts +++ b/packages/backend/src/core/middleware/services/show.service.ts @@ -1,6 +1,7 @@ import type { CaptchaConfig } from '@/helpers/captcha.service'; import { ABSOLUTE_PATHS } from '@/app.module'; +import { AiHelperService } from '@/helpers/ai.service'; import { SSOAuthHelper } from '@/helpers/auth/sso/sso.service'; import { ConfigHelperService } from '@/helpers/config.service'; import { EmailHelperService } from '@/helpers/email/email.service'; @@ -28,6 +29,7 @@ export class ShowMiddlewareService { private readonly configService: ConfigHelperService, @Inject('VITNODE_CAPTCHA_CONFIG') private readonly captchaConfig?: CaptchaConfig, + private readonly aiHelper?: AiHelperService, ) {} protected async getManifests({ @@ -110,7 +112,7 @@ export class ShowMiddlewareService { plugins: ['admin', 'core', ...plugins.map(plugin => plugin.code)], languages_code_default: langs.find(lang => lang.default)?.code ?? 'en', is_email_enabled: this.mailService.checkIfEnable(), - is_ai_enabled: false, // TODO: Add AI service + is_ai_enabled: !!this.aiHelper, site_name: configFromDb.site_name, site_short_name: configFromDb.site_short_name, security: { diff --git a/packages/create-vitnode-app/helpers/create-packages-json.ts b/packages/create-vitnode-app/helpers/create-packages-json.ts index 668cad10a..3a5cccea4 100644 --- a/packages/create-vitnode-app/helpers/create-packages-json.ts +++ b/packages/create-vitnode-app/helpers/create-packages-json.ts @@ -88,7 +88,7 @@ export const createPackagesJSON = async ({ 'next-intl': '4.0.0-beta-ddd5ae5', react: '^19.0.0', 'react-dom': '^19.0.0', - 'react-hook-form': '^7.54.1', + 'react-hook-form': '^7.54.2', recharts: '^2.15.0', sonner: '^1.7.1', 'vitnode-frontend': `^${pkg.version}`, @@ -138,7 +138,7 @@ export const createPackagesJSON = async ({ 'class-transformer': '^0.5.1', 'class-validator': '^0.14.1', 'drizzle-kit': '^0.30.1', - 'drizzle-orm': '^0.38.2', + 'drizzle-orm': '^0.38.3', react: '^19.0.0', 'react-dom': '^19.0.0', 'reflect-metadata': '^0.2.2', diff --git a/packages/eslint-config-typescript-vitnode/package.json b/packages/eslint-config-typescript-vitnode/package.json index a01c36b63..bbf0408dd 100644 --- a/packages/eslint-config-typescript-vitnode/package.json +++ b/packages/eslint-config-typescript-vitnode/package.json @@ -45,11 +45,11 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^4.4.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124", "eslint-plugin-react-hooks": "^5.1.0", "globals": "^15.14.0", "prettier-plugin-tailwindcss": "^0.6.9", - "typescript-eslint": "^8.18.1" + "typescript-eslint": "^8.18.2" } } diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 9b7f94d80..ce979f4d0 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -157,7 +157,7 @@ "@radix-ui/react-tooltip": "^1.1.6", "@radix-ui/react-visually-hidden": "^1.1.1", "@tailwindcss/container-queries": "^0.1.1", - "@tanstack/react-query": "^5.62.8", + "@tanstack/react-query": "^5.62.10", "@tiptap/extension-code-block-lowlight": "^2.10.4", "@tiptap/extension-color": "^2.10.4", "@tiptap/extension-heading": "^2.10.4", @@ -187,7 +187,7 @@ "react-cropper": "^2.3.3", "react-day-picker": "^8.10.1", "react-moveable": "^0.56.0", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", "use-debounce": "^10.0.4", "vaul": "^1.1.2", diff --git a/packages/frontend/src/components/editor/footer/footer.tsx b/packages/frontend/src/components/editor/footer/footer.tsx index 489f33fc5..494195d47 100644 --- a/packages/frontend/src/components/editor/footer/footer.tsx +++ b/packages/frontend/src/components/editor/footer/footer.tsx @@ -1,3 +1,5 @@ +import { useMiddlewareData } from '@/hooks/use-middleware-data'; + import { useEditorState } from '../hooks/use-editor-state'; import { ListFilesFooterEditor } from './files/list'; import { @@ -10,13 +12,17 @@ interface Props extends LanguageSelectFooterEditorProps { } export const FooterEditor = ({ - disableLanguages, + disableLanguages = false, selectedLanguage, setSelectedLanguage, }: Props) => { const { files } = useEditorState(); + const { languages } = useMiddlewareData(); + const enableLanguages = languages.filter( + lang => lang.enabled && lang.allow_in_input, + ); - if (disableLanguages && !files.length) { + if (!files.length && (enableLanguages.length <= 1 || disableLanguages)) { return null; } diff --git a/packages/frontend/src/components/editor/footer/language-select.tsx b/packages/frontend/src/components/editor/footer/language-select.tsx index 115ff3729..bdb6c908f 100644 --- a/packages/frontend/src/components/editor/footer/language-select.tsx +++ b/packages/frontend/src/components/editor/footer/language-select.tsx @@ -21,9 +21,9 @@ export const LanguageSelectFooterEditor = ({ }: LanguageSelectFooterEditorProps) => { const { languages: languagesFromGlobal } = useMiddlewareData(); const { editor } = useEditorState(); - const languages = languagesFromGlobal.filter(item => item.allow_in_input); - - if (languages.length <= 1) return null; + const languages = languagesFromGlobal.filter( + item => item.allow_in_input && item.enabled, + ); return (