17 The CMS API
The CMS API is the NodeStore over HTTP. The CMS interface runs on it, and so do its plugins. It has one command endpoint, which dispatches on the command's type, and one event stream. Every request carries a bearer token that names a user node, and the command runs as that user, so a query returns only what the user can read.
builder.AddRelatudeCMS()
.AddApi(o => {
// auth/login: the id of the user node these belong to, or null
o.VerifyLogin = (login, cms) => {
var name = login.UserName?.Trim().ToLowerInvariant() ?? "";
var user = cms.Admin.Query<ICMSUser>().Where(u => u.Username == name).Execute().FirstOrDefault();
var ok = user != null && !string.IsNullOrEmpty(login.Password) && user.Password.Verify(login.Password);
return Task.FromResult(ok ? user!.Id : (Guid?)null);
};
});
var app = builder.Build();
app.UseRelatudeCMS();
app.MapRelatudeCmsApi(); // before MapRelatudeCMS(), whose catch-all route would be tried first
app.MapRelatudeCmsUI(); // the interface itself, at /relatude.cms/
app.MapRelatudeCMS();VerifyLogin says how auth/login checks a user name and password. This one finds the ICMSUser
with that Username and verifies its Password. Without VerifyLogin, auth/login answers 404.
- Routes — Path is /relatude.cms/api unless CmsApiOptions.Path says otherwise. A failure answers { error } with a status:
- Tokens and who a request runs as — The sign-in routes answer { token, expires, userId }. Every other request sends the token as Authorization: Bearer <token>. A token runs out after TokenLifetime, 12 hours by default. Renew it with auth/refresh. A token…
- Commands — A command is POST {Path}/command, with the token, and the JSON body { type, payload }:
- Revisions and cultures — A node has one version until it gets a second: a draft beside the published version, or a version in another culture. From then on each version is a revision, with its own id, type, culture and values. A translation…
- Previews of drafts — The interface can preview versions that readers do not see, such as drafts. It names the versions with preview, then loads the page with its instance id in the address:
- Sites — One database can hold several sites. A site is a Relatude.DB collection (ISystemCollection, which the CMS's ISite is). A node is in one site or none, the same for all its revisions. Shared content, such as templates, is…
- Nodes and values on the wire — A node is one JSON object: its properties by CodeName, and system fields with a $ prefix that no C# name can have.
- The event stream — GET {Path}/stream sends server-sent events. It takes the same bearer token as a command. A browser's EventSource cannot send that header, so read the stream with fetch, as the SDK does.
- Modules — Commands of your own are registered the way the built-in ones are. A module adds them with Command(…) on its builder, named after the module (chapter 18). Host code registers them on CmsApiCommands:
- Processes — Work that takes longer than a request should, such as copying a large branch or importing a feed, runs as a process. The command starts it and answers at once. The CMS interface shows the process with its progress, and…
- From TypeScript — @relatude/sdk is the client: a channel to the API's routes, a TokenSession that signs in and keeps the token fresh, and RelatudeStore, which has the NodeStore's methods and a query builder typed by your model's…
- The CMS interface — The interface editors work in is not part of the NuGet package. MapRelatudeCmsUI() serves it from the site's own address, at /relatude.cms/ by default, and the installation gets it from services.relatude.com: Each time…