Skip to content

Integrate Silex with your infrastructure

Alex Hoyau edited this page May 28, 2024 · 9 revisions

⚠️ This page is obsolete: It concerns Silex v2. Please read Silex v3 "Getting started with self hosting" docs and Silex v3 "Connect Silex with your data storage and website hosting services"

Here is the code which is explained in this page

The idea behind integrating Silex with your infrastructure is to customize how files are store in Silex:

  • When the user opens/saves a website
  • When the user loads/uploads assets
  • When the user publishes a website

This page describes how to take control over

  • The user experience to select a service, i.e. what appears by default when the user opens silex file browser
  • The user experience to publish a website, i.e. customize the dialogs when you click "publish"
  • Which cloud services are available to the user, e.g. Github, Dropbox, SFTP... or a custom service which saves to your servers or a data base
  • Where the publication process ends up publishing, e.g. github page, your servers under the user name...

There is also a way to use Silex as an editor for one website only, which means:

  • skip the Dashboard and open a website directly
  • prevent the user from opening another site (hide the open menu and disable the shortcut)
  • prevent the user from "saving as"

If you just want to select which services and publication options are available on your Silex instance, you can do that by hosting an instance of Silex and playing with the env vars

Related topics:

Important terms

Please read the following sections which explain what we mean here when talking of these terms

You might also be interested in reading more about Silex vocabulary.

Add a custom service to your Silex instance

Unifile services are low level code that allows read/write to cloud services. It uses the excellent unifile project (also by the Silex Labs team).

Here is why you may want to create a unifile service for your Silex instance

  • Allow the user to save/load/publish websites and assets to/from another service, such as google drive or S3 or your servers
  • Make it possible to add a custom hosting provider which publishes to your servers, see below

Create a project

You can add services to your instance for your users. Start by creating a new nodejs project as explained in the page How To Add Silex To Your Node.js Project. In short, Silex is included in your project as an npm dependency.

Create a service

Here is an example of a very simple unifile service which can be used in Silex:

const { FsConnector } = require('unifile');

/**
 * Service connector extends the local filesystem connector (unifile-fs)
 * The root URL will depend on the user name, i.e. in ${ rootUrl }/${ session.user }/
 */
class CustomService extends FsConnector {
  // **
  // extend Fs service
  constructor(config) {
    super(config);
    // change fs infos
    this.name = 'custom-service';
    this.infos.name = 'custom-service';
    this.infos.displayName = 'Custom Service disaplay name';
    this.infos.description = 'Custom Service description';
  }

  // **
  //Auth commands
  getInfos(session) {
    return Object.assign({
      isLoggedIn: !!session.user,
      isOAuth: false,
      username: session.user
    }, this.infos);
  }
  // In this example the form will set a user in the session
  // The form is one specific form for this example
  getAuthorizeURL(session) { return Promise.resolve('/login-form.html') }

  // check authentication
  // and create a root folder for the user
  login(session, loginInfos) {
    if(loginInfos.password === 'pass') {
      // store the user
      session.user = loginInfos.user;
      // create the user's directory if it does not exist already
      return this.mkdir(session, '')
      .then(() => this.mkdir(session, 'Website'))
      .catch((e) => session);
    }
    // logout if pass is not correct
    session.user = null;
    return Promise.resolve(session);
  }
  // **
  //Filesystem commands: prepend the user to all paths
  readdir(session, path) { return super.readdir(session, `/tmp/${ session.user }/${ path }`) }
  stat(session, path) { return super.stat(session, `/tmp/${ session.user }/${ path }`) }
  mkdir(session, path) { return super.mkdir(session, `/tmp/${ session.user }/${ path }`) }
  writeFile(session, path, data) { return super.writeFile(session, `/tmp/${ session.user }/${ path }`, data) }
  createWriteStream(session, path) { return super.createWriteStream(session, `/tmp/${ session.user }/${ path }`) }
  readFile(session, path) { return super.readFile(session, `/tmp/${ session.user }/${ path }`) }
  createReadStream(session, path) { return super.createReadStream(session, `/tmp/${ session.user }/${ path }`) }
  rename(session, src, dest) { return super.rename(session, src, dest) }
  unlink(session, path) { return super.unlink(session, `/tmp/${ session.user }/${ path }`) }
  rmdir(session, path) { return super.rmdir(session, `/tmp/${ session.user }/${ path }`) }
  batch(session, actions, message) { return super.batch(session, actions, message) }
}
// export for use in index.js
module.exports = CustomService;

Please read the comments in this code, it explains how

  • This service expects the password to be pass, the user name can be any user name
  • This service behaves like the Fs service (local file system) but all the paths are relative to /tmp/ + user name
  • When a user logs in, we create a new home folder for it in /tmp/ + user name

Read more about how to create a unifile service

Login form

This service uses a login form, which we need to create:

In your project, add a file named login-form.html with this HTML code:

  <form action="/ce/custom-service/login_callback" method="post" accept-charset="utf-8">
    <h1>Unifile would like access to your files and folders.</h1>
    <p>This is an example of a custom service. All the username work with the <strong>password "pass"</strong></p>
    <div>
      <label for="user">username</label>
      <input type="text" name="user" placeholder="user" id="user">
    </div>
    <div>
      <label for="password">password</label>
      <input type="password" name="password" id="password">
    </div>
    <div>
      <input type="submit" name="submit" value="Log in" id="submit">
    </div>
  </form>

Note that it will send the info to a route which Silex creates automatically, with the custom service name in it.

Register the service to Silex

Modify the file index.js of your project to look like this:

const { SilexServer, Config } = require('silex-website-builder');
const CustomService = require('./custom-service');

// create a default config
const config = new Config();

// disable other services
config.ceOptions.enableSftp = false;

// create the Silex server
const silex = new SilexServer(config);

// add our custom service
silex.unifile.use(new CustomService());

// define a form to get the user login and password
silex.app.use('/login-form.html', (req, res) => res.sendFile(__dirname + '/login-form.html'));

// start Silex
silex.start(function() {
  console.log('server started');
});

Test your service

Now you can type npm start, open http://localhost:6805/ and click on "import". It should look like this:

Screenshot from 2019-05-06 18-16-53

Then double click on your service name:

Screenshot from 2019-05-06 18-17-08

Login with any user login and the password "pass", and you will see your folder /tmp/your-login-name:

Screenshot from 2019-05-06 18-17-26

Also note that if your user is authenticated before it arrives in Silex, the file manager will detect that there is only 1 service and will open it automatically, so that your user will not even see the name of your custom service.

Add a custom hosting provider to Silex

Now that we have a custom service we can add a custom hosting provider. A hosting provider is what controls this dialog in Silex:

Screenshot from 2019-05-06 18-27-28

Our hosting provider will publish in our user's special folder /tmp/ + user name, and inside it, in a folder Website. This is arbitrary and only an example.

Create a hosting provider

Create a file at the root of your project named custom-hosting-provider.js with this content:

module.exports = function(unifile) {
  this.unifile = unifile;
};

module.exports.prototype.getOptions = function(session) {
  const infos = this.unifile.getInfos(session, 'custom-service');
  return {
    name: 'custom-hosting-provider',
    displayName: 'Custom hosting provider based on custom-service',
    isLoggedIn: infos.isLoggedIn,
    username: infos.username,
    authorizeUrl: '/ce/custom-service/authorize',
    dashboardUrl: 'https://www.custom-provider.com/projects',
    pleaseCreateAVhost: 'create a new project in our service.',
    vhostsUrl: '/hosting/custom-hosting-provider/vhost', // route created by silex automatically
    buyDomainUrl: 'https://www.custom-provider.com/domains',
    skipVhostSelection: true,
    skipFolderSelection: true,
    afterPublishMessage: 'Thx for using our service.<br><br>',
  };
};

const WEBSITE_FOLDER_NAME = 'Website';
module.exports.prototype.getVhosts = async function(session) {
  return [{
    name: WEBSITE_FOLDER_NAME,
    // domainUrl: `/hosting/custom-provider/vhost/get`,
    skipDomainSelection: true,
    publicationPath: {
      //absPath: `/ce/github/get/${ WEBSITE_FOLDER_NAME }/gh-pages`,
      name: WEBSITE_FOLDER_NAME,
      folder: WEBSITE_FOLDER_NAME,
      path: `${ WEBSITE_FOLDER_NAME }/`,
      service: 'custom-service',
      url: `/ce/custom-service/get/Website/`
    }
  }];
};
module.exports.prototype.finalizePublication = (from, to, session, onStatus) => Promise.resolve();
module.exports.prototype.getDefaultPageFileName = () => 'index.html';

help wanted: more details about the code here. Ask questions in the issues and create content here

Declare your custom hosting provider to Silex

Modify your index.js file to look like this:

'use strict';

const { SilexServer, Config } = require('silex-website-builder');
const CustomProvider = require('./custom-hosting-provider');

// create a default config
const config = new Config();

// provide only our custom hosting to user when they want to publish
config.publisherOptions.skipHostingSelection = true;
config.publisherOptions.enableHostingUnifile = false;

// disable other services, show only our custom service
config.ceOptions.enableSftp = false;

// create the Silex server
const silex = new SilexServer(config);

// add our custom service
const CustomService = require('./custom-service');
silex.unifile.use(new CustomService());

// add our custom hosting provider
silex.publishRouter.addHostingProvider(new CustomProvider(silex.unifile))

// define a form to get the user login and password
silex.app.use('/login-form.html', (req, res) => res.sendFile(__dirname + '/login-form.html'));

// start Silex
silex.start(function() {
  console.log('server started');
});

help wanted: more details about the code here. Ask questions in the issues and create content here

Test your hosting provider

Type npm start and open http://localhost:6805/, then select a template, save it and click "publish" from the ☁ menu. You will see that Silex asks nothing to the user, it just publishes to the user's folder.

Screenshot from 2019-05-06 18-53-51

WARNING: Support for Silex v2 has stopped. Try Silex v3 alpha, Read about it, Subscribe to the newsletter

Clone this wiki locally