@indiekit/indiekit
Version:
The little server that connects your website to the independent web
202 lines (166 loc) • 5.62 kB
JavaScript
import { createRequire } from "node:module";
import process from "node:process";
import KeyvMongo from "@keyv/mongo";
import makeDebug from "debug";
import Keyv from "keyv";
import { expressConfig } from "./config/express.js";
import { locales } from "./config/locales.js";
import { getCategories } from "./lib/categories.js";
import { getIndiekitConfig } from "./lib/config.js";
import { getLocaleCatalog } from "./lib/locale-catalog.js";
import { getMongodbClient } from "./lib/mongodb.js";
import { getInstalledPlugins } from "./lib/plugins.js";
import { getPostTemplate } from "./lib/post-template.js";
import { getPostTypes } from "./lib/post-types.js";
import { getMediaStore, getStore } from "./lib/store.js";
const require = createRequire(import.meta.url);
const package_ = require("./package.json");
const debug = makeDebug(`indiekit:index`);
export const Indiekit = class {
static async initialize(options = {}) {
const config = await getIndiekitConfig({
config: options.config,
configFilePath: options.configFilePath,
});
return new Indiekit(config);
}
/**
* @private
* @param {object} config - Indiekit configuration
*/
constructor(config) {
this.config = config;
this.package = package_;
this.collections = new Map();
this.endpoints = new Set();
this.installedPlugins = new Set();
this.locale = config.application?.locale;
this.locales = locales;
this.mongodbUrl = config.application?.mongodbUrl;
this.postTypes = new Map();
this.publication = this.config.publication;
this.stores = new Set();
this.validationSchemas = new Map();
}
addCollection(name) {
if (this.collections.has(name)) {
console.warn(`Collection ‘${name}’ already added`);
} else if (this.database) {
this.collections.set(name, this.database.collection(name));
debug(`Added database collection: ${name}`);
}
}
addEndpoint(endpoint) {
this.endpoints.add(endpoint);
debug(`Added endpoint: ${endpoint.name}`);
}
addPostType(type, postType) {
if (postType.config) {
this.postTypes.set(type, {
...this.postTypes.get(type),
...postType.config,
});
}
if (postType.validationSchemas) {
for (const [field, schema] of Object.entries(
postType.validationSchemas,
)) {
this.validationSchemas.set(field, schema);
}
}
}
addPreset(preset) {
this.publication.preset = preset;
debug(`Added publication preset: ${preset.name}`);
}
addStore(store) {
this.stores.add(store);
debug(`Added content store: ${store.name}`);
}
addSyndicator(syndicator) {
syndicator = Array.isArray(syndicator) ? syndicator : [syndicator];
this.publication.syndicationTargets = [
...this.publication.syndicationTargets,
...syndicator,
];
const names = this.publication.syndicationTargets.map(
(target) => target.name,
);
debug(`Added ${names.length} syndication target/s: ${names.join(", ")}`);
}
async connectMongodbClient() {
const mongodbClientOrError = await getMongodbClient(this.mongodbUrl);
if (mongodbClientOrError?.client) {
this.mongodbClient = mongodbClientOrError.client;
}
if (mongodbClientOrError?.error) {
this.mongodbClientError = mongodbClientOrError.error;
}
}
closeMongodbClient() {
if (this.mongodbClient) {
console.info(`Closing MongoDB client`);
this.mongodbClient.close();
}
}
get cache() {
return this.mongodbClient
? new Keyv(new KeyvMongo(this.mongodbUrl))
: false;
}
get database() {
if (this.mongodbClient) {
// Get database name from connection string
let { databaseName } = this.mongodbClient.db();
// If no database given, use ‘indiekit’ as default database, not ‘test’
databaseName = databaseName === "test" ? "indiekit" : databaseName;
debug(`Connect to MongoDB database ${databaseName}`);
return this.mongodbClient.db(databaseName);
}
return false;
}
get localeCatalog() {
return getLocaleCatalog(this);
}
async installPlugins() {
await getInstalledPlugins(this);
}
async updatePublicationConfig() {
if (!this.publication.me) {
console.error("No publication URL in configuration");
console.info("https://getindiekit.com/configuration/publication#me");
process.exit();
}
this.publication.categories = await getCategories(this);
this.publication.mediaStore = getMediaStore(this);
this.publication.postTemplate = getPostTemplate(this.publication);
this.publication.postTypes = getPostTypes(this);
this.publication.store = getStore(this);
}
stop(server, name) {
server.close(() => {
console.info(`Stopping ${name}`);
this.closeMongodbClient();
process.exit(0);
});
}
async server(options = {}) {
await this.connectMongodbClient();
await this.installPlugins();
await this.updatePublicationConfig();
const app = expressConfig(this);
let { name, port } = this.config.application;
const { version } = this.package;
port = options.port || port;
const server = app.listen(port, () => {
debug(`Start ${name} (v${version}) on port ${port}`);
console.info(`Starting ${name} (v${version}) on port ${port}`);
});
debug(`Attach SIGINT handler`);
process.on("SIGINT", () => this.stop(server, name));
debug(`Attach SIGTERM handler`);
process.on("SIGTERM", () => this.stop(server, name));
return server;
}
};