Bearweb Database
Bearweb CMS is a database-driven framework. This article demonstrates the structure of the database, including how to create, edit and maintain the database.
CMS, Centent Management System, Database, DBMS, SQLite, Database-driven, Sitemap, Session Control, User management
--by @ Jan 24, 2026Index
One of the reasons I created my own CMS is because of the complex database of existing CMSs. Therefore, when I designed this CMS, I aimed to make the underlying database structure simple, portable and manageable.
Bearweb CMS is database-driven, each module has its own private database. Bearweb CMS is designed with SQLite in mind, but should be easily adapt to other relational DBMS. For SQLite, each database is a file; therefore, using dedicated database (file) for each module should minimize congestion.
Bearweb CMS has a very simple database structure. It has 3 modules (sitemap, session and transaction, user), it relies on only 4 databases in 3 SQLite database files:
Sitemap- Each record represents a resource, it can be a webpage, a file, or an API.Session- A session is a series of requests from one client to the server.Transaction- A transaction represents a HTTP request from client to server and a response from the server to the client.User- Each record represents a registered user.
In Bearweb CMS, modules are database-backed classes instances. An instance is a row in the table. Each variable in the instance can be mapped to a field in the module's database table, with the same field name for variable name. int will be saved as integer in the database, human-readable string will be saved as text, array and object will be saved as JSON text, binary string will be saved as blob.
Bearweb CMS is portable. It is designed to use a portable DBMS, the SQLite. Transferring the website from one machine to another requires copying and pasting the PHP files (the CMS) and the database files (the data).
Make sure that the database files and the directory containing the database files are readable and writable.
Although it is recommended to modify the website through APIs, we all like the quick-and-dirty way. The database is very managementable, by design, there is no foreign key or trigger. You can directly make modifications to the database to make changes to the website, as long as you:
- Respect the data type. When making changes to a field, you should use the same data type. For example,
Sitemap.createwants a timestamp inint, you should provide a timestamp inint(for example:0) instead of astring(for example:1970-01-01 00:00:00). - Respect the JSON format. Bearweb CMS relies heavily on JSON to store key-value pairs, such as
Sitemap.metaandUser.group. If the JSON format is damaged, Bearweb CMS cannot parse it correctly. - I recommend using DB browser for SQLite to manage the database. It prevents changing variable type or breaking JSON format by accident. It provides syntax highlighting, formatting and compressing (remove formatting spaces) for JSON.
Don't worry. If Brarweb CMS senses a data in bad format, it will use the default value.
Bearweb Sitemap
Requesting a webpage, a file (for example, an image), or any other resource is in fact fetching data from the database, with access control and some data manipulation. This database is called Sitemap because the table is a map of the website.
CREATE TABLE "Sitemap" (
"url" TEXT NOT NULL UNIQUE,
"category" TEXT NOT NULL DEFAULT '',
"template" TEXT NOT NULL DEFAULT '["object", "blob"]',
"owner" TEXT NOT NULL DEFAULT '',
"create" INTEGER NOT NULL DEFAULT 0,
"modify" INTEGER NOT NULL DEFAULT 0,
"meta" TEXT NOT NULL DEFAULT '{}',
"content" BLOB DEFAULT '',
"aux" TEXT NOT NULL DEFAULT '{}',
PRIMARY KEY("url")
) WITHOUT ROWID
| Column | Type (SQLite) | Default | Description | Example |
|---|---|---|---|---|
| url | text, PK, Unique, Not null | No default value | Resource URL. All resources are indexed by its URL. |
|
| category | text, Not null | '' |
Resource category, for management purpose, ignored by Bearweb CMS framework. |
|
| template | JSON array in text, Not null | '["object", "blob"]' - Object (file) template, direct blob output. |
Template used to process this resource. Bearweb CMS will invoke template[0] (template), then template[0] will invoke template[1] (sub-template). |
|
| owner | text, Not null | '' (empty string) - System-owned resource. |
Owner's user ID of this resource. Only the owner and admin (group 0) can modify this resource. |
|
| create | int, Not null | 0 (Bearweb_Site::TIME_NULL) - No actual time. |
Create timestamp. |
|
| modify | int, Not null | 0 (Bearweb_Site::TIME_NULL) - No actual time. |
Modify timestamp. |
|
| meta | JSON object in text, Not null | '{}' - Empty metadate. |
Resource metadata. Optional data used by Bearweb CMS framework and the template. |
|
| content | text, or null | '' - Empty content. |
Resource content, the content should be directly output to reduce server process load (for a webpage, that means use HTML code instead of markdown). If null, Bearweb will try read a file with the same pathname as the url under the resource directory. This is used to offload large files from the database. See my blog for details. |
|
| aux | JSON object in text, Not null | '{}' - Empty aux data. |
Resource auxiliary data. Optional data used by the template. |
|
meta and aux store optional data. It doesn't make sense to create seperate database field for each of those optional data (which will make the table extremely wide and unmanageable, and create a lot of unused field); therefore, they are aggregated into JSON objects.
| Key | Type | Description | Used by template with example |
|---|---|---|---|
| title | string | Title |
All
|
| keywords | string, comma-seperated words | Keywords |
All
|
| description | string | Description |
All
|
| r301 | string | Permanently redirect to target URL. |
Core
|
| r302 | string | Temporarily redirect to target URL. |
Core
|
| robots | string | Tells crawlers to bot not index and/or follow. |
CoreAll
This is a request, but some inresponsible crawlers won't respect it. Do not use anything other than |
| access | array of number and string | If specified, Bearweb will enable access control on this resource. Only user id or user group in the array can read this resource; others will received a HTTP 403 error. Note, Bearweb CMS uses string for user ID and int for user group. |
Core
|
| img | string | URL to a poster image. |
All
|
| hd | string | URL to a HD image. |
Image
|
| ratio | number | Provide the aspect ratio (width / height) of this image. |
Image
|
| task | API task | Specify task to execute. |
All
|
All those data are optional. Bearweb will use default value or skip it if a specific data is not given.
| Key | Type | Description | Used by template with example |
|---|---|---|---|
| lang-* | string | Alternative language for multilingual resources. |
All
|
| mime | string | Object MIME type |
All
|
All those data are optional. Bearweb will use default value or skip it if a specific data is not given.
Bearweb Session
A transaction represents a HTTP request from the client-side to the server-side and the corresponding response from the server-side to the client-side.
For Bearweb CMS, a request can be divided into 3 parts:
- URL: A request must include a URL (this is also mandatory for HTTP, the first line of a HTTP request is always
METHOD URL VERSION, for example:GET /bearweb-resource-optimization/zh HTTP/1.1). It tells the server what resource (or API) the client wants. - Session token: HTTP is stateless, which means that the server cannot know who sent the request. To differentiate clients, the server will issue a session token (a cookie) in the first transaction. The client should always include this token in all subsequent transactions to identify the client’s session. If the client disables the cookie, or omits the session token cookie, the server will issue a new one.
- Data: The request can optionally include some data. This is used in APIs.
For Bearweb CMS, a session is a series of transactions from the same client.
- Session token in cookie is used to identify the transaction’s corresponding session. As discussed above.
Bearweb CMS uses two databases to record transactions and sessions.
CREATE TABLE "Transaction" (
"id" TEXT NOT NULL UNIQUE,
"create" INTEGER NOT NULL DEFAULT -1,
"ip" TEXT NOT NULL DEFAULT '',
"url" TEXT NOT NULL DEFAULT '',
"status" INTEGER NOT NULL DEFAULT 0,
"time" INTEGER NOT NULL DEFAULT -1,
"memory" INTEGER NOT NULL DEFAULT -1,
"session" TEXT NOT NULL DEFAULT '',
"log" TEXT NOT NULL DEFAULT '',
PRIMARY KEY("id")
) WITHOUT ROWID,STRICT
CREATE TABLE "Session" (
"id" TEXT NOT NULL UNIQUE,
"create" INTEGER NOT NULL DEFAULT -1,
"lastuse" INTEGER NOT NULL DEFAULT -1,
"user" TEXT NOT NULL DEFAULT '',
"key" TEXT NOT NULL DEFAULT '',
PRIMARY KEY("id")
) WITHOUT ROWID,STRICT
| Column | Type (SQLite) | Default | Description | Example |
|---|---|---|---|---|
| id | text, PK, Unique, Not null | No default value | Transaction ID. |
|
| create | int, Not null | -1 - Invalid value, this shouldn't happen. |
Create timestamp. |
|
| ip | text, Not null | '' |
Client-side IP address and port, it can be a IPv4 address or a IPv6 address. |
|
| url | text, Not null | '' |
Request resource or API URL. |
|
| status | int, Not null | 0 - Invalid value, unhandled exception occured. |
Response HTTP code. |
|
| time | int, Not null | -1 - Invalid value, unhandled exception occured. |
Time in microseconds used to process this trqansaction. This includes the time server waiting for data transfer (this can be a very large number, like tens of seconds, if sends a file). This value comes from (microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) * 1e6. |
|
| memory | int, Not null | -1 - Invalid value, unhandled exception occured. |
Memory usage in kiB used to process this trqansaction. This value comes from memory_get_peak_usage(false) / 1024. |
|
| session | text, Not null | '' |
The ID of the corresponding session. |
|
| log | text, or null | '' - Empty log. |
Debug use. Use $BW->log('Hello world'); to append a new line of log. |
|
| Column | Type (SQLite) | Default | Description | Example |
|---|---|---|---|---|
| id | text, PK, Unique, Not null | No default value | Session ID. |
|
| create | int, Not null | -1 - Invalid value, this shouldn't happen. |
Create timestamp. |
|
| lastuse | int, Not null | -1 - Invalid value, this shouldn't happen. |
Last use timestamp. |
|
| user | text, Not null | '' - No user binded (guest). |
Session binded user ID. |
|
| key | text, Not null | '' - Invalid. |
A key used to verify the session. |
|
Session and Transaction Database Maintenance
To view the transaction and its corresponding session, use a view:
CREATE VIEW `Transaction_View` AS
select all
t.`id`,
datetime(t.`create`, 'unixepoch') as `create`,
t.`ip`,
t.`url`,
t.`status`,
t.`time` as `time (us)`,
t.`memory` as `memory (kB)`,
t.`log`,
t.`session`,
datetime(s.`create`, 'unixepoch') as `firstuse`,
datetime(s.`lastuse`, 'unixepoch') as `lastuse`,
s.`user`,
s.`key`
from `Transaction` t join `Session` s where t.`session` = s.`id`
order by `create` desc
The transaction and session database grows over time. You may want to occasionally clean expired sessions and old transactions:
delete from `Session` where strftime('%s', 'now') - `lastuse` > 30 * 24 * 3600;
delete from `Transaction` where strftime('%s', 'now') - `create` > 30 * 24 * 3600;
vacuum;
Bearweb User
Bearweb saves user in a database.
CREATE TABLE "User" (
"id" TEXT NOT NULL UNIQUE,
"name" TEXT NOT NULL DEFAULT '',
"salt" TEXT NOT NULL DEFAULT ':',
"password" TEXT NOT NULL DEFAULT ':',
"registertime" INTEGER NOT NULL DEFAULT 0,
"lastactive" INTEGER NOT NULL DEFAULT 0,
"group" TEXT NOT NULL DEFAULT '{}',
"data" TEXT NOT NULL DEFAULT '{}',
PRIMARY KEY("id")
) WITHOUT ROWID,STRICT
| Column | Type (SQLite) | Default | Description | Example |
|---|---|---|---|---|
| id | text, PK, Unique, Not null | No default value | User ID. |
|
| name | text, Not null | '' |
User's nickname. This name can be duplicated. |
|
| salt | text, Not null | ':' - Invalid value. |
Salt for password. |
|
| password | text, Not null | ':' - Invalid value. |
Password after salt 32-byte (256-bit) cipher. |
|
| registertime | int, Not null | 0 |
Create timestamp. |
|
| lastactive | int, Not null | 0 |
Last active timestamp. |
|
| group | JSON array in text, Not null | '[]' - No group. |
User's group. Use int for group ID. Group 0 is for "admin". |
|
| data | JSON object in text, Not null | '{}' - Empty data. |
Other data of this user. |
|