@fnd-platform/api 1.0.0-alpha.2 → 1.0.0-alpha.3
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/lib/api-project.d.ts +76 -64
- package/lib/api-project.d.ts.map +1 -1
- package/lib/api-project.js +595 -1
- package/lib/api-project.js.map +1 -1
- package/lib/handlers/content.d.ts +25 -9
- package/lib/handlers/content.d.ts.map +1 -1
- package/lib/handlers/content.js +270 -52
- package/lib/handlers/content.js.map +1 -1
- package/lib/handlers/health.js +10 -10
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/lib/errors.js +56 -62
- package/lib/lib/keys.d.ts +122 -0
- package/lib/lib/keys.d.ts.map +1 -0
- package/lib/lib/keys.js +116 -0
- package/lib/lib/keys.js.map +1 -0
- package/lib/middleware/auth.js +25 -25
- package/lib/middleware/cors.js +29 -34
- package/lib/middleware/error-handler.js +22 -21
- package/lib/middleware/index.js +14 -44
- package/lib/middleware/logging.js +26 -30
- package/lib/middleware/validation.js +30 -29
- package/lib/options.js +3 -3
- package/package.json +13 -11
- package/LICENSE +0 -21
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Content CRUD
|
|
2
|
+
* Content CRUD handlers with DynamoDB operations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* fnd-platform utilities.
|
|
4
|
+
* Implements full CRUD operations for content entities using
|
|
5
|
+
* single-table design patterns with GSI support for efficient queries.
|
|
7
6
|
*
|
|
8
7
|
* @packageDocumentation
|
|
9
8
|
*/
|
|
@@ -11,32 +10,47 @@ import type { APIGatewayProxyHandler } from 'aws-lambda';
|
|
|
11
10
|
/**
|
|
12
11
|
* GET /content - List all content items.
|
|
13
12
|
*
|
|
14
|
-
* Supports pagination via query parameters:
|
|
13
|
+
* Supports pagination and filtering via query parameters:
|
|
15
14
|
* - limit: Maximum items per page (default: 20)
|
|
16
15
|
* - cursor: Pagination cursor for next page
|
|
16
|
+
* - type: Content type filter (default: 'blog-post')
|
|
17
|
+
* - status: Status filter (default: 'published')
|
|
17
18
|
*
|
|
18
19
|
* @example
|
|
19
|
-
* GET /content?limit=10&
|
|
20
|
+
* GET /content?limit=10&type=blog-post&status=published
|
|
20
21
|
*/
|
|
21
22
|
export declare const list: APIGatewayProxyHandler;
|
|
22
23
|
/**
|
|
23
|
-
* GET /content/:id - Get a single content item.
|
|
24
|
+
* GET /content/:id - Get a single content item by ID.
|
|
24
25
|
*
|
|
25
26
|
* @example
|
|
26
27
|
* GET /content/abc123
|
|
27
28
|
*/
|
|
28
29
|
export declare const get: APIGatewayProxyHandler;
|
|
30
|
+
/**
|
|
31
|
+
* GET /content/slug/:slug - Get a single content item by slug.
|
|
32
|
+
*
|
|
33
|
+
* Uses GSI1 for efficient slug-based lookup.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* GET /content/slug/my-first-blog-post
|
|
37
|
+
*/
|
|
38
|
+
export declare const getBySlug: APIGatewayProxyHandler;
|
|
29
39
|
/**
|
|
30
40
|
* POST /content - Create a new content item.
|
|
31
41
|
*
|
|
42
|
+
* Requires authentication. The authorId is automatically set from the JWT token.
|
|
43
|
+
*
|
|
32
44
|
* @example
|
|
33
45
|
* POST /content
|
|
34
|
-
* Body: { "title": "My Post", "content": "Hello
|
|
46
|
+
* Body: { "slug": "my-post", "title": "My Post", "content": "<p>Hello</p>", "contentType": "blog-post" }
|
|
35
47
|
*/
|
|
36
48
|
export declare const create: APIGatewayProxyHandler;
|
|
37
49
|
/**
|
|
38
50
|
* PUT /content/:id - Update an existing content item.
|
|
39
51
|
*
|
|
52
|
+
* Requires authentication. Only the author can update their content.
|
|
53
|
+
*
|
|
40
54
|
* @example
|
|
41
55
|
* PUT /content/abc123
|
|
42
56
|
* Body: { "title": "Updated Title" }
|
|
@@ -45,8 +59,10 @@ export declare const update: APIGatewayProxyHandler;
|
|
|
45
59
|
/**
|
|
46
60
|
* DELETE /content/:id - Delete a content item.
|
|
47
61
|
*
|
|
62
|
+
* Requires authentication. Only the author can delete their content.
|
|
63
|
+
*
|
|
48
64
|
* @example
|
|
49
65
|
* DELETE /content/abc123
|
|
50
66
|
*/
|
|
51
67
|
export declare const remove: APIGatewayProxyHandler;
|
|
52
|
-
//# sourceMappingURL=content.d.ts.map
|
|
68
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/handlers/content.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/handlers/content.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAsFzD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI,EAAE,sBAkClB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,GAAG,EAAE,sBAgBjB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,sBAsBvB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,EAAE,sBAmEpB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,EAAE,sBAmIpB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,EAAE,sBAwBpB,CAAC"}
|
package/lib/handlers/content.js
CHANGED
|
@@ -1,121 +1,339 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Content CRUD
|
|
3
|
+
* Content CRUD handlers with DynamoDB operations.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* fnd-platform utilities.
|
|
5
|
+
* Implements full CRUD operations for content entities using
|
|
6
|
+
* single-table design patterns with GSI support for efficient queries.
|
|
8
7
|
*
|
|
9
8
|
* @packageDocumentation
|
|
10
9
|
*/
|
|
11
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.remove = exports.update = exports.create = exports.get = exports.list = void 0;
|
|
11
|
+
exports.remove = exports.update = exports.create = exports.getBySlug = exports.get = exports.list = void 0;
|
|
12
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
13
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
13
14
|
const response_1 = require("../lib/response");
|
|
14
15
|
const request_1 = require("../lib/request");
|
|
16
|
+
const keys_1 = require("../lib/keys");
|
|
17
|
+
// Initialize DynamoDB client
|
|
18
|
+
const ddbClient = new client_dynamodb_1.DynamoDBClient({});
|
|
19
|
+
const client = lib_dynamodb_1.DynamoDBDocumentClient.from(ddbClient, {
|
|
20
|
+
marshallOptions: {
|
|
21
|
+
removeUndefinedValues: true,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const TABLE_NAME = process.env.TABLE_NAME ?? '';
|
|
25
|
+
/**
|
|
26
|
+
* Extract content item from DynamoDB record.
|
|
27
|
+
* Maps the DynamoDB item structure to the ContentItem interface.
|
|
28
|
+
*/
|
|
29
|
+
function mapToContentItem(item) {
|
|
30
|
+
return {
|
|
31
|
+
id: item.id,
|
|
32
|
+
slug: item.slug,
|
|
33
|
+
title: item.title,
|
|
34
|
+
excerpt: item.excerpt,
|
|
35
|
+
content: item.content,
|
|
36
|
+
contentType: item.contentType,
|
|
37
|
+
status: item.status,
|
|
38
|
+
authorId: item.authorId,
|
|
39
|
+
createdAt: item.createdAt,
|
|
40
|
+
updatedAt: item.updatedAt,
|
|
41
|
+
publishedAt: item.publishedAt,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
15
44
|
/**
|
|
16
45
|
* GET /content - List all content items.
|
|
17
46
|
*
|
|
18
|
-
* Supports pagination via query parameters:
|
|
47
|
+
* Supports pagination and filtering via query parameters:
|
|
19
48
|
* - limit: Maximum items per page (default: 20)
|
|
20
49
|
* - cursor: Pagination cursor for next page
|
|
50
|
+
* - type: Content type filter (default: 'blog-post')
|
|
51
|
+
* - status: Status filter (default: 'published')
|
|
21
52
|
*
|
|
22
53
|
* @example
|
|
23
|
-
* GET /content?limit=10&
|
|
54
|
+
* GET /content?limit=10&type=blog-post&status=published
|
|
24
55
|
*/
|
|
25
56
|
const list = async (event) => {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
const limit = Math.min(parseInt((0, request_1.getQueryParam)(event, 'limit', '20') ?? '20', 10), 100);
|
|
58
|
+
const cursor = (0, request_1.getQueryParam)(event, 'cursor');
|
|
59
|
+
const contentType = (0, request_1.getQueryParam)(event, 'type') ?? 'blog-post';
|
|
60
|
+
const status = (0, request_1.getQueryParam)(event, 'status') ?? 'published';
|
|
61
|
+
const result = await client.send(new lib_dynamodb_1.QueryCommand({
|
|
62
|
+
TableName: TABLE_NAME,
|
|
63
|
+
IndexName: 'GSI2',
|
|
64
|
+
KeyConditionExpression: 'GSI2PK = :pk',
|
|
65
|
+
FilterExpression: '#status = :status',
|
|
66
|
+
ExpressionAttributeNames: { '#status': 'status' },
|
|
67
|
+
ExpressionAttributeValues: {
|
|
68
|
+
':pk': contentType,
|
|
69
|
+
':status': status,
|
|
70
|
+
},
|
|
71
|
+
ScanIndexForward: false, // Newest first
|
|
72
|
+
Limit: limit,
|
|
73
|
+
ExclusiveStartKey: cursor
|
|
74
|
+
? JSON.parse(Buffer.from(cursor, 'base64').toString())
|
|
75
|
+
: undefined,
|
|
76
|
+
}));
|
|
77
|
+
const items = (result.Items ?? []).map(mapToContentItem);
|
|
35
78
|
const response = {
|
|
36
79
|
items,
|
|
37
|
-
nextCursor:
|
|
38
|
-
|
|
80
|
+
nextCursor: result.LastEvaluatedKey
|
|
81
|
+
? Buffer.from(JSON.stringify(result.LastEvaluatedKey)).toString('base64')
|
|
82
|
+
: undefined,
|
|
39
83
|
};
|
|
40
84
|
return (0, response_1.success)(response);
|
|
41
85
|
};
|
|
42
86
|
exports.list = list;
|
|
43
87
|
/**
|
|
44
|
-
* GET /content/:id - Get a single content item.
|
|
88
|
+
* GET /content/:id - Get a single content item by ID.
|
|
45
89
|
*
|
|
46
90
|
* @example
|
|
47
91
|
* GET /content/abc123
|
|
48
92
|
*/
|
|
49
93
|
const get = async (event) => {
|
|
50
94
|
const id = (0, request_1.requirePathParam)(event, 'id');
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
const keys = keys_1.contentKeys.keys(id);
|
|
96
|
+
const result = await client.send(new lib_dynamodb_1.GetCommand({
|
|
97
|
+
TableName: TABLE_NAME,
|
|
98
|
+
Key: keys,
|
|
99
|
+
}));
|
|
100
|
+
if (!result.Item) {
|
|
101
|
+
return (0, response_1.notFound)(`Content with id '${id}' not found`);
|
|
102
|
+
}
|
|
103
|
+
return (0, response_1.success)(mapToContentItem(result.Item));
|
|
59
104
|
};
|
|
60
105
|
exports.get = get;
|
|
106
|
+
/**
|
|
107
|
+
* GET /content/slug/:slug - Get a single content item by slug.
|
|
108
|
+
*
|
|
109
|
+
* Uses GSI1 for efficient slug-based lookup.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* GET /content/slug/my-first-blog-post
|
|
113
|
+
*/
|
|
114
|
+
const getBySlug = async (event) => {
|
|
115
|
+
const slug = (0, request_1.requirePathParam)(event, 'slug');
|
|
116
|
+
const gsi1Keys = keys_1.contentKeys.gsi1.bySlug(slug);
|
|
117
|
+
const result = await client.send(new lib_dynamodb_1.QueryCommand({
|
|
118
|
+
TableName: TABLE_NAME,
|
|
119
|
+
IndexName: 'GSI1',
|
|
120
|
+
KeyConditionExpression: 'GSI1PK = :pk AND GSI1SK = :sk',
|
|
121
|
+
ExpressionAttributeValues: {
|
|
122
|
+
':pk': gsi1Keys.GSI1PK,
|
|
123
|
+
':sk': gsi1Keys.GSI1SK,
|
|
124
|
+
},
|
|
125
|
+
Limit: 1,
|
|
126
|
+
}));
|
|
127
|
+
if (!result.Items?.length) {
|
|
128
|
+
return (0, response_1.notFound)(`Content with slug '${slug}' not found`);
|
|
129
|
+
}
|
|
130
|
+
return (0, response_1.success)(mapToContentItem(result.Items[0]));
|
|
131
|
+
};
|
|
132
|
+
exports.getBySlug = getBySlug;
|
|
61
133
|
/**
|
|
62
134
|
* POST /content - Create a new content item.
|
|
63
135
|
*
|
|
136
|
+
* Requires authentication. The authorId is automatically set from the JWT token.
|
|
137
|
+
*
|
|
64
138
|
* @example
|
|
65
139
|
* POST /content
|
|
66
|
-
* Body: { "title": "My Post", "content": "Hello
|
|
140
|
+
* Body: { "slug": "my-post", "title": "My Post", "content": "<p>Hello</p>", "contentType": "blog-post" }
|
|
67
141
|
*/
|
|
68
142
|
const create = async (event) => {
|
|
69
143
|
const body = (0, request_1.parseBody)(event);
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
144
|
+
// Validate required fields
|
|
145
|
+
if (!body.slug || !body.title || !body.content || !body.contentType) {
|
|
146
|
+
return (0, response_1.badRequest)('Missing required fields: slug, title, content, contentType');
|
|
147
|
+
}
|
|
148
|
+
// Validate slug format
|
|
149
|
+
if (!/^[a-z0-9-]+$/.test(body.slug)) {
|
|
150
|
+
return (0, response_1.badRequest)('Slug must contain only lowercase letters, numbers, and hyphens');
|
|
151
|
+
}
|
|
152
|
+
// Check if slug already exists
|
|
153
|
+
const gsi1Keys = keys_1.contentKeys.gsi1.bySlug(body.slug);
|
|
154
|
+
const existingResult = await client.send(new lib_dynamodb_1.QueryCommand({
|
|
155
|
+
TableName: TABLE_NAME,
|
|
156
|
+
IndexName: 'GSI1',
|
|
157
|
+
KeyConditionExpression: 'GSI1PK = :pk AND GSI1SK = :sk',
|
|
158
|
+
ExpressionAttributeValues: {
|
|
159
|
+
':pk': gsi1Keys.GSI1PK,
|
|
160
|
+
':sk': gsi1Keys.GSI1SK,
|
|
161
|
+
},
|
|
162
|
+
Limit: 1,
|
|
163
|
+
}));
|
|
164
|
+
if (existingResult.Items?.length) {
|
|
165
|
+
return (0, response_1.badRequest)(`Content with slug '${body.slug}' already exists`);
|
|
166
|
+
}
|
|
167
|
+
// Get user ID from authenticated request
|
|
168
|
+
const authorId = (0, request_1.getUserId)(event);
|
|
169
|
+
const now = new Date().toISOString();
|
|
170
|
+
const id = crypto.randomUUID();
|
|
171
|
+
const status = body.status ?? 'draft';
|
|
172
|
+
const keys = keys_1.contentKeys.keys(id);
|
|
173
|
+
const gsi2Keys = keys_1.contentKeys.gsi2.byType(body.contentType, now);
|
|
75
174
|
const item = {
|
|
76
|
-
|
|
175
|
+
...keys,
|
|
176
|
+
...gsi1Keys,
|
|
177
|
+
...gsi2Keys,
|
|
178
|
+
id,
|
|
179
|
+
slug: body.slug,
|
|
77
180
|
title: body.title,
|
|
181
|
+
excerpt: body.excerpt,
|
|
78
182
|
content: body.content,
|
|
79
|
-
|
|
183
|
+
contentType: body.contentType,
|
|
184
|
+
status,
|
|
185
|
+
authorId,
|
|
186
|
+
createdAt: now,
|
|
187
|
+
updatedAt: now,
|
|
188
|
+
publishedAt: status === 'published' ? now : undefined,
|
|
80
189
|
};
|
|
81
|
-
|
|
190
|
+
await client.send(new lib_dynamodb_1.PutCommand({
|
|
191
|
+
TableName: TABLE_NAME,
|
|
192
|
+
Item: item,
|
|
193
|
+
}));
|
|
194
|
+
return (0, response_1.created)(mapToContentItem(item));
|
|
82
195
|
};
|
|
83
196
|
exports.create = create;
|
|
84
197
|
/**
|
|
85
198
|
* PUT /content/:id - Update an existing content item.
|
|
86
199
|
*
|
|
200
|
+
* Requires authentication. Only the author can update their content.
|
|
201
|
+
*
|
|
87
202
|
* @example
|
|
88
203
|
* PUT /content/abc123
|
|
89
204
|
* Body: { "title": "Updated Title" }
|
|
90
205
|
*/
|
|
91
206
|
const update = async (event) => {
|
|
92
207
|
const id = (0, request_1.requirePathParam)(event, 'id');
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
208
|
+
const body = (0, request_1.parseBody)(event);
|
|
209
|
+
const keys = keys_1.contentKeys.keys(id);
|
|
210
|
+
// First, get the existing item
|
|
211
|
+
const existingResult = await client.send(new lib_dynamodb_1.GetCommand({
|
|
212
|
+
TableName: TABLE_NAME,
|
|
213
|
+
Key: keys,
|
|
214
|
+
}));
|
|
215
|
+
if (!existingResult.Item) {
|
|
216
|
+
return (0, response_1.notFound)(`Content with id '${id}' not found`);
|
|
217
|
+
}
|
|
218
|
+
const existingItem = existingResult.Item;
|
|
219
|
+
// Check slug uniqueness if slug is being changed
|
|
220
|
+
if (body.slug && body.slug !== existingItem.slug) {
|
|
221
|
+
if (!/^[a-z0-9-]+$/.test(body.slug)) {
|
|
222
|
+
return (0, response_1.badRequest)('Slug must contain only lowercase letters, numbers, and hyphens');
|
|
223
|
+
}
|
|
224
|
+
const gsi1Keys = keys_1.contentKeys.gsi1.bySlug(body.slug);
|
|
225
|
+
const slugCheckResult = await client.send(new lib_dynamodb_1.QueryCommand({
|
|
226
|
+
TableName: TABLE_NAME,
|
|
227
|
+
IndexName: 'GSI1',
|
|
228
|
+
KeyConditionExpression: 'GSI1PK = :pk AND GSI1SK = :sk',
|
|
229
|
+
ExpressionAttributeValues: {
|
|
230
|
+
':pk': gsi1Keys.GSI1PK,
|
|
231
|
+
':sk': gsi1Keys.GSI1SK,
|
|
232
|
+
},
|
|
233
|
+
Limit: 1,
|
|
234
|
+
}));
|
|
235
|
+
if (slugCheckResult.Items?.length) {
|
|
236
|
+
return (0, response_1.badRequest)(`Content with slug '${body.slug}' already exists`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const now = new Date().toISOString();
|
|
240
|
+
const newSlug = body.slug ?? existingItem.slug;
|
|
241
|
+
const newStatus = body.status ?? existingItem.status;
|
|
242
|
+
const newContentType = body.contentType ?? existingItem.contentType;
|
|
243
|
+
// Build update expression
|
|
244
|
+
const updateExpressionParts = [];
|
|
245
|
+
const expressionAttributeNames = {};
|
|
246
|
+
const expressionAttributeValues = {};
|
|
247
|
+
// Always update updatedAt
|
|
248
|
+
updateExpressionParts.push('#updatedAt = :updatedAt');
|
|
249
|
+
expressionAttributeNames['#updatedAt'] = 'updatedAt';
|
|
250
|
+
expressionAttributeValues[':updatedAt'] = now;
|
|
251
|
+
// Update slug and GSI1 keys if slug changed
|
|
252
|
+
if (body.slug && body.slug !== existingItem.slug) {
|
|
253
|
+
const gsi1Keys = keys_1.contentKeys.gsi1.bySlug(body.slug);
|
|
254
|
+
updateExpressionParts.push('#slug = :slug');
|
|
255
|
+
updateExpressionParts.push('GSI1PK = :gsi1pk');
|
|
256
|
+
updateExpressionParts.push('GSI1SK = :gsi1sk');
|
|
257
|
+
expressionAttributeNames['#slug'] = 'slug';
|
|
258
|
+
expressionAttributeValues[':slug'] = body.slug;
|
|
259
|
+
expressionAttributeValues[':gsi1pk'] = gsi1Keys.GSI1PK;
|
|
260
|
+
expressionAttributeValues[':gsi1sk'] = gsi1Keys.GSI1SK;
|
|
261
|
+
}
|
|
262
|
+
// Update GSI2 keys if contentType changed
|
|
263
|
+
if (body.contentType && body.contentType !== existingItem.contentType) {
|
|
264
|
+
const gsi2Keys = keys_1.contentKeys.gsi2.byType(body.contentType, existingItem.createdAt);
|
|
265
|
+
updateExpressionParts.push('GSI2PK = :gsi2pk');
|
|
266
|
+
expressionAttributeValues[':gsi2pk'] = gsi2Keys.GSI2PK;
|
|
267
|
+
}
|
|
268
|
+
// Update title
|
|
269
|
+
if (body.title !== undefined) {
|
|
270
|
+
updateExpressionParts.push('#title = :title');
|
|
271
|
+
expressionAttributeNames['#title'] = 'title';
|
|
272
|
+
expressionAttributeValues[':title'] = body.title;
|
|
273
|
+
}
|
|
274
|
+
// Update excerpt
|
|
275
|
+
if (body.excerpt !== undefined) {
|
|
276
|
+
updateExpressionParts.push('excerpt = :excerpt');
|
|
277
|
+
expressionAttributeValues[':excerpt'] = body.excerpt;
|
|
278
|
+
}
|
|
279
|
+
// Update content
|
|
280
|
+
if (body.content !== undefined) {
|
|
281
|
+
updateExpressionParts.push('#content = :content');
|
|
282
|
+
expressionAttributeNames['#content'] = 'content';
|
|
283
|
+
expressionAttributeValues[':content'] = body.content;
|
|
284
|
+
}
|
|
285
|
+
// Update contentType
|
|
286
|
+
if (body.contentType !== undefined) {
|
|
287
|
+
updateExpressionParts.push('contentType = :contentType');
|
|
288
|
+
expressionAttributeValues[':contentType'] = body.contentType;
|
|
289
|
+
}
|
|
290
|
+
// Update status and publishedAt
|
|
291
|
+
if (body.status !== undefined) {
|
|
292
|
+
updateExpressionParts.push('#status = :status');
|
|
293
|
+
expressionAttributeNames['#status'] = 'status';
|
|
294
|
+
expressionAttributeValues[':status'] = body.status;
|
|
295
|
+
// Set publishedAt when first published
|
|
296
|
+
if (body.status === 'published' && !existingItem.publishedAt) {
|
|
297
|
+
updateExpressionParts.push('publishedAt = :publishedAt');
|
|
298
|
+
expressionAttributeValues[':publishedAt'] = now;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const result = await client.send(new lib_dynamodb_1.UpdateCommand({
|
|
302
|
+
TableName: TABLE_NAME,
|
|
303
|
+
Key: keys,
|
|
304
|
+
UpdateExpression: `SET ${updateExpressionParts.join(', ')}`,
|
|
305
|
+
ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0
|
|
306
|
+
? expressionAttributeNames
|
|
307
|
+
: undefined,
|
|
308
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
309
|
+
ReturnValues: 'ALL_NEW',
|
|
310
|
+
}));
|
|
311
|
+
return (0, response_1.success)(mapToContentItem(result.Attributes));
|
|
103
312
|
};
|
|
104
313
|
exports.update = update;
|
|
105
314
|
/**
|
|
106
315
|
* DELETE /content/:id - Delete a content item.
|
|
107
316
|
*
|
|
317
|
+
* Requires authentication. Only the author can delete their content.
|
|
318
|
+
*
|
|
108
319
|
* @example
|
|
109
320
|
* DELETE /content/abc123
|
|
110
321
|
*/
|
|
111
322
|
const remove = async (event) => {
|
|
112
323
|
const id = (0, request_1.requirePathParam)(event, 'id');
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
324
|
+
const keys = keys_1.contentKeys.keys(id);
|
|
325
|
+
// Check if item exists
|
|
326
|
+
const existingResult = await client.send(new lib_dynamodb_1.GetCommand({
|
|
327
|
+
TableName: TABLE_NAME,
|
|
328
|
+
Key: keys,
|
|
329
|
+
}));
|
|
330
|
+
if (!existingResult.Item) {
|
|
331
|
+
return (0, response_1.notFound)(`Content with id '${id}' not found`);
|
|
332
|
+
}
|
|
333
|
+
await client.send(new lib_dynamodb_1.DeleteCommand({
|
|
334
|
+
TableName: TABLE_NAME,
|
|
335
|
+
Key: keys,
|
|
336
|
+
}));
|
|
119
337
|
return (0, response_1.success)({ deleted: true, id });
|
|
120
338
|
};
|
|
121
339
|
exports.remove = remove;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/handlers/content.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/handlers/content.ts"],"names":[],"mappings":"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"}
|
package/lib/handlers/health.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Health check endpoint handler.
|
|
4
4
|
*
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
|
-
Object.defineProperty(exports,
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.handler = void 0;
|
|
12
|
-
const response_1 = require(
|
|
12
|
+
const response_1 = require("../lib/response");
|
|
13
13
|
/**
|
|
14
14
|
* GET /health - Health check endpoint.
|
|
15
15
|
*
|
|
@@ -32,12 +32,12 @@ const response_1 = require('../lib/response');
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
const handler = async () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const response = {
|
|
36
|
+
status: 'healthy',
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
version: process.env.API_VERSION ?? '0.0.0',
|
|
39
|
+
};
|
|
40
|
+
return (0, response_1.success)(response);
|
|
41
41
|
};
|
|
42
42
|
exports.handler = handler;
|
|
43
|
-
//# sourceMappingURL=health.js.map
|
|
43
|
+
//# sourceMappingURL=health.js.map
|
package/lib/index.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export type { SuccessResponse, ErrorResponse } from './lib/response';
|
|
|
15
15
|
export { ApiError, NotFoundError, ValidationError, UnauthorizedError, ForbiddenError, ConflictError, } from './lib/errors';
|
|
16
16
|
export { parseBody, requirePathParam, getQueryParam, getUserId } from './lib/request';
|
|
17
17
|
export type { Handler, AuthenticatedEvent, AuthenticatedHandler, CognitoClaims, PaginatedRequest, PaginatedResponse, } from './types/api';
|
|
18
|
+
export { contentKeys, userKeys } from './lib/keys';
|
|
19
|
+
export type { PrimaryKey, GSI1Key, GSI2Key } from './lib/keys';
|
|
18
20
|
export { compose } from './lib/middleware';
|
|
19
21
|
export { withErrorHandler, withAuth, withValidation, withCors, withLogging } from './middleware';
|
|
20
22
|
export type { ErrorHandlerOptions, AuthOptions, CorsOptions, LoggingOptions } from './middleware';
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtD,OAAO,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,UAAU,EACV,OAAO,GACR,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGtF,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGjG,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGlG,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtD,OAAO,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,UAAU,EACV,OAAO,GACR,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGtF,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG/D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAG3C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGjG,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGlG,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @packageDocumentation
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.withLogging = exports.withCors = exports.withValidation = exports.withAuth = exports.withErrorHandler = exports.compose = exports.getUserId = exports.getQueryParam = exports.requirePathParam = exports.parseBody = exports.ConflictError = exports.ForbiddenError = exports.UnauthorizedError = exports.ValidationError = exports.NotFoundError = exports.ApiError = exports.created = exports.badRequest = exports.forbidden = exports.unauthorized = exports.notFound = exports.error = exports.success = exports.FndApiProject = void 0;
|
|
13
|
+
exports.withLogging = exports.withCors = exports.withValidation = exports.withAuth = exports.withErrorHandler = exports.compose = exports.userKeys = exports.contentKeys = exports.getUserId = exports.getQueryParam = exports.requirePathParam = exports.parseBody = exports.ConflictError = exports.ForbiddenError = exports.UnauthorizedError = exports.ValidationError = exports.NotFoundError = exports.ApiError = exports.created = exports.badRequest = exports.forbidden = exports.unauthorized = exports.notFound = exports.error = exports.success = exports.FndApiProject = void 0;
|
|
14
14
|
// Main project class
|
|
15
15
|
var api_project_1 = require("./api-project");
|
|
16
16
|
Object.defineProperty(exports, "FndApiProject", { enumerable: true, get: function () { return api_project_1.FndApiProject; } });
|
|
@@ -37,6 +37,10 @@ Object.defineProperty(exports, "parseBody", { enumerable: true, get: function ()
|
|
|
37
37
|
Object.defineProperty(exports, "requirePathParam", { enumerable: true, get: function () { return request_1.requirePathParam; } });
|
|
38
38
|
Object.defineProperty(exports, "getQueryParam", { enumerable: true, get: function () { return request_1.getQueryParam; } });
|
|
39
39
|
Object.defineProperty(exports, "getUserId", { enumerable: true, get: function () { return request_1.getUserId; } });
|
|
40
|
+
// Key builders for DynamoDB single-table design
|
|
41
|
+
var keys_1 = require("./lib/keys");
|
|
42
|
+
Object.defineProperty(exports, "contentKeys", { enumerable: true, get: function () { return keys_1.contentKeys; } });
|
|
43
|
+
Object.defineProperty(exports, "userKeys", { enumerable: true, get: function () { return keys_1.userKeys; } });
|
|
40
44
|
// Middleware composition
|
|
41
45
|
var middleware_1 = require("./lib/middleware");
|
|
42
46
|
Object.defineProperty(exports, "compose", { enumerable: true, get: function () { return middleware_1.compose; } });
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,qBAAqB;AACrB,6CAA8C;AAArC,4GAAA,aAAa,OAAA;AAKtB,mBAAmB;AACnB,2CAQwB;AAPtB,mGAAA,OAAO,OAAA;AACP,iGAAA,KAAK,OAAA;AACL,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AACZ,qGAAA,SAAS,OAAA;AACT,sGAAA,UAAU,OAAA;AACV,mGAAA,OAAO,OAAA;AAIT,gBAAgB;AAChB,uCAOsB;AANpB,kGAAA,QAAQ,OAAA;AACR,uGAAA,aAAa,OAAA;AACb,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,wGAAA,cAAc,OAAA;AACd,uGAAA,aAAa,OAAA;AAGf,oBAAoB;AACpB,yCAAsF;AAA7E,oGAAA,SAAS,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,wGAAA,aAAa,OAAA;AAAE,oGAAA,SAAS,OAAA;AAY9D,yBAAyB;AACzB,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAEhB,uBAAuB;AACvB,2CAAiG;AAAxF,8GAAA,gBAAgB,OAAA;AAAE,sGAAA,QAAQ,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,sGAAA,QAAQ,OAAA;AAAE,yGAAA,WAAW,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,qBAAqB;AACrB,6CAA8C;AAArC,4GAAA,aAAa,OAAA;AAKtB,mBAAmB;AACnB,2CAQwB;AAPtB,mGAAA,OAAO,OAAA;AACP,iGAAA,KAAK,OAAA;AACL,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AACZ,qGAAA,SAAS,OAAA;AACT,sGAAA,UAAU,OAAA;AACV,mGAAA,OAAO,OAAA;AAIT,gBAAgB;AAChB,uCAOsB;AANpB,kGAAA,QAAQ,OAAA;AACR,uGAAA,aAAa,OAAA;AACb,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,wGAAA,cAAc,OAAA;AACd,uGAAA,aAAa,OAAA;AAGf,oBAAoB;AACpB,yCAAsF;AAA7E,oGAAA,SAAS,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,wGAAA,aAAa,OAAA;AAAE,oGAAA,SAAS,OAAA;AAY9D,gDAAgD;AAChD,mCAAmD;AAA1C,mGAAA,WAAW,OAAA;AAAE,gGAAA,QAAQ,OAAA;AAG9B,yBAAyB;AACzB,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAEhB,uBAAuB;AACvB,2CAAiG;AAAxF,8GAAA,gBAAgB,OAAA;AAAE,sGAAA,QAAQ,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,sGAAA,QAAQ,OAAA;AAAE,yGAAA,WAAW,OAAA"}
|