@jant/core 0.3.30 → 0.3.32
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/bin/commands/export.js +112 -0
- package/bin/commands/reset-password.js +40 -0
- package/bin/jant.js +49 -0
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/bin/reset-password.js +0 -22
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
|
|
6
|
+
function sqlValue(v) {
|
|
7
|
+
if (v === null) return "NULL";
|
|
8
|
+
if (typeof v === "number") return String(v);
|
|
9
|
+
return "'" + String(v).replaceAll("'", "''") + "'";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function query(sql, flag) {
|
|
13
|
+
let stdout;
|
|
14
|
+
try {
|
|
15
|
+
stdout = execSync(
|
|
16
|
+
`npx wrangler d1 execute DB ${flag} --command "${sql}" --json`,
|
|
17
|
+
{ encoding: "utf-8" },
|
|
18
|
+
);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
const output = err.stdout || err.stderr || "";
|
|
21
|
+
try {
|
|
22
|
+
const errJson = JSON.parse(output.trim());
|
|
23
|
+
if (errJson.error?.text) {
|
|
24
|
+
console.error(`Wrangler error: ${errJson.error.text}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// Not JSON, fall through
|
|
29
|
+
}
|
|
30
|
+
console.error(`Failed to query database: ${output || err.message}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const parsed = JSON.parse(stdout);
|
|
34
|
+
if (parsed.error?.text) {
|
|
35
|
+
console.error(`Wrangler error: ${parsed.error.text}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
return parsed[0]?.results || [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function dumpTable(name, flag, customQuery) {
|
|
42
|
+
const rows = query(customQuery || `SELECT * FROM ${name}`, flag);
|
|
43
|
+
return rows
|
|
44
|
+
.map(
|
|
45
|
+
(row) =>
|
|
46
|
+
`INSERT INTO ${name} VALUES(${Object.values(row).map(sqlValue).join(",")});`,
|
|
47
|
+
)
|
|
48
|
+
.join("\n");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function run(argv) {
|
|
52
|
+
const { values } = parseArgs({
|
|
53
|
+
args: argv,
|
|
54
|
+
options: {
|
|
55
|
+
remote: { type: "boolean", default: false },
|
|
56
|
+
output: { type: "string", short: "o", default: "jant-export.sql" },
|
|
57
|
+
help: { type: "boolean", short: "h" },
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (values.help) {
|
|
62
|
+
console.log("Usage: jant export [--remote] [--output <file>]");
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log("Export D1 database to a SQL file.");
|
|
65
|
+
console.log("");
|
|
66
|
+
console.log("Options:");
|
|
67
|
+
console.log(
|
|
68
|
+
" --remote Export from remote D1 database (default: local)",
|
|
69
|
+
);
|
|
70
|
+
console.log(
|
|
71
|
+
" --output, -o Output file path (default: jant-export.sql)",
|
|
72
|
+
);
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const flag = values.remote ? "--remote" : "--local";
|
|
77
|
+
const output = values.output;
|
|
78
|
+
|
|
79
|
+
// Order matters for foreign key constraints
|
|
80
|
+
const tables = [
|
|
81
|
+
// Auth
|
|
82
|
+
["settings"],
|
|
83
|
+
["user"],
|
|
84
|
+
["account"],
|
|
85
|
+
// Content
|
|
86
|
+
["pages"],
|
|
87
|
+
["collections"],
|
|
88
|
+
["posts", "SELECT * FROM posts WHERE deleted_at IS NULL"],
|
|
89
|
+
["post_collections"],
|
|
90
|
+
["collection_dividers"],
|
|
91
|
+
["nav_items"],
|
|
92
|
+
["media"],
|
|
93
|
+
["redirects"],
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const timestamp = new Date().toISOString();
|
|
97
|
+
const source = values.remote ? "remote" : "local";
|
|
98
|
+
let sql = `-- Jant database export\n`;
|
|
99
|
+
sql += `-- Exported: ${timestamp}\n`;
|
|
100
|
+
sql += `-- Source: ${source}\n\n`;
|
|
101
|
+
|
|
102
|
+
for (const [name, customQuery] of tables) {
|
|
103
|
+
const data = dumpTable(name, flag, customQuery);
|
|
104
|
+
if (data) {
|
|
105
|
+
sql += `-- ${name}\n${data}\n\n`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const outPath = resolve(process.cwd(), output);
|
|
110
|
+
writeFileSync(outPath, sql);
|
|
111
|
+
console.log(`Exported ${source} database to ${output}`);
|
|
112
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
|
|
5
|
+
export async function run(argv) {
|
|
6
|
+
const { values } = parseArgs({
|
|
7
|
+
args: argv,
|
|
8
|
+
options: {
|
|
9
|
+
remote: { type: "boolean", default: false },
|
|
10
|
+
help: { type: "boolean", short: "h" },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (values.help) {
|
|
15
|
+
console.log("Usage: jant reset-password [--remote]");
|
|
16
|
+
console.log("");
|
|
17
|
+
console.log("Generate a password reset token (expires in 15 minutes).");
|
|
18
|
+
console.log("");
|
|
19
|
+
console.log("Options:");
|
|
20
|
+
console.log(" --remote Run against remote D1 database (default: local)");
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const flag = values.remote ? "--remote" : "--local";
|
|
25
|
+
|
|
26
|
+
const token = randomBytes(32).toString("hex");
|
|
27
|
+
const expiry = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
28
|
+
const value = `${token}:${expiry}`;
|
|
29
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
30
|
+
|
|
31
|
+
const sql = `INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES ('PASSWORD_RESET_TOKEN', '${value}', ${timestamp})`;
|
|
32
|
+
|
|
33
|
+
execSync(`npx wrangler d1 execute DB ${flag} --command "${sql}"`, {
|
|
34
|
+
stdio: "inherit",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log("");
|
|
38
|
+
console.log("Password reset token generated (expires in 15 minutes).");
|
|
39
|
+
console.log(`Visit: /reset?token=${token}`);
|
|
40
|
+
}
|
package/bin/jant.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readdir } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, join, basename } from "node:path";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const commandsDir = join(__dirname, "commands");
|
|
9
|
+
|
|
10
|
+
async function listCommands() {
|
|
11
|
+
const files = await readdir(commandsDir);
|
|
12
|
+
return files
|
|
13
|
+
.filter((f) => f.endsWith(".js"))
|
|
14
|
+
.map((f) => basename(f, ".js"));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function showHelp() {
|
|
18
|
+
const commands = await listCommands();
|
|
19
|
+
console.log("Usage: jant <command> [options]");
|
|
20
|
+
console.log("");
|
|
21
|
+
console.log("Commands:");
|
|
22
|
+
for (const cmd of commands) {
|
|
23
|
+
console.log(` ${cmd}`);
|
|
24
|
+
}
|
|
25
|
+
console.log("");
|
|
26
|
+
console.log("Run 'jant <command> --help' for command-specific help.");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// First non-flag argument is the command name
|
|
30
|
+
const argv = process.argv.slice(2);
|
|
31
|
+
const command = argv.find((arg) => !arg.startsWith("-"));
|
|
32
|
+
|
|
33
|
+
if (!command) {
|
|
34
|
+
await showHelp();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const commands = await listCommands();
|
|
39
|
+
if (!commands.includes(command)) {
|
|
40
|
+
console.error(`Unknown command: ${command}`);
|
|
41
|
+
console.error("");
|
|
42
|
+
await showHelp();
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Pass everything after the command name to the subcommand
|
|
47
|
+
const commandIndex = argv.indexOf(command);
|
|
48
|
+
const mod = await import(join(commandsDir, `${command}.js`));
|
|
49
|
+
await mod.run(argv.slice(commandIndex + 1));
|
package/dist/index.js
CHANGED
|
@@ -4692,7 +4692,7 @@ const I18nProvider = ({ c, children })=>{
|
|
|
4692
4692
|
}
|
|
4693
4693
|
|
|
4694
4694
|
const IS_VITE_DEV = typeof __JANT_DEV__ !== "undefined" && __JANT_DEV__ === true;
|
|
4695
|
-
const CORE_VERSION = "0.3.
|
|
4695
|
+
const CORE_VERSION = "0.3.32";
|
|
4696
4696
|
|
|
4697
4697
|
const BaseLayout = ({ title, description, lang, c, toast, faviconUrl, faviconVersion, noindex, isAuthenticated = false, children })=>{
|
|
4698
4698
|
// Read lang from Hono context if available, otherwise use prop or default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jant/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.32",
|
|
4
4
|
"description": "A modern, open-source microblogging platform built on Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"./i18n": "./src/i18n/index.ts"
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|
|
14
|
-
"jant
|
|
14
|
+
"jant": "bin/jant.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bin",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"i18n:build": "pnpm i18n:extract && pnpm i18n:compile",
|
|
100
100
|
"dev": "pnpm db:migrate:local && vite dev",
|
|
101
101
|
"dev:debug": "pnpm db:migrate:local && vite dev --port 19019",
|
|
102
|
-
"db:migrate:local": "
|
|
102
|
+
"db:migrate:local": "echo y | wrangler d1 migrations apply DB --local",
|
|
103
103
|
"db:migrate:remote": "wrangler d1 migrations apply DB --remote",
|
|
104
104
|
"test": "vitest run",
|
|
105
105
|
"test:watch": "vitest",
|
package/bin/reset-password.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { randomBytes } from "crypto";
|
|
4
|
-
import { execSync } from "child_process";
|
|
5
|
-
|
|
6
|
-
const isRemote = process.argv.includes("--remote");
|
|
7
|
-
const flag = isRemote ? "--remote" : "--local";
|
|
8
|
-
|
|
9
|
-
const token = randomBytes(32).toString("hex");
|
|
10
|
-
const expiry = Math.floor(Date.now() / 1000) + 15 * 60; // 15 minutes
|
|
11
|
-
const value = `${token}:${expiry}`;
|
|
12
|
-
const timestamp = Math.floor(Date.now() / 1000);
|
|
13
|
-
|
|
14
|
-
const sql = `INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES ('PASSWORD_RESET_TOKEN', '${value}', ${timestamp})`;
|
|
15
|
-
|
|
16
|
-
execSync(`npx wrangler d1 execute DB ${flag} --command "${sql}"`, {
|
|
17
|
-
stdio: "inherit",
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
console.log("");
|
|
21
|
-
console.log("Password reset token generated (expires in 15 minutes).");
|
|
22
|
-
console.log(`Visit: /reset?token=${token}`);
|