Skip to content

Commit 818bb8d

Browse files
Merge pull request #24 from depatchedmode/v0.8.0
v0.8.0 - migration to frames.js #23
2 parents fab7ad6 + f18c5ff commit 818bb8d

File tree

11 files changed

+87
-99
lines changed

11 files changed

+87
-99
lines changed

api/index.js

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,65 @@
1+
import { getFrameMessage } from "frames.js"
12
import landingPage from '../src/landing-page';
2-
import frames from '../src/frames';
33
import { parseRequest, objectToURLSearchParams } from '../modules/utils';
44
import buildButtons from '../modules/buildButtons';
55
import buildInputs from '../modules/buildInputs';
66
import getTargetFrame from '../modules/getTargetFrame';
7-
import { validateMessage } from '../src/data/message';
8-
import { isFrameStolen } from '../src/data/antitheft';
97

108
export default async (req, context) => {
119
try {
1210
const requestURL = new URL(req.url);
1311
const payload = await parseRequest(req);
14-
let from = requestURL.searchParams.get('frame');
15-
let buttonId = null;
16-
let frameIsStolen = false;
12+
const frameMessage = payload ? await getFrameMessage(payload, {
13+
hubHttpUrl: process.env.FARCASTER_HUB
14+
}) : {};
1715

18-
if (payload) {
19-
payload.referringFrame = from;
20-
payload.validData = await validateMessage(payload.trustedData.messageBytes);
21-
}
22-
23-
if (payload?.validData) {
24-
buttonId = payload.validData.data.frameActionBody.buttonIndex;
25-
frameIsStolen = await isFrameStolen(payload);
26-
}
16+
// extending the frames.js frameMessage object with a few things
17+
// we require
18+
// TODO: see about refactoring this more towards the frames.js style
19+
frameMessage.from = requestURL.searchParams.get('frame');
20+
frameMessage.requestURL = payload?.untrustedData.url;
2721

28-
const { targetFrameSrc, targetFrameName, redirectUrl } = getTargetFrame(from, buttonId, frames);
22+
const { targetFrame, redirectURL } = await getTargetFrame(frameMessage);
2923

30-
if (redirectUrl) {
31-
return await respondWithRedirect(redirectUrl);
32-
} else if (frameIsStolen) {
33-
return await respondWithFrame('stolen', frames['stolen'], payload);
34-
} else if (targetFrameSrc) {
35-
return await respondWithFrame(targetFrameName, targetFrameSrc, payload);
24+
if (redirectURL) {
25+
return await respondWithRedirect(redirectURL);
26+
} else if (targetFrame) {
27+
return await respondWithFrame(targetFrame, frameMessage);
3628
} else {
37-
console.error(`Unknown frame requested: ${targetFrameName}`);
29+
console.error(`Unknown frame requested: ${targetFrame.name}`);
3830
}
3931
} catch (error) {
4032
console.error(`Error processing request: ${error}`);
4133
}
4234
};
4335

44-
const respondWithRedirect = (redirectUrl) => {
45-
const internalRedirectUrl = new URL(`${process.env.URL}/redirect`)
46-
internalRedirectUrl.searchParams.set('redirectUrl',redirectUrl);
36+
const respondWithRedirect = (redirectURL) => {
37+
const internalRedirectURL = new URL(`${process.env.URL}/redirect`)
38+
internalRedirectURL.searchParams.set('redirectURL',redirectURL);
4739
return new Response('<div>redirect</div>',
4840
{
4941
status: 302,
5042
headers: {
51-
'Location': internalRedirectUrl,
43+
'Location': internalRedirectURL,
5244
},
5345
}
5446
);
5547
}
5648

57-
const respondWithFrame = async (targetFrameName, targetFrameSrc, payload) => {
49+
const respondWithFrame = async (targetFrame, frameMessage) => {
5850
const searchParams = {
59-
targetFrameName,
60-
payload
51+
targetFrameName: targetFrame.name,
52+
frameMessage
6153
}
54+
6255
const host = process.env.URL;
6356
const frameContent = {
64-
image: targetFrameSrc.image ?
65-
`${host}/${targetFrameSrc.image}` :
57+
image: targetFrame.image ?
58+
`${host}/${targetFrame.image}` :
6659
`${host}/og-image?${objectToURLSearchParams(searchParams)}` || '',
67-
buttons: targetFrameSrc.buttons ? buildButtons(targetFrameSrc.buttons) : '',
68-
inputs: targetFrameSrc.inputs ? buildInputs(targetFrameSrc.inputs) : '',
69-
postURL: `${host}/?frame=${targetFrameName}`
60+
buttons: targetFrame.buttons ? buildButtons(targetFrame.buttons) : '',
61+
inputs: targetFrame.inputs ? buildInputs(targetFrame.inputs) : '',
62+
postURL: `${host}/?frame=${targetFrame.name}`
7063
};
7164

7265
return new Response(await landingPage(frameContent),

api/og-image.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { URLSearchParamsToObject } from '../modules/utils';
88
export default async (req, context) => {
99
const url = new URL(req.url);
1010
const params = URLSearchParamsToObject(url.searchParams);
11-
const targetFrameSrc = frames[params.targetFrameName];
12-
const markup = await targetFrameSrc.build(params.payload);
11+
const targetFrame = frames[params.targetFrameName];
12+
const markup = await targetFrame.build(params.frameMessage);
1313

1414
const svg = await satori(
1515
html(markup),

api/redirect.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
// Redirects to an external URL based on buttonIndex parameter. Used to work
33
// around the same origin policy on frames, which is being removed soon.
44
export default async (req, context) => {
5-
const requestUrl = new URL(req.url);
6-
const redirectUrl = requestUrl.searchParams.get('redirectUrl');
5+
const requestURL = new URL(req.url);
6+
const redirectURL = requestURL.searchParams.get('redirectURL');
77

88
return new Response('<div>redirect</div>',
99
{
1010
status: 302,
1111
headers: {
12-
'Location': redirectUrl,
12+
'Location': redirectURL,
1313
},
1414
}
1515
);
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getStore } from '@netlify/blobs';
2+
import frame from '../api/frame';
23

34
// Utility functions to abstract the fetching and setting operations
45
const fetchData = async (key) => {
@@ -50,14 +51,25 @@ const removeBoundCast = (castHash) => removeFromList(getBoundCasts, setBoundCast
5051
// 1. The castAuthorID is in boundAccounts.
5152
// 2. The castHash is in boundCasts.
5253
// 3. Both boundCasts & boundAccounts are empty.
53-
const isFrameStolen = async (payload) => {
54-
const { fid: castAuthorID, hash: castHash } = payload.validData.data.frameActionBody.castId;
54+
const isFrameStolen = async (frameMessage) => {
55+
console.log('isFrameStolen', frameMessage);
56+
const { castId, requestURL } = frameMessage;
57+
if (!castId || !requestURL) {
58+
console.log('isFrameStolen:quickExit', castId, requestURL);
59+
return false;
60+
}
61+
62+
const { fid: castAuthorID, hash: castHash } = castId;
5563
const boundCasts = await getBoundCasts();
5664
const boundAccounts = await getBoundAccounts();
5765

5866
const isAuthorAllowed = boundAccounts.includes(castAuthorID) || boundAccounts.length === 0;
5967
const isCastAllowed = boundCasts.includes(castHash) || boundCasts.length === 0;
60-
const isFirstParty = payload.untrustedData.url.indexOf(process.env.URL) > -1;
68+
const isFirstParty = requestURL ? requestURL.indexOf(process.env.URL) > -1 : true;
69+
70+
console.log('isAuthorAllowed', isAuthorAllowed, castAuthorID, boundAccounts);
71+
console.log('isCastAllowed', isCastAllowed, castHash, boundCasts);
72+
console.log('isFirstParty', isFirstParty);
6173

6274
return !isFirstParty || !isAuthorAllowed || !isCastAllowed;
6375
};

modules/getTargetFrame.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
const DEFAULT_FRAME = 'poster';
2+
import frames from '../src/frames';
3+
import { isFrameStolen } from './antitheft';
24

3-
export default (name, buttonId, frames) => {
5+
export default async (frameMessage) => {
6+
const frameIsStolen = await isFrameStolen(frameMessage);
47
let targetFrameName = DEFAULT_FRAME;
5-
let redirectUrl = null;
6-
if (name && buttonId) {
7-
const originFrame = frames[name];
8-
const button = originFrame.buttons[buttonId-1];
8+
let redirectURL = null;
9+
console.log('getTargetFrame:frameIsStolen', frameIsStolen);
10+
if (frameIsStolen) {
11+
targetFrameName = 'stolen';
12+
} else if (frameMessage.buttonIndex) {
13+
const originFrame = frames[frameMessage.from];
14+
const button = originFrame.buttons[frameMessage.buttonIndex-1];
915
targetFrameName = button.goTo;
10-
redirectUrl = button.url;
11-
}
12-
const targetFrameSrc = frames[targetFrameName];
16+
redirectURL = button.url;
17+
}
18+
1319
return {
14-
targetFrameSrc,
15-
targetFrameName,
16-
redirectUrl
17-
};
20+
targetFrame: frames[targetFrameName],
21+
redirectURL
22+
}
1823
}

modules/safeDecode.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

modules/sanitize.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { JSDOM } from 'jsdom';
2+
import DOMPurify from 'dompurify';
3+
4+
export default (text) => {
5+
try {
6+
const window = new JSDOM('').window;
7+
const purify = DOMPurify(window);
8+
return purify.sanitize(text);
9+
} catch {
10+
throw new Error(`That ain't no string mfr`)
11+
}
12+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"dependencies": {
66
"@netlify/blobs": "^6.4.2",
77
"dompurify": "^3.0.8",
8+
"frames.js": "^0.1.1",
89
"jsdom": "^24.0.0",
910
"satori": "^0.10.11",
1011
"satori-html": "^0.3.2",

src/data/framer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getStore } from '@netlify/blobs';
22
import { getUsername } from './username';
3+
import sanitize from '../../modules/sanitize';
34

45
const getFramer = async() => {
56
const store = getStore('gameState');
@@ -15,7 +16,7 @@ const getFramer = async() => {
1516
const setFramer = async(fid, taunt) => {
1617
const store = getStore('gameState');
1718
await store.set('framer', fid);
18-
await store.set('taunt', taunt);
19+
await store.set('taunt', sanitize(taunt));
1920
}
2021

2122
export {

src/data/message.js

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)