modern-cms 1.1.4 → 1.1.6
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
CHANGED
|
@@ -84,7 +84,7 @@ npm run dev
|
|
|
84
84
|
|
|
85
85
|
Then open the web app at the URL you chose (default `http://localhost:3000`) and log in at `/admin` with the admin email/password from the wizard.
|
|
86
86
|
|
|
87
|
-
> **Prefer yarn for the scaffolded project too?** `yarn install` works fine, and `dev`/`db:migrate`/`db:seed` all run correctly
|
|
87
|
+
> **Prefer yarn for the scaffolded project too?** `yarn install` works fine, and `dev`/`build`/`db:migrate`/`db:seed` all run correctly. A stray duplicate `@types/express` (pulled in by `@types/multer`'s unpinned `"*"` dependency range) used to break `tsc`/`npm run build` under both npm and yarn — the generated `package.json` now pins it via `overrides` (npm) and `resolutions` (yarn), so a fresh install with either tool is clean.
|
|
88
88
|
>
|
|
89
89
|
> **Logged in with the wrong password?** The admin user is only created the *first* time `db:seed` runs — editing `.env` afterward doesn't retroactively change it. Run `npm run db:reset-admin && npm run db:seed` to delete and recreate it from the current `.env` values.
|
|
90
90
|
|
package/bin/modern-cms.js
CHANGED
|
@@ -40,10 +40,10 @@ function onCancel() {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
async function main() {
|
|
43
|
-
console.log("\
|
|
43
|
+
console.log("\n🚀 modern-cms — self-hosted drag-and-drop website CMS\n");
|
|
44
44
|
|
|
45
45
|
const { targetDir } = await prompts(
|
|
46
|
-
{ type: "text", name: "targetDir", message: "Where should the project be created?", initial: "./modern-cms-app" },
|
|
46
|
+
{ type: "text", name: "targetDir", message: "📁 Where should the project be created?", initial: "./modern-cms-app" },
|
|
47
47
|
{ onCancel }
|
|
48
48
|
);
|
|
49
49
|
const dest = path.resolve(process.cwd(), targetDir);
|
|
@@ -52,19 +52,20 @@ async function main() {
|
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
console.log(`\
|
|
55
|
+
console.log(`\n📦 Copying project files into ${dest} ...`);
|
|
56
56
|
copyDir(TEMPLATE_DIR, dest);
|
|
57
57
|
|
|
58
58
|
// --- Database ---
|
|
59
|
+
console.log("\n🗄️ Database");
|
|
59
60
|
const db = await prompts(
|
|
60
61
|
[
|
|
61
62
|
{
|
|
62
63
|
type: "select",
|
|
63
64
|
name: "engine",
|
|
64
|
-
message: "Database",
|
|
65
|
+
message: "Database engine",
|
|
65
66
|
choices: [
|
|
66
|
-
{ title: "PostgreSQL", value: "postgresql" },
|
|
67
|
-
{ title: "MySQL", value: "mysql" },
|
|
67
|
+
{ title: "🐘 PostgreSQL", value: "postgresql" },
|
|
68
|
+
{ title: "🐬 MySQL", value: "mysql" },
|
|
68
69
|
],
|
|
69
70
|
},
|
|
70
71
|
{ type: "text", name: "host", message: "Database host", initial: "localhost" },
|
|
@@ -92,6 +93,7 @@ async function main() {
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
// --- Ports ---
|
|
96
|
+
console.log("\n🔌 Ports");
|
|
95
97
|
const ports = await prompts(
|
|
96
98
|
[
|
|
97
99
|
{ type: "text", name: "webPort", message: "Web app port", initial: "3000" },
|
|
@@ -101,15 +103,16 @@ async function main() {
|
|
|
101
103
|
);
|
|
102
104
|
|
|
103
105
|
// --- Storage ---
|
|
106
|
+
console.log("\n☁️ File storage");
|
|
104
107
|
const { provider } = await prompts(
|
|
105
108
|
{
|
|
106
109
|
type: "select",
|
|
107
110
|
name: "provider",
|
|
108
|
-
message: "
|
|
111
|
+
message: "Storage provider",
|
|
109
112
|
choices: [
|
|
110
|
-
{ title: "AWS S3 (or any S3-compatible: MinIO, R2, Spaces...)", value: "s3" },
|
|
111
|
-
{ title: "Azure Blob Storage", value: "azure" },
|
|
112
|
-
{ title: "Cloudinary", value: "cloudinary" },
|
|
113
|
+
{ title: "🪣 AWS S3 (or any S3-compatible: MinIO, R2, Spaces...)", value: "s3" },
|
|
114
|
+
{ title: "🔷 Azure Blob Storage", value: "azure" },
|
|
115
|
+
{ title: "🌤️ Cloudinary", value: "cloudinary" },
|
|
113
116
|
],
|
|
114
117
|
},
|
|
115
118
|
{ onCancel }
|
|
@@ -160,6 +163,7 @@ async function main() {
|
|
|
160
163
|
|
|
161
164
|
// --- Site / admin (necessary extras beyond what was asked for: the app needs to
|
|
162
165
|
// know its own public URLs, and the CMS needs an initial admin login) ---
|
|
166
|
+
console.log("\n👤 Site & admin account");
|
|
163
167
|
const site = await prompts(
|
|
164
168
|
[
|
|
165
169
|
{ type: "text", name: "siteUrl", message: "Public site URL", initial: `http://localhost:${ports.webPort}` },
|
|
@@ -190,11 +194,11 @@ async function main() {
|
|
|
190
194
|
fs.writeFileSync(path.join(dest, ".env"), rootEnv);
|
|
191
195
|
|
|
192
196
|
if (!site.adminPassword) {
|
|
193
|
-
console.log(`\
|
|
197
|
+
console.log(`\n🔑 Generated admin password: ${adminPassword}\n (save this — it's only shown once)`);
|
|
194
198
|
}
|
|
195
199
|
|
|
196
|
-
console.log("\
|
|
197
|
-
console.log("Next steps:");
|
|
200
|
+
console.log("\n✅ Done! Generated .env files for apps/api and apps/web.\n");
|
|
201
|
+
console.log("👉 Next steps:");
|
|
198
202
|
console.log(` cd ${targetDir}`);
|
|
199
203
|
console.log(" npm install");
|
|
200
204
|
if (usesDbPush(db.engine)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modern-cms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Self-hosted, drag-and-drop website CMS (Next.js + Express + Prisma). Interactive installer scaffolds a full project with your choice of PostgreSQL/MySQL and AWS S3/Azure Blob/Cloudinary storage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/template/README.md
CHANGED
|
@@ -60,7 +60,7 @@ npm run db:seed
|
|
|
60
60
|
npm run dev
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
> **Using yarn to install the scaffolded project itself?** `yarn install` works fine
|
|
63
|
+
> **Using yarn to install the scaffolded project itself?** `yarn install` works fine — `npm run dev` / `build` / `db:migrate` / `db:seed` all run correctly under a yarn-installed `node_modules`. (Earlier versions of this README blamed a duplicate `@types/express` type error on "yarn's hoisting" — that diagnosis was wrong. The real cause is that `@types/multer` depends on `@types/express@"*"`, which both npm and yarn will happily resolve to a stray v5 copy alongside the project's pinned v4, breaking `tsc`/`npm run build` under either package manager. The root `package.json`'s `overrides` + `resolutions` fields pin that nested dependency to v4 for both, so this no longer happens with a fresh install.)
|
|
64
64
|
|
|
65
65
|
See [`packages/modern-cms/README.md`](packages/modern-cms/README.md) for the full prompt-by-prompt reference and configuration details.
|
|
66
66
|
|
|
@@ -2,6 +2,7 @@ import express from "express";
|
|
|
2
2
|
import cors from "cors";
|
|
3
3
|
import cookieParser from "cookie-parser";
|
|
4
4
|
import { env } from "./env.js";
|
|
5
|
+
import { initStorage } from "./lib/storage/index.js";
|
|
5
6
|
import { securityHeaders } from "./middleware/securityHeaders.js";
|
|
6
7
|
import { authRouter } from "./routes/auth.routes.js";
|
|
7
8
|
import { pagesRouter } from "./routes/pages.routes.js";
|
|
@@ -61,6 +62,8 @@ app.use((err: any, _req: express.Request, res: express.Response, _next: express.
|
|
|
61
62
|
res.status(err.status ?? 500).json({ error: err.message ?? "Internal server error" });
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
initStorage();
|
|
66
|
+
|
|
64
67
|
app.listen(env.port, () => {
|
|
65
68
|
console.log(`pg-cms API listening on http://localhost:${env.port}`);
|
|
66
69
|
});
|
|
@@ -6,7 +6,21 @@ import { createCloudinaryStorage } from "./cloudinary.js";
|
|
|
6
6
|
|
|
7
7
|
let cached: StorageProvider | null = null;
|
|
8
8
|
|
|
9
|
+
// Printed once, at first upload/delete — tells you exactly where files are actually
|
|
10
|
+
// going (bucket/container/cloud + folder), since that's the #1 source of "my upload
|
|
11
|
+
// succeeded but I can't find the file" confusion when double-checking the wrong place.
|
|
12
|
+
function logActiveProvider() {
|
|
13
|
+
if (env.storageProvider === "azure") {
|
|
14
|
+
console.log(`[storage] Using Azure Blob Storage — container "${env.azure!.container}"`);
|
|
15
|
+
} else if (env.storageProvider === "cloudinary") {
|
|
16
|
+
console.log(`[storage] Using Cloudinary — cloud "${env.cloudinary!.cloudName}", folder "${env.cloudinary!.folder}"`);
|
|
17
|
+
} else {
|
|
18
|
+
console.log(`[storage] Using S3 — bucket "${env.s3!.bucket}" at ${env.s3!.endpoint}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
function build(): StorageProvider {
|
|
23
|
+
logActiveProvider();
|
|
10
24
|
if (env.storageProvider === "azure") return createAzureStorage();
|
|
11
25
|
if (env.storageProvider === "cloudinary") return createCloudinaryStorage();
|
|
12
26
|
return createS3Storage();
|
|
@@ -16,6 +30,12 @@ function getStorage(): StorageProvider {
|
|
|
16
30
|
return (cached ??= build());
|
|
17
31
|
}
|
|
18
32
|
|
|
33
|
+
/** Called once at server boot so the active provider/destination is visible in the
|
|
34
|
+
* logs immediately, rather than only appearing after the first upload. */
|
|
35
|
+
export function initStorage(): void {
|
|
36
|
+
getStorage();
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
export async function uploadBuffer(key: string, buffer: Buffer, contentType: string): Promise<string> {
|
|
20
40
|
return getStorage().upload(key, buffer, contentType);
|
|
21
41
|
}
|
package/template/package.json
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
"packages/*",
|
|
10
10
|
"apps/*"
|
|
11
11
|
],
|
|
12
|
+
"overrides": {
|
|
13
|
+
"@types/multer": {
|
|
14
|
+
"@types/express": "^4.17.21"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"resolutions": {
|
|
18
|
+
"**/@types/multer/**/@types/express": "^4.17.21"
|
|
19
|
+
},
|
|
12
20
|
"scripts": {
|
|
13
21
|
"dev": "concurrently -n api,web -c blue,green \"npm run dev -w apps/api\" \"npm run dev -w apps/web\"",
|
|
14
22
|
"dev:api": "npm run dev -w apps/api",
|