Skip to content

Commit

Permalink
feat: Add client side authentication to gravityforms rest api (#7)
Browse files Browse the repository at this point in the history
* Move form field to a seperate comp & started to work on textarea

* More textarea work & default confirmation text

* Fix linting

* Moved backend url to props instead of direclty use .env

* added .env example

* Feat: add client side authentication

* Fix linitng

* Remove js-sha1
  • Loading branch information
HubbeDev authored Jan 7, 2024
1 parent 5719ffd commit 0f85af5
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 23 deletions.
3 changes: 3 additions & 0 deletions apps/svelte-gravity-forms/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PUBLIC_GF_CONSUMER_KEY=
PUBLIC_GF_CONSUMER_SECRECT=
PUBLIC_GF_API_URL=
3 changes: 3 additions & 0 deletions apps/svelte-gravity-forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/crypto-js": "^4.2.1",
"@types/eslint": "8.56.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
Expand Down Expand Up @@ -63,8 +64,10 @@
"dependencies": {
"bits-ui": "^0.13.0",
"clsx": "^2.1.0",
"crypto-js": "^4.2.0",
"formsnap": "^0.4.2",
"lucide-svelte": "^0.304.0",
"oauth-1.0a": "^2.2.6",
"sveltekit-superforms": "^1.13.1",
"tailwind-merge": "^2.2.0",
"tailwind-variants": "^0.1.20",
Expand Down
9 changes: 7 additions & 2 deletions apps/svelte-gravity-forms/src/lib/components/root.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@
type $$Props = Props;
export let formId: $$Props['formId'] = undefined;
export let backendUrl: $$Props['backendUrl'] = undefined;
export let consumerKey: $$Props['consumerKey'] = undefined;
export let consumerSecret: $$Props['consumerSecret'] = undefined;
const {
methods: { onSubmitForm },
states: { formSchema, formFields, formObject, isSubmitted, defaultConfirmation },
refs: { formRef }
} = setCtx({
formId: formId
formId: formId,
backendUrl: backendUrl,
consumerKey: consumerKey,
consumerSecret: consumerSecret
});
$: form = $formSchema
Expand Down
5 changes: 4 additions & 1 deletion apps/svelte-gravity-forms/src/lib/components/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export type Props = {
formId?: number;
formId: number | undefined;
backendUrl: string | undefined;
consumerKey: string | undefined;
consumerSecret: string | undefined;
};
56 changes: 42 additions & 14 deletions apps/svelte-gravity-forms/src/lib/internal/gf.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { onMount } from 'svelte';
import { get, writable } from 'svelte/store';
import { PUBLIC_GF_API_URL } from '$env/static/public';
import { z, type AnyZodObject } from 'zod';
import { effect, omit, removeUndefined, toWritableStores } from '$lib/internal/helpers/index.js';

Expand All @@ -11,13 +10,21 @@ import type {
GFFormObjectProps
} from './types.js';
import type { HTMLAttributes } from 'svelte/elements';
import { getClientFormObject, sendSubmission } from './helpers/gf-rest.js';

export type CreateGravityFromsProps = {
formId?: number;
formId?: number | undefined;
backendUrl?: string;
consumerKey?: string;
consumerSecret?: string;
};

const defaultProps = {
formId: undefined
formId: undefined,
backendUrl: 'http://localhost:8888/wp-json',
formObjectData: undefined,
consumerKey: undefined,
consumerSecret: undefined
};

export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
Expand All @@ -27,13 +34,20 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
} satisfies CreateGravityFromsProps;

// Create writable stores for the form options
const options = toWritableStores(omit({ ...withDefaults }));
const options = toWritableStores(
omit({
...withDefaults
})
);

const { formId, backendUrl } = options;

// refs
const formRef = writable<HTMLFormElement | undefined>(undefined);
const submitButtonRef = writable<HTMLAttributes<HTMLButtonElement> | undefined>(undefined);

// states

const formObject = writable<GFFormObjectProps>(undefined);
const formFields = writable<GFFieldsProps[]>(undefined);
const formIdStore = writable(withDefaults.formId);
Expand All @@ -43,16 +57,23 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
const defaultConfirmation = writable<GFComfirmationProps>(undefined);
const isSubmitted = writable<boolean>(false);

const consumerKeyStore = writable(withDefaults.consumerKey);
const consumerSecretStore = writable(withDefaults.consumerSecret);

// Fetch form object from Gravity Forms API
async function getFormObject(formId: number) {
const res = await fetch(`${PUBLIC_GF_API_URL}/forms/${formId}`, { method: 'GET' });
const data = (await res.json()) as GFFormObjectProps;
return data;
}
async function onSubmitForm(req: { [x: string]: unknown }) {
try {
const submit = await sendSubmission(req, backendUrl, formId);

// Handle form submission
async function onSubmitForm(_formData: unknown) {
isSubmitted.set(true);
if (!submit.is_valid) {
throw new Error(submit.message);
}

isSubmitted.set(true);
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);
}
}

// Calculate column span for a field
Expand Down Expand Up @@ -159,7 +180,12 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
if (!get(formIdStore)) {
return;
}
const formData = await getFormObject(get(formIdStore));
const formData = await getClientFormObject(
backendUrl,
formIdStore,
consumerKeyStore,
consumerSecretStore
);
formObject.set(formData);
});

Expand Down Expand Up @@ -273,7 +299,9 @@ export function createSvelteGravityFroms(props: CreateGravityFromsProps) {
formRequiredIndicator,
formSubmtiButton,
isSubmitted,
defaultConfirmation
defaultConfirmation,
formId,
backendUrl
},
methods: {
onSubmitForm
Expand Down
64 changes: 64 additions & 0 deletions apps/svelte-gravity-forms/src/lib/internal/helpers/gf-rest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { get, type Writable } from 'svelte/store';
import type { GFFormObjectProps } from '../types.js';

import OAuth from 'oauth-1.0a';
import CryptoJS from 'crypto-js';

export async function sendSubmission(
req: { [x: string]: unknown },
backendUrl: Writable<string | undefined>,
formId: Writable<number>
) {
const res = await fetch(`${get(backendUrl)}gf/v2/forms/${get(formId)}/submissions`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
}
});
const data = await res.json();
return data;
}

export async function getClientFormObject(
backendUrl: Writable<string | undefined>,
formId: Writable<number>,
consumer_key: Writable<string>,
consumer_secret: Writable<string>
) {
const oauth = new OAuth({
consumer: {
key: get(consumer_key),
secret: get(consumer_secret)
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
}
});

// Generate OAuth 1.0 parameters
const oauthData = oauth.authorize({
url: `${get(backendUrl)}gf/v2/forms/${get(formId)}`,
method: 'GET'
});

// Convert OAuth parameters to URL parameters
const params = new URLSearchParams({
oauth_consumer_key: oauthData.oauth_consumer_key,
oauth_nonce: oauthData.oauth_nonce,
oauth_signature_method: oauthData.oauth_signature_method,
oauth_timestamp: oauthData.oauth_timestamp.toString(),
oauth_version: oauthData.oauth_version,
oauth_signature: oauthData.oauth_signature
});

// Append OAuth parameters to URL
const url = `${get(backendUrl)}gf/v2/forms/${get(formId)}?${params.toString()}`;

const res = await fetch(url, { method: 'GET' });
// eslint-disable-next-line no-console
console.log(res);
const data = (await res.json()) as GFFormObjectProps;
return data;
}
4 changes: 1 addition & 3 deletions apps/svelte-gravity-forms/src/lib/internal/helpers/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Ref: https://github.com/huntabyte/vaul-svelte/blob/main/src/lib/internal/helpers/store.ts

import type { Readable, Stores, StoresValues, Updater, Writable } from 'svelte/store';
import { derived, writable } from 'svelte/store';
import { onDestroy, onMount } from 'svelte';
Expand Down Expand Up @@ -38,6 +36,7 @@ export function effect<S extends Stores>(
safeOnDestroy(unsub);
return unsub;
}

/**
* A utility function that creates a derived store that automatically
* unsubscribes from its dependencies.
Expand Down Expand Up @@ -133,7 +132,6 @@ export type ToWritableStores<T extends Record<string, unknown>> = {
};

/**
*
* Given an object of properties, returns an object of writable stores
* with the same properties and values.
*/
Expand Down
2 changes: 1 addition & 1 deletion apps/svelte-gravity-forms/src/lib/internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type GFButtonProps = {
id?: string;
};

type GFComfirmationProps = {
export type GFComfirmationProps = {
type?: string;
id?: string;
isDefault?: boolean;
Expand Down
12 changes: 11 additions & 1 deletion apps/svelte-gravity-forms/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
<script lang="ts">
import { SvelteGravityForm } from '$lib/index.js';
import {
PUBLIC_GF_API_URL,
PUBLIC_GF_CONSUMER_KEY,
PUBLIC_GF_CONSUMER_SECRECT
} from '$env/static/public';
</script>

<SvelteGravityForm formId={1} />
<SvelteGravityForm
formId={1}
backendUrl={PUBLIC_GF_API_URL}
consumerKey={PUBLIC_GF_CONSUMER_KEY}
consumerSecret={PUBLIC_GF_CONSUMER_SECRECT}
/>
8 changes: 7 additions & 1 deletion apps/wp/config/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

# END WordPress

#allow cors
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0f85af5

Please sign in to comment.