Simple cms for simple services and websites
npm install @tripod311/zy_cmsCMS engine for Node.js. Name can be interpreted as laZY/eaZY CMS.
Once upon a time I had to make 3 similar services (database + file storage + admin panel + API) in one month. I don't want to do it again, so I created this.
---
* Installation
* Configuration
* Database schema
* ZY CMS types
* Relations
* Basic tables
* First start
* API overview
* DB
* Auth
* Storage
* Type reference
* StorageFile
* ReadOptions
* UpdateOptions
---
Simply run:
``bash`
npm install @tripod311/zy_cms
---
Here's an example configuration file:
`yaml`
admin_panel: true
storage:
enable: true
path: "./data/media"
publicGET: true
db:
type: "postgres"
path: "./data/db.sqlite"
host: "localhost"
port: 5432
user: "tripod"
password: "1234"
database: "test"
http:
cookie_secret: "cookie_secret"
port: 8080
credentials:
key: "path-to-key"
cert: "path-to-cert"
ca: "path-to-ca"
cors:
origin:
- "http://localhost:8080"
methods:
- GET
- POST
- PUT
- DELETE
auth:
enable: true
jwt_secret: "jwt_secret"
secure_cookies: false
localization:
enable: true
locales: ["ru", "en"]
fallbackLocale: "en"
Explanation of fields:
* admin\_panel β toggle default admin panel
* storage β file storage options
* enable β toggle file storage
* path β path to where files will be stored
* publicGET β if true, /storage/:alias can be accessed without authorizationsqlite
* db β supports , mysql, and postgres
* path β only for sqlite
* other options apply to mysql/postgres
* http β webserver settings
* cookie\_secret β optional
* port β port to listen on
* credentials β set to null for http mode
* cors β CORS rules
* auth β authorization system
* must be enabled if using admin panel
* localization β currently affects only localized fields in schema
Put this config into config.yaml in the project root.
---
Example schema.yaml:
`yaml`
tables:
- name: pages
fields:
- name: name
type: "VARCHAR(300)"
required: true
unique: true
- name: content
type: "json"
distinct_type: "TEXT"
required: true
localized: true
- name: content
type: "markdown"
distinct_type: "TEXT"
required: true
localized: true
- name: switch
type: "BOOLEAN"
required: false
- name: text
type: "TEXT"
- name: date
type: "datetime"
- name: author
type: "VARCHAR(255)"
relation:
table: "users"
column: "login"
kind: many-to-one
onDelete: setNull
- name: some_media
type: "VARCHAR(255)"
relation:
table: "media"
column: "alias"
kind: one-to-one
onDelete: setNull
* tables is a list of table definitions
* each field has:
* name β column nametype
* β SQL or CMS-specificdistinct_type
* β enforced SQL type for CMS typesrequired
* β like NOT NULLlocalized
* β creates per-locale columnsrelation
* β SQL-like foreign key
CMS-specific types:
* json β stored as LONGTEXT or equivalentmarkdown
* β stored as LONGTEXTdatetime
* β stored as ISO string (VARCHAR(30))
> For PostgreSQL: you must specify distinct_type, since PostgreSQL doesnβt have LONGTEXT
Analogous to SQL REFERENCES. Requires:
* table and columnkind
* : one-to-one or many-to-oneonDelete
* : cascade, setNull, restrict, noAction, setDefault
> Many-to-many must be modeled manually with intermediate tables
If auth is enabled, a users table is created with:
* id, login, passwordCHAR(60)
* password is stored as bcrypt hash in
If storage is enabled, a media table is created with:
* id, alias, path
---
Create an empty file called firstLaunch in your project root.
This allows creating the first user in admin panel.
Example:
`ts
import path from "path";
import Application from "@tripod311/zy_cms";
import FastifyStatic from "@fastify/static";
const app = new Application();
async function start () {
await app.setup();
await app.start();
}
function stop () {
(async () => {
await app.stop();
})();
}
process.on("SIGINT", stop);
process.on("SIGTERM", stop);
start();
`
Admin panel will be available at /admin/.
---
`ts`
app.db.create
app.db.read
app.db.update
app.db.delete
`ts`
app.auth.create(login: string, password: string): Promise
app.auth.delete(login: string): Promise
Middlewares:
`ts`
app.auth.forceAuth(request, reply) // restricts to logged-in users
app.auth.checkAuth(request, reply) // optional login (request.user?.login)
Route handler:
`ts`
app.auth.authorize(request, reply) // expects JSON { login, password }, sets cookie
`ts`
app.storage.create(file: StorageFile): Promise
app.storage.read(file: StorageFile): Promise
app.storage.update(file: StorageFile): Promise
app.storage.delete(file: StorageFile): Promise
---
`ts`
interface StorageFile {
id?: number;
alias: string;
extension?: string;
path?: string;
content?: Buffer;
}
`ts${keyof T & string} ASC
interface ReadOptions
where?: Partial
fields?: (keyof T)[];
orderBy?: keyof T | | ${keyof T & string} DESC;`
limit?: number;
offset?: number;
}
`ts``
interface UpdateOptions
returning?: boolean;
where?: Partial
}
---
> Feel free to open an issue or PR if youβd like to contribute or request a feature β¨