Self hosted domain redirect (#756)

* Integration pages from Notion

* Self hosted domain redirect

* Refactor root-redirect middleware and update 404 page layout

- Simplified the `root-redirect.js` middleware by removing the specific route checks, allowing for a more general redirect based on the `self_hosted` feature flag.
- Updated the 404 error page in `[...all].vue` by removing the unnecessary `NuxtLayout` wrapper and replacing `NuxtLink` with a custom `UButton` for navigation, enhancing the overall layout and user experience.

These changes aim to streamline the redirect logic and improve the presentation of the 404 error page.

* Refactor feature flag handling and update middleware

- Updated the condition in `Navbar.vue` to correctly evaluate the feature flags for rendering the AI form builder link.
- Removed the `feature-flags.global.js` middleware as it was no longer needed, streamlining the middleware structure.
- Enhanced the `root-redirect.js` middleware to utilize `h3`'s `sendRedirect` for server-side redirection, improving the redirect logic.
- Modified the `ai-form-builder.vue` page to include the new `root-redirect` middleware, ensuring proper redirection based on feature flags.

These changes aim to improve the handling of feature flags and redirection logic, enhancing the overall application flow.

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Chirag Chhatrala 2025-05-20 18:21:44 +05:30 committed by GitHub
parent cc2b0e989d
commit b2b04d7f2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 100 additions and 35 deletions

View File

@ -8,3 +8,4 @@ NUXT_PUBLIC_GOOGLE_ANALYTICS_CODE=
NUXT_PUBLIC_H_CAPTCHA_SITE_KEY=
NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY=
NUXT_API_SECRET=secret
NUXT_PUBLIC_ROOT_REDIRECT_URL=

View File

@ -53,7 +53,7 @@
</a>
</template>
<NuxtLink
v-if="($route.name !== 'ai-form-builder' && user === null) && (!useFeatureFlag('self_hosted') || useFeatureFlag('ai_features'))"
v-if="($route.name !== 'ai-form-builder' && user === null) && (!useFeatureFlag('self_hosted') && useFeatureFlag('ai_features'))"
:to="{ name: 'ai-form-builder' }"
:class="navLinkClasses"
class="hidden lg:inline"

21
client/middleware/root-redirect.js vendored Normal file
View File

@ -0,0 +1,21 @@
import { sendRedirect } from 'h3'
export default defineNuxtRouteMiddleware(() => {
if (!useFeatureFlag('self_hosted')) return
const redirectUrl = useRuntimeConfig().public.rootRedirectUrl
// Only run if env var is set and is a valid URL
if (!redirectUrl || !/^https?:\/\//.test(redirectUrl)) return
// Server-side: use h3's sendRedirect
if (import.meta.server) {
const event = useRequestEvent()
if (event) {
return sendRedirect(event, redirectUrl, 301)
}
}
// Client-side handling
return navigateTo(redirectUrl, { external: true })
})

39
client/pages/[...all].vue Normal file
View File

@ -0,0 +1,39 @@
<template>
<div>
<div class="flex mt-6">
<div class="w-full md:w-2/3 md:mx-auto md:max-w-md">
<img
alt="Nice plant as we have nothing else to show!"
src="/img/icons/plant.png"
class="w-56 mb-5"
>
<h1 class="mb-6 font-semibold text-3xl text-gray-900">
Page not found (404)
</h1>
<div class="links">
<UButton
:to="{ name: 'index' }"
class="hover:underline"
>
Go Home
</UButton>
</div>
</div>
</div>
</div>
</template>
<script setup>
definePageMeta({
middleware: ['root-redirect']
})
useOpnSeoMeta({
title: "404 - Page not found",
})
const event = useRequestEvent()
setResponseStatus(event, 404, 'Page Not Found')
</script>

View File

@ -629,7 +629,7 @@
<script setup>
const authStore = useAuthStore()
definePageMeta({
middleware: ["self-hosted",]
middleware: ['root-redirect','self-hosted']
})
useOpnSeoMeta({
title: "Free AI form builder",

View File

@ -323,6 +323,9 @@ export default {
defineRouteRules({
swr: 3600,
})
definePageMeta({
middleware: ['root-redirect']
})
return {
authenticated: computed(() => authStore.check),

View File

@ -85,7 +85,7 @@ defineRouteRules({
})
definePageMeta({
stickyNavbar: true,
middleware: ["self-hosted"]
middleware: ['root-redirect','self-hosted']
})
useOpnSeoMeta({

View File

@ -174,7 +174,7 @@ defineRouteRules({
})
definePageMeta({
stickyNavbar: true,
middleware: ["self-hosted"]
middleware: ['root-redirect','self-hosted']
})
const crisp = useCrisp()

View File

@ -1,6 +1,6 @@
import { useFeatureFlagsStore } from '~/stores/featureFlags'
export default defineNuxtRouteMiddleware(async () => {
export default defineNuxtPlugin(async () => {
const featureFlagsStore = useFeatureFlagsStore()
// Load flags if they haven't been loaded yet

View File

@ -1,4 +1,3 @@
function parseNumber(value, defaultValue = 0) {
const parsedValue = parseFloat(value)
return isNaN(parsedValue) ? defaultValue : parsedValue
@ -15,6 +14,7 @@ export default {
gtmCode: process.env.NUXT_PUBLIC_GTM_CODE || null,
amplitudeCode: process.env.NUXT_PUBLIC_AMPLITUDE_CODE || null,
crispWebsiteId: process.env.NUXT_PUBLIC_CRISP_WEBSITE_ID || null,
rootRedirectUrl: process.env.NUXT_PUBLIC_ROOT_REDIRECT_URL || null,
featureBaseOrganization: process.env.NUXT_PUBLIC_FEATURE_BASE_ORGANISATION || null,

View File

@ -21,26 +21,26 @@ There are dedicated configuration pages available for more detailed setup instru
### Configuration Environment Variables
| Variable Name | Description |
| -------------------------- | --------------------------------------------- |
| `JWT_TTL` | Time to live for JSON Web Tokens (JWT). |
| `JWT_SECRET` | Secret key used to sign JWTs. |
| `H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration. |
| `H_CAPTCHA_SECRET_KEY` | Secret key for hCaptcha integration. |
| `RE_CAPTCHA_SITE_KEY` | Site key for reCAPTCHA integration. |
| `RE_CAPTCHA_SECRET_KEY` | Secret key for reCAPTCHA integration. |
| `OPEN_AI_API_KEY` | API key for accessing OpenAI services. |
| `UNSPLASH_ACCESS_KEY` | Access key for Unsplash API. |
| `UNSPLASH_SECRET_KEY` | Secret key for Unsplash API. |
| `GOOGLE_CLIENT_ID` | Client ID for Google OAuth. |
| `GOOGLE_CLIENT_SECRET` | Client secret for Google OAuth. |
| `GOOGLE_REDIRECT_URL` | Redirect URL for Google OAuth. |
| `GOOGLE_AUTH_REDIRECT_URL` | Authentication redirect URL for Google OAuth. |
| `GOOGLE_FONTS_API_KEY` | API key for accessing Google Fonts. |
| `FRONT_URL` | Public facing URL of the front-end. |
| `FRONT_API_SECRET` | Shared secret with the front-end. |
| `TELEGRAM_BOT_ID` | ID of your Telegram bot for notifications. |
| `TELEGRAM_BOT_TOKEN` | Authentication token for your Telegram bot. |
| Variable Name | Description |
| --------------------------- | -------------------------------------------------------------------------------------------------------- |
| `JWT_TTL` | Time to live for JSON Web Tokens (JWT). |
| `JWT_SECRET` | Secret key used to sign JWTs. |
| `H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration. |
| `H_CAPTCHA_SECRET_KEY` | Secret key for hCaptcha integration. |
| `RE_CAPTCHA_SITE_KEY` | Site key for reCAPTCHA integration. |
| `RE_CAPTCHA_SECRET_KEY` | Secret key for reCAPTCHA integration. |
| `OPEN_AI_API_KEY` | API key for accessing OpenAI services. |
| `UNSPLASH_ACCESS_KEY` | Access key for Unsplash API. |
| `UNSPLASH_SECRET_KEY` | Secret key for Unsplash API. |
| `GOOGLE_CLIENT_ID` | Client ID for Google OAuth. |
| `GOOGLE_CLIENT_SECRET` | Client secret for Google OAuth. |
| `GOOGLE_REDIRECT_URL` | Redirect URL for Google OAuth. |
| `GOOGLE_AUTH_REDIRECT_URL` | Authentication redirect URL for Google OAuth. |
| `GOOGLE_FONTS_API_KEY` | API key for accessing Google Fonts. |
| `FRONT_URL` | Public facing URL of the front-end. |
| `FRONT_API_SECRET` | Shared secret with the front-end. |
| `TELEGRAM_BOT_ID` | ID of your Telegram bot for notifications. |
| `TELEGRAM_BOT_TOKEN` | Authentication token for your Telegram bot. |
| `JWT_SKIP_IP_UA_VALIDATION` | Set to `true` to disable JWT IP and User Agent validation (defaults to `false`). Useful for dynamic IPs. |
### User Options Environment Variables
@ -57,13 +57,14 @@ There are dedicated configuration pages available for more detailed setup instru
### Front-end Environment Variables
| Variable Name | Description |
| --------------------------------- | ---------------------------------------------------- |
| `NUXT_PUBLIC_APP_URL` | Public facing URL of the Nuxt application. |
| `NUXT_PUBLIC_API_BASE` | Base URL for the Laravel API. |
| `NUXT_PUBLIC_H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration on the front-end. |
| `NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY` | Site key for reCAPTCHA integration on the front-end. |
| `NUXT_API_SECRET` | Shared secret key between Nuxt and Laravel backend. |
| Variable Name | Description |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NUXT_PUBLIC_APP_URL` | Public facing URL of the Nuxt application. |
| `NUXT_PUBLIC_API_BASE` | Base URL for the Laravel API. |
| `NUXT_PUBLIC_H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration on the front-end. |
| `NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY` | Site key for reCAPTCHA integration on the front-end. |
| `NUXT_API_SECRET` | Shared secret key between Nuxt and Laravel backend. |
| `NUXT_PUBLIC_ROOT_REDIRECT_URL` | If set, permanently redirects users visiting the root path (/), /integrations, or any non-existent (404) page to the specified URL. Useful for self-hosted subdomains to avoid exposing the default landing page. |
import CloudVersion from "/snippets/cloud-version.mdx";