@miguelmorales13/nestkit 0.7.0 → 0.8.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/README.md +45 -1
- package/dist/bootstrap/index.cjs +3 -3
- package/dist/bootstrap/index.js +2 -2
- package/dist/index.cjs +10 -10
- package/dist/index.js +14 -14
- package/dist/pinterest/index.cjs +101 -0
- package/dist/pinterest/index.d.ts +3 -0
- package/dist/pinterest/index.js +101 -0
- package/dist/pinterest/pinterest.module.d.ts +16 -0
- package/dist/pinterest/pinterest.service.d.ts +51 -0
- package/package.json +6 -1
- package/dist/{chunk-YARLPYG5.cjs → chunk-54ZXIB5T.cjs} +2 -2
- package/dist/{chunk-ANQ3YPDI.js → chunk-AOCF5QCZ.js} +3 -3
package/README.md
CHANGED
|
@@ -535,6 +535,49 @@ contenedor esté listo — no hace falta encadenarlo a mano.
|
|
|
535
535
|
`forRoot()` sin argumentos toma las credenciales del entorno; pasándoselas explícitas se puede tener
|
|
536
536
|
un proceso publicando en varias cuentas.
|
|
537
537
|
|
|
538
|
+
### `pinterest` — publicar pines, y por qué no es una red social más
|
|
539
|
+
|
|
540
|
+
Pinterest es un **buscador visual**, y eso cambia dos cosas frente a Instagram
|
|
541
|
+
y Facebook.
|
|
542
|
+
|
|
543
|
+
**Cada pin lleva su propio enlace.** Hay un camino directo del contenido al
|
|
544
|
+
sitio, cosa que en Instagram no existe porque el enlace solo vive en la
|
|
545
|
+
biografía.
|
|
546
|
+
|
|
547
|
+
**El contenido no caduca.** Un pin sigue apareciendo en búsquedas meses después
|
|
548
|
+
de publicarse, mientras que una historia dura 24 horas. Por eso el título y la
|
|
549
|
+
descripción pesan mucho más que un pie de foto en cualquier otro destino: son
|
|
550
|
+
lo que hace que alguien lo encuentre medio año después.
|
|
551
|
+
|
|
552
|
+
```ts
|
|
553
|
+
import { PinterestModule, PinterestService } from '@miguelmorales13/nestkit/pinterest';
|
|
554
|
+
|
|
555
|
+
@Module({ imports: [PinterestModule.forRoot()] }) // lee PINTEREST_ACCESS_TOKEN
|
|
556
|
+
export class AppModule {}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
await pinterest.listBoards(); // para sacar los ids
|
|
561
|
+
await pinterest.publish({
|
|
562
|
+
boardId, title, description,
|
|
563
|
+
link: 'https://tusitio.com/receta', // esto es lo que importa
|
|
564
|
+
imageUrl, // Pinterest la descarga él
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Igual que Meta, **no acepta el binario**: se le pasa una URL pública y va a
|
|
569
|
+
buscarla. `media` sirve justo para eso.
|
|
570
|
+
|
|
571
|
+
El servicio se puede usar **suelto, fuera de Nest**: recibe credenciales
|
|
572
|
+
planas y solo usa `fetch`, así que un script de CLI hace
|
|
573
|
+
`new PinterestService({ accessToken })` sin nada de inyección de dependencias.
|
|
574
|
+
Es lo que hace nutrimx, donde quien publica es un script con Playwright y no el
|
|
575
|
+
backend.
|
|
576
|
+
|
|
577
|
+
El token se saca en developers.pinterest.com con los permisos `boards:read`,
|
|
578
|
+
`pins:read` y `pins:write`. Los títulos se recortan a 100 caracteres y las
|
|
579
|
+
descripciones a 800, que son los límites del API — pasarse devuelve un 400.
|
|
580
|
+
|
|
538
581
|
### `stripe` — cliente de la SDK oficial
|
|
539
582
|
|
|
540
583
|
```ts
|
|
@@ -979,6 +1022,7 @@ trae los datos.
|
|
|
979
1022
|
| `@miguelmorales13/nestkit/whatsapp` | `WhatsAppModule`, `WHATSAPP_CLIENT`, `WhatsAppClient` |
|
|
980
1023
|
| `@miguelmorales13/nestkit/meta` | `MetaModule`, `MetaService` — publicar en Instagram y Facebook Pages |
|
|
981
1024
|
| `@miguelmorales13/nestkit/media` | `MediaModule`, `MediaService` — hosting efímero en memoria |
|
|
1025
|
+
| `@miguelmorales13/nestkit/pinterest` | `PinterestModule`, `PinterestService` — publicar pines |
|
|
982
1026
|
| `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
|
|
983
1027
|
| `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
|
|
984
1028
|
| `@miguelmorales13/nestkit/auth/oauth` | `OAuthAuthModule`, `OAuthAuthService`, `createOAuthController`, `googleProvider`, `facebookProvider`, `OAuthStorePort`, `RefreshTokenStorePort` |
|
|
@@ -991,7 +1035,7 @@ trae los datos.
|
|
|
991
1035
|
|
|
992
1036
|
## Estado del paquete
|
|
993
1037
|
|
|
994
|
-
`0.
|
|
1038
|
+
`0.8.0`. Sin adaptador Mongo real. Sin tests unitarios propios.
|
|
995
1039
|
|
|
996
1040
|
`auth/oauth` y `umami` sí tienen un consumidor real: **nutrimx** corre su acceso con Google y
|
|
997
1041
|
Facebook sobre este módulo en producción. El resto sigue sin ejercitarse de verdad.
|
package/dist/bootstrap/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-J7TURDAL.cjs');
|
|
3
|
+
var _chunk54ZXIB5Tcjs = require('../chunk-54ZXIB5T.cjs');
|
|
5
4
|
require('../chunk-TJHRABML.cjs');
|
|
5
|
+
require('../chunk-J7TURDAL.cjs');
|
|
6
6
|
require('../chunk-ZA56XBCK.cjs');
|
|
7
7
|
require('../chunk-R7BVS6CI.cjs');
|
|
8
8
|
require('../chunk-2REOCMUD.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
exports.applyNestKitDefaults =
|
|
11
|
+
exports.applyNestKitDefaults = _chunk54ZXIB5Tcjs.applyNestKitDefaults;
|
package/dist/bootstrap/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applyNestKitDefaults
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-7I2Y7V52.js";
|
|
3
|
+
} from "../chunk-AOCF5QCZ.js";
|
|
5
4
|
import "../chunk-KDAA6GFF.js";
|
|
5
|
+
import "../chunk-7I2Y7V52.js";
|
|
6
6
|
import "../chunk-EBO6UKHL.js";
|
|
7
7
|
import "../chunk-YFYHLYHN.js";
|
|
8
8
|
import "../chunk-4MGIQFAJ.js";
|
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,6 @@ var _chunkSPTDXUEDcjs = require('./chunk-SPTDXUED.cjs');
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
var _chunkRF75KC63cjs = require('./chunk-RF75KC63.cjs');
|
|
12
|
-
require('./chunk-7SOM7EZP.cjs');
|
|
13
12
|
require('./chunk-MR2IFCZE.cjs');
|
|
14
13
|
|
|
15
14
|
|
|
@@ -18,33 +17,34 @@ require('./chunk-MR2IFCZE.cjs');
|
|
|
18
17
|
var _chunkV2M75FN3cjs = require('./chunk-V2M75FN3.cjs');
|
|
19
18
|
|
|
20
19
|
|
|
20
|
+
var _chunk54ZXIB5Tcjs = require('./chunk-54ZXIB5T.cjs');
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
var _chunkFDNGAYTZcjs = require('./chunk-FDNGAYTZ.cjs');
|
|
26
26
|
|
|
27
|
+
var _chunk3CLYZC3Tcjs = require('./chunk-3CLYZC3T.cjs');
|
|
27
28
|
|
|
28
|
-
var _chunkYARLPYG5cjs = require('./chunk-YARLPYG5.cjs');
|
|
29
29
|
|
|
30
|
+
var _chunkQS2W5XCQcjs = require('./chunk-QS2W5XCQ.cjs');
|
|
31
|
+
require('./chunk-7SOM7EZP.cjs');
|
|
30
32
|
|
|
31
|
-
var _chunkJ7TURDALcjs = require('./chunk-J7TURDAL.cjs');
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
|
|
38
37
|
|
|
38
|
+
var _chunkFDNGAYTZcjs = require('./chunk-FDNGAYTZ.cjs');
|
|
39
39
|
|
|
40
|
-
var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
|
|
41
40
|
|
|
41
|
+
var _chunkJ7TURDALcjs = require('./chunk-J7TURDAL.cjs');
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
var
|
|
44
|
+
var _chunkZA56XBCKcjs = require('./chunk-ZA56XBCK.cjs');
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
var
|
|
47
|
+
var _chunkR7BVS6CIcjs = require('./chunk-R7BVS6CI.cjs');
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
|
|
@@ -75,4 +75,4 @@ require('./chunk-2REOCMUD.cjs');
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.BaseCrudService = _chunk3CLYZC3Tcjs.BaseCrudService; exports.BaseResponseDto = _chunkQS2W5XCQcjs.BaseResponseDto; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunkJ7TURDALcjs.GlobalExceptionFilter; exports.I18nModule = _chunkM3EL5O6Tcjs.I18nModule; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.PG_POOL = _chunkRF75KC63cjs.PG_POOL; exports.PgModule = _chunkRF75KC63cjs.PgModule; exports.REQUEST_ID_HEADER = _chunkV2M75FN3cjs.REQUEST_ID_HEADER; exports.RequestContext = _chunkZA56XBCKcjs.RequestContext; exports.RequestIdMiddleware = _chunkV2M75FN3cjs.RequestIdMiddleware; exports.ResponseInterceptor = _chunkTJHRABMLcjs.ResponseInterceptor; exports.SUPABASE_ANON_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_ANON_CLIENT; exports.SUPABASE_SERVICE_ROLE_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_SERVICE_ROLE_CLIENT; exports.SupabaseModule = _chunkSPTDXUEDcjs.SupabaseModule; exports.TrackingModule = _chunkV2M75FN3cjs.TrackingModule; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException; exports.applyNestKitDefaults =
|
|
78
|
+
exports.AppException = _chunkR7BVS6CIcjs.AppException; exports.BaseCrudService = _chunk3CLYZC3Tcjs.BaseCrudService; exports.BaseResponseDto = _chunkQS2W5XCQcjs.BaseResponseDto; exports.ConflictAppException = _chunkFDNGAYTZcjs.ConflictAppException; exports.ForbiddenAppException = _chunkFDNGAYTZcjs.ForbiddenAppException; exports.GlobalExceptionFilter = _chunkJ7TURDALcjs.GlobalExceptionFilter; exports.I18nModule = _chunkM3EL5O6Tcjs.I18nModule; exports.NotFoundAppException = _chunkFDNGAYTZcjs.NotFoundAppException; exports.PG_POOL = _chunkRF75KC63cjs.PG_POOL; exports.PgModule = _chunkRF75KC63cjs.PgModule; exports.REQUEST_ID_HEADER = _chunkV2M75FN3cjs.REQUEST_ID_HEADER; exports.RequestContext = _chunkZA56XBCKcjs.RequestContext; exports.RequestIdMiddleware = _chunkV2M75FN3cjs.RequestIdMiddleware; exports.ResponseInterceptor = _chunkTJHRABMLcjs.ResponseInterceptor; exports.SUPABASE_ANON_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_ANON_CLIENT; exports.SUPABASE_SERVICE_ROLE_CLIENT = _chunkSPTDXUEDcjs.SUPABASE_SERVICE_ROLE_CLIENT; exports.SupabaseModule = _chunkSPTDXUEDcjs.SupabaseModule; exports.TrackingModule = _chunkV2M75FN3cjs.TrackingModule; exports.UnauthorizedAppException = _chunkFDNGAYTZcjs.UnauthorizedAppException; exports.ValidationAppException = _chunkFDNGAYTZcjs.ValidationAppException; exports.applyNestKitDefaults = _chunk54ZXIB5Tcjs.applyNestKitDefaults; exports.createCrudController = _chunk3CLYZC3Tcjs.createCrudController; exports.translateOr = _chunkM3EL5O6Tcjs.translateOr; exports.withTenantScope = _chunkRF75KC63cjs.withTenantScope;
|
package/dist/index.js
CHANGED
|
@@ -9,13 +9,26 @@ import {
|
|
|
9
9
|
PgModule,
|
|
10
10
|
withTenantScope
|
|
11
11
|
} from "./chunk-VKOPDDCC.js";
|
|
12
|
-
import "./chunk-DQYAIQQ5.js";
|
|
13
12
|
import "./chunk-NAK4WDKS.js";
|
|
14
13
|
import {
|
|
15
14
|
REQUEST_ID_HEADER,
|
|
16
15
|
RequestIdMiddleware,
|
|
17
16
|
TrackingModule
|
|
18
17
|
} from "./chunk-EYURGACO.js";
|
|
18
|
+
import {
|
|
19
|
+
applyNestKitDefaults
|
|
20
|
+
} from "./chunk-AOCF5QCZ.js";
|
|
21
|
+
import {
|
|
22
|
+
ResponseInterceptor
|
|
23
|
+
} from "./chunk-KDAA6GFF.js";
|
|
24
|
+
import {
|
|
25
|
+
BaseCrudService,
|
|
26
|
+
createCrudController
|
|
27
|
+
} from "./chunk-JOVBJDJ2.js";
|
|
28
|
+
import {
|
|
29
|
+
BaseResponseDto
|
|
30
|
+
} from "./chunk-XX2HPTRU.js";
|
|
31
|
+
import "./chunk-DQYAIQQ5.js";
|
|
19
32
|
import {
|
|
20
33
|
ConflictAppException,
|
|
21
34
|
ForbiddenAppException,
|
|
@@ -23,28 +36,15 @@ import {
|
|
|
23
36
|
UnauthorizedAppException,
|
|
24
37
|
ValidationAppException
|
|
25
38
|
} from "./chunk-ORWJ7LES.js";
|
|
26
|
-
import {
|
|
27
|
-
applyNestKitDefaults
|
|
28
|
-
} from "./chunk-ANQ3YPDI.js";
|
|
29
39
|
import {
|
|
30
40
|
GlobalExceptionFilter
|
|
31
41
|
} from "./chunk-7I2Y7V52.js";
|
|
32
|
-
import {
|
|
33
|
-
ResponseInterceptor
|
|
34
|
-
} from "./chunk-KDAA6GFF.js";
|
|
35
42
|
import {
|
|
36
43
|
RequestContext
|
|
37
44
|
} from "./chunk-EBO6UKHL.js";
|
|
38
45
|
import {
|
|
39
46
|
AppException
|
|
40
47
|
} from "./chunk-YFYHLYHN.js";
|
|
41
|
-
import {
|
|
42
|
-
BaseCrudService,
|
|
43
|
-
createCrudController
|
|
44
|
-
} from "./chunk-JOVBJDJ2.js";
|
|
45
|
-
import {
|
|
46
|
-
BaseResponseDto
|
|
47
|
-
} from "./chunk-XX2HPTRU.js";
|
|
48
48
|
import {
|
|
49
49
|
I18nModule,
|
|
50
50
|
translateOr
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
2
|
+
|
|
3
|
+
var _chunk2REOCMUDcjs = require('../chunk-2REOCMUD.cjs');
|
|
4
|
+
|
|
5
|
+
// src/pinterest/pinterest.service.ts
|
|
6
|
+
var _common = require('@nestjs/common');
|
|
7
|
+
var API = "https://api.pinterest.com/v5";
|
|
8
|
+
var MAX_TITLE = 100;
|
|
9
|
+
var MAX_DESCRIPTION = 800;
|
|
10
|
+
var PinterestService = class {
|
|
11
|
+
constructor(credentials) {
|
|
12
|
+
this.credentials = credentials;
|
|
13
|
+
}
|
|
14
|
+
/** The account's boards. Used to resolve the ids that configuration needs. */
|
|
15
|
+
async listBoards() {
|
|
16
|
+
const data = await this.request("/boards?page_size=50");
|
|
17
|
+
return _nullishCoalesce(data.items, () => ( []));
|
|
18
|
+
}
|
|
19
|
+
/** Creates a pin and returns its id. */
|
|
20
|
+
async publish(pin) {
|
|
21
|
+
const created = await this.request("/pins", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
body: {
|
|
24
|
+
board_id: pin.boardId,
|
|
25
|
+
title: pin.title.slice(0, MAX_TITLE),
|
|
26
|
+
description: pin.description.slice(0, MAX_DESCRIPTION),
|
|
27
|
+
link: pin.link,
|
|
28
|
+
media_source: { source_type: "image_url", url: pin.imageUrl }
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return created.id;
|
|
32
|
+
}
|
|
33
|
+
/** Whether there is a token at all. */
|
|
34
|
+
get configured() {
|
|
35
|
+
return Boolean(this.credentials.accessToken);
|
|
36
|
+
}
|
|
37
|
+
async request(path, options = {}) {
|
|
38
|
+
if (!this.credentials.accessToken) {
|
|
39
|
+
throw new Error("PinterestService: no access token configured");
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(`${API}${path}`, {
|
|
42
|
+
method: _nullishCoalesce(options.method, () => ( "GET")),
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${this.credentials.accessToken}`,
|
|
45
|
+
"Content-Type": "application/json"
|
|
46
|
+
},
|
|
47
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
48
|
+
});
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
let data;
|
|
51
|
+
try {
|
|
52
|
+
data = JSON.parse(text);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
throw new Error(`Pinterest answered ${response.status} with non-JSON: ${text.slice(0, 200)}`);
|
|
55
|
+
}
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const error = data;
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Pinterest answered ${response.status}: ${_nullishCoalesce(error.message, () => ( text.slice(0, 200)))}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
PinterestService = exports.PinterestService = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
|
|
66
|
+
_common.Injectable.call(void 0, )
|
|
67
|
+
], PinterestService);
|
|
68
|
+
|
|
69
|
+
// src/pinterest/pinterest.module.ts
|
|
70
|
+
|
|
71
|
+
var PinterestModule = class {
|
|
72
|
+
static forRoot(credentials) {
|
|
73
|
+
return {
|
|
74
|
+
module: PinterestModule,
|
|
75
|
+
providers: [
|
|
76
|
+
{
|
|
77
|
+
provide: PinterestService,
|
|
78
|
+
useFactory: () => {
|
|
79
|
+
const resolved = _nullishCoalesce(credentials, () => ( {
|
|
80
|
+
accessToken: _nullishCoalesce(process.env.PINTEREST_ACCESS_TOKEN, () => ( ""))
|
|
81
|
+
}));
|
|
82
|
+
if (!resolved.accessToken) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"PinterestModule: no access token. Set PINTEREST_ACCESS_TOKEN or pass credentials to forRoot()."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return new PinterestService(resolved);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
exports: [PinterestService]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
PinterestModule = exports.PinterestModule = _chunk2REOCMUDcjs.__decorateClass.call(void 0, [
|
|
96
|
+
_common.Module.call(void 0, {})
|
|
97
|
+
], PinterestModule);
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
exports.PinterestModule = PinterestModule; exports.PinterestService = PinterestService;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateClass
|
|
3
|
+
} from "../chunk-4MGIQFAJ.js";
|
|
4
|
+
|
|
5
|
+
// src/pinterest/pinterest.service.ts
|
|
6
|
+
import { Injectable } from "@nestjs/common";
|
|
7
|
+
var API = "https://api.pinterest.com/v5";
|
|
8
|
+
var MAX_TITLE = 100;
|
|
9
|
+
var MAX_DESCRIPTION = 800;
|
|
10
|
+
var PinterestService = class {
|
|
11
|
+
constructor(credentials) {
|
|
12
|
+
this.credentials = credentials;
|
|
13
|
+
}
|
|
14
|
+
/** The account's boards. Used to resolve the ids that configuration needs. */
|
|
15
|
+
async listBoards() {
|
|
16
|
+
const data = await this.request("/boards?page_size=50");
|
|
17
|
+
return data.items ?? [];
|
|
18
|
+
}
|
|
19
|
+
/** Creates a pin and returns its id. */
|
|
20
|
+
async publish(pin) {
|
|
21
|
+
const created = await this.request("/pins", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
body: {
|
|
24
|
+
board_id: pin.boardId,
|
|
25
|
+
title: pin.title.slice(0, MAX_TITLE),
|
|
26
|
+
description: pin.description.slice(0, MAX_DESCRIPTION),
|
|
27
|
+
link: pin.link,
|
|
28
|
+
media_source: { source_type: "image_url", url: pin.imageUrl }
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return created.id;
|
|
32
|
+
}
|
|
33
|
+
/** Whether there is a token at all. */
|
|
34
|
+
get configured() {
|
|
35
|
+
return Boolean(this.credentials.accessToken);
|
|
36
|
+
}
|
|
37
|
+
async request(path, options = {}) {
|
|
38
|
+
if (!this.credentials.accessToken) {
|
|
39
|
+
throw new Error("PinterestService: no access token configured");
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(`${API}${path}`, {
|
|
42
|
+
method: options.method ?? "GET",
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${this.credentials.accessToken}`,
|
|
45
|
+
"Content-Type": "application/json"
|
|
46
|
+
},
|
|
47
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
48
|
+
});
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
let data;
|
|
51
|
+
try {
|
|
52
|
+
data = JSON.parse(text);
|
|
53
|
+
} catch {
|
|
54
|
+
throw new Error(`Pinterest answered ${response.status} with non-JSON: ${text.slice(0, 200)}`);
|
|
55
|
+
}
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const error = data;
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Pinterest answered ${response.status}: ${error.message ?? text.slice(0, 200)}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
PinterestService = __decorateClass([
|
|
66
|
+
Injectable()
|
|
67
|
+
], PinterestService);
|
|
68
|
+
|
|
69
|
+
// src/pinterest/pinterest.module.ts
|
|
70
|
+
import { Module } from "@nestjs/common";
|
|
71
|
+
var PinterestModule = class {
|
|
72
|
+
static forRoot(credentials) {
|
|
73
|
+
return {
|
|
74
|
+
module: PinterestModule,
|
|
75
|
+
providers: [
|
|
76
|
+
{
|
|
77
|
+
provide: PinterestService,
|
|
78
|
+
useFactory: () => {
|
|
79
|
+
const resolved = credentials ?? {
|
|
80
|
+
accessToken: process.env.PINTEREST_ACCESS_TOKEN ?? ""
|
|
81
|
+
};
|
|
82
|
+
if (!resolved.accessToken) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"PinterestModule: no access token. Set PINTEREST_ACCESS_TOKEN or pass credentials to forRoot()."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return new PinterestService(resolved);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
exports: [PinterestService]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
PinterestModule = __decorateClass([
|
|
96
|
+
Module({})
|
|
97
|
+
], PinterestModule);
|
|
98
|
+
export {
|
|
99
|
+
PinterestModule,
|
|
100
|
+
PinterestService
|
|
101
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { type PinterestCredentials } from './pinterest.service.js';
|
|
3
|
+
/**
|
|
4
|
+
* Outbound client for Pinterest. See `PinterestService`.
|
|
5
|
+
*
|
|
6
|
+
* Credentials come from `PINTEREST_ACCESS_TOKEN` by default, or are passed
|
|
7
|
+
* explicitly to `forRoot` — which is what a process publishing to several
|
|
8
|
+
* accounts needs.
|
|
9
|
+
*
|
|
10
|
+
* Unlike most modules here, the service is also useful on its own: it takes
|
|
11
|
+
* plain credentials and uses nothing but `fetch`, so a script outside Nest can
|
|
12
|
+
* do `new PinterestService({ accessToken })` without any of the DI machinery.
|
|
13
|
+
*/
|
|
14
|
+
export declare class PinterestModule {
|
|
15
|
+
static forRoot(credentials?: PinterestCredentials): DynamicModule;
|
|
16
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface PinterestCredentials {
|
|
2
|
+
/** Token from developers.pinterest.com with boards:read, pins:read, pins:write. */
|
|
3
|
+
accessToken: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PinterestBoard {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export interface NewPin {
|
|
10
|
+
boardId: string;
|
|
11
|
+
/** What people read in search results. Trimmed to 100 characters. */
|
|
12
|
+
title: string;
|
|
13
|
+
/** The long text carrying the words you want to be found by. */
|
|
14
|
+
description: string;
|
|
15
|
+
/** Where the pin leads when tapped. This is the whole point of Pinterest. */
|
|
16
|
+
link: string;
|
|
17
|
+
/** Publicly reachable image URL — Pinterest downloads it when creating the pin. */
|
|
18
|
+
imageUrl: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Publishes pins through the Pinterest API v5.
|
|
22
|
+
*
|
|
23
|
+
* Pinterest is not one more social network: it is a visual search engine, and
|
|
24
|
+
* that changes two things about how content behaves there.
|
|
25
|
+
*
|
|
26
|
+
* **Every pin carries its own link.** So there is a direct path from a piece of
|
|
27
|
+
* content to a site — unlike Instagram, where a link only lives in the bio.
|
|
28
|
+
*
|
|
29
|
+
* **The content does not expire.** A pin keeps surfacing in searches months
|
|
30
|
+
* after it was published, while a story lasts a day. That makes the title and
|
|
31
|
+
* description matter far more than a caption elsewhere: they are what makes
|
|
32
|
+
* someone find the pin half a year later.
|
|
33
|
+
*
|
|
34
|
+
* Like Meta's API, Pinterest does not take the binary — it is given a public
|
|
35
|
+
* URL and fetches the image itself, so the caller needs somewhere to serve it
|
|
36
|
+
* from during publication. `MediaModule` in this same package exists for that.
|
|
37
|
+
*
|
|
38
|
+
* Send-only, like `TelegramModule` and `MetaModule`: it publishes and lists
|
|
39
|
+
* boards, it does not read analytics or manage comments.
|
|
40
|
+
*/
|
|
41
|
+
export declare class PinterestService {
|
|
42
|
+
private readonly credentials;
|
|
43
|
+
constructor(credentials: PinterestCredentials);
|
|
44
|
+
/** The account's boards. Used to resolve the ids that configuration needs. */
|
|
45
|
+
listBoards(): Promise<PinterestBoard[]>;
|
|
46
|
+
/** Creates a pin and returns its id. */
|
|
47
|
+
publish(pin: NewPin): Promise<string>;
|
|
48
|
+
/** Whether there is a token at all. */
|
|
49
|
+
get configured(): boolean;
|
|
50
|
+
private request;
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miguelmorales13/nestkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -116,6 +116,11 @@
|
|
|
116
116
|
"import": "./dist/meta/index.js",
|
|
117
117
|
"require": "./dist/meta/index.cjs"
|
|
118
118
|
},
|
|
119
|
+
"./pinterest": {
|
|
120
|
+
"types": "./dist/pinterest/index.d.ts",
|
|
121
|
+
"import": "./dist/pinterest/index.js",
|
|
122
|
+
"require": "./dist/pinterest/index.cjs"
|
|
123
|
+
},
|
|
119
124
|
"./media": {
|
|
120
125
|
"types": "./dist/media/index.d.ts",
|
|
121
126
|
"import": "./dist/media/index.js",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkTJHRABMLcjs = require('./chunk-TJHRABML.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkJ7TURDALcjs = require('./chunk-J7TURDAL.cjs');
|
|
7
7
|
|
|
8
8
|
// src/bootstrap/apply-defaults.ts
|
|
9
9
|
|