diff --git a/README.md b/README.md index 3e3b7d962..3cbb75194 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,23 @@ you can run a [Docker](https://www.docker.com/) version instead. ### ๐Ÿ’ป Installing and running locally After installing Node.js, -you can install and run the latest published version -from the [npm package repository](https://www.npmjs.com/) -like this: +install the latest server version +from the [npm package repository](https://www.npmjs.com/): ```shell npm install -g @solid/community-server +``` + +To run the server with in-memory storage, use: +```shell community-solid-server # add parameters if needed ``` +To run the server with your current folder as storage, use: +```shell +community-solid-server -c @css:config/file.json +``` + ### ๐Ÿ“ƒ Installing and running from source If you rather prefer to run the latest source code version, or if you want to try a specific [branch](https://www.npmjs.com/) of the code, @@ -84,17 +92,17 @@ by passing parameters to the server command. These parameters give you direct access to some commonly used settings: -| parameter name | default value | description | -| --------- | ------- | ----------- | -| `--port, -p` | `3000` | The TCP port on which the server runs. | -| `--baseUrl. -b` | `http://localhost:$PORT/` | The public URL of your server. | -| `--loggingLevel, -l` | `info` | The detail level of logging; useful for debugging problems. | -| `--config, -c` | `config/default.json` | The configuration for the server. The default only stores data in memory; to persist to your filesystem, try `config/file.json` | -| `--rootFilePath, -f` | `./` | Root folder of the server, when using a file-based configuration. | -| `--sparqlEndpoint, -s` | | URL of the SPARQL endpoint, when using a quadstore-based configuration. | -| `--showStackTrace, -t` | false | Enables detailed logging on error pages. | -| `--podConfigJson` | `./pod-config.json` | Path to the file that keeps track of dynamic Pod configurations. | -| `--mainModulePath, -m` | | Path from where Components.js will start its lookup when initializing configurations. +| parameter name | default value | description | +| -------------- | ------------- | ----------- | +| `--port, -p` | `3000` | The TCP port on which the server runs. | +| `--baseUrl. -b` | `http://localhost:$PORT/` | The public URL of your server. | +| `--loggingLevel, -l` | `info` | The detail level of logging; useful for debugging problems. | +| `--config, -c` | `@css:config/default.json` | The configuration for the server. The default only stores data in memory; to persist to your filesystem, use `@css:config/file.json` | +| `--rootFilePath, -f` | `./` | Root folder of the server, when using a file-based configuration. | +| `--sparqlEndpoint, -s` | | URL of the SPARQL endpoint, when using a quadstore-based configuration. | +| `--showStackTrace, -t` | false | Enables detailed logging on error pages. | +| `--podConfigJson` | `./pod-config.json` | Path to the file that keeps track of dynamic Pod configurations. | +| `--mainModulePath, -m` | | Path from where Components.js will start its lookup when initializing configurations. ### ๐Ÿงถ Custom configurations More substantial changes to server behavior can be achieved diff --git a/config/app/init/initializers/root.json b/config/app/init/initializers/root.json index c6cae58e8..ef92f7275 100644 --- a/config/app/init/initializers/root.json +++ b/config/app/init/initializers/root.json @@ -9,7 +9,7 @@ "store": { "@id": "urn:solid-server:default:ResourceStore" }, "generator": { "@type": "TemplatedResourcesGenerator", - "templateFolder": "$PACKAGE_ROOT/templates/root", + "templateFolder": "@css:templates/root", "factory": { "@type": "ExtensionBasedMapperFactory" }, "templateEngine": { "@type": "HandlebarsTemplateEngine" } } diff --git a/config/http/static/default.json b/config/http/static/default.json index 27b3eb457..53d7339a4 100644 --- a/config/http/static/default.json +++ b/config/http/static/default.json @@ -9,19 +9,19 @@ "assets": [ { "StaticAssetHandler:_assets_key": "/favicon.ico", - "StaticAssetHandler:_assets_value": "$PACKAGE_ROOT/templates/images/favicon.ico" + "StaticAssetHandler:_assets_value": "@css:templates/images/favicon.ico" }, { "StaticAssetHandler:_assets_key": "/.well_known/css/styles/", - "StaticAssetHandler:_assets_value": "$PACKAGE_ROOT/templates/styles/" + "StaticAssetHandler:_assets_value": "@css:templates/styles/" }, { "StaticAssetHandler:_assets_key": "/.well_known/css/fonts/", - "StaticAssetHandler:_assets_value": "$PACKAGE_ROOT/templates/fonts/" + "StaticAssetHandler:_assets_value": "@css:templates/fonts/" }, { "StaticAssetHandler:_assets_key": "/.well_known/css/images/", - "StaticAssetHandler:_assets_value": "$PACKAGE_ROOT/templates/images/" + "StaticAssetHandler:_assets_value": "@css:templates/images/" } ] } diff --git a/config/identity/handler/default.json b/config/identity/handler/default.json index 4fc4fbeda..215131d81 100644 --- a/config/identity/handler/default.json +++ b/config/identity/handler/default.json @@ -35,7 +35,7 @@ { "comment": "Will embed the result of the first engine into the main HTML template.", "@type": "EjsTemplateEngine", - "template": "$PACKAGE_ROOT/templates/main.html.ejs", + "template": "@css:templates/main.html.ejs", } ] } diff --git a/config/identity/handler/interaction/routes/forgot-password.json b/config/identity/handler/interaction/routes/forgot-password.json index 872042938..fc0e48d38 100644 --- a/config/identity/handler/interaction/routes/forgot-password.json +++ b/config/identity/handler/interaction/routes/forgot-password.json @@ -6,8 +6,8 @@ "@id": "urn:solid-server:auth:password:ForgotPasswordRoute", "@type": "InteractionRoute", "route": "^/forgotpassword/?$", - "viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password.html.ejs", - "responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password-response.html.ejs", + "viewTemplate": "@css:templates/identity/email-password/forgot-password.html.ejs", + "responseTemplate": "@css:templates/identity/email-password/forgot-password-response.html.ejs", "handler": { "@type": "ForgotPasswordHandler", "args_accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" }, @@ -15,7 +15,7 @@ "args_idpPath": "/idp", "args_templateEngine": { "@type": "EjsTemplateEngine", - "template": "$PACKAGE_ROOT/templates/identity/email-password/reset-password-email.html.ejs" + "template": "@css:templates/identity/email-password/reset-password-email.html.ejs" }, "args_emailSender": { "@id": "urn:solid-server:default:EmailSender" } } diff --git a/config/identity/handler/interaction/routes/login.json b/config/identity/handler/interaction/routes/login.json index 3eedbf37e..60b583f2a 100644 --- a/config/identity/handler/interaction/routes/login.json +++ b/config/identity/handler/interaction/routes/login.json @@ -7,7 +7,7 @@ "@type": "InteractionRoute", "route": "^/login/?$", "prompt": "default", - "viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/login.html.ejs", + "viewTemplate": "@css:templates/identity/email-password/login.html.ejs", "handler": { "@type": "LoginHandler", "accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" } diff --git a/config/identity/handler/interaction/routes/reset-password.json b/config/identity/handler/interaction/routes/reset-password.json index 3dac8c282..b7af5b605 100644 --- a/config/identity/handler/interaction/routes/reset-password.json +++ b/config/identity/handler/interaction/routes/reset-password.json @@ -7,8 +7,8 @@ "@id": "urn:solid-server:auth:password:ResetPasswordRoute", "@type": "InteractionRoute", "route": "^/resetpassword(/[^/]*)?$", - "viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password.html.ejs", - "responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password-response.html.ejs", + "viewTemplate": "@css:templates/identity/email-password/reset-password.html.ejs", + "responseTemplate": "@css:templates/identity/email-password/reset-password-response.html.ejs", "handler": { "@type": "ResetPasswordHandler", "accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" } diff --git a/config/identity/handler/interaction/routes/session.json b/config/identity/handler/interaction/routes/session.json index 0bb261148..8e16f17f0 100644 --- a/config/identity/handler/interaction/routes/session.json +++ b/config/identity/handler/interaction/routes/session.json @@ -7,7 +7,7 @@ "@type": "InteractionRoute", "route": "^/confirm/?$", "prompt": "consent", - "viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/confirm.html.ejs", + "viewTemplate": "@css:templates/identity/email-password/confirm.html.ejs", "handler": { "@type": "SessionHttpHandler", "providerFactory": { "@id": "urn:solid-server:default:IdentityProviderFactory" } diff --git a/config/identity/pod/resource-generators/templated.json b/config/identity/pod/resource-generators/templated.json index 75e94add7..bd57370ae 100644 --- a/config/identity/pod/resource-generators/templated.json +++ b/config/identity/pod/resource-generators/templated.json @@ -5,7 +5,7 @@ "comment": "Generates resources based on the templates stored in the template folder.", "@id": "urn:solid-server:default:ResourcesGenerator", "@type": "TemplatedResourcesGenerator", - "templateFolder": "$PACKAGE_ROOT/templates/pod", + "templateFolder": "@css:templates/pod", "factory": { "@type": "ExtensionBasedMapperFactory" }, diff --git a/config/identity/registration/route/registration.json b/config/identity/registration/route/registration.json index f4b8fec9e..ce68c8516 100644 --- a/config/identity/registration/route/registration.json +++ b/config/identity/registration/route/registration.json @@ -6,8 +6,8 @@ "@id": "urn:solid-server:auth:password:RegistrationRoute", "@type": "InteractionRoute", "route": "^/register/?$", - "viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/register.html.ejs", - "responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/register-response.html.ejs", + "viewTemplate": "@css:templates/identity/email-password/register.html.ejs", + "responseTemplate": "@css:templates/identity/email-password/register-response.html.ejs", "handler": { "@type": "RegistrationHandler", "args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }, diff --git a/config/util/representation-conversion/converters/markdown.json b/config/util/representation-conversion/converters/markdown.json index 3ad49ca26..be095db5a 100644 --- a/config/util/representation-conversion/converters/markdown.json +++ b/config/util/representation-conversion/converters/markdown.json @@ -12,7 +12,7 @@ "@type": "MarkdownToHtmlConverter", "templateEngine": { "@type": "EjsTemplateEngine", - "template": "$PACKAGE_ROOT/templates/main.html.ejs" + "template": "@css:templates/main.html.ejs" } }, { @@ -21,7 +21,7 @@ "@type": "ContainerToTemplateConverter", "templateEngine": { "@type": "HandlebarsTemplateEngine", - "template": "$PACKAGE_ROOT/templates/container.md.hbs" + "template": "@css:templates/container.md.hbs" }, "contentType": "text/markdown", "identifierStrategy": { "@id": "urn:solid-server:default:IdentifierStrategy" } diff --git a/src/init/AppRunner.ts b/src/init/AppRunner.ts index 086c796dd..87bda754e 100644 --- a/src/init/AppRunner.ts +++ b/src/init/AppRunner.ts @@ -5,9 +5,11 @@ import type { IComponentsManagerBuilderOptions, LogLevel } from 'componentsjs'; import { ComponentsManager } from 'componentsjs'; import yargs from 'yargs'; import { getLoggerFor } from '../logging/LogUtil'; -import { ensureTrailingSlash, resolveAssetPath } from '../util/PathUtil'; +import { ensureTrailingSlash, resolveAssetPath, modulePathPlaceholder } from '../util/PathUtil'; import type { App } from './App'; +const defaultConfig = `${modulePathPlaceholder}config/default.json`; + export interface CliParams { loggingLevel: string; port: number; @@ -72,7 +74,7 @@ export class AppRunner { }) .options({ baseUrl: { type: 'string', alias: 'b', requiresArg: true }, - config: { type: 'string', alias: 'c', requiresArg: true }, + config: { type: 'string', alias: 'c', default: defaultConfig, requiresArg: true }, loggingLevel: { type: 'string', alias: 'l', default: 'info', requiresArg: true }, mainModulePath: { type: 'string', alias: 'm', requiresArg: true }, port: { type: 'number', alias: 'p', default: 3000, requiresArg: true }, @@ -89,7 +91,7 @@ export class AppRunner { dumpErrorState: true, logLevel: params.loggingLevel as LogLevel, }; - const configFile = resolveAssetPath(params.config ?? '$PACKAGE_ROOT/config/default.json'); + const configFile = resolveAssetPath(params.config); // Create and execute the app this.createApp(loaderProperties, configFile, params) diff --git a/src/pods/generate/TemplatedResourcesGenerator.ts b/src/pods/generate/TemplatedResourcesGenerator.ts index 50fa1e74c..0ad5ebdd4 100644 --- a/src/pods/generate/TemplatedResourcesGenerator.ts +++ b/src/pods/generate/TemplatedResourcesGenerator.ts @@ -29,7 +29,7 @@ interface TemplateResourceLink extends ResourceLink { * A FileIdentifierMapper will be used to generate identifiers that correspond to the relative structure. * * A relative `templateFolder` is resolved relative to cwd, - * unless it's preceded by $PACKAGE_ROOT/, e.g. $PACKAGE_ROOT/foo/bar. + * unless it's preceded by `@css:`, e.g. `@css:foo/bar`. */ export class TemplatedResourcesGenerator implements ResourcesGenerator { private readonly templateFolder: string; diff --git a/src/server/middleware/StaticAssetHandler.ts b/src/server/middleware/StaticAssetHandler.ts index 61091453f..369f33ff7 100644 --- a/src/server/middleware/StaticAssetHandler.ts +++ b/src/server/middleware/StaticAssetHandler.ts @@ -14,7 +14,7 @@ import type { HttpRequest } from '../HttpRequest'; /** * Handler that serves static resources on specific paths. * Relative file paths are assumed to be relative to cwd. - * Relative file paths can be preceded by $PACKAGE_ROOT/, e.g. $PACKAGE_ROOT/foo/bar, + * Relative file paths can be preceded by `@css:`, e.g. `@css:foo/bar`, * in case they need to be relative to the module root. */ export class StaticAssetHandler extends HttpHandler { diff --git a/src/storage/conversion/ErrorToTemplateConverter.ts b/src/storage/conversion/ErrorToTemplateConverter.ts index 00b3e058f..61b5932db 100644 --- a/src/storage/conversion/ErrorToTemplateConverter.ts +++ b/src/storage/conversion/ErrorToTemplateConverter.ts @@ -5,6 +5,7 @@ import type { Representation } from '../../ldp/representation/Representation'; import { INTERNAL_ERROR } from '../../util/ContentTypes'; import { HttpError } from '../../util/errors/HttpError'; import { InternalServerError } from '../../util/errors/InternalServerError'; +import { modulePathPlaceholder } from '../../util/PathUtil'; import type { TemplateEngine } from '../../util/templates/TemplateEngine'; import type { RepresentationConverterArgs } from './RepresentationConverter'; import { TypedRepresentationConverter } from './TypedRepresentationConverter'; @@ -18,8 +19,8 @@ export interface TemplateOptions { } const DEFAULT_TEMPLATE_OPTIONS: TemplateOptions = { - mainTemplatePath: '$PACKAGE_ROOT/templates/error/main.md.hbs', - codeTemplatesPath: '$PACKAGE_ROOT/templates/error/descriptions/', + mainTemplatePath: `${modulePathPlaceholder}templates/error/main.md.hbs`, + codeTemplatesPath: `${modulePathPlaceholder}templates/error/descriptions/`, extension: '.md.hbs', contentType: 'text/markdown', }; diff --git a/src/util/PathUtil.ts b/src/util/PathUtil.ts index c118769d5..894396539 100644 --- a/src/util/PathUtil.ts +++ b/src/util/PathUtil.ts @@ -173,16 +173,20 @@ export function getModuleRoot(): string { return joinFilePath(__dirname, '../../'); } -const modulePath = '$PACKAGE_ROOT/'; +/** + * A placeholder for the path to the `@solid/community-server` module root. + * The resolveAssetPath function will replace this string with the actual path. + */ +export const modulePathPlaceholder = '@css:'; /** * Converts file path inputs into absolute paths. - * Works similar to `absoluteFilePath` but paths that start with '$PACKAGE_ROOT/' + * Works similar to `absoluteFilePath` but paths that start with the `modulePathPlaceholder` * will be relative to the module directory instead of the cwd. */ -export function resolveAssetPath(path: string = modulePath): string { - if (path.startsWith(modulePath)) { - return joinFilePath(getModuleRoot(), path.slice(modulePath.length)); +export function resolveAssetPath(path: string = modulePathPlaceholder): string { + if (path.startsWith(modulePathPlaceholder)) { + return joinFilePath(getModuleRoot(), path.slice(modulePathPlaceholder.length)); } return absoluteFilePath(path); } diff --git a/test/unit/init/AppRunner.test.ts b/test/unit/init/AppRunner.test.ts index 4530914a7..ec206e4df 100644 --- a/test/unit/init/AppRunner.test.ts +++ b/test/unit/init/AppRunner.test.ts @@ -211,7 +211,7 @@ describe('AppRunner', (): void => { new AppRunner().runCli({ argv: [ 'node', 'script', - '--config', '$PACKAGE_ROOT/config/file.json', + '--config', '@css:config/file.json', ], }); await new Promise(setImmediate); diff --git a/test/unit/server/middleware/StaticAssetHandler.test.ts b/test/unit/server/middleware/StaticAssetHandler.test.ts index 0d75e1a0a..e3b9eb3f5 100644 --- a/test/unit/server/middleware/StaticAssetHandler.test.ts +++ b/test/unit/server/middleware/StaticAssetHandler.test.ts @@ -16,7 +16,7 @@ describe('A StaticAssetHandler', (): void => { '/foo/bar/main': '/assets/scripts/bar.js', '/foo/bar/unknown': '/assets/bar.unknown', '/foo/bar/cwd': 'paths/cwd.txt', - '/foo/bar/module': '$PACKAGE_ROOT/paths/module.txt', + '/foo/bar/module': '@css:paths/module.txt', '/foo/bar/folder1/': '/assets/folders/1/', '/foo/bar/folder2/': '/assets/folders/2', '/foo/bar/folder2/subfolder/': '/assets/folders/3', diff --git a/test/unit/storage/conversion/ErrorToTemplateConverter.test.ts b/test/unit/storage/conversion/ErrorToTemplateConverter.test.ts index ab111e86a..85907046c 100644 --- a/test/unit/storage/conversion/ErrorToTemplateConverter.test.ts +++ b/test/unit/storage/conversion/ErrorToTemplateConverter.test.ts @@ -164,9 +164,9 @@ describe('An ErrorToTemplateConverter', (): void => { expect(templateEngine.render).toHaveBeenCalledTimes(2); expect(templateEngine.render).toHaveBeenNthCalledWith(1, { key: 'val' }, - { templatePath: '$PACKAGE_ROOT/templates/error/descriptions/', templateFile: 'E0001.md.hbs' }); + { templatePath: '@css:templates/error/descriptions/', templateFile: 'E0001.md.hbs' }); expect(templateEngine.render).toHaveBeenNthCalledWith(2, { name: 'BadRequestHttpError', message: 'error text', stack: error.stack, description: '' }, - { templateFile: '$PACKAGE_ROOT/templates/error/main.md.hbs' }); + { templateFile: '@css:templates/error/main.md.hbs' }); }); }); diff --git a/test/unit/util/PathUtil.test.ts b/test/unit/util/PathUtil.test.ts index c6be92d3e..194234ec0 100644 --- a/test/unit/util/PathUtil.test.ts +++ b/test/unit/util/PathUtil.test.ts @@ -141,12 +141,12 @@ describe('PathUtil', (): void => { }); describe('#resolvePathInput', (): void => { - it('interprets paths relative to the module root when starting with $PACKAGE_ROOT/.', async(): Promise => { - expect(resolveAssetPath('$PACKAGE_ROOT/foo/bar')).toBe(joinFilePath(getModuleRoot(), '/foo/bar')); + it('interprets paths relative to the module root when starting with @css:.', async(): Promise => { + expect(resolveAssetPath('@css:foo/bar')).toBe(joinFilePath(getModuleRoot(), '/foo/bar')); }); - it('handles ../ paths with $PACKAGE_ROOT/.', async(): Promise => { - expect(resolveAssetPath('$PACKAGE_ROOT/foo/bar/../baz')).toBe(joinFilePath(getModuleRoot(), '/foo/baz')); + it('handles ../ paths with @css:.', async(): Promise => { + expect(resolveAssetPath('@css:foo/bar/../baz')).toBe(joinFilePath(getModuleRoot(), '/foo/baz')); }); it('leaves absolute paths as they are.', async(): Promise => {