Skip to content

Commit

Permalink
createaccount script now automatically gets injected with our reserve…
Browse files Browse the repository at this point in the history
…d usernames on startup, and saved. This prevents that page having to request this data after it loads.
  • Loading branch information
Naviary2 committed Jul 16, 2024
1 parent 4e8aa7b commit 21f26df
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 17 deletions.
7 changes: 5 additions & 2 deletions src/client/scripts/createaccount.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@

// THIS LINE WILL BE INJECTED by HTMLScriptInjector!
// const reservedUsernames = [...];

const element_usernameInput = document.getElementById('username');
const element_emailInput = document.getElementById('email');
const element_passwordInput = document.getElementById('password');
const element_submitButton = document.getElementById('submit');

// This will be an object with 3 arrays: memberList, reservedUsernames, profainWords
// This will be an object with 1 array: profainWords
let data;
fetch('/createaccount/data')
.then((response) => response.json())
Expand All @@ -26,7 +29,7 @@ element_usernameInput.addEventListener('input', (event) => { // When username fi
const formatError = !onlyLettersAndNumbers(element_usernameInput.value);
// If data is still uninitiated (late fetch call), just assume there's no error.
const usernameReservedError =
data ? !lengthError && data.reservedUsernames.indexOf(element_usernameInput.value.toLowerCase()) !== -1
data ? !lengthError && reservedUsernames.includes(element_usernameInput.value.toLowerCase())
: false;
const profainError =
data ? !lengthError && checkProfanity(element_usernameInput.value)
Expand Down
1 change: 0 additions & 1 deletion src/server/controllers/createaccountController.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ async function generateAccount({ username, email, password, autoVerify }) {
// into the createaccount html instead.
function getRegisterData(req, res) {
res.json({
reservedUsernames,
profainWords
});
}
Expand Down
6 changes: 5 additions & 1 deletion src/server/routes/createaccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ const path = require('path');
const createaccountController = require('../controllers/createaccountController')
const {getRegisterData, checkEmailAssociated, checkUsernameAssociated} = require('../controllers/createaccountController');

const createAccountHTMLPath = path.join(__dirname, '..', '..', '..', 'dist', 'views', 'createaccount.html');



router.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '..', '..', '..', 'dist', 'views', 'createaccount.html'));
res.sendFile(createAccountHTMLPath);
})

router.post('/', createaccountController.createNewMember);
Expand Down
69 changes: 56 additions & 13 deletions src/server/utility/HTMLScriptInjector.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const { getReservedUsernames } = require('../controllers/createaccountController');

/**
* A cache object that has file paths for the keys, and for the values-
Expand All @@ -26,47 +27,44 @@ let htmlCache = {};
* a specified tag, then cache's that content into {@link htmlCache}
* @param {string} htmlFilePath - The path of the html document in the project
* @param {string} jsFilePath - The path of the javascript file containing the desired javascript code to inject.
* @param {string} injectAfterTag - The HTML tag after which the JavaScript code will be injected (typically the `<head>`).
* @param {Object} [stringInjection] - Optional argument: An object of the form {string: "htmlstring", injectafter: "tags"}.
* The string will be insterted after the specified tags into the html doc
*/
function prepareAndCacheHTML(htmlFilePath, jsFilePath, injectAfterTag, stringInjection = {}) {
injectScript(htmlFilePath, jsFilePath, injectAfterTag, stringInjection)
function prepareAndCacheHTML(htmlFilePath, jsFilePath, stringInjection = {}) {
injectScriptIntoHeadFromPaths(htmlFilePath, jsFilePath, stringInjection)
.then(modifiedHTML => {
htmlCache[htmlFilePath] = modifiedHTML;
addHTMLToCache(htmlFilePath, modifiedHTML)
})
.catch(error => console.error("Failed to inject script: ", error));
}

/**
* Injects a JavaScript file's content into an HTML file
* after a specified tag, returning the new content.
* RECEIVES file paths, not raw data.
* @param {string} htmlFilePath - The path of the html document in the project
* @param {string} jsFilePath - The path of the javascript file containing the desired javascript code to inject.
* @param {string} injectAfterTag - The HTML tag after which the JavaScript code will be injected (typically the `<head>`).
* @param {Object} [stringInjection] - Optional argument: An object of the form {string: "htmlstring", injectafter: "tags"}.
* The string will be insterted after the specified tags into the html doc
* @returns {Promise<string>} - A promise that resolves with the modified HTML content, or rejects with an error message.
*/
function injectScript(htmlFilePath, jsFilePath, injectAfterTag, stringInjection = {}) {
function injectScriptIntoHeadFromPaths(htmlFilePath, jsFilePath, stringInjection = {}) {
return new Promise((resolve, reject) => {
// Read the JavaScript file
fs.readFile(jsFilePath, 'utf8', (jsErr, jsData) => {
if (jsErr) {
reject("Error reading the JavaScript file: " + jsErr);
return;
}
// Create a script tag with the JavaScript content
const scriptTag = `<script>${jsData}</script>`;

// Read the HTML file and inject the script tag
fs.readFile(htmlFilePath, 'utf8', (htmlErr, htmlData) => {
if (htmlErr) {
reject("Error reading the HTML file: " + htmlErr);
return;
}
// Inject the script tag before the specified closing tag
let modifiedHTML = htmlData.replace(injectAfterTag, `${injectAfterTag}${scriptTag}`);

let modifiedHTML = insertScriptInHead(htmlData, jsData)

// Inject the string of the optional argument "stringInjection" into the HTML file, if applicable
if (Object.keys(stringInjection).length != 0){
modifiedHTML = modifiedHTML.replace(stringInjection.injectafter, `${stringInjection.injectafter}${stringInjection.string}`);
Expand All @@ -77,6 +75,28 @@ function injectScript(htmlFilePath, jsFilePath, injectAfterTag, stringInjection
});
}

/**
* Inserts the given javascript code into a script tag in the html header.
* Receives RAW, pre-read data.
* @param {string} html - The preloaded html file
* @param {string} js - The javascript code
*/
function insertScriptInHead(html, js) {
// Create a script tag with the JavaScript content
const scriptTag = `<script>${js}</script>`;
// Inject the script tag before the specified closing tag
return html.replace('<head>', `<head>${scriptTag}`);
}

/**
* Adds the modified html to the cache.
* @param {string} path - The path of the html (typically inside /dist)
* @param {string} contents - The modified contents of the html.
*/
function addHTMLToCache(path, contents) {
htmlCache[path] = contents;
}

/**
* Sends our cached HTML file with injected code at the specified path, to the client.
* If the HTML content is not ready or doesn't exist, an error message will be sent instead.
Expand Down Expand Up @@ -104,7 +124,7 @@ function getCachedHTML(htmlFilePath) {
// Inject the scripts we want...
{
// Prepare the injection of our (potentially minified) htmlscript.js script into play.html
const htmlFilePath = path.join(__dirname, '..', '..', "..", 'dist', 'views', 'play.html');
const htmlFilePath = path.join(__dirname, '..', '..', '..', 'dist', 'views', 'play.html');
const jsFilePath = path.join(__dirname, '..', '..', '..', 'dist', 'scripts', 'game', 'htmlscript.js');

// Prepare the injection of references to all other game scripts into play.html
Expand All @@ -122,7 +142,30 @@ function getCachedHTML(htmlFilePath) {
}

// Finally, perform the injection into play.html
prepareAndCacheHTML(htmlFilePath, jsFilePath, '<head>', {string: HTML_callGame_JS_string, injectafter: injectafter_string});
prepareAndCacheHTML(htmlFilePath, jsFilePath, {string: HTML_callGame_JS_string, injectafter: injectafter_string});
}

// Inject the reserved usernames into createaccount.html, then SAVE it in /dist!
// Does using synchronious read and write methods slow down startup?
{
// Retrieve the reserved usernames
const reservedUsernames = getReservedUsernames();
const reservedUsernamesJS = `const reservedUsernames = ${JSON.stringify(reservedUsernames)};`

// Read the HTML file and inject the script tag
const createAccountScriptFilePath = path.join(__dirname, '..', '..', '..', 'dist', 'scripts', 'createaccount.js');
let createAccountScript;
try {
createAccountScript = fs.readFileSync(createAccountScriptFilePath, 'utf8')
} catch (e) {
console.log("Error reading createaccount.js script in HTMLScriptInjector: " + e.stack);
return;
}

const modifiedScript = `// Injected by HTMLScriptInjector\n${reservedUsernamesJS}\n\n${createAccountScript}`;

// Write new script to /dist
fs.writeFileSync(createAccountScriptFilePath, modifiedScript);
}

module.exports = {
Expand Down

0 comments on commit 21f26df

Please sign in to comment.