@newcms/database 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -0
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @newcms/database
|
|
2
|
+
|
|
3
|
+
Database schema, Redis object cache, and repositories for [NewCMS](https://github.com/durvs/newcms) — a modern, TypeScript-first content management system.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @newcms/database
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Requirements:** PostgreSQL 17+ and Redis 7+.
|
|
12
|
+
|
|
13
|
+
## Database Schema
|
|
14
|
+
|
|
15
|
+
Type-safe [Drizzle ORM](https://orm.drizzle.team/) schema with 14 tables covering the full CMS data model:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
posts, postmeta,
|
|
20
|
+
users, usermeta,
|
|
21
|
+
comments, commentmeta,
|
|
22
|
+
terms, termTaxonomy, termRelationships, termmeta,
|
|
23
|
+
options,
|
|
24
|
+
links,
|
|
25
|
+
sessions,
|
|
26
|
+
scheduledEvents,
|
|
27
|
+
} from '@newcms/database/schema';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Tables
|
|
31
|
+
|
|
32
|
+
| Table | Purpose |
|
|
33
|
+
|-------|---------|
|
|
34
|
+
| `posts` | All content types (posts, pages, attachments, custom types) |
|
|
35
|
+
| `postmeta` | Post metadata (EAV with JSONB support) |
|
|
36
|
+
| `users` | User accounts |
|
|
37
|
+
| `usermeta` | User metadata |
|
|
38
|
+
| `comments` | Threaded comments |
|
|
39
|
+
| `commentmeta` | Comment metadata |
|
|
40
|
+
| `terms` | Taxonomy terms |
|
|
41
|
+
| `term_taxonomy` | Term-taxonomy relationships with hierarchy |
|
|
42
|
+
| `term_relationships` | Object-term assignments |
|
|
43
|
+
| `termmeta` | Term metadata |
|
|
44
|
+
| `options` | Site settings (with autoload and JSONB) |
|
|
45
|
+
| `links` | Blogroll / link manager |
|
|
46
|
+
| `sessions` | User sessions (Redis-backed with DB fallback) |
|
|
47
|
+
| `scheduled_events` | Cron-like scheduled tasks (BullMQ-backed) |
|
|
48
|
+
|
|
49
|
+
### Key Design Decisions
|
|
50
|
+
|
|
51
|
+
- **JSONB columns** on all meta tables — queryable structured data instead of opaque serialized text
|
|
52
|
+
- **GIN indexes** on JSONB columns for efficient structured queries
|
|
53
|
+
- **Composite indexes** on frequently queried combinations (e.g., `post_type + status + date`)
|
|
54
|
+
- **Dedicated tables** for sessions and scheduled events (instead of serialized blobs in options/usermeta)
|
|
55
|
+
|
|
56
|
+
## Connection
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { createConnection } from '@newcms/database';
|
|
60
|
+
|
|
61
|
+
// From environment variables (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD)
|
|
62
|
+
const { db, client } = createConnection();
|
|
63
|
+
|
|
64
|
+
// Or with explicit config
|
|
65
|
+
const { db, client } = createConnection({
|
|
66
|
+
host: 'localhost',
|
|
67
|
+
port: 5432,
|
|
68
|
+
database: 'newcms',
|
|
69
|
+
user: 'newcms',
|
|
70
|
+
password: 'your-secure-password',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Use Drizzle query builder
|
|
74
|
+
const allPosts = await db.select().from(posts);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Note:** `DB_PASSWORD` is required — there is no default. Set it in your environment or `.env` file.
|
|
78
|
+
|
|
79
|
+
## Object Cache
|
|
80
|
+
|
|
81
|
+
Redis-backed object cache with group isolation, TTL, and multisite support:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { ObjectCache } from '@newcms/database';
|
|
85
|
+
|
|
86
|
+
const cache = new ObjectCache({
|
|
87
|
+
host: 'localhost',
|
|
88
|
+
port: 6379,
|
|
89
|
+
});
|
|
90
|
+
await cache.connect();
|
|
91
|
+
|
|
92
|
+
// Basic get/set
|
|
93
|
+
await cache.set('my_key', { hello: 'world' }, 'my_group');
|
|
94
|
+
const value = await cache.get('my_key', 'my_group');
|
|
95
|
+
|
|
96
|
+
// TTL (in seconds)
|
|
97
|
+
await cache.set('temp', 'data', 'default', 300); // expires in 5 min
|
|
98
|
+
|
|
99
|
+
// Batch operations (uses Redis pipeline)
|
|
100
|
+
await cache.setMultiple({ a: 1, b: 2, c: 3 }, 'counters');
|
|
101
|
+
const results = await cache.getMultiple(['a', 'b', 'c'], 'counters');
|
|
102
|
+
|
|
103
|
+
// Increment/decrement
|
|
104
|
+
await cache.set('views', 0);
|
|
105
|
+
await cache.incr('views'); // => 1
|
|
106
|
+
|
|
107
|
+
// Group-level TTL
|
|
108
|
+
cache.setGroupTtl('transients', 3600); // 1 hour for all transients
|
|
109
|
+
|
|
110
|
+
// Multisite: global groups shared across sites
|
|
111
|
+
cache.addGlobalGroups(['users', 'site-options']);
|
|
112
|
+
cache.setSiteId(2); // switch site context
|
|
113
|
+
|
|
114
|
+
// Flush
|
|
115
|
+
await cache.flushGroup('posts'); // flush one group
|
|
116
|
+
await cache.flushAll(); // flush everything
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Options Repository
|
|
120
|
+
|
|
121
|
+
CRUD for site options with integrated Redis cache:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { createConnection, ObjectCache, OptionsRepository } from '@newcms/database';
|
|
125
|
+
|
|
126
|
+
const { db } = createConnection();
|
|
127
|
+
const cache = new ObjectCache({ host: 'localhost', port: 6379 });
|
|
128
|
+
await cache.connect();
|
|
129
|
+
|
|
130
|
+
const options = new OptionsRepository(db, cache);
|
|
131
|
+
|
|
132
|
+
// Read (with cache — hits DB only on first read)
|
|
133
|
+
const siteName = await options.getOption('blogname');
|
|
134
|
+
const perPage = await options.getOption('posts_per_page', 10); // with default
|
|
135
|
+
|
|
136
|
+
// Write (invalidates cache automatically)
|
|
137
|
+
await options.updateOption('blogname', 'My New Site');
|
|
138
|
+
await options.addOption('custom_setting', { enabled: true, threshold: 50 });
|
|
139
|
+
|
|
140
|
+
// Delete
|
|
141
|
+
await options.deleteOption('old_setting');
|
|
142
|
+
|
|
143
|
+
// Bootstrap: pre-load all autoloaded options into cache
|
|
144
|
+
const allOptions = await options.loadAutoloadedOptions();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Cache Behavior
|
|
148
|
+
|
|
149
|
+
- **Autoloaded options** are bulk-loaded into Redis on bootstrap
|
|
150
|
+
- **Cache-aside pattern** — reads check cache first, fall back to DB
|
|
151
|
+
- **Not-found caching** — missing keys are cached to prevent repeated DB misses
|
|
152
|
+
- **Granular invalidation** — writes invalidate only the affected key
|
|
153
|
+
- **JSONB** — complex values (objects, arrays) stored in queryable JSONB column
|
|
154
|
+
|
|
155
|
+
## Environment Variables
|
|
156
|
+
|
|
157
|
+
| Variable | Required | Default | Description |
|
|
158
|
+
|----------|----------|---------|-------------|
|
|
159
|
+
| `DB_HOST` | No | `localhost` | PostgreSQL host |
|
|
160
|
+
| `DB_PORT` | No | `5432` | PostgreSQL port |
|
|
161
|
+
| `DB_NAME` | No | `newcms` | Database name |
|
|
162
|
+
| `DB_USER` | No | `newcms` | Database user |
|
|
163
|
+
| `DB_PASSWORD` | **Yes** | — | Database password |
|
|
164
|
+
| `DB_MAX_CONNECTIONS` | No | `10` | Connection pool size |
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
GPL-2.0-or-later
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newcms/database",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Database schema, object cache, and repositories for NewCMS",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
|
-
"dist"
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md"
|
|
33
34
|
],
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"drizzle-orm": "^0.44.2",
|