@stacksjs/actions 0.70.87 → 0.70.90
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/dist/action.js +78 -2
- package/dist/actions.js +2 -2
- package/dist/add.js +20 -2
- package/dist/ai/request-model-access.js +10 -2
- package/dist/auth/client.js +44 -3
- package/dist/auth/prune.js +35 -2
- package/dist/auth/setup.js +152 -36
- package/dist/auth/token.js +10 -2
- package/dist/blog-admin.d.ts +55 -0
- package/dist/blog-admin.js +156 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.js +306 -56
- package/dist/build/cli.js +6 -2
- package/dist/build/component-libs.js +7 -2
- package/dist/build/core.js +55 -2
- package/dist/build/desktop.js +5 -2
- package/dist/build/docs.js +5 -2
- package/dist/build/frontend-static.js +123 -8
- package/dist/build/server.js +6 -2
- package/dist/build/stacks.js +8 -2
- package/dist/build/views.js +11 -2
- package/dist/build.js +64 -2
- package/dist/bump.js +162 -7
- package/dist/cdn/invalidate-cache.js +0 -1
- package/dist/changelog.js +27 -3
- package/dist/check/ports.js +16 -2
- package/dist/clean.js +4 -2
- package/dist/commit.js +12 -2
- package/dist/copy-types.js +10 -3
- package/dist/database/seed.js +26 -2
- package/dist/deploy/create-receipt-rule.js +36 -2
- package/dist/deploy/index.js +1318 -44
- package/dist/dev/api.js +94 -6
- package/dist/dev/components.js +7 -2
- package/dist/dev/dashboard-globals.js +89 -3
- package/dist/dev/dashboard-utils.js +380 -2
- package/dist/dev/dashboard.js +435 -8
- package/dist/dev/desktop.js +5 -2
- package/dist/dev/docs.js +24 -2
- package/dist/dev/index.js +29 -2
- package/dist/dev/migrate-watcher.js +74 -2
- package/dist/dev/system-tray.js +5 -2
- package/dist/dev/views.js +147 -2
- package/dist/discover-packages.js +32 -2
- package/dist/domains/add.js +61 -2
- package/dist/domains/purchase.js +66 -2
- package/dist/domains/remove.js +24 -2
- package/dist/examples.js +34 -2
- package/dist/find-projects.js +16 -2
- package/dist/fresh.js +17 -2
- package/dist/generate/action-types.js +9 -4
- package/dist/generate/component-meta.js +9 -3
- package/dist/generate/custom-cli-file.js +20 -3
- package/dist/generate/env-files.js +46 -6
- package/dist/generate/ide-helpers.js +8 -3
- package/dist/generate/index.js +158 -2
- package/dist/generate/lib-entries.js +16 -3
- package/dist/generate/pantry-file.js +46 -2
- package/dist/generate/symlink-core.js +2 -2
- package/dist/generate/types.js +3 -2
- package/dist/generate/vscode-custom-data.js +9 -3
- package/dist/generate/web-types.js +0 -1
- package/dist/helpers/component-meta.js +295 -2
- package/dist/helpers/index.js +1 -2
- package/dist/helpers/lib-entries.js +73 -2
- package/dist/helpers/package-json.js +45 -14
- package/dist/helpers/utils.js +105 -2
- package/dist/helpers/vscode-custom-data.js +30 -2
- package/dist/index.d.ts +13 -0
- package/dist/index.js +50 -2
- package/dist/key-generate.js +24 -2
- package/dist/lint/fix.js +4 -3
- package/dist/lint/index.js +4 -3
- package/dist/lint/lint.js +34 -2
- package/dist/make-command.js +64 -14
- package/dist/make-job.js +41 -16
- package/dist/make-policy.js +74 -26
- package/dist/make-resource.js +50 -21
- package/dist/make.js +328 -2
- package/dist/migrate/database.js +16 -2
- package/dist/migrate/fresh.js +31 -2
- package/dist/orm/base.js +0 -2
- package/dist/orm/test.js +0 -1
- package/dist/prepublish.js +16 -2
- package/dist/queue/clear.js +42 -2
- package/dist/queue/dlq-purge.js +28 -2
- package/dist/queue/dlq-retry.js +35 -2
- package/dist/queue/dlq.js +55 -5
- package/dist/queue/failed.js +69 -5
- package/dist/queue/flush.js +49 -3
- package/dist/queue/inspect.js +154 -8
- package/dist/queue/list.js +69 -5
- package/dist/queue/monitor.js +94 -6
- package/dist/queue/pause.js +28 -2
- package/dist/queue/quarantine.js +42 -3
- package/dist/queue/resume.js +27 -2
- package/dist/queue/retry.js +41 -2
- package/dist/queue/schedule-list.js +33 -5
- package/dist/queue/schedule.js +12 -2
- package/dist/queue/status.js +68 -5
- package/dist/queue/table.js +10 -2
- package/dist/queue/unquarantine.js +27 -2
- package/dist/queue/work.js +57 -2
- package/dist/release.js +21 -3
- package/dist/route/list.js +78 -3
- package/dist/run-action.js +1 -2
- package/dist/saas/setup.js +9 -2
- package/dist/scaffold-crud.js +164 -40
- package/dist/schedule/run.js +7 -2
- package/dist/search/flush.js +10 -2
- package/dist/search/import.js +10 -2
- package/dist/search/settings-list.js +10 -2
- package/dist/search/settings.js +9 -2
- package/dist/serve/api.js +35 -2
- package/dist/setup/index.js +1 -2
- package/dist/setup/ssl.js +271 -2
- package/dist/stacks.js +265 -2
- package/dist/templates.js +301 -2
- package/dist/test/feature.js +11 -2
- package/dist/test/index.js +11 -2
- package/dist/test/ui.js +4 -2
- package/dist/test/unit.js +11 -2
- package/dist/typecheck.js +4 -2
- package/dist/types.js +16 -2
- package/dist/upgrade/binary.js +29 -2
- package/dist/upgrade/bun.js +0 -1
- package/dist/upgrade/dependencies.js +0 -2
- package/dist/upgrade/framework-utils.js +193 -2
- package/dist/upgrade/framework.js +310 -7
- package/dist/upgrade/index.js +17 -2
- package/dist/upgrade/packages.js +99 -12
- package/dist/upgrade/shell.js +40 -4
- package/dist/upgrade.js +38 -2
- package/package.json +16 -16
- package/dist/chunk-0c8sd1t6.js +0 -2
- package/dist/chunk-0g2nzhx9.js +0 -240
- package/dist/chunk-0gadhvn7.js +0 -3
- package/dist/chunk-0h27aa4r.js +0 -93
- package/dist/chunk-3peh8kqs.js +0 -851
- package/dist/chunk-62gdfav8.js +0 -3
- package/dist/chunk-6bk53y02.js +0 -3
- package/dist/chunk-78c5n3s2.js +0 -3
- package/dist/chunk-c74qv38e.js +0 -17
- package/dist/chunk-cdk2r2jd.js +0 -1
- package/dist/chunk-dbz9dp2h.js +0 -3
- package/dist/chunk-e142dheq.js +0 -3
- package/dist/chunk-ec4gky40.js +0 -3
- package/dist/chunk-h3d81wrg.js +0 -3
- package/dist/chunk-m3q0x4h0.js +0 -688
- package/dist/chunk-mqgbyhph.js +0 -5
- package/dist/chunk-n24m8prb.js +0 -3
- package/dist/chunk-n5eqwhck.js +0 -7
- package/dist/chunk-nsvnqv8y.js +0 -5
- package/dist/chunk-nty6mmx4.js +0 -283
- package/dist/chunk-s735m9mg.js +0 -1
- package/dist/chunk-tpb8jgsk.js +0 -7
- package/dist/chunk-tsbk5vne.js +0 -3
- package/dist/chunk-xg9xscqz.js +0 -1
- package/dist/chunk-y6e448he.js +0 -7
package/dist/action.js
CHANGED
|
@@ -1,2 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export class Action {
|
|
2
|
+
name;
|
|
3
|
+
description;
|
|
4
|
+
apiResponse;
|
|
5
|
+
rate;
|
|
6
|
+
tries;
|
|
7
|
+
backoff;
|
|
8
|
+
enabled;
|
|
9
|
+
path;
|
|
10
|
+
method;
|
|
11
|
+
validations;
|
|
12
|
+
requestFile;
|
|
13
|
+
skipCsrf;
|
|
14
|
+
csrf;
|
|
15
|
+
authorize;
|
|
16
|
+
before;
|
|
17
|
+
handle;
|
|
18
|
+
model;
|
|
19
|
+
_dependenciesFactory;
|
|
20
|
+
_resolvedDeps;
|
|
21
|
+
_depsOverrides;
|
|
22
|
+
constructor({
|
|
23
|
+
name,
|
|
24
|
+
description,
|
|
25
|
+
apiResponse,
|
|
26
|
+
validations,
|
|
27
|
+
handle,
|
|
28
|
+
rate,
|
|
29
|
+
tries,
|
|
30
|
+
backoff,
|
|
31
|
+
enabled,
|
|
32
|
+
path,
|
|
33
|
+
method,
|
|
34
|
+
requestFile,
|
|
35
|
+
model,
|
|
36
|
+
skipCsrf,
|
|
37
|
+
csrf,
|
|
38
|
+
authorize,
|
|
39
|
+
before,
|
|
40
|
+
dependencies
|
|
41
|
+
}) {
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.description = description;
|
|
44
|
+
this.apiResponse = apiResponse;
|
|
45
|
+
this.validations = validations;
|
|
46
|
+
this.rate = rate;
|
|
47
|
+
this.tries = tries;
|
|
48
|
+
this.backoff = backoff;
|
|
49
|
+
this.enabled = enabled;
|
|
50
|
+
this.path = path;
|
|
51
|
+
this.method = method;
|
|
52
|
+
this.handle = handle;
|
|
53
|
+
this.requestFile = requestFile;
|
|
54
|
+
this.skipCsrf = skipCsrf;
|
|
55
|
+
this.csrf = csrf;
|
|
56
|
+
this.authorize = authorize;
|
|
57
|
+
this.before = before;
|
|
58
|
+
this._dependenciesFactory = dependencies;
|
|
59
|
+
if (model && typeof model === "object" && "name" in model)
|
|
60
|
+
this.model = model.name;
|
|
61
|
+
else if (typeof model === "string")
|
|
62
|
+
this.model = model;
|
|
63
|
+
}
|
|
64
|
+
get deps() {
|
|
65
|
+
if (!this._resolvedDeps)
|
|
66
|
+
this._resolvedDeps = this._dependenciesFactory?.() ?? {};
|
|
67
|
+
if (this._depsOverrides)
|
|
68
|
+
return { ...this._resolvedDeps, ...this._depsOverrides };
|
|
69
|
+
return this._resolvedDeps;
|
|
70
|
+
}
|
|
71
|
+
overrideDependencies(overrides) {
|
|
72
|
+
this._depsOverrides = { ...this._depsOverrides ?? {}, ...overrides };
|
|
73
|
+
}
|
|
74
|
+
resetDependencies() {
|
|
75
|
+
this._depsOverrides = void 0;
|
|
76
|
+
this._resolvedDeps = void 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/actions.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export * from "./helpers/utils";
|
|
2
|
+
export * as actions from "./helpers/utils";
|
package/dist/add.js
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { installPackage } from "@stacksjs/cli";
|
|
2
|
+
export async function invoke(options) {
|
|
3
|
+
if (options?.table)
|
|
4
|
+
await addTable();
|
|
5
|
+
if (options?.calendar)
|
|
6
|
+
await addCalendar();
|
|
7
|
+
}
|
|
8
|
+
export async function add(options) {
|
|
9
|
+
return await invoke(options);
|
|
10
|
+
}
|
|
11
|
+
export async function addTable() {
|
|
12
|
+
await installPackage("@stacksjs/table");
|
|
13
|
+
}
|
|
14
|
+
export async function addCalendar() {
|
|
15
|
+
await installPackage("@stacksjs/calendar");
|
|
16
|
+
}
|
|
17
|
+
export async function installPackages(names) {
|
|
18
|
+
for (const name of names)
|
|
19
|
+
await installPackage(name);
|
|
20
|
+
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var {require}=import.meta;import { requestModelAccess } from "@stacksjs/ai";
|
|
2
|
+
async function main() {
|
|
3
|
+
try {
|
|
4
|
+
await requestModelAccess();
|
|
5
|
+
} catch (error) {
|
|
6
|
+
console.error("Error requesting model access:", error);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
if (import.meta.main)
|
|
10
|
+
main();
|
package/dist/auth/client.js
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { randomBytes, scryptSync } from "node:crypto";
|
|
3
|
+
import { db } from "@stacksjs/database";
|
|
4
|
+
import { HttpError } from "@stacksjs/error-handling";
|
|
5
|
+
import { log } from "@stacksjs/logging";
|
|
6
|
+
const args = process.argv.slice(2), getArg = (name) => {
|
|
7
|
+
const idx = args.findIndex((a) => a.startsWith(`--${name}=`) || a.startsWith(`-${name.charAt(0)}=`));
|
|
8
|
+
if (idx !== -1) {
|
|
9
|
+
const arg = args[idx];
|
|
10
|
+
if (!arg)
|
|
11
|
+
return;
|
|
12
|
+
const eqIdx = arg.indexOf("=");
|
|
13
|
+
return eqIdx >= 0 ? arg.slice(eqIdx + 1) : void 0;
|
|
14
|
+
}
|
|
15
|
+
const flagIdx = args.findIndex((a) => a === `--${name}` || a === `-${name.charAt(0)}`), value = flagIdx !== -1 ? args[flagIdx + 1] : void 0;
|
|
16
|
+
if (value && !value.startsWith("-"))
|
|
17
|
+
return value;
|
|
18
|
+
return;
|
|
19
|
+
}, hasFlag = (name) => {
|
|
20
|
+
return args.includes(`--${name}`);
|
|
21
|
+
}, name = getArg("name") || "OAuth Client", redirect = getArg("redirect") || "http://localhost", isPersonalAccess = hasFlag("personal"), isPasswordClient = hasFlag("password");
|
|
22
|
+
log.info(`Creating OAuth client: ${name}`);
|
|
23
|
+
const secret = randomBytes(40).toString("hex"), salt = randomBytes(16).toString("hex"), hashedSecret = `${salt}:${scryptSync(secret, salt, 64).toString("hex")}`, result = await db.insertInto("oauth_clients").values({
|
|
24
|
+
name,
|
|
25
|
+
secret: hashedSecret,
|
|
26
|
+
provider: "local",
|
|
27
|
+
redirect,
|
|
28
|
+
personal_access_client: isPersonalAccess,
|
|
29
|
+
password_client: isPasswordClient,
|
|
30
|
+
revoked: !1
|
|
31
|
+
}).executeTakeFirst(), insertId = result?.insertId || Number(result?.numInsertedOrUpdatedRows);
|
|
32
|
+
if (!insertId)
|
|
33
|
+
throw new HttpError(500, "Failed to create OAuth client");
|
|
34
|
+
log.success("OAuth client created successfully");
|
|
35
|
+
log.info("");
|
|
36
|
+
log.info("Client Details:");
|
|
37
|
+
log.info(` Client ID: ${insertId}`);
|
|
38
|
+
log.info(" Client Secret: [REDACTED]");
|
|
39
|
+
process.stdout.write(`Client Secret (save now, shown once): ${secret}
|
|
40
|
+
`);
|
|
41
|
+
log.info(` Redirect URI: ${redirect}`);
|
|
42
|
+
log.info("");
|
|
43
|
+
log.warn("Make sure to save the client secret. You will not be able to retrieve it again.");
|
|
44
|
+
process.exit(0);
|
package/dist/auth/prune.js
CHANGED
|
@@ -1,2 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { db, sql } from "@stacksjs/database";
|
|
3
|
+
import { log } from "@stacksjs/logging";
|
|
4
|
+
const args = process.argv.slice(2), hasFlag = (name) => {
|
|
5
|
+
return args.includes(`--${name}`);
|
|
6
|
+
}, getArg = (name) => {
|
|
7
|
+
const idx = args.findIndex((a) => a.startsWith(`--${name}=`) || a.startsWith(`-${name.charAt(0)}=`));
|
|
8
|
+
if (idx !== -1) {
|
|
9
|
+
const arg = args[idx];
|
|
10
|
+
if (!arg)
|
|
11
|
+
return;
|
|
12
|
+
const eqIdx = arg.indexOf("=");
|
|
13
|
+
return eqIdx >= 0 ? arg.slice(eqIdx + 1) : void 0;
|
|
14
|
+
}
|
|
15
|
+
const flagIdx = args.findIndex((a) => a === `--${name}` || a === `-${name.charAt(0)}`), value = flagIdx !== -1 ? args[flagIdx + 1] : void 0;
|
|
16
|
+
if (value && !value.startsWith("-"))
|
|
17
|
+
return value;
|
|
18
|
+
return;
|
|
19
|
+
}, pruneExpired = !hasFlag("no-expired"), pruneRevoked = !hasFlag("no-revoked"), daysOld = Number(getArg("days")) || 7;
|
|
20
|
+
log.info("Pruning authentication tokens...");
|
|
21
|
+
let expiredCount = 0, revokedCount = 0;
|
|
22
|
+
if (pruneExpired) {
|
|
23
|
+
log.info("Removing expired tokens...");
|
|
24
|
+
expiredCount = await db.deleteFrom("personal_access_tokens").where("expires_at", "<", sql.raw("NOW()")).execute();
|
|
25
|
+
log.success(`Removed ${expiredCount} expired token(s)`);
|
|
26
|
+
}
|
|
27
|
+
if (pruneRevoked) {
|
|
28
|
+
log.info(`Removing tokens revoked more than ${daysOld} day(s) ago...`);
|
|
29
|
+
const cutoffDate = new Date;
|
|
30
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
31
|
+
revokedCount = await db.deleteFrom("personal_access_tokens").where("revoked_at", "is not", null).where("revoked_at", "<", cutoffDate.toISOString()).execute();
|
|
32
|
+
log.success(`Removed ${revokedCount} revoked token(s)`);
|
|
33
|
+
}
|
|
34
|
+
log.success(`Token pruning complete. Total removed: ${expiredCount + revokedCount}`);
|
|
35
|
+
process.exit(0);
|
package/dist/auth/setup.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { db } from "@stacksjs/database";
|
|
4
|
+
import { log } from "@stacksjs/logging";
|
|
5
|
+
import { makeHash } from "@stacksjs/security";
|
|
6
|
+
import { env } from "@stacksjs/env";
|
|
7
|
+
import { sqlHelpers, usersEmailVerifiedAtSql, usersPasswordChangedAtSql } from "@stacksjs/database";
|
|
8
|
+
const dbDriver = env.DB_CONNECTION || "sqlite", sql = sqlHelpers(dbDriver), { isPostgres, isMysql, now, boolTrue, boolFalse: _boolFalse } = sql;
|
|
9
|
+
log.info("Setting up authentication...");
|
|
10
|
+
log.info(`Database driver: ${dbDriver}`);
|
|
11
|
+
log.info("Ensuring OAuth tables exist...");
|
|
12
|
+
try {
|
|
13
|
+
if (isPostgres)
|
|
14
|
+
await db.unsafe(`
|
|
3
15
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
4
16
|
id SERIAL PRIMARY KEY,
|
|
5
17
|
name VARCHAR(255) NOT NULL,
|
|
@@ -12,7 +24,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
12
24
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
13
25
|
updated_at TIMESTAMP
|
|
14
26
|
)
|
|
15
|
-
`);
|
|
27
|
+
`);
|
|
28
|
+
else if (isMysql)
|
|
29
|
+
await db.unsafe(`
|
|
16
30
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
17
31
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
18
32
|
name VARCHAR(255) NOT NULL,
|
|
@@ -25,7 +39,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
25
39
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
26
40
|
updated_at TIMESTAMP
|
|
27
41
|
)
|
|
28
|
-
`);
|
|
42
|
+
`);
|
|
43
|
+
else
|
|
44
|
+
await db.unsafe(`
|
|
29
45
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
30
46
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
47
|
name VARCHAR(255) NOT NULL,
|
|
@@ -38,7 +54,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
38
54
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
39
55
|
updated_at TIMESTAMP
|
|
40
56
|
)
|
|
41
|
-
`);
|
|
57
|
+
`);
|
|
58
|
+
if (isPostgres)
|
|
59
|
+
await db.unsafe(`
|
|
42
60
|
CREATE TABLE IF NOT EXISTS oauth_access_tokens (
|
|
43
61
|
id SERIAL PRIMARY KEY,
|
|
44
62
|
user_id INTEGER NOT NULL,
|
|
@@ -51,7 +69,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
51
69
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
52
70
|
updated_at TIMESTAMP
|
|
53
71
|
)
|
|
54
|
-
`);
|
|
72
|
+
`);
|
|
73
|
+
else if (isMysql)
|
|
74
|
+
await db.unsafe(`
|
|
55
75
|
CREATE TABLE IF NOT EXISTS oauth_access_tokens (
|
|
56
76
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
57
77
|
user_id INTEGER NOT NULL,
|
|
@@ -64,7 +84,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
64
84
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
65
85
|
updated_at TIMESTAMP NULL
|
|
66
86
|
)
|
|
67
|
-
`);
|
|
87
|
+
`);
|
|
88
|
+
else
|
|
89
|
+
await db.unsafe(`
|
|
68
90
|
CREATE TABLE IF NOT EXISTS oauth_access_tokens (
|
|
69
91
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
70
92
|
user_id INTEGER NOT NULL,
|
|
@@ -77,9 +99,12 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
77
99
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
78
100
|
updated_at TIMESTAMP
|
|
79
101
|
)
|
|
80
|
-
`);
|
|
102
|
+
`);
|
|
103
|
+
await db.unsafe(`
|
|
81
104
|
CREATE INDEX IF NOT EXISTS idx_oauth_access_tokens_token ON oauth_access_tokens(token)
|
|
82
|
-
`)
|
|
105
|
+
`);
|
|
106
|
+
if (isPostgres)
|
|
107
|
+
await db.unsafe(`
|
|
83
108
|
CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
|
|
84
109
|
id SERIAL PRIMARY KEY,
|
|
85
110
|
access_token_id INTEGER NOT NULL,
|
|
@@ -88,7 +113,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
88
113
|
expires_at TIMESTAMP,
|
|
89
114
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
90
115
|
)
|
|
91
|
-
`);
|
|
116
|
+
`);
|
|
117
|
+
else if (isMysql)
|
|
118
|
+
await db.unsafe(`
|
|
92
119
|
CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
|
|
93
120
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
94
121
|
access_token_id INTEGER NOT NULL,
|
|
@@ -97,7 +124,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
97
124
|
expires_at TIMESTAMP NULL,
|
|
98
125
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
99
126
|
)
|
|
100
|
-
`);
|
|
127
|
+
`);
|
|
128
|
+
else
|
|
129
|
+
await db.unsafe(`
|
|
101
130
|
CREATE TABLE IF NOT EXISTS oauth_refresh_tokens (
|
|
102
131
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
132
|
access_token_id INTEGER NOT NULL,
|
|
@@ -106,9 +135,19 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
106
135
|
expires_at TIMESTAMP,
|
|
107
136
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
108
137
|
)
|
|
109
|
-
`);
|
|
138
|
+
`);
|
|
139
|
+
await db.unsafe(`
|
|
110
140
|
CREATE INDEX IF NOT EXISTS idx_oauth_refresh_tokens_token ON oauth_refresh_tokens(token)
|
|
111
|
-
`)
|
|
141
|
+
`);
|
|
142
|
+
log.success("OAuth tables ready");
|
|
143
|
+
} catch (error) {
|
|
144
|
+
log.error("Failed to create OAuth tables", error);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
log.info("Ensuring password_resets table exists...");
|
|
148
|
+
try {
|
|
149
|
+
if (isPostgres)
|
|
150
|
+
await db.unsafe(`
|
|
112
151
|
CREATE TABLE IF NOT EXISTS password_resets (
|
|
113
152
|
id SERIAL PRIMARY KEY,
|
|
114
153
|
email VARCHAR(255) NOT NULL,
|
|
@@ -116,7 +155,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
116
155
|
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
117
156
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
118
157
|
)
|
|
119
|
-
`);
|
|
158
|
+
`);
|
|
159
|
+
else if (isMysql)
|
|
160
|
+
await db.unsafe(`
|
|
120
161
|
CREATE TABLE IF NOT EXISTS password_resets (
|
|
121
162
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
122
163
|
email VARCHAR(255) NOT NULL,
|
|
@@ -124,7 +165,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
124
165
|
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
125
166
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
126
167
|
)
|
|
127
|
-
`);
|
|
168
|
+
`);
|
|
169
|
+
else
|
|
170
|
+
await db.unsafe(`
|
|
128
171
|
CREATE TABLE IF NOT EXISTS password_resets (
|
|
129
172
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
130
173
|
email VARCHAR(255) NOT NULL,
|
|
@@ -132,9 +175,22 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
132
175
|
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
133
176
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
134
177
|
)
|
|
135
|
-
`);
|
|
178
|
+
`);
|
|
179
|
+
try {
|
|
180
|
+
await db.unsafe("ALTER TABLE password_resets ADD COLUMN expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
|
181
|
+
} catch {}
|
|
182
|
+
await db.unsafe(`
|
|
136
183
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_password_resets_email_unique ON password_resets(email)
|
|
137
|
-
`)
|
|
184
|
+
`);
|
|
185
|
+
log.success("Password resets table ready");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
log.error("Failed to create password_resets table", error);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
log.info("Ensuring email_verifications table exists...");
|
|
191
|
+
try {
|
|
192
|
+
if (isPostgres)
|
|
193
|
+
await db.unsafe(`
|
|
138
194
|
CREATE TABLE IF NOT EXISTS email_verifications (
|
|
139
195
|
id SERIAL PRIMARY KEY,
|
|
140
196
|
user_id INTEGER NOT NULL,
|
|
@@ -142,7 +198,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
142
198
|
expires_at TIMESTAMP NOT NULL,
|
|
143
199
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
144
200
|
)
|
|
145
|
-
`);
|
|
201
|
+
`);
|
|
202
|
+
else if (isMysql)
|
|
203
|
+
await db.unsafe(`
|
|
146
204
|
CREATE TABLE IF NOT EXISTS email_verifications (
|
|
147
205
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
148
206
|
user_id INTEGER NOT NULL,
|
|
@@ -150,7 +208,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
150
208
|
expires_at TIMESTAMP NOT NULL,
|
|
151
209
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
152
210
|
)
|
|
153
|
-
`);
|
|
211
|
+
`);
|
|
212
|
+
else
|
|
213
|
+
await db.unsafe(`
|
|
154
214
|
CREATE TABLE IF NOT EXISTS email_verifications (
|
|
155
215
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
156
216
|
user_id INTEGER NOT NULL,
|
|
@@ -158,9 +218,25 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
158
218
|
expires_at TIMESTAMP NOT NULL,
|
|
159
219
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
160
220
|
)
|
|
161
|
-
`);
|
|
221
|
+
`);
|
|
222
|
+
await db.unsafe(`
|
|
162
223
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_email_verifications_user_id_unique ON email_verifications(user_id)
|
|
163
|
-
`);
|
|
224
|
+
`);
|
|
225
|
+
try {
|
|
226
|
+
await db.unsafe(usersEmailVerifiedAtSql(sql));
|
|
227
|
+
} catch {}
|
|
228
|
+
try {
|
|
229
|
+
await db.unsafe(usersPasswordChangedAtSql(sql));
|
|
230
|
+
} catch {}
|
|
231
|
+
log.success("Email verifications table ready");
|
|
232
|
+
} catch (error) {
|
|
233
|
+
log.error("Failed to create email_verifications table", error);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
log.info("Ensuring webauthn_challenges table exists...");
|
|
237
|
+
try {
|
|
238
|
+
if (isPostgres)
|
|
239
|
+
await db.unsafe(`
|
|
164
240
|
CREATE TABLE IF NOT EXISTS webauthn_challenges (
|
|
165
241
|
id SERIAL PRIMARY KEY,
|
|
166
242
|
user_id INTEGER NOT NULL,
|
|
@@ -169,7 +245,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
169
245
|
expires_at TIMESTAMP NOT NULL,
|
|
170
246
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
171
247
|
)
|
|
172
|
-
`);
|
|
248
|
+
`);
|
|
249
|
+
else if (isMysql)
|
|
250
|
+
await db.unsafe(`
|
|
173
251
|
CREATE TABLE IF NOT EXISTS webauthn_challenges (
|
|
174
252
|
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
|
175
253
|
user_id INTEGER NOT NULL,
|
|
@@ -178,7 +256,9 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
178
256
|
expires_at TIMESTAMP NOT NULL,
|
|
179
257
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
180
258
|
)
|
|
181
|
-
`);
|
|
259
|
+
`);
|
|
260
|
+
else
|
|
261
|
+
await db.unsafe(`
|
|
182
262
|
CREATE TABLE IF NOT EXISTS webauthn_challenges (
|
|
183
263
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
184
264
|
user_id INTEGER NOT NULL,
|
|
@@ -187,20 +267,56 @@ import J from"process";import{randomBytes as U}from"crypto";import{db as x}from"
|
|
|
187
267
|
expires_at TIMESTAMP NOT NULL,
|
|
188
268
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
189
269
|
)
|
|
190
|
-
`);
|
|
270
|
+
`);
|
|
271
|
+
await db.unsafe(`
|
|
191
272
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_webauthn_challenges_user_purpose ON webauthn_challenges(user_id, purpose)
|
|
192
|
-
`)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
273
|
+
`);
|
|
274
|
+
log.success("WebAuthn challenges table ready");
|
|
275
|
+
} catch (error) {
|
|
276
|
+
log.error("Failed to create webauthn_challenges table", error);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
log.info("Creating personal access client...");
|
|
280
|
+
try {
|
|
281
|
+
if ((await db.unsafe(`
|
|
282
|
+
SELECT id FROM oauth_clients WHERE personal_access_client = ${boolTrue} LIMIT 1
|
|
283
|
+
`))?.length > 0)
|
|
284
|
+
console.log(`
|
|
285
|
+
\u2713 Personal access client already exists`);
|
|
286
|
+
else {
|
|
287
|
+
const secret = randomBytes(40).toString("hex"), hashedSecret = await makeHash(secret, { algorithm: "bcrypt" });
|
|
288
|
+
if (isPostgres)
|
|
289
|
+
await db.unsafe(`
|
|
196
290
|
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
197
291
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
|
|
198
|
-
`,["Personal Access Client",
|
|
292
|
+
`, ["Personal Access Client", hashedSecret, "local", "http://localhost", !0, !1, !1]);
|
|
293
|
+
else
|
|
294
|
+
await db.unsafe(`
|
|
199
295
|
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
200
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ${
|
|
201
|
-
`,["Personal Access Client",
|
|
202
|
-
|
|
203
|
-
\u2713
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
296
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ${now})
|
|
297
|
+
`, ["Personal Access Client", hashedSecret, "local", "http://localhost", 1, 0, 0]);
|
|
298
|
+
console.log(`
|
|
299
|
+
\u2713 Personal access client created successfully`);
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error("Failed to create personal access client:", error);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
console.log(`
|
|
306
|
+
\u2713 Authentication setup complete!`);
|
|
307
|
+
console.log(`
|
|
308
|
+
Features enabled:`);
|
|
309
|
+
console.log(" - Token hashing (SHA-256) for secure storage");
|
|
310
|
+
console.log(" - Refresh tokens for seamless token renewal");
|
|
311
|
+
console.log(" - Scope-based authorization");
|
|
312
|
+
console.log(" - Password reset flow");
|
|
313
|
+
console.log(`
|
|
314
|
+
Next steps:`);
|
|
315
|
+
console.log(" 1. Use createToken(userId, name, scopes) to generate tokens");
|
|
316
|
+
console.log(" 2. Use refreshToken(token) to exchange refresh tokens");
|
|
317
|
+
console.log(' 3. Use the "auth" middleware to protect routes');
|
|
318
|
+
console.log(" 4. Use tokenCan(scope) for authorization checks");
|
|
319
|
+
console.log(" 5. POST /password/forgot to request password reset");
|
|
320
|
+
console.log(` 6. POST /password/reset to reset password with token
|
|
321
|
+
`);
|
|
322
|
+
process.exit(0);
|
package/dist/auth/token.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { createPersonalAccessClient } from "@stacksjs/auth";
|
|
3
|
+
import { log } from "@stacksjs/logging";
|
|
4
|
+
const result = await createPersonalAccessClient();
|
|
5
|
+
if (result?.isErr) {
|
|
6
|
+
console.error(result.error);
|
|
7
|
+
log.error("createPersonalAccessClient failed", result.error);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
process.exit(0);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a title into a filename-safe slug.
|
|
3
|
+
*
|
|
4
|
+
* Kept intentionally strict (lowercase a-z, 0-9, single hyphens) because the
|
|
5
|
+
* slug becomes both a filename and a public URL.
|
|
6
|
+
*/
|
|
7
|
+
export declare function slugify(input: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Every post on disk, newest first, drafts included.
|
|
10
|
+
*
|
|
11
|
+
* Unlike the public `listPosts` in `blog.ts` this does not require a `date`, so
|
|
12
|
+
* a half-finished post still shows up in the dashboard instead of vanishing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function listBlogPosts(): BlogPost[];
|
|
15
|
+
export declare function getBlogPost(slug: string): BlogPost | null;
|
|
16
|
+
/**
|
|
17
|
+
* Create or update a post file.
|
|
18
|
+
*
|
|
19
|
+
* Pass `originalSlug` to rename: the new file is written first, then the old one
|
|
20
|
+
* is removed, so a failure mid-way leaves the original post intact.
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveBlogPost(input: BlogPostInput): BlogPost;
|
|
23
|
+
export declare function deleteBlogPost(slug: string): boolean;
|
|
24
|
+
export declare interface BlogPost {
|
|
25
|
+
slug: string
|
|
26
|
+
title: string
|
|
27
|
+
description: string
|
|
28
|
+
date: string
|
|
29
|
+
author: string
|
|
30
|
+
authorBio: string
|
|
31
|
+
poster: string
|
|
32
|
+
featured: boolean
|
|
33
|
+
draft: boolean
|
|
34
|
+
body: string
|
|
35
|
+
wordCount: number
|
|
36
|
+
readingTime: number
|
|
37
|
+
updatedAt: string
|
|
38
|
+
}
|
|
39
|
+
export declare interface BlogPostInput {
|
|
40
|
+
slug?: string
|
|
41
|
+
originalSlug?: string
|
|
42
|
+
title: string
|
|
43
|
+
description?: string
|
|
44
|
+
date?: string
|
|
45
|
+
author?: string
|
|
46
|
+
authorBio?: string
|
|
47
|
+
poster?: string
|
|
48
|
+
featured?: boolean
|
|
49
|
+
draft?: boolean
|
|
50
|
+
body?: string
|
|
51
|
+
}
|
|
52
|
+
export declare class BlogAdminError extends Error {
|
|
53
|
+
readonly status?: number;
|
|
54
|
+
constructor(message: string, status?: number);
|
|
55
|
+
}
|