FaxStore
Creating an app is a fairly simple process if the basics of JavaScript are known. Apps come with access to local variables through the exported function along with global variables, functions and, events. Apps should follow recommendations to ensure that the structure between apps is similar. For example, if a config file is present for an app it should be located in the apps/configs folder.
Table of Contents
Apps support two file types:
JavaScript (.js)
Sharf (.sharf) these are encrypted Weblutions files, generally present in sold or locked extensions
If an extension is being sold, it can be submitted to Weblutions for assessment and be encrypted.
There is currently no public Sharf encryption access.
If an app is requiring a configuration file it's recommended to place them into the configs folder inside the apps folder (/faxstore/apps/configs/example-config.json). This helps with consistency between extensions and to create less config file clutter in the main apps folder.
Rendered pages should be kept in the apps folder within views (/faxstore/src/views/apps/file.ejs). Other items or directories in views may be overwritten in updates, the views apps folder is not updated.
Keep app file names and configuration files unique to avoid conflicting file names with other extensions, whether that's an added seed/ID, version number, or developer name in front of the extension files. For example the below examples use two methods to make the file name unique and have less risk of being the same name from other developers.
cooldev-fileuploader-config.json, or;
2030482342334-fileuploader-config.json
cooldev-fileuploader.js, or;
2030482342334-fileuploader.js
└── faxstore/
├── apps/
│ ├── configs/
│ │ ├── app1-config.json
│ │ └── app3-config.json
│ ├── app1.js
│ ├── app2.js
│ └── app3.sharf
├── public/
| ├── assets/
│ │ ├── colors.css
│ │ ├── app1-person-standing.png
│ ├── themes/
│ │ ├── app1-theme.css
└── src/
├── static
├── backend
├── pages
├── views/
│ └── apps/
│ └── app1.ejs
├── index.js
├── backend.js
├── updatemanager.js
└── setup.jsApps uploaded to the FaxStore App Store require a manifest to define which files are for which function within the app. A manifest can include:
backend: include javascript content intended to be used for routes, or any other backend functionality. These are extracted on install to the apps folder.
css: are the style sheets for any theme or app requiring styling content outside of the existing CSS Library.
The first css file in the array/listed will be used to set the theme of the store.
ejs: include the embedded JavaScript files for page views within FaxStore, these are most common for themes.
js: front-end JavaScript functions that execute within the browser.
npm: include a list of NPM modules that may be required. Anything outside the base FaxStore package.json modules should be included.
{
"backend": ["logger-2140ddae.js"],
"css": ["logger-2140ddae.min.css"],
"ejs": ["logger-2140ddae-view.ejs"],
"js": [],
"npm": ["winston"]
}Any app within the FaxStore apps folder will attempt to execute. If it loads or doesn't load it will be logged at start-up. Therefore, it's not recommended to have a "my extension starting" log to the console as FaxStore does this as apart of regular boots.
There's some content worth learning or at least being aware of before making an app, such as the express JS eco-system, and MySQL if there's any plans to perform databasing actions. Some resources to learn are linked below
MySQL Package Documentation
FaxStore uses a pool connection which is a connection globally accessible in FaxStore via connection. Generally, the query() function is used to execute MySQL content.
Discord.js v14 Documentation
Recommended if an extension is using the Discord bot, it's also recommended to view the Discord Integration Documentation as well.
For theme content refer to the themes pages.
A base app file without having any actual functionality other than having it setup for the environment should look like the below. This ensures database, express, and other functions can be used by the extension.
test-app.js
const config = require('../config.json'); // Original Config For Faxstore
const extConfig = require('./configs/EXTENSION_CONFIG.json'); // Extensions config, these should be created for extensions. Full path; extensions/configs/file.json
/*
module.exports params:
- app: ExpressJS application.
- connection: MySQL connection
- bot: Discord bot
- faxstore: FaxStore event system
*/
module.exports = async function(app, connection, bot, faxstore) {
console.log(`Extension started!`); // Did you read above? This is a test..
app.get(`/myextension`, function(req, res) {
res.send(`Here is a page running!`);
});
app.get(`/myextension-view`, function(req, res) {
res.render('apps/test-app'); // renders the EJS file that was created.
});
}Create the base app file and name it something unique. In this guide we'll use test-helloworld.js
Seeming this extension doesn't need a config file the above base app file can be used without the configuration content
The app.get() function creates a GET request on the express application.
The res.send() function sends the content as plain text to the users browser.
module.exports = async function(app, connection, bot, faxstore) {
app.get(`/hello-world`, function(req, res) {
res.send(`Hello world of FaxStore!`);
});
}Save the app file and upload it to the FaxStore app folder
Restart FaxStore and see it load in the console
Next learn how to register an app. It's always recommended to register FaxStore app to have it displayed and populate properly on the FaxStore and it's staff pages.
This app will use the FaxStore Global cache to get the logged in user, otherwise a redirect to the login page will be performed. Data will be returned in JSON form. Additionally, this app will be registered.
Create the base app file and name it something unique. In this guide we'll use test-fetchuser.js
Seeming this app doesn't need a config file the above base app file can be used without the configuration content.
Register the app with the faxstore.apps.register() function.
module.exports = async function(app, connection, bot, faxstore) {
faxstore.apps.register({
name: 'A really unsafe fetch user API',
description: 'A basic unsecure fetch user API for any logged in user.',
icon: 'https://weblutions.com/assets/logo.png',
version: '1.0.0',
author: 'Weblutions',
url: 'https://github.com/FAXES',
}, __filename);
}Add the app.get for a "/@me" route
module.exports = async function(app, connection, bot, faxstore) {
faxstore.apps.register({
name: 'A really unsafe fetch user API',
description: 'A basic unsecure fetch user API for any logged in user.',
icon: 'https://weblutions.com/assets/logo.png',
version: '1.0.0',
author: 'Weblutions',
url: 'https://github.com/FAXES',
}, __filename);
app.get(`/@me`, function(req, res) {
});
}Now by using a req.isAuthenticated() function it can be determined if the user is logged in when navigating to @me, this function returns a true or false. Adding this to an if statement would be an easy way to do this.
After, the res.redirect() function from express can be used to redirect the user to the specified page.
module.exports = async function(app, connection, bot, faxstore) {
... app registration.
app.get(`/@me`, function(req, res) {
if(req.isAuthenticated() == false) return res.redirect('/login');
});
}Add the return if the user is authenticated by adding the res.json() function for the user object which that data can be captured from the global cache.
module.exports = async function(app, connection, bot, faxstore) {
... app registration.
app.get(`/@me`, function(req, res) {
if(req.isAuthenticated() == false) return res.redirect('/login');
let user = CACHE.customers.find(f => f.id === req.user.id) || {};
return res.json(user);
});
}This will return the user data if found in the cache, otherwise an empty object ({}) is provided.
It's best to use data that FaxStore directly stores and that is updated. While the req.user object contains most user data, it may not be the most up to date in the database. Hence why the cache is used for this data. It's also better on a security level in the event data is injected by a user into the session data which could occur from a bad actor.
Both of these examples are fairly basic. However, they provide a nice base and starting point. Apps can get complex pretty easily and have many functions. Reach out to our amazing Discord community for assistance in creating content and apps.