Skip to content

Commit

Permalink
Publishing example examples/v7-edge-functions
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Jan 9, 2024
1 parent aee6dfa commit 2a65a6f
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 455 deletions.
2 changes: 2 additions & 0 deletions edgio.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// This file was automatically added by edgio init.
// You should commit this file to source control.
// Learn more about this file at https://docs.edg.io/guides/edgio_config
require('dotenv').config();

module.exports = {
connector: '@edgio/next',

Expand Down
144 changes: 144 additions & 0 deletions functions/database/upstash/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import createFetchForOrigin from '../../../utils/createFetchForOrigin';
import {
getCookiesFromRequest,
setCookieToResponse,
} from '../../../utils/cookies';
import { setEnvFromContext } from '../../../utils/polyfills/process.env';
import '../../../utils/polyfills/URL';
import waitingPage from './waiting-room-capacity.html';
import landingPage from './waiting-room-landing.html';

// Constants
const COOKIE_NAME_ID = '__booking_id';
const COOKIE_NAME_TIME = '__booking_last_update_time';
const TOTAL_ACTIVE_USERS = 2;
const SESSION_DURATION_SECONDS = 15;

// Setup fetch function for Upstash
const fetch = createFetchForOrigin('upstash');

/**
* Main handler for the edge request.
*/
export async function handleHttpRequest(request, context) {
let resp;

// Set context environment variables to process.env
setEnvFromContext(context);

const cookies = getCookiesFromRequest(request);

// Get user ID from cookie or generate a new one
const userId = cookies[COOKIE_NAME_ID] ?? generateId();

// Get the current number of active sessions and the active user
const size = await getRecordCount();
const isActiveUser = (await getRecord(userId)) === '1';

console.log('Current number of active sessions: ', size);

// Check capacity
if (size < TOTAL_ACTIVE_USERS || isActiveUser) {
resp = await getDefaultResponse(request, userId);
} else {
resp = await getWaitingRoomResponse();
}

return resp;
}

/**
* Generate a random ID
*/
function generateId(len = 10) {
return Array.from({ length: len }, () =>
((Math.random() * 36) | 0).toString(36)
).join('');
}

/**
* Handle the default response.
*/
async function getDefaultResponse(request, userId) {
const response = new Response(
landingPage({
domain: getDomainFromRequest(request),
maxUsers: TOTAL_ACTIVE_USERS,
sessionDuration: SESSION_DURATION_SECONDS,
})
);
response.headers.set('content-type', 'text/html;charset=UTF-8');

const cookies = getCookiesFromRequest(request);
const now = Date.now();
const lastUpdate = cookies[COOKIE_NAME_TIME];
let lastUpdateTime = 0;

if (lastUpdate) {
lastUpdateTime = parseInt(lastUpdate);
}

const diff = now - lastUpdateTime;
const updateInterval = (SESSION_DURATION_SECONDS * 1000) / 2;
if (diff > updateInterval) {
await setExpiryRecord(userId, '1', SESSION_DURATION_SECONDS);
setCookieToResponse(response, [[COOKIE_NAME_TIME, now.toString()]]);
}

setCookieToResponse(response, [[COOKIE_NAME_ID, userId]]);
return response;
}

/**
* Send a REST request to Upstash.
*/
async function sendUpstashRequest(cmd) {
cmd = Array.isArray(cmd) ? cmd.join('/') : cmd;

return (
await fetch(`${process.env.UPSTASH_REDIS_REST_URL}`, {
method: 'POST',
body: JSON.stringify(cmd.split('/')),
headers: {
Authorization: `Bearer ${process.env.UPSTASH_REDIS_REST_TOKEN}`,
},
})
).json();
}

/**
* Get the current number of records.
*/
async function getRecordCount() {
const data = await sendUpstashRequest('DBSIZE');
return data.result;
}

/**
* Fetch a record from Upstash by key.
*/
async function getRecord(key) {
const data = await sendUpstashRequest(['GET', key]);
return data.result;
}

/**
* Set a record with an expiry time in Upstash.
*/
async function setExpiryRecord(key, value, seconds) {
return sendUpstashRequest(['SET', key, value, 'EX', seconds]);
}

/**
* Response for the waiting room.
*/
async function getWaitingRoomResponse() {
const response = new Response(waitingPage());
response.headers.set('content-type', 'text/html;charset=UTF-8');
return response;
}

function getDomainFromRequest(request) {
const url = new URL(request.url);
return url.origin;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const template = ({ queuePosition }) => `
const template = () => `
<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -70,10 +70,10 @@ const template = ({ queuePosition }) => `
<div class="loader"></div>
<h1>Almost There!</h1>
<p>Our site is currently at full capacity. Thanks for your patience.</p>
<p>You are number <strong>${queuePosition}</strong> in line.</p>
<p>You'll be redirected shortly. Please do not close your browser.</p>
</div>
</body>
</html>
`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const template = ({ requestUrl }) => `
const template = ({ domain, maxUsers, sessionDuration }) => `
<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -91,7 +91,7 @@ const template = ({ requestUrl }) => `
}
function keepSessionAlive() {
fetch('${requestUrl}/ping');
fetch(window.location.origin + '/example/upstash-database');
}
// Set interval to keep session alive every 15 seconds
Expand All @@ -101,26 +101,24 @@ const template = ({ requestUrl }) => `
<body>
<div class='container'>
<h1>Welcome to the Waiting Room Demo</h1>
<Here>This demo showcases an effective way to manage website traffic during high-volume periods. When the site is at full capacity, visitors are temporarily placed in a waiting room, ensuring a smooth user experience. Here's how it works:</p>
<p>
If you see this page, you have established a session as an active user since the session limit has not been reached.
</p>
<p>
<strong>To simulate creating new sessions, issue the following command:</strong>
<div class="code-block">for i in {1..5}; do curl ${requestUrl}; done</div>
<button class="copy-btn" onclick="copyCodeToClipboard()">Copy Code</button>
</p>
<p>
<strong>To start a new session which is queued,</strong> follow these instructions:
<p>Open the link in a new private/incognito window: <a href="${requestUrl}" target="_blank">Start New Session</a>. This will attempt to establish a new session, which will be queued if the active session limit is reached.</p>
</p>
<p>Dive into the code to see how it works.</p>
<p><a href="https://github.com/edgio-docs/edgio-v7-edge-functions-example" target="_blank">View the demo code on GitHub</a></p>
</div>
<h1>Welcome to the Waiting Room Demo</h1>
<p>
This demo showcases an effective way to manage website traffic during high-volume periods.
When the site is at full capacity, visitors are temporarily placed in a waiting room, ensuring a smooth user experience.
</p>
<p>
This is configured for a maximum of ${maxUsers} active sessions, with each session lasting ${sessionDuration} seconds.
</p>
<p>
Experience this firsthand by opening <a href="${domain}/example/upstash-database">this link</a> in multiple incognito/private browser sessions.
Once the site is at full capacity (2 active sessions), you will be placed in a waiting room until a spot opens up.
</p>
<p>Optionally, issue the <pre>curl</pre> command below to make multiple requests:</p>
<div class="code-block">curl ${domain}/example/upstash-database</div>
<button class="copy-btn" onclick="copyCodeToClipboard()">Copy Code</button>
<p>Dive into the code to see how it works.</p>
<p><a href="https://github.com/edgio-docs/edgio-v7-edge-functions-example" target="_blank">View the demo code on GitHub</a></p>
</div>
</body>
</html>
Expand Down
4 changes: 2 additions & 2 deletions functions/general/sample-html-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ export async function handleHttpRequest(request, context) {
<p>Transactional queries with a PlanetScale database.</p>
</li>
<li>
<h4>Waiting Room using PlanetScale (<a href="/example/waiting-room">View Example</a>)</h4>
<p>A waiting room example using PlanetScale for session tracking.</p>
<h4>Upstash Database (<a href="/example/upstash-database">View Example</a>)</h4>
<p>A waiting room example using Upstash + Redis.</p>
</li>
</ul>
</section>
Expand Down
77 changes: 0 additions & 77 deletions functions/waiting-room/constants.js

This file was deleted.

78 changes: 0 additions & 78 deletions functions/waiting-room/index.js

This file was deleted.

Loading

0 comments on commit 2a65a6f

Please sign in to comment.