URL generation (front&back) + fixed authJWT for SSR
This commit is contained in:
parent
630ae1df1d
commit
5a3978874a
|
@ -45,8 +45,8 @@ class SubscriptionController extends Controller
|
|||
$checkout = $checkoutBuilder
|
||||
->collectTaxIds()
|
||||
->checkout([
|
||||
'success_url' => url('/subscriptions/success'),
|
||||
'cancel_url' => url('/subscriptions/error'),
|
||||
'success_url' => front_url('/subscriptions/success'),
|
||||
'cancel_url' => front_url('/subscriptions/error'),
|
||||
'billing_address_collection' => 'required',
|
||||
'customer_update' => [
|
||||
'address' => 'auto',
|
||||
|
|
|
@ -8,6 +8,7 @@ use Tymon\JWTAuth\Exceptions\JWTException;
|
|||
|
||||
class AuthenticateJWT
|
||||
{
|
||||
const API_SERVER_SECRET_HEADER_NAME = 'x-api-secret';
|
||||
|
||||
/**
|
||||
* Verifies the JWT token and validates the IP and User Agent
|
||||
|
@ -24,6 +25,13 @@ class AuthenticateJWT
|
|||
|
||||
// Validate IP and User Agent
|
||||
if ($payload) {
|
||||
if ($frontApiSecret = $request->header(self::API_SERVER_SECRET_HEADER_NAME)) {
|
||||
// If it's a trusted SSR request, skip the rest
|
||||
if ($frontApiSecret === config('app.front_api_secret')) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
$error = null;
|
||||
if (!\Hash::check($request->ip(), $payload->get('ip'))) {
|
||||
$error = 'Origin IP is invalid';
|
||||
|
|
|
@ -164,14 +164,14 @@ class StoreFormSubmissionJob implements ShouldQueue
|
|||
return null;
|
||||
}
|
||||
|
||||
if(filter_var($value, FILTER_VALIDATE_URL) !== FALSE && str_contains($value, parse_url(config('app.url'))['host'])) { // In case of prefill we have full url so convert to s3
|
||||
if(filter_var($value, FILTER_VALIDATE_URL) !== false && str_contains($value, parse_url(config('app.url'))['host'])) { // In case of prefill we have full url so convert to s3
|
||||
$fileName = basename($value);
|
||||
$path = FormController::ASSETS_UPLOAD_PATH . '/' . $fileName;
|
||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||
Storage::move($path, $newPath.'/'.$fileName);
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
|
||||
if($this->isSkipForUpload($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
|
|
@ -157,12 +157,12 @@ class Form extends Model implements CachableAttributes
|
|||
if ($this->custom_domain) {
|
||||
return 'https://' . $this->custom_domain . '/forms/' . $this->slug;
|
||||
}
|
||||
return '/forms/' . $this->slug;
|
||||
return front_url('/forms/' . $this->slug);
|
||||
}
|
||||
|
||||
public function getEditUrlAttribute()
|
||||
{
|
||||
return url('/forms/' . $this->slug . '/show');
|
||||
return front_url('/forms/' . $this->slug . '/show');
|
||||
}
|
||||
|
||||
public function getSubmissionsCountAttribute()
|
||||
|
|
|
@ -48,7 +48,7 @@ class Template extends Model
|
|||
|
||||
public function getShareUrlAttribute()
|
||||
{
|
||||
return url('/form-templates/'.$this->slug);
|
||||
return front_url('/form-templates/'.$this->slug);
|
||||
}
|
||||
|
||||
public function setDescriptionAttribute($value)
|
||||
|
|
|
@ -17,7 +17,7 @@ class ResetPassword extends Notification
|
|||
{
|
||||
return (new MailMessage)
|
||||
->line('You are receiving this email because we received a password reset request for your account.')
|
||||
->action('Reset Password', url('password/reset/'.$this->token).'?email='.urlencode($notifiable->email))
|
||||
->action('Reset Password', front_url('password/reset/'.$this->token).'?email='.urlencode($notifiable->email))
|
||||
->line('If you did not request a password reset, no further action is required.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,6 @@ class FailedPaymentNotification extends Notification implements ShouldQueue
|
|||
->line(__('Please go to OpenForm, click on your name on the top right corner, and click on "Billing".
|
||||
You will then be able to update your card details. To avoid any service disruption, you can reply to this email whenever
|
||||
you updated your card details, and we\'ll manually attempt to charge your card.'))
|
||||
->action(__('Go to OpenForm'), url('/'));
|
||||
->action(__('Go to OpenForm'), front_url('/'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class DiscordHandler extends AbstractWebhookHandler
|
|||
$externalLinks[] = '[**🔗 Open Form**](' . $this->form->share_url . ')';
|
||||
}
|
||||
if(Arr::get($settings, 'link_edit_form', true)){
|
||||
$editFormURL = url('forms/' . $this->form->slug . '/show');
|
||||
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
|
||||
$externalLinks[] = '[**✍️ Edit Form**](' . $editFormURL . ')';
|
||||
}
|
||||
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
|
||||
|
|
|
@ -27,7 +27,7 @@ class SlackHandler extends AbstractWebhookHandler
|
|||
$externalLinks[] = '*<' . $this->form->share_url . '|🔗 Open Form>*';
|
||||
}
|
||||
if(Arr::get($settings, 'link_edit_form', true)){
|
||||
$editFormURL = url('forms/' . $this->form->slug . '/show');
|
||||
$editFormURL = front_url('forms/' . $this->form->slug . '/show');
|
||||
$externalLinks[] = '*<' . $editFormURL . '|✍️ Edit Form>*';
|
||||
}
|
||||
if (Arr::get($settings, 'link_edit_submission', true) && $this->form->editable_submissions) {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
function front_url($path = '')
|
||||
{
|
||||
$baseUrl = config('app.front_url');
|
||||
if (!$baseUrl) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return rtrim($baseUrl, '/'). '/' . ltrim($path, '/');
|
||||
}
|
|
@ -103,6 +103,7 @@
|
|||
|
||||
<script setup>
|
||||
import { ref, defineProps, computed } from 'vue'
|
||||
import {appUrl} from "~/lib/utils.js";
|
||||
|
||||
const { copy } = useClipboard()
|
||||
const crisp = useCrisp()
|
||||
|
@ -135,7 +136,7 @@ let embedPopupCode = computed(() => {
|
|||
width: advancedOptions.value.width
|
||||
}
|
||||
previewPopup(nfData)
|
||||
return '<script async data-nf=\'' + JSON.stringify(nfData) + '\' src=\'' + embedScriptUrl + '\'></scrip' + 't>'
|
||||
return '<script async data-nf=\'' + JSON.stringify(nfData) + '\' src=\'' + appUrl(embedScriptUrl) + '\'></scrip' + 't>'
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
@ -18,13 +18,18 @@ function addPasswordToFormRequest(request, options) {
|
|||
}
|
||||
|
||||
export function getOpnRequestsOptions(request, opts) {
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
opts.headers = {accept: 'application/json', ...opts.headers}
|
||||
|
||||
// Authenticate requests coming from the server
|
||||
if (process.server && config.apiSecret) {
|
||||
opts.headers['x-api-secret'] = config.apiSecret
|
||||
}
|
||||
|
||||
addAuthHeader(request, opts)
|
||||
addPasswordToFormRequest(request, opts)
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
return {
|
||||
baseURL: config.public.apiBase,
|
||||
onResponseError({response}) {
|
||||
|
|
|
@ -13,3 +13,21 @@ export const hash = (str, seed = 0) => {
|
|||
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
}
|
||||
|
||||
export const appUrl = (path = '/') => {
|
||||
let baseUrl = useRuntimeConfig().public.appUrl
|
||||
if (!baseUrl) {
|
||||
console.warn('appUrl not set in runtimeConfig')
|
||||
return path
|
||||
}
|
||||
|
||||
if (path.startsWith('/')) {
|
||||
path = path.substring(1)
|
||||
}
|
||||
|
||||
if (!baseUrl.endsWith('/')) {
|
||||
baseUrl += '/'
|
||||
}
|
||||
|
||||
return baseUrl + path
|
||||
}
|
||||
|
|
|
@ -86,11 +86,11 @@
|
|||
class="w-full mt-12 relative px-6 mx-auto max-w-4xl sm:px-10 lg:px-0 z-10 flex items-center justify-center"
|
||||
>
|
||||
<div
|
||||
class="-m-2 rounded-xl bg-blue-900/5 p-2 backdrop-blur-sm ring-1 ring-inset ring-blue-900/10 lg:-m-4 lg:rounded-2xl lg:p-4"
|
||||
class="-m-2 rounded-xl bg-blue-900/5 p-2 backdrop-blur-sm ring-1 ring-inset ring-blue-900/10 lg:-m-4 lg:rounded-2xl lg:p-4 w-full"
|
||||
>
|
||||
<NuxtImg src="/img/pages/welcome/product-cover.jpg" placeholder
|
||||
sizes="320px sm:650px lg:900px"
|
||||
alt="Product screenshot" loading="lazy" class="rounded-md shadow-2xl ring-1 ring-gray-900/10"
|
||||
<NuxtImg src="/img/pages/welcome/product-cover.jpg"
|
||||
sizes="320px sm:650px lg:896px"
|
||||
alt="Product screenshot" loading="lazy" class="rounded-md w-full shadow-2xl ring-1 ring-gray-900/10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,5 +13,10 @@ export default {
|
|||
s3Enabled: process.env.NUXT_PUBLIC_S3_ENABLED || false,
|
||||
paidPlansEnabled: process.env.NUXT_PUBLIC_PAID_PLANS_ENABLED || false,
|
||||
customDomainsEnabled: process.env.NUXT_PUBLIC_CUSTOM_DOMAINS_ENABLED || false,
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to authenticate that the requests are coming from the server - not from a client.
|
||||
*/
|
||||
apiSecret: process.env.NUXT_API_SECRET || '',
|
||||
}
|
||||
|
|
|
@ -80,7 +80,10 @@
|
|||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"app/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -56,6 +56,15 @@ return [
|
|||
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Client (Nuxt Front-end)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'front_url' => env('FRONT_URL', null),
|
||||
'front_api_secret' => env('FRONT_API_SECRET', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{-- Header --}}
|
||||
@slot('header')
|
||||
@if (!(isset($noBranding) && $noBranding))
|
||||
@component('mail::header', ['url' => config('app.url')])
|
||||
@component('mail::header', ['url' => front_url()])
|
||||
{{ config('app.name') }}
|
||||
@endcomponent
|
||||
@endif
|
||||
|
|
Loading…
Reference in New Issue