<commit_before>// The Ghost Boot Sequence
// -----------------------
// - This is intentionally one big file at the moment, so that we don't have to follow boot logic all over the place
// - This file is FULL of debug statements so we can see timings for the various steps because the boot needs to be as fast as possible
// - As we manage to break the codebase down into distinct components for e.g. the frontend, their boot logic can be offloaded to them
// - app.js is separate as the first example of each component having it's own app.js file colocated with it, instead of inside of server/web

// IMPORTANT: The only global requires here should be overrides + debug so we can monitor timings with DEBUG=ghost:boot* node ghost
require('./server/overrides');
const debug = require('ghost-ignition').debug('boot');
// END OF GLOBAL REQUIRES

/**
 * Helper class to create consistent log messages
 */class BootLogger {
    constructor(logging, startTime) {
        this.logging = logging;
        this.startTime = startTime;
    }
    log(message) {
        let {logging, startTime} = this;
        logging.info(`Ghost ${message} in ${(Date.now() - startTime) / 1000}s`);
    }
}

/**
 * Helper function to handle sending server ready notifications
 */
function notifyServerReady(error) {
    const notify = require('./server/notify');

    if (error) {
        debug('Notifying server ready (error)');
        notify.notifyServerReady(error);
    } else {
        debug('Notifying server ready (success)');
        notify.notifyServerReady();
    }
}

/**
  * Get the Database into a ready state
  * - DatabaseStateManager handles doing all this for us
  * - Passing logging makes it output state messages
  */
async function initDatabase({config, logging}) {
    const DatabaseStateManager = require('./server/data/db/state-manager');
    const dbStateManager = new DatabaseStateManager({knexMigratorFilePath: config.get('paths:appRoot')});
    await dbStateManager.makeReady({logging});
}

/**
 * Core is intended to be all the bits of Ghost that are fundamental and we can't do anything without them!
 * (There's more to do to make this true)
 */
async function initCore({ghostServer}) {
    debug('Begin: initCore');
    // Models are the heart of Ghost - this is a syncronous operation
    debug('Begin: models');
    const models = require('./server/models');
    models.init();
    debug('End: models');

    // Settings are a core concept we use settings to store key-value pairs used in critical pathways as well as public data like the site title
    debug('Begin: settings');
    const settings = require('./server/services/settings');
    await settings.init();
    debug('End: settings');

    // The URLService is a core part of Ghost, which depends on models. It needs moving from the frontend to make this clear.
    debug('Begin: Url Service');
    const urlService = require('./frontend/services/url');
    // Note: there is no await here, we do not wait for the url service to finish
    // We can return, but the site will remain in (the shared, not global) maintenance mode until this finishes
    // This is managed on request: https://github.com/TryGhost/Ghost/blob/main/core/server/web/shared/middlewares/maintenance.js#L13
    urlService.init();
    debug('End: Url Service');

    // Job Service allows parts of Ghost to run in the background
    debug('Begin: Job Service');
    const jobService = require('./server/services/jobs');
    ghostServer.registerCleanupTask(async () => {
        await jobService.shutdown();
    });
    debug('End: Job Service');

    debug('End: initCore');
}

/**
 * Frontend is intended to be just Ghost's frontend
 * This is technically wrong currently because the theme & frontend settings services contain code used by the API to upload themes & settings
 */
async function initFrontend() {
    debug('Begin: initFrontend');

    debug('Begin: Frontend Settings');
    const frontendSettings = require('./frontend/services/settings');
    await frontendSettings.init();
    debug('End: Frontend Settings');

    debug('Begin: Themes');
    const themeService = require('./server/services/themes');
    await themeService.init();
    debug('End: Themes');

    debug('End: initFrontend');
}

/**
 * At the moment we load our express apps all in one go, they require themselves and are co-located
 * What we want is to be able to optionally load various components and mount them
 * So eventually this function should go away
 */
async function initExpressApps() {
    debug('Begin: initExpressApps');
    const parentApp = require('./server/web/parent/app')();
    debug('End: initExpressApps');
    return parentApp;
}

/**
 * Services are components that make up part of Ghost and need initialising on boot
 * These services should all be part of core, frontend services should be loaded with the frontend
 * We are working towards this being a service loader, with the ability to make certain services optional
 */
async function initServices({config}) {
    debug('Begin: initServices');

    const defaultApiVersion = config.get('api:versions:default');
    debug(`Default API Version: ${defaultApiVersion}`);

    debug('Begin: Dynamic Routing');
    // Dynamic routing is generated from the routes.yaml file, which is part of the settings service
    // When Ghost's DB and core are loaded, we can access this file and call routing.bootstrap.start
    // However this _must_ happen after the express Apps are loaded, hence why this is here and not in initFrontend
    // Routing is currently tightly coupled between the frontend and backend
    const routing = require('./frontend/services/routing');
    const bridge = require('./bridge');
    // We pass the frontend API version here, so that the frontend services are slightly less tightly-coupled
    routing.bootstrap.start(bridge.getFrontendApiVersion());
    const settings = require('./server/services/settings');
    const frontendSettings = require('./frontend/services/settings');
    const getRoutesHash = () => frontendSettings.getCurrentHash('routes');
    await settings.syncRoutesHash(getRoutesHash);
    debug('End: Dynamic Routing');

    debug('Begin: Services');
    const permissions = require('./server/services/permissions');
    const xmlrpc = require('./server/services/xmlrpc');
    const slack = require('./server/services/slack');
    const {mega} = require('./server/services/mega');
    const webhooks = require('./server/services/webhooks');
    const appService = require('./frontend/services/apps');
    const limits = require('./server/services/limits');
    const scheduling = require('./server/adapters/scheduling');

    const urlUtils = require('./shared/url-utils');

    // NOTE: limits service has to be initialized first
    // in case it limits initialization of any other service (e.g. webhooks)
    await limits.init();

    await Promise.all([
        permissions.init(),
        xmlrpc.listen(),
        slack.listen(),
        mega.listen(),
        webhooks.listen(),
        appService.init(),
        scheduling.init({
            apiUrl: urlUtils.urlFor('api', {version: defaultApiVersion, versionType: 'admin'}, true)
        })
    ]);
    debug('End: Services');

    // Initialise analytics events
    if (config.get('segment:key')) {
        require('./server/analytics-events').init();
    }

    debug('End: initServices');
}

/**
 * Kick off recurring jobs and background services
 * These are things that happen on boot, but we don't need to wait for them to finish
 * Later, this might be a service hook
 */
async function initBackgroundServices({config}) {<commit_msg>Added members service init to boot sequence<commit_after>// The Ghost Boot Sequence
// -----------------------
// - This is intentionally one big file at the moment, so that we don't have to follow boot logic all over the place
// - This file is FULL of debug statements so we can see timings for the various steps because the boot needs to be as fast as possible
// - As we manage to break the codebase down into distinct components for e.g. the frontend, their boot logic can be offloaded to them
// - app.js is separate as the first example of each component having it's own app.js file colocated with it, instead of inside of server/web

// IMPORTANT: The only global requires here should be overrides + debug so we can monitor timings with DEBUG=ghost:boot* node ghost
require('./server/overrides');
const debug = require('ghost-ignition').debug('boot');
// END OF GLOBAL REQUIRES

/**
 * Helper class to create consistent log messages
 */class BootLogger {
    constructor(logging, startTime) {
        this.logging = logging;
        this.startTime = startTime;
    }
    log(message) {
        let {logging, startTime} = this;
        logging.info(`Ghost ${message} in ${(Date.now() - startTime) / 1000}s`);
    }
}

/**
 * Helper function to handle sending server ready notifications
 */
function notifyServerReady(error) {
    const notify = require('./server/notify');

    if (error) {
        debug('Notifying server ready (error)');
        notify.notifyServerReady(error);
    } else {
        debug('Notifying server ready (success)');
        notify.notifyServerReady();
    }
}

/**
  * Get the Database into a ready state
  * - DatabaseStateManager handles doing all this for us
  * - Passing logging makes it output state messages
  */
async function initDatabase({config, logging}) {
    const DatabaseStateManager = require('./server/data/db/state-manager');
    const dbStateManager = new DatabaseStateManager({knexMigratorFilePath: config.get('paths:appRoot')});
    await dbStateManager.makeReady({logging});
}

/**
 * Core is intended to be all the bits of Ghost that are fundamental and we can't do anything without them!
 * (There's more to do to make this true)
 */
async function initCore({ghostServer}) {
    debug('Begin: initCore');
    // Models are the heart of Ghost - this is a syncronous operation
    debug('Begin: models');
    const models = require('./server/models');
    models.init();
    debug('End: models');

    // Settings are a core concept we use settings to store key-value pairs used in critical pathways as well as public data like the site title
    debug('Begin: settings');
    const settings = require('./server/services/settings');
    await settings.init();
    debug('End: settings');

    // The URLService is a core part of Ghost, which depends on models. It needs moving from the frontend to make this clear.
    debug('Begin: Url Service');
    const urlService = require('./frontend/services/url');
    // Note: there is no await here, we do not wait for the url service to finish
    // We can return, but the site will remain in (the shared, not global) maintenance mode until this finishes
    // This is managed on request: https://github.com/TryGhost/Ghost/blob/main/core/server/web/shared/middlewares/maintenance.js#L13
    urlService.init();
    debug('End: Url Service');

    // Job Service allows parts of Ghost to run in the background
    debug('Begin: Job Service');
    const jobService = require('./server/services/jobs');
    ghostServer.registerCleanupTask(async () => {
        await jobService.shutdown();
    });
    debug('End: Job Service');

    debug('End: initCore');
}

/**
 * Frontend is intended to be just Ghost's frontend
 * This is technically wrong currently because the theme & frontend settings services contain code used by the API to upload themes & settings
 */
async function initFrontend() {
    debug('Begin: initFrontend');

    debug('Begin: Frontend Settings');
    const frontendSettings = require('./frontend/services/settings');
    await frontendSettings.init();
    debug('End: Frontend Settings');

    debug('Begin: Themes');
    const themeService = require('./server/services/themes');
    await themeService.init();
    debug('End: Themes');

    debug('End: initFrontend');
}

/**
 * At the moment we load our express apps all in one go, they require themselves and are co-located
 * What we want is to be able to optionally load various components and mount them
 * So eventually this function should go away
 */
async function initExpressApps() {
    debug('Begin: initExpressApps');
    const parentApp = require('./server/web/parent/app')();
    debug('End: initExpressApps');
    return parentApp;
}

/**
 * Services are components that make up part of Ghost and need initialising on boot
 * These services should all be part of core, frontend services should be loaded with the frontend
 * We are working towards this being a service loader, with the ability to make certain services optional
 */
async function initServices({config}) {
    debug('Begin: initServices');

    const defaultApiVersion = config.get('api:versions:default');
    debug(`Default API Version: ${defaultApiVersion}`);

    debug('Begin: Dynamic Routing');
    // Dynamic routing is generated from the routes.yaml file, which is part of the settings service
    // When Ghost's DB and core are loaded, we can access this file and call routing.bootstrap.start
    // However this _must_ happen after the express Apps are loaded, hence why this is here and not in initFrontend
    // Routing is currently tightly coupled between the frontend and backend
    const routing = require('./frontend/services/routing');
    const bridge = require('./bridge');
    // We pass the frontend API version here, so that the frontend services are slightly less tightly-coupled
    routing.bootstrap.start(bridge.getFrontendApiVersion());
    const settings = require('./server/services/settings');
    const frontendSettings = require('./frontend/services/settings');
    const getRoutesHash = () => frontendSettings.getCurrentHash('routes');
    await settings.syncRoutesHash(getRoutesHash);
    debug('End: Dynamic Routing');

    debug('Begin: Services');
    const members = require('./server/services/members');
    const permissions = require('./server/services/permissions');
    const xmlrpc = require('./server/services/xmlrpc');
    const slack = require('./server/services/slack');
    const {mega} = require('./server/services/mega');
    const webhooks = require('./server/services/webhooks');
    const appService = require('./frontend/services/apps');
    const limits = require('./server/services/limits');
    const scheduling = require('./server/adapters/scheduling');

    const urlUtils = require('./shared/url-utils');

    // NOTE: limits service has to be initialized first
    // in case it limits initialization of any other service (e.g. webhooks)
    await limits.init();

    await Promise.all([
        members.init(),
        permissions.init(),
        xmlrpc.listen(),
        slack.listen(),
        mega.listen(),
        webhooks.listen(),
        appService.init(),
        scheduling.init({
            apiUrl: urlUtils.urlFor('api', {version: defaultApiVersion, versionType: 'admin'}, true)
        })
    ]);
    debug('End: Services');

    // Initialise analytics events
    if (config.get('segment:key')) {
        require('./server/analytics-events').init();
    }

    debug('End: initServices');
}

/**
 * Kick off recurring jobs and background services
 * These are things that happen on boot, but we don't need to wait for them to finish
 * Later, this might be a service hook
 */
async function initBackgroundServices({config}) {
{'fraction_non_alphanumeric': 0.1139076284379865, 'fraction_numerical': 0.0007784120394395433, 'mean_word_length': 3.454492921121063, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1397391', 'n_tokens_mistral': 4006, 'n_tokens_neox': 3937, 'n_words': 1554}