@nabnflow/cli 0.0.5 → 0.1.0
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/commands/blog/content-format.d.ts +7 -0
- package/dist/commands/blog/content-format.d.ts.map +1 -0
- package/dist/commands/blog/content-format.js +30 -0
- package/dist/commands/blog/content-format.js.map +1 -0
- package/dist/commands/blog/create.d.ts +22 -0
- package/dist/commands/blog/create.d.ts.map +1 -0
- package/dist/commands/blog/create.js +40 -0
- package/dist/commands/blog/create.js.map +1 -0
- package/dist/commands/blog/deploy.d.ts +12 -0
- package/dist/commands/blog/deploy.d.ts.map +1 -0
- package/dist/commands/blog/deploy.js +20 -0
- package/dist/commands/blog/deploy.js.map +1 -0
- package/dist/commands/blog/publish.d.ts +22 -0
- package/dist/commands/blog/publish.d.ts.map +1 -0
- package/dist/commands/blog/publish.js +23 -0
- package/dist/commands/blog/publish.js.map +1 -0
- package/dist/commands/blog/set-cover.d.ts +22 -0
- package/dist/commands/blog/set-cover.d.ts.map +1 -0
- package/dist/commands/blog/set-cover.js +92 -0
- package/dist/commands/blog/set-cover.js.map +1 -0
- package/dist/commands/blog/unpublish.d.ts +21 -0
- package/dist/commands/blog/unpublish.d.ts.map +1 -0
- package/dist/commands/blog/unpublish.js +20 -0
- package/dist/commands/blog/unpublish.js.map +1 -0
- package/dist/commands/blog/update.d.ts +5 -0
- package/dist/commands/blog/update.d.ts.map +1 -1
- package/dist/commands/blog/update.js +40 -6
- package/dist/commands/blog/update.js.map +1 -1
- package/dist/commands/files/upload.d.ts +18 -0
- package/dist/commands/files/upload.d.ts.map +1 -0
- package/dist/commands/files/upload.js +83 -0
- package/dist/commands/files/upload.js.map +1 -0
- package/dist/commands/notes/content-format.d.ts +7 -0
- package/dist/commands/notes/content-format.d.ts.map +1 -0
- package/dist/commands/notes/content-format.js +30 -0
- package/dist/commands/notes/content-format.js.map +1 -0
- package/dist/commands/notes/create.d.ts +2 -2
- package/dist/commands/notes/create.d.ts.map +1 -1
- package/dist/commands/notes/create.js +76 -12
- package/dist/commands/notes/create.js.map +1 -1
- package/dist/commands/notes/get.d.ts +12 -5
- package/dist/commands/notes/get.d.ts.map +1 -1
- package/dist/commands/notes/get.js +12 -6
- package/dist/commands/notes/get.js.map +1 -1
- package/dist/commands/notes/update.d.ts +3 -2
- package/dist/commands/notes/update.d.ts.map +1 -1
- package/dist/commands/notes/update.js +108 -11
- package/dist/commands/notes/update.js.map +1 -1
- package/dist/hooks/init/auto-update.d.ts +4 -0
- package/dist/hooks/init/auto-update.d.ts.map +1 -0
- package/dist/hooks/init/auto-update.js +28 -0
- package/dist/hooks/init/auto-update.js.map +1 -0
- package/dist/lib/client.js +1 -1
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -0
- package/dist/lib/config.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-format.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/content-format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,aAAa,CAAA;AAGnC,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,OAAO;IACpD,MAAM,CAAC,WAAW,SAC0I;IAE5J,MAAM,CAAC,QAAQ,WAGd;IAEK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB3B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { requireAuth, requireBaseUrl } from '../../lib/client.js';
|
|
3
|
+
export default class BlogContentFormat extends Command {
|
|
4
|
+
static description = "Show the block (JSONContent) format expected by the --content flag. Use this to learn how to structure post content for 'blog create' and 'blog update'.";
|
|
5
|
+
static examples = [
|
|
6
|
+
'<%= config.bin %> blog content-format',
|
|
7
|
+
'<%= config.bin %> blog content-format | less',
|
|
8
|
+
];
|
|
9
|
+
async run() {
|
|
10
|
+
const token = requireAuth();
|
|
11
|
+
const baseUrl = requireBaseUrl();
|
|
12
|
+
const url = `${baseUrl.replace(/\/$/, '')}/api/docs/editor-blocks`;
|
|
13
|
+
const res = await fetch(url, {
|
|
14
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok) {
|
|
17
|
+
let message = `HTTP ${res.status}`;
|
|
18
|
+
try {
|
|
19
|
+
const body = (await res.json());
|
|
20
|
+
if (body.error)
|
|
21
|
+
message = body.error;
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
this.error(`Failed to fetch content format docs: ${message}`);
|
|
25
|
+
}
|
|
26
|
+
const docs = await res.text();
|
|
27
|
+
this.log(docs);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=content-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-format.js","sourceRoot":"","sources":["../../../src/commands/blog/content-format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,aAAa,CAAA;AACnC,OAAO,EAAC,WAAW,EAAE,cAAc,EAAC,MAAM,qBAAqB,CAAA;AAE/D,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,OAAO;IACpD,MAAM,CAAC,WAAW,GAChB,0JAA0J,CAAA;IAE5J,MAAM,CAAC,QAAQ,GAAG;QAChB,uCAAuC;QACvC,8CAA8C;KAC/C,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,KAAK,GAAG,WAAW,EAAE,CAAA;QAC3B,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;QAEhC,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,yBAAyB,CAAA;QAClE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,OAAO,EAAE,EAAC,aAAa,EAAE,UAAU,KAAK,EAAE,EAAC;SAC5C,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,OAAO,GAAG,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAA;YAClC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAA;gBACnD,IAAI,IAAI,CAAC,KAAK;oBAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAA;YACtC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface BlogPost {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: string;
|
|
7
|
+
}
|
|
8
|
+
interface CreateBlogPostResponse {
|
|
9
|
+
post: BlogPost;
|
|
10
|
+
}
|
|
11
|
+
export default class BlogCreate extends Command {
|
|
12
|
+
static description: string;
|
|
13
|
+
static enableJsonFlag: boolean;
|
|
14
|
+
static examples: string[];
|
|
15
|
+
static flags: {
|
|
16
|
+
title: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<CreateBlogPostResponse>;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAQ,MAAM,aAAa,CAAA;AAG1C,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,SAA6C;IAE/D,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAId;IAED,MAAM,CAAC,KAAK;;;MASX;IAEK,GAAG,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAwB7C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { apiRequest } from '../../lib/client.js';
|
|
3
|
+
export default class BlogCreate extends Command {
|
|
4
|
+
static description = 'Create a new blog post (starts as draft)';
|
|
5
|
+
static enableJsonFlag = true;
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> blog create --title "My First Post"',
|
|
8
|
+
'<%= config.bin %> blog create --title "Deep Dive" --content \'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Hello world"}]}]}\'',
|
|
9
|
+
'<%= config.bin %> blog create --title "My Post" --json',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
title: Flags.string({
|
|
13
|
+
description: 'Post title',
|
|
14
|
+
required: true,
|
|
15
|
+
}),
|
|
16
|
+
content: Flags.string({
|
|
17
|
+
description: "Post content in block (JSONContent) format. Run 'nabnflow blog content-format' to see the expected JSON format.",
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
async run() {
|
|
21
|
+
const { flags } = await this.parse(BlogCreate);
|
|
22
|
+
const body = { title: flags.title };
|
|
23
|
+
if (flags.content !== undefined) {
|
|
24
|
+
try {
|
|
25
|
+
body.content = JSON.parse(flags.content);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
this.error("Invalid JSON in --content flag. Run 'nabnflow blog content-format' to see the expected JSON format.");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const result = await apiRequest('/api/blog/posts', {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
body: JSON.stringify(body),
|
|
34
|
+
});
|
|
35
|
+
this.log(`Created post: ${result.post.slug}`);
|
|
36
|
+
this.log(`Status: ${result.post.status}`);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/blog/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,KAAK,EAAC,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAA;AAa9C,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,GAAG,0CAA0C,CAAA;IAE/D,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,QAAQ,GAAG;QAChB,uDAAuD;QACvD,kKAAkK;QAClK,wDAAwD;KACzD,CAAA;IAED,MAAM,CAAC,KAAK,GAAG;QACb,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;YAClB,WAAW,EAAE,YAAY;YACzB,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;YACpB,WAAW,EACT,iHAAiH;SACpH,CAAC;KACH,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,KAAK,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAE5C,MAAM,IAAI,GAA4B,EAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAC,CAAA;QAE1D,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,KAAK,CACR,qGAAqG,CACtG,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAyB,iBAAiB,EAAE;YACzE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QACzC,OAAO,MAAM,CAAA;IACf,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface DeployResponse {
|
|
3
|
+
success: boolean;
|
|
4
|
+
}
|
|
5
|
+
export default class BlogDeploy extends Command {
|
|
6
|
+
static description: string;
|
|
7
|
+
static enableJsonFlag: boolean;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
run(): Promise<DeployResponse>;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,aAAa,CAAA;AAGnC,UAAU,cAAc;IACtB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,SAA0D;IAE5E,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAGd;IAEK,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;CAUrC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { apiRequest } from '../../lib/client.js';
|
|
3
|
+
export default class BlogDeploy extends Command {
|
|
4
|
+
static description = 'Trigger a Cloudflare Pages deploy to rebuild the blog';
|
|
5
|
+
static enableJsonFlag = true;
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> blog deploy',
|
|
8
|
+
'<%= config.bin %> blog deploy --json',
|
|
9
|
+
];
|
|
10
|
+
async run() {
|
|
11
|
+
const result = await apiRequest('/api/blog/deploy', {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
body: '{}',
|
|
14
|
+
});
|
|
15
|
+
this.log('Deploy triggered successfully.');
|
|
16
|
+
this.log('Cloudflare Pages will rebuild your blog.');
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/commands/blog/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,aAAa,CAAA;AACnC,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAA;AAM9C,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,GAAG,uDAAuD,CAAA;IAE5E,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,QAAQ,GAAG;QAChB,+BAA+B;QAC/B,sCAAsC;KACvC,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,MAAM,UAAU,CAAiB,kBAAkB,EAAE;YAClE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAA;QAC1C,IAAI,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;QACpD,OAAO,MAAM,CAAA;IACf,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface BlogPost {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: string;
|
|
7
|
+
publishedAt?: string;
|
|
8
|
+
}
|
|
9
|
+
interface BlogPostResponse {
|
|
10
|
+
post: BlogPost;
|
|
11
|
+
}
|
|
12
|
+
export default class BlogPublish extends Command {
|
|
13
|
+
static description: string;
|
|
14
|
+
static enableJsonFlag: boolean;
|
|
15
|
+
static examples: string[];
|
|
16
|
+
static args: {
|
|
17
|
+
slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<BlogPostResponse>;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=publish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAC,MAAM,aAAa,CAAA;AAGzC,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,MAAM,CAAC,WAAW,SAA8B;IAEhD,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAGd;IAED,MAAM,CAAC,IAAI;;MAEV;IAEK,GAAG,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAevC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { apiRequest } from '../../lib/client.js';
|
|
3
|
+
export default class BlogPublish extends Command {
|
|
4
|
+
static description = 'Publish a draft blog post';
|
|
5
|
+
static enableJsonFlag = true;
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> blog publish my-first-post',
|
|
8
|
+
'<%= config.bin %> blog publish my-first-post --json',
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
slug: Args.string({ description: 'Post slug', required: true }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { args } = await this.parse(BlogPublish);
|
|
15
|
+
const result = await apiRequest(`/api/blog/posts/${args.slug}/publish`, { method: 'POST', body: '{}' });
|
|
16
|
+
this.log(`Published: ${result.post.slug}`);
|
|
17
|
+
if (result.post.publishedAt) {
|
|
18
|
+
this.log(`Published at: ${result.post.publishedAt}`);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../../src/commands/blog/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,OAAO,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAA;AAc9C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,MAAM,CAAC,WAAW,GAAG,2BAA2B,CAAA;IAEhD,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,QAAQ,GAAG;QAChB,8CAA8C;QAC9C,qDAAqD;KACtD,CAAA;IAED,MAAM,CAAC,IAAI,GAAG;QACZ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;KAC9D,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QAE5C,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,mBAAmB,IAAI,CAAC,IAAI,UAAU,EACtC,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAC,CAC7B,CAAA;QAED,IAAI,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;QACtD,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface BlogPost {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
title: string;
|
|
6
|
+
ogImage: string | null;
|
|
7
|
+
}
|
|
8
|
+
interface BlogPostResponse {
|
|
9
|
+
post: BlogPost;
|
|
10
|
+
}
|
|
11
|
+
export default class BlogSetCover extends Command {
|
|
12
|
+
static description: string;
|
|
13
|
+
static enableJsonFlag: boolean;
|
|
14
|
+
static examples: string[];
|
|
15
|
+
static args: {
|
|
16
|
+
slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
17
|
+
imagePath: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<BlogPostResponse>;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=set-cover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-cover.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/set-cover.ts"],"names":[],"mappings":"AAGA,OAAO,EAAO,OAAO,EAAC,MAAM,aAAa,CAAA;AAmBzC,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IAC/C,MAAM,CAAC,WAAW,SAC0E;IAE5F,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAId;IAED,MAAM,CAAC,IAAI;;;MAGV;IAEK,GAAG,IAAI,OAAO,CAAC,gBAAgB,CAAC;CA2EvC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
import { Args, Command } from '@oclif/core';
|
|
4
|
+
import { requireAuth, requireBaseUrl } from '../../lib/client.js';
|
|
5
|
+
const MIME_TYPES = {
|
|
6
|
+
'.jpg': 'image/jpeg',
|
|
7
|
+
'.jpeg': 'image/jpeg',
|
|
8
|
+
'.png': 'image/png',
|
|
9
|
+
'.gif': 'image/gif',
|
|
10
|
+
'.webp': 'image/webp',
|
|
11
|
+
'.avif': 'image/avif',
|
|
12
|
+
};
|
|
13
|
+
export default class BlogSetCover extends Command {
|
|
14
|
+
static description = 'Set the cover image for a blog post. Uploads the image file and sets it as the OG image.';
|
|
15
|
+
static enableJsonFlag = true;
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> blog set-cover my-post ./hero.png',
|
|
18
|
+
'<%= config.bin %> blog set-cover my-post ~/Desktop/cover.jpg',
|
|
19
|
+
'<%= config.bin %> blog set-cover my-post ./image.webp --json',
|
|
20
|
+
];
|
|
21
|
+
static args = {
|
|
22
|
+
slug: Args.string({ description: 'Post slug', required: true }),
|
|
23
|
+
imagePath: Args.string({ description: 'Path to local image file', required: true }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { args } = await this.parse(BlogSetCover);
|
|
27
|
+
const token = requireAuth();
|
|
28
|
+
const baseUrl = requireBaseUrl();
|
|
29
|
+
// Detect MIME type from extension
|
|
30
|
+
const ext = extname(args.imagePath).toLowerCase();
|
|
31
|
+
const mimeType = MIME_TYPES[ext];
|
|
32
|
+
if (!mimeType) {
|
|
33
|
+
this.error(`Unsupported image format: ${ext}. Supported formats: ${Object.keys(MIME_TYPES).join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
// Read the file
|
|
36
|
+
let fileBuffer;
|
|
37
|
+
try {
|
|
38
|
+
fileBuffer = readFileSync(args.imagePath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
this.error(`Could not read file: ${args.imagePath}`);
|
|
42
|
+
}
|
|
43
|
+
const apiBase = baseUrl.replace(/\/$/, '');
|
|
44
|
+
// Step 1: Upload the file
|
|
45
|
+
this.log(`Uploading image...`);
|
|
46
|
+
const uploadRes = await fetch(`${apiBase}/upload`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
'Content-Type': mimeType,
|
|
51
|
+
},
|
|
52
|
+
body: fileBuffer,
|
|
53
|
+
});
|
|
54
|
+
if (!uploadRes.ok) {
|
|
55
|
+
let message = `Upload failed: HTTP ${uploadRes.status}`;
|
|
56
|
+
try {
|
|
57
|
+
const body = (await uploadRes.json());
|
|
58
|
+
if (body.error)
|
|
59
|
+
message = `Upload failed: ${body.error}`;
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
this.error(message);
|
|
63
|
+
}
|
|
64
|
+
const upload = (await uploadRes.json());
|
|
65
|
+
// Step 2: Set as cover image
|
|
66
|
+
const coverRes = await fetch(`${apiBase}/api/blog/posts/${args.slug}/cover`, {
|
|
67
|
+
method: 'PUT',
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${token}`,
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({ storageId: upload.storageId }),
|
|
73
|
+
});
|
|
74
|
+
if (!coverRes.ok) {
|
|
75
|
+
let message = `Failed to set cover: HTTP ${coverRes.status}`;
|
|
76
|
+
try {
|
|
77
|
+
const body = (await coverRes.json());
|
|
78
|
+
if (body.error)
|
|
79
|
+
message = `Failed to set cover: ${body.error}`;
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
this.error(message);
|
|
83
|
+
}
|
|
84
|
+
const result = (await coverRes.json());
|
|
85
|
+
this.log(`Cover image set for: ${result.post.slug}`);
|
|
86
|
+
if (result.post.ogImage) {
|
|
87
|
+
this.log(`OG image URL: ${result.post.ogImage}`);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=set-cover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-cover.js","sourceRoot":"","sources":["../../../src/commands/blog/set-cover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface BlogPost {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: string;
|
|
7
|
+
}
|
|
8
|
+
interface BlogPostResponse {
|
|
9
|
+
post: BlogPost;
|
|
10
|
+
}
|
|
11
|
+
export default class BlogUnpublish extends Command {
|
|
12
|
+
static description: string;
|
|
13
|
+
static enableJsonFlag: boolean;
|
|
14
|
+
static examples: string[];
|
|
15
|
+
static args: {
|
|
16
|
+
slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<BlogPostResponse>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=unpublish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unpublish.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/unpublish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAC,MAAM,aAAa,CAAA;AAGzC,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,OAAO;IAChD,MAAM,CAAC,WAAW,SAA0C;IAE5D,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAGd;IAED,MAAM,CAAC,IAAI;;MAEV;IAEK,GAAG,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAWvC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { apiRequest } from '../../lib/client.js';
|
|
3
|
+
export default class BlogUnpublish extends Command {
|
|
4
|
+
static description = 'Revert a published blog post to draft';
|
|
5
|
+
static enableJsonFlag = true;
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> blog unpublish my-first-post',
|
|
8
|
+
'<%= config.bin %> blog unpublish my-first-post --json',
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
slug: Args.string({ description: 'Post slug', required: true }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { args } = await this.parse(BlogUnpublish);
|
|
15
|
+
const result = await apiRequest(`/api/blog/posts/${args.slug}/unpublish`, { method: 'POST', body: '{}' });
|
|
16
|
+
this.log(`Unpublished: ${result.post.slug} (now draft)`);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=unpublish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unpublish.js","sourceRoot":"","sources":["../../../src/commands/blog/unpublish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,OAAO,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAA;AAa9C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,OAAO;IAChD,MAAM,CAAC,WAAW,GAAG,uCAAuC,CAAA;IAE5D,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,QAAQ,GAAG;QAChB,gDAAgD;QAChD,uDAAuD;KACxD,CAAA;IAED,MAAM,CAAC,IAAI,GAAG;QACZ,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;KAC9D,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAE9C,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,mBAAmB,IAAI,CAAC,IAAI,YAAY,EACxC,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAC,CAC7B,CAAA;QAED,IAAI,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,CAAA;QACxD,OAAO,MAAM,CAAA;IACf,CAAC"}
|
|
@@ -17,6 +17,11 @@ export default class BlogUpdate extends Command {
|
|
|
17
17
|
};
|
|
18
18
|
static flags: {
|
|
19
19
|
title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
slug: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
'seo-title': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
'seo-desc': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
excerpt: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
25
|
};
|
|
21
26
|
run(): Promise<UpdateBlogPostResponse>;
|
|
22
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAQ,MAAM,aAAa,CAAA;AAGhD,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/blog/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,OAAO,EAAQ,MAAM,aAAa,CAAA;AAGhD,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,SAA2D;IAE7E,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAMd;IAED,MAAM,CAAC,IAAI;;MAEV;IAED,MAAM,CAAC,KAAK;;;;;;;MAUX;IAEK,GAAG,IAAI,OAAO,CAAC,sBAAsB,CAAC;CA2C7C"}
|
|
@@ -1,27 +1,61 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest } from '../../lib/client.js';
|
|
3
3
|
export default class BlogUpdate extends Command {
|
|
4
|
-
static description = 'Update a blog post';
|
|
4
|
+
static description = 'Update a blog post. Accepts any combination of fields.';
|
|
5
5
|
static enableJsonFlag = true;
|
|
6
6
|
static examples = [
|
|
7
|
-
'<%= config.bin %> blog update my-post
|
|
8
|
-
'<%= config.bin %> blog update my-post
|
|
7
|
+
'<%= config.bin %> blog update my-post --title "New Title"',
|
|
8
|
+
'<%= config.bin %> blog update my-post --slug new-post-slug',
|
|
9
|
+
'<%= config.bin %> blog update my-post --title "New Title" --slug new-slug',
|
|
10
|
+
'<%= config.bin %> blog update my-post --seo-title "SEO Title" --seo-desc "Description"',
|
|
11
|
+
'<%= config.bin %> blog update my-post --content \'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Updated content"}]}]}\'',
|
|
9
12
|
];
|
|
10
13
|
static args = {
|
|
11
14
|
slug: Args.string({ description: 'Post slug', required: true }),
|
|
12
15
|
};
|
|
13
16
|
static flags = {
|
|
14
17
|
title: Flags.string({ description: 'New title' }),
|
|
18
|
+
content: Flags.string({
|
|
19
|
+
description: "New content in block (JSONContent) format. Run 'nabnflow blog content-format' to see the expected JSON format.",
|
|
20
|
+
}),
|
|
21
|
+
slug: Flags.string({ description: 'New URL slug (must be unique)' }),
|
|
22
|
+
'seo-title': Flags.string({ description: 'SEO title for meta tags' }),
|
|
23
|
+
'seo-desc': Flags.string({ description: 'SEO description for meta tags' }),
|
|
24
|
+
excerpt: Flags.string({ description: 'Short excerpt or summary' }),
|
|
15
25
|
};
|
|
16
26
|
async run() {
|
|
17
27
|
const { args, flags } = await this.parse(BlogUpdate);
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
const hasFlag = [
|
|
29
|
+
flags.title,
|
|
30
|
+
flags.content,
|
|
31
|
+
flags.slug,
|
|
32
|
+
flags['seo-title'],
|
|
33
|
+
flags['seo-desc'],
|
|
34
|
+
flags.excerpt,
|
|
35
|
+
].some((f) => f !== undefined);
|
|
36
|
+
if (!hasFlag) {
|
|
37
|
+
this.error('At least one flag is required.\n\nAvailable flags: --title, --content, --slug, --seo-title, --seo-desc, --excerpt');
|
|
20
38
|
}
|
|
21
39
|
const body = {};
|
|
22
40
|
if (flags.title !== undefined)
|
|
23
41
|
body.title = flags.title;
|
|
24
|
-
|
|
42
|
+
if (flags.slug !== undefined)
|
|
43
|
+
body.slug = flags.slug;
|
|
44
|
+
if (flags['seo-title'] !== undefined)
|
|
45
|
+
body.seoTitle = flags['seo-title'];
|
|
46
|
+
if (flags['seo-desc'] !== undefined)
|
|
47
|
+
body.seoDescription = flags['seo-desc'];
|
|
48
|
+
if (flags.excerpt !== undefined)
|
|
49
|
+
body.excerpt = flags.excerpt;
|
|
50
|
+
if (flags.content !== undefined) {
|
|
51
|
+
try {
|
|
52
|
+
body.content = JSON.parse(flags.content);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
this.error("Invalid JSON in --content flag. Run 'nabnflow blog content-format' to see the expected JSON format.");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const result = await apiRequest(`/api/blog/posts/${args.slug}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
25
59
|
this.log(`Updated post: ${result.post.slug}`);
|
|
26
60
|
return result;
|
|
27
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/commands/blog/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAC,MAAM,aAAa,CAAA;AAChD,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAA;AAa9C,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,MAAM,CAAC,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/commands/blog/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
interface UploadResponse {
|
|
3
|
+
storageId: string;
|
|
4
|
+
url: string;
|
|
5
|
+
contentType: string;
|
|
6
|
+
size: number;
|
|
7
|
+
}
|
|
8
|
+
export default class FilesUpload extends Command {
|
|
9
|
+
static description: string;
|
|
10
|
+
static enableJsonFlag: boolean;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
static args: {
|
|
13
|
+
filePath: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<UploadResponse>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/commands/files/upload.ts"],"names":[],"mappings":"AAGA,OAAO,EAAO,OAAO,EAAC,MAAM,aAAa,CAAA;AAqBzC,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,MAAM,CAAC,WAAW,SACgG;IAElH,MAAM,CAAC,cAAc,UAAO;IAE5B,MAAM,CAAC,QAAQ,WAId;IAED,MAAM,CAAC,IAAI;;MAEV;IAEK,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;CAyDrC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
3
|
+
import { Args, Command } from '@oclif/core';
|
|
4
|
+
import { requireAuth, requireBaseUrl } from '../../lib/client.js';
|
|
5
|
+
const MIME_TYPES = {
|
|
6
|
+
'.jpg': 'image/jpeg',
|
|
7
|
+
'.jpeg': 'image/jpeg',
|
|
8
|
+
'.png': 'image/png',
|
|
9
|
+
'.gif': 'image/gif',
|
|
10
|
+
'.webp': 'image/webp',
|
|
11
|
+
'.avif': 'image/avif',
|
|
12
|
+
'.svg': 'image/svg+xml',
|
|
13
|
+
'.pdf': 'application/pdf',
|
|
14
|
+
'.txt': 'text/plain',
|
|
15
|
+
'.md': 'text/markdown',
|
|
16
|
+
'.csv': 'text/csv',
|
|
17
|
+
'.json': 'application/json',
|
|
18
|
+
'.zip': 'application/zip',
|
|
19
|
+
'.mp4': 'video/mp4',
|
|
20
|
+
'.mp3': 'audio/mpeg',
|
|
21
|
+
};
|
|
22
|
+
export default class FilesUpload extends Command {
|
|
23
|
+
static description = 'Upload a file to storage. Returns a storageId you can use to embed the file in content or attach it to a note.';
|
|
24
|
+
static enableJsonFlag = true;
|
|
25
|
+
static examples = [
|
|
26
|
+
'<%= config.bin %> files upload ./hero.png',
|
|
27
|
+
'<%= config.bin %> files upload ./report.pdf --json',
|
|
28
|
+
'<%= config.bin %> files upload ~/Desktop/diagram.png',
|
|
29
|
+
];
|
|
30
|
+
static args = {
|
|
31
|
+
filePath: Args.string({ description: 'Path to the local file to upload', required: true }),
|
|
32
|
+
};
|
|
33
|
+
async run() {
|
|
34
|
+
const { args } = await this.parse(FilesUpload);
|
|
35
|
+
const token = requireAuth();
|
|
36
|
+
const baseUrl = requireBaseUrl();
|
|
37
|
+
const ext = extname(args.filePath).toLowerCase();
|
|
38
|
+
const mimeType = MIME_TYPES[ext] ?? 'application/octet-stream';
|
|
39
|
+
let fileBuffer;
|
|
40
|
+
try {
|
|
41
|
+
fileBuffer = readFileSync(args.filePath);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
this.error(`Could not read file: ${args.filePath}`);
|
|
45
|
+
}
|
|
46
|
+
const apiBase = baseUrl.replace(/\/$/, '');
|
|
47
|
+
const filename = basename(args.filePath);
|
|
48
|
+
const res = await fetch(`${apiBase}/upload`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
Authorization: `Bearer ${token}`,
|
|
52
|
+
'Content-Type': mimeType,
|
|
53
|
+
},
|
|
54
|
+
body: fileBuffer,
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
let message = `Upload failed: HTTP ${res.status}`;
|
|
58
|
+
try {
|
|
59
|
+
const body = (await res.json());
|
|
60
|
+
if (body.error)
|
|
61
|
+
message = `Upload failed: ${body.error}`;
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
this.error(message);
|
|
65
|
+
}
|
|
66
|
+
const result = (await res.json());
|
|
67
|
+
this.log(`Uploaded: ${filename}`);
|
|
68
|
+
this.log(`Storage ID: ${result.storageId}`);
|
|
69
|
+
this.log(`URL: ${result.url}`);
|
|
70
|
+
this.log(`Content-Type: ${result.contentType}`);
|
|
71
|
+
this.log(`Size: ${result.size.toLocaleString()} bytes`);
|
|
72
|
+
this.log(``);
|
|
73
|
+
if (result.contentType.startsWith('image/')) {
|
|
74
|
+
this.log(`To embed inline in content (blog or notes):`);
|
|
75
|
+
this.log(` {"type":"figure","attrs":{"src":"storage:${result.storageId}","alt":"Description"}}`);
|
|
76
|
+
this.log(``);
|
|
77
|
+
}
|
|
78
|
+
this.log(`To attach as a file to a note:`);
|
|
79
|
+
this.log(` nabnflow notes create --title "..." --file ${args.filePath}`);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/commands/files/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,SAAS,CAAA;AACpC,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAC,MAAM,WAAW,CAAA;AAE3C,OAAO,EAAC,IAAI,EAAE,OAAO,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,WAAW,EAAE,cAAc,EAAC,MAAM,qBAAqB,CAAA;AAE/D,MAAM,UAAU,GAA2B;IACzC,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,eAAe;IACvB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,kBAAkB;IAC3B,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;CACrB,CAAA;AASD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,MAAM,CAAC,WAAW,GAChB,gHAAgH,CAAA;IAElH,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,QAAQ,GAAG;QAChB,2CAA2C;QAC3C,oDAAoD;QACpD,sDAAsD;KACvD,CAAA;IAED,MAAM,CAAC,IAAI,GAAG;QACZ,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,EAAC,WAAW,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;KACzF,CAAA;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QAE5C,MAAM,KAAK,GAAG,WAAW,EAAE,CAAA;QAC3B,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;QAEhC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAA;QAChD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAA;QAE9D,IAAI,UAAkB,CAAA;QACtB,IAAI,CAAC;YACH,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,KAAK,CAAC,wBAAwB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACrD,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAExC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,SAAS,EAAE;YAC3C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,cAAc,EAAE,QAAQ;aACzB;YACD,IAAI,EAAE,UAAiC;SACxC,CAAC,CAAA;QAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,OAAO,GAAG,uBAAuB,GAAG,CAAC,MAAM,EAAE,CAAA;YACjD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAA;gBACnD,IAAI,IAAI,CAAC,KAAK;oBAAE,OAAO,GAAG,kBAAkB,IAAI,CAAC,KAAK,EAAE,CAAA;YAC1D,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrB,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAA;QAEnD,IAAI,CAAC,GAAG,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;QAC3C,IAAI,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,GAAG,EAAE,CAAC,CAAA;QAC9B,IAAI,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAA;QAC/C,IAAI,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;QACvD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAEZ,IAAI,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5C,IAAI,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAA;YACvD,IAAI,CAAC,GAAG,CAAC,8CAA8C,MAAM,CAAC,SAAS,yBAAyB,CAAC,CAAA;YACjG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACd,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAA;QAC1C,IAAI,CAAC,GAAG,CAAC,gDAAgD,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAEzE,OAAO,MAAM,CAAA;IACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-format.d.ts","sourceRoot":"","sources":["../../../src/commands/notes/content-format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,aAAa,CAAA;AAGnC,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,OAAO;IACrD,MAAM,CAAC,WAAW,SAC8L;IAEhN,MAAM,CAAC,QAAQ,WAGd;IAEK,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB3B"}
|