Design References
PIXEL 2022
Website
PIXEL 2022: https://pixel.usm.my/
PIXEL 2021: https://pixelcsusm.wixsite.com/pixel2021
PIXEL 2020: https://xlanz79.wixsite.com/website-1
PIXEL 2022 Endpoints
Test Judges (email and pw same): pixel2022-1@ynshung.com, pixel2022-2@ynshung.com
Prototype
Infrastructure
Hosting: Cloudflare Pages
Domain: Liasing with USM PPKT through PIC
Mr Amran: 016-4947860
Student Database: Airtable
Front-end: SvelteKit & Svelte
Authentication & Judging Database: Firebase
Proxy / Caching: Cloudflare Workers
There is currently one caching proxy at https://data.cssocietyusm.com/at/*
Format is /at/[airtableId]/[paths]
API key is from cssocietytech@gmail.com, give the email full access (not sure about read-only) to give access to the API to read the database
Underlying code
JavaScript
Copy
addEventListener("fetch", event => {
event.respondWith(handleRequest(event));
})
const CACHE_TIME = 30;
let return_headers = new Headers({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': '*',
"Cache-Control": "s-maxage="+CACHE_TIME,
"Content-Type": "application/json",
});
let return_header_nc = new Headers({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': '*',
"Content-Type": "application/json",
});
/**
* Respond to the request
* @param {Request} request
*/
async function handleRequest(event) {
try {
// Get request URL, pathname and query
const request = event.request;
const { pathname, search, searchParams } = new URL(request.url);
if (request.method.toUpperCase() === 'OPTIONS') {
return new Response('OK', {
status: 200,
headers: return_headers
});
}
// Split by / and remove first empty element
var paths = pathname.split('/').slice(2);
var airtableId = paths[0];
paths = paths.slice(1);
// Check if the paths (after /api/v1) is not empty
if (paths.length === 0 || paths[0] === "" || airtableId === "") {
return error("Invalid format");
}
// Check if request is cached before
const cacheURL = request.url;
const cache = caches.default;
const cachedResponse = await cache.match(cacheURL);
if (cachedResponse) {
console.log(`Serving from cache: ${cacheURL}`);
return cachedResponse;
} else {
console.log(`Cache miss: ${cacheURL}`);
}
// Fetch Airtable API
const result = await (
await fetch(
`https://api.airtable.com/v0/${airtableId}/${paths.join("/")}${search}`,
{
method: 'get',
headers: new Headers({
"Authorization": `Bearer ${API_KEY}`,
}),
}
)
).json();
if (result.error) {
return error(result.error.message);
}
var data = result;
console.log(data)
if (!data) {
return error("Not Found", 404);
}
const apiResponse = new Response(JSON.stringify(data), {
status: 200,
headers: return_headers
});
event.waitUntil(cache.put(cacheURL, apiResponse.clone()));
// Return the data
return apiResponse;
} catch (err) {
return error(`Internal Server Error: ${err}`, 500);
}
};
const error = (message, status = 400) => {
return new Response(JSON.stringify({ error: message }), {
status: status,
headers: return_headers
});
};
Data
Judging score calculation is completely done within Google Sheets using Pivot Table:
Related Video
I forget what the following video is about but you can just refer to it AHAHAHAH










