Migrate front-end to Nuxt app (#284)

* wip

* Managed to load a page

* Stuck at changing routes

* Fixed the router, and editable div

* WIP

* Fix app loader

* WIP

* Fix check-auth middleware

* Started to refactor input components

* WIP

* Added select input, v-click-outside for vselect

* update vselect & phone input

* Fixed the mixin

* input component updates

* Fix signature input import

* input component updates in vue3

* image input in vue3

* small fixes

* fix useFormInput watcher

* scale input in vue3

* Vue3: migrating from vuex to Pinia (#249)

* Vue3: migrating from vuex to Pinia

* toggle input fixes

* update configureCompat

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>

* support vue3 query builder

* Refactor inpus

* fix: Vue3 Query Builder - Logic Editor (#251)

* support vue3 query builder

* upgrade

* remove local from middleware

* Submission table pagination & migrate chart to vue3 (#254)

* Submission table Pagination in background

* migrate chart to vue3

* Form submissions pagination

* Form submissions

* Fix form starts

* Fix openSelect key issue

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>
Co-authored-by: Julien Nahum <julien@nahum.net>

* Vue 3 better animation (#257)

* vue-3-better-animation

* Working on migration to vueuse/motion

* Form sidebar animations

* Clean code

* Added animations for modal

* Finished implementing better animations

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>

* Work in progress

* Migrating amplitude and crisp plugin/composable

* Started to refactor pages

* WIP

* vue3-scroll-shadow-fixes (#260)

* WIP

* WIP

* WIP

* Figured out auth & middlewares

* WI

* Refactoring stores and templates pages to comp. api

* Finishing the templates pages

* fix collapsible

* Finish reworking most templates pages

* Reworked workspaces store

* Working on home page and modal

* Fix dropdown

* Fix modal

* Fixed form creation

* Fixed most of the form/show pages

* Updated cors dependency

* fix custom domain warning

* NuxtLink migration (#262)

Co-authored-by: Forms Dev <chirag+new@notionforms.io>

* Tiny fixes + start pre-rendering

* migrate-to-nuxt-useappconfig (#263)

* migrate-to-nuxt-useappconfig

* defineAppConfig

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>

* Working on form/show and editor

* Globally import form inputs to fix resolve

* Remove vform - working on form public page

* Remove initform mixin

* Work in progress for form create guess user

* Nuxt Migration notifications (#265)

* Nuxt Migration notifications

* @input to @update:model-value

* change field type fixes

* @update:model-value

* Enable form-block-logic-editor

* vue-confetti migration

* PR request changes

* useAlert in setup

* Migrate to nuxt settings page AND remove axios (#266)

* Settings pages migration

* remove axios and use opnFetch

* Make created form reactive (#267)

* Remove verify pages and axios lib

---------

Co-authored-by: Julien Nahum <julien@nahum.net>

* Fix alert styling + bug fixes and cleaning

* Refactor notifications + add shadow

* Fix vselect issue

* Working on page pre-rendering

* Created NotionPages store

* Added sitemap on nuxt side

* Sitemap done, working on aws amplify

* Adding missing module

* Remove axios and commit backend changes to sitemap

* Fix notifications

* fix guestpage editor (#269)

Co-authored-by: Julien Nahum <julien@nahum.net>

* Remove appconfig in favor of runtimeconfig

* Fixed amplitude bugs, and added staging environment

* Added amplify file

* Change basdirectory amplify

* Fix loading bar position

* Fix custom redirect (#273)

* Dirty form handling - nuxt migration (#272)

* SEO meta nuxt migration (#274)

* SEO meta nuxt migration

* Polish seo metas, add defaults for OG and twitter

---------

Co-authored-by: Julien Nahum <julien@nahum.net>

* migrate to nuxt useClipboard (#268)

* Set middleware on pages (#278)

* Se middleware on pages

* Se middleware on account page

* add robots.txt (#276)

* 404 page migration (#277)

* Templates pages migration (#275)

* NuxtImg Migration (#279)

Co-authored-by: Julien Nahum <julien@nahum.net>

* Update package json

* Fix build script

* Add loglevel param

* Disable page pre-rendering

* Attempt to allow svgs

* Fix SVGs with NuxtImage

* Add .env file at AWS build time

* tRGIGGER deploy

* Fix issue

* ANother attrempt

* Fix typo

* Fix env?

* Attempt to simplify build

* Enable swr caching instead of prerenderign

* Better image compression

* Last attempt at nuxt images efficiency

* Improve image optimization again

* Remove NuxtImg for non asset files

* Restore templates pages cache

* Remove useless images + fix templates show page

* image optimization caching + fix hydratation issue form template page

* URL generation (front&back) + fixed authJWT for SSR

* Fix composable issue

* Fix form share page

* Embeddable form as a nuxt middleware

* Fix URL for embeddable middleware

* Debugging embeddable on amplify

* Add custom domain support

* No follow for non-production env

* Fix sentry nuxt and custom domain redirect

* remove api prefix from routes (#280)

* remove api prefix from routes

* PR changes

---------

Co-authored-by: Julien Nahum <julien@nahum.net>

* nuxt migration -file upload - WIP (#271)

Co-authored-by: Julien Nahum <julien@nahum.net>

* Fix local file upload

* Fix file submissions preview

* API redirect to back-end from nuxt

* API redirect to back-end from nuxt

* Remove old JS app, update deploy script

* Fix tests, added gh action nuxt step

* Updated package-lock.json

* Setup node in GH Nuxt action

* Setup client directory for GH workflow

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>
Co-authored-by: Chirag Chhatrala <60499540+chiragchhatrala@users.noreply.github.com>
Co-authored-by: Rishi Raj Jain <rishi18304@iiitd.ac.in>
Co-authored-by: formsdev <136701234+formsdev@users.noreply.github.com>
This commit is contained in:
Julien Nahum
2024-01-15 12:14:47 +01:00
committed by GitHub
parent c01f566ba9
commit 0adce5a2ff
478 changed files with 27676 additions and 34120 deletions

View File

@@ -28,10 +28,10 @@ class AppSumoAuthController extends Controller
// otherwise start login flow by passing the encrypted license key id
if (is_null($license->user_id)) {
return redirect(url('/register?appsumo_license='.encrypt($license->id)));
return redirect(front_url('/register?appsumo_license='.encrypt($license->id)));
}
return redirect(url('/register?appsumo_error=1'));
return redirect(front_url('/register?appsumo_error=1'));
}
private function retrieveAccessToken(string $requestCode): string
@@ -82,11 +82,11 @@ class AppSumoAuthController extends Controller
if (is_null($license->user_id)) {
$license->user_id = Auth::id();
$license->save();
return redirect(url('/home?appsumo_connect=1'));
return redirect(front_url('/home?appsumo_connect=1'));
}
// Licensed already attached
return redirect(url('/home?appsumo_error=1'));
return redirect(front_url('/home?appsumo_error=1'));
}
/**

View File

@@ -17,6 +17,7 @@ class FileUploadController extends Controller
*/
public function upload(Request $request)
{
$request->validate(['file' => 'required|file']);
$uuid = (string) Str::uuid();
$path = $request->file('file')->storeAs(PublicFormController::TMP_FILE_UPLOAD_PATH, $uuid);

View File

@@ -15,7 +15,8 @@ class FormSubmissionController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('auth', ['except' => ['submissionFile']]);
$this->middleware('signed', ['only' => ['submissionFile']]);
}
public function submissions(string $id)
@@ -51,9 +52,6 @@ class FormSubmissionController extends Controller
public function submissionFile($id, $fileName)
{
$form = Form::findOrFail((int) $id);
$this->authorize('view', $form);
$fileName = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $id).'/'
.urldecode($fileName);
@@ -63,8 +61,12 @@ class FormSubmissionController extends Controller
], 404);
}
if (config('filesystems.default') !== 's3') {
return response()->file(Storage::path($fileName));
}
return redirect(
Storage::temporaryUrl($fileName, now()->addMinute())
Storage::temporaryUrl($fileName, now()->addMinute())
);
}
}

View File

@@ -9,62 +9,24 @@ use App\Models\Template;
class SitemapController extends Controller
{
/**
* Contains route name and the associated priority
*
* @var array
*/
protected $urls = [
['/', 1],
['/pricing', 0.9],
['/privacy-policy', 0.5],
['/terms-conditions', 0.5],
['/login', 0.4],
['/register', 0.4],
['/password/reset', 0.3],
['/form-templates', 0.9],
];
public function getSitemap(Request $request)
public function index(Request $request)
{
$sitemap = Sitemap::create();
foreach ($this->urls as $url) {
$sitemap->add($this->createUrl($url[0], $url[1]));
}
$this->addTemplatesUrls($sitemap);
$this->addTemplatesTypesUrls($sitemap);
$this->addTemplatesIndustriesUrls($sitemap);
return $sitemap->toResponse($request);
return [
...$this->getTemplatesUrls()
];
}
private function createUrl($url, $priority, $frequency = 'daily')
private function getTemplatesUrls()
{
return Url::create($url)->setPriority($priority)->setChangeFrequency($frequency);
}
private function addTemplatesUrls(Sitemap $sitemap)
{
Template::where('publicly_listed', true)->chunk(100, function ($templates) use ($sitemap) {
$urls = [];
Template::where('publicly_listed', true)->chunk(100, function ($templates) use (&$urls) {
foreach ($templates as $template) {
$sitemap->add($this->createUrl('/form-templates/' . $template->slug, 0.8));
$urls[] = [
'loc' => '/templates/' . $template->slug
];
}
});
}
private function addTemplatesTypesUrls(Sitemap $sitemap)
{
$types = json_decode(file_get_contents(resource_path('data/forms/templates/types.json')), true);
foreach ($types as $type) {
$sitemap->add($this->createUrl('/form-templates/types/' . $type['slug'], 0.7));
}
}
private function addTemplatesIndustriesUrls(Sitemap $sitemap)
{
$industries = json_decode(file_get_contents(resource_path('data/forms/templates/industries.json')), true);
foreach ($industries as $industry) {
$sitemap->add($this->createUrl('/form-templates/industries/' . $industry['slug'], 0.7));
}
return $urls;
}
}

View File

@@ -1,18 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Service\SeoMetaResolver;
use Illuminate\Http\Request;
class SpaController extends Controller
{
/**
* Get the SPA view.
*/
public function __invoke(Request $request)
{
return view('spa',[
'meta' => (new SeoMetaResolver($request))->getMetas(),
]);
}
}

View File

@@ -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',

View File

@@ -4,7 +4,6 @@ namespace App\Http;
use App\Http\Middleware\AuthenticateJWT;
use App\Http\Middleware\CustomDomainRestriction;
use App\Http\Middleware\EmbeddableForms;
use App\Http\Middleware\IsAdmin;
use App\Http\Middleware\IsNotSubscribed;
use App\Http\Middleware\IsSubscribed;
@@ -20,9 +19,9 @@ class Kernel extends HttpKernel
* @var array
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
@@ -46,16 +45,14 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
EmbeddableForms::class
],
'spa' => [
\Illuminate\Routing\Middleware\SubstituteBindings::class,
EmbeddableForms::class
],
'api' => [
'throttle:60,1',
'throttle:100,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,

View File

@@ -15,7 +15,7 @@ class Authenticate extends Middleware
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return redirect('/login');
return redirect(front_url('login'));
}
}
}

View File

@@ -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';

View File

@@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\Builder;
class CustomDomainRestriction
{
const CUSTOM_DOMAIN_HEADER = "User-Custom-Domain";
const CUSTOM_DOMAIN_HEADER = "x-custom-domain";
/**
* Handle an incoming request.
@@ -27,7 +27,8 @@ class CustomDomainRestriction
return response()->json([
'success' => false,
'message' => 'Invalid domain',
], 400);
'error' => 'invalid_domain',
], 420);
}
// Check if domain is different from current domain
@@ -41,7 +42,8 @@ class CustomDomainRestriction
return response()->json([
'success' => false,
'message' => 'Unknown domain',
], 400);
'error' => 'invalid_domain',
], 420);
}
Workspace::addGlobalScope('domain-restricted', function (Builder $builder) use ($workspace) {

View File

@@ -1,36 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Response;
class EmbeddableForms
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->expectsJson() || $request->wantsJson()) {
return $next($request);
}
$response = $next($request);
if (!str_starts_with($request->url(), url('/forms/'))) {
if ($response instanceof Response) {
$response->header('X-Frame-Options', 'SAMEORIGIN');
} elseif ($response instanceof \Symfony\Component\HttpFoundation\Response) {
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
}
}
return $response;
}
}

View File

@@ -50,7 +50,11 @@ class FormSubmissionResource extends JsonResource
return $file !== null && $file;
})->map(function ($file) {
return [
'file_url' => route('open.forms.submissions.file', [$this->form_id, $file]),
'file_url' => \URL::signedRoute(
'open.forms.submissions.file',
[$this->form_id, $file],
now()->addMinutes(10)
),
'file_name' => $file,
];
});