@fydemy/cms 1.0.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/CHANGELOG.md +50 -0
- package/LICENSE +21 -0
- package/dist/index.d.mts +431 -0
- package/dist/index.d.ts +431 -0
- package/dist/index.js +934 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +862 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +78 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2024-12-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial stable release of @fydemy/cms
|
|
13
|
+
- File-based CMS for Next.js with markdown support
|
|
14
|
+
- GitHub integration for production deployments
|
|
15
|
+
- Simple authentication with username/password from environment variables
|
|
16
|
+
- TypeScript support with full type definitions
|
|
17
|
+
- Local and GitHub storage providers
|
|
18
|
+
- API handlers for content management (CRUD operations)
|
|
19
|
+
- Authentication middleware for protecting admin routes
|
|
20
|
+
- Session management with JWT
|
|
21
|
+
- File upload functionality
|
|
22
|
+
- Collection and directory listing utilities
|
|
23
|
+
|
|
24
|
+
### Security
|
|
25
|
+
|
|
26
|
+
- Timing-safe password comparison using `crypto.timingSafeEqual` to prevent timing attacks
|
|
27
|
+
- Rate limiting on login endpoint (5 attempts per 15 minutes)
|
|
28
|
+
- Input validation and sanitization for all user inputs
|
|
29
|
+
- Path validation to prevent directory traversal attacks
|
|
30
|
+
- File size limits (10MB maximum)
|
|
31
|
+
- Frontmatter sanitization to prevent injection attacks
|
|
32
|
+
- Secure session cookies with httpOnly, sameSite, and secure flags
|
|
33
|
+
- Length limits on username (100 chars) and password (1000 chars) inputs
|
|
34
|
+
- Generic error messages to prevent username enumeration
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- Comprehensive README with installation and usage instructions
|
|
39
|
+
- JSDoc documentation for all public APIs
|
|
40
|
+
- Security policy and vulnerability reporting guidelines
|
|
41
|
+
- MIT License
|
|
42
|
+
|
|
43
|
+
## [0.1.0] - 2024-12-05
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- Initial development release
|
|
48
|
+
- Basic file-based CMS functionality
|
|
49
|
+
- Simple authentication
|
|
50
|
+
- Markdown file operations
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Fydemy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
interface MarkdownData {
|
|
4
|
+
content: string;
|
|
5
|
+
data: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse markdown content with frontmatter
|
|
9
|
+
* @param rawContent - Raw markdown content to parse
|
|
10
|
+
* @returns Parsed markdown data with frontmatter and content
|
|
11
|
+
*/
|
|
12
|
+
declare function parseMarkdown(rawContent: string): MarkdownData;
|
|
13
|
+
/**
|
|
14
|
+
* Convert data and content back to markdown with frontmatter
|
|
15
|
+
*/
|
|
16
|
+
declare function stringifyMarkdown(data: Record<string, any>, content: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Read and parse a markdown file
|
|
19
|
+
* @param filePath - Path to the markdown file
|
|
20
|
+
* @returns Parsed markdown data
|
|
21
|
+
* @throws Error if file path is invalid or file is too large
|
|
22
|
+
*/
|
|
23
|
+
declare function getMarkdownContent(filePath: string): Promise<MarkdownData>;
|
|
24
|
+
/**
|
|
25
|
+
* Write markdown file with frontmatter
|
|
26
|
+
* @param filePath - Path to the markdown file
|
|
27
|
+
* @param data - Frontmatter data object
|
|
28
|
+
* @param content - Markdown content
|
|
29
|
+
* @throws Error if file path is invalid or content is too large
|
|
30
|
+
*/
|
|
31
|
+
declare function saveMarkdownContent(filePath: string, data: Record<string, any>, content: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete a markdown file
|
|
34
|
+
* @param filePath - Path to the markdown file
|
|
35
|
+
* @throws Error if file path is invalid
|
|
36
|
+
*/
|
|
37
|
+
declare function deleteMarkdownContent(filePath: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* List all markdown files in a directory
|
|
40
|
+
* @param directory - Directory path (optional, defaults to root)
|
|
41
|
+
* @returns Array of file paths
|
|
42
|
+
* @throws Error if directory path is invalid
|
|
43
|
+
*/
|
|
44
|
+
declare function listMarkdownFiles(directory?: string): Promise<string[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a markdown file exists
|
|
47
|
+
* @param filePath - Path to the markdown file
|
|
48
|
+
* @returns True if file exists, false otherwise
|
|
49
|
+
* @throws Error if file path is invalid
|
|
50
|
+
*/
|
|
51
|
+
declare function markdownFileExists(filePath: string): Promise<boolean>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Represents a file or directory entry
|
|
55
|
+
*/
|
|
56
|
+
interface FileEntry {
|
|
57
|
+
/** Relative path from base content directory */
|
|
58
|
+
path: string;
|
|
59
|
+
/** File or directory name */
|
|
60
|
+
name: string;
|
|
61
|
+
/** Type of entry */
|
|
62
|
+
type: "file" | "directory";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Interface for file storage operations
|
|
66
|
+
*/
|
|
67
|
+
interface StorageProvider {
|
|
68
|
+
/** Read file content as string */
|
|
69
|
+
readFile(filePath: string): Promise<string>;
|
|
70
|
+
/** Write content to file, creating parent directories if needed */
|
|
71
|
+
writeFile(filePath: string, content: string): Promise<void>;
|
|
72
|
+
/** Delete a file */
|
|
73
|
+
deleteFile(filePath: string): Promise<void>;
|
|
74
|
+
/** List files in a directory */
|
|
75
|
+
listFiles(directory: string): Promise<FileEntry[]>;
|
|
76
|
+
/** Check if a file exists */
|
|
77
|
+
exists(filePath: string): Promise<boolean>;
|
|
78
|
+
/** Upload a binary file */
|
|
79
|
+
uploadFile(filePath: string, buffer: Buffer): Promise<string>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Local file system storage (for development)
|
|
83
|
+
*/
|
|
84
|
+
declare class LocalStorage implements StorageProvider {
|
|
85
|
+
private baseDir;
|
|
86
|
+
constructor(baseDir?: string);
|
|
87
|
+
private getFullPath;
|
|
88
|
+
readFile(filePath: string): Promise<string>;
|
|
89
|
+
writeFile(filePath: string, content: string): Promise<void>;
|
|
90
|
+
deleteFile(filePath: string): Promise<void>;
|
|
91
|
+
listFiles(directory: string): Promise<FileEntry[]>;
|
|
92
|
+
exists(filePath: string): Promise<boolean>;
|
|
93
|
+
uploadFile(filePath: string, buffer: Buffer): Promise<string>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* GitHub storage (for production)
|
|
97
|
+
*/
|
|
98
|
+
declare class GitHubStorage implements StorageProvider {
|
|
99
|
+
private octokit;
|
|
100
|
+
private owner;
|
|
101
|
+
private repo;
|
|
102
|
+
private branch;
|
|
103
|
+
private baseDir;
|
|
104
|
+
constructor();
|
|
105
|
+
private getGitHubPath;
|
|
106
|
+
readFile(filePath: string): Promise<string>;
|
|
107
|
+
writeFile(filePath: string, content: string): Promise<void>;
|
|
108
|
+
deleteFile(filePath: string): Promise<void>;
|
|
109
|
+
listFiles(directory: string): Promise<FileEntry[]>;
|
|
110
|
+
exists(filePath: string): Promise<boolean>;
|
|
111
|
+
uploadFile(filePath: string, buffer: Buffer): Promise<string>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the appropriate storage provider based on environment
|
|
115
|
+
*/
|
|
116
|
+
declare function getStorageProvider(): StorageProvider;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* List all files and directories in a directory
|
|
120
|
+
* @param directory - Relative path to the directory (default: root)
|
|
121
|
+
* @returns Array of file entries (files and directories)
|
|
122
|
+
*/
|
|
123
|
+
declare function listDirectory(directory?: string): Promise<FileEntry[]>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Base interface for collection items with flexible frontmatter
|
|
127
|
+
*/
|
|
128
|
+
interface CollectionItem<T = Record<string, any>> {
|
|
129
|
+
/** The markdown content body */
|
|
130
|
+
content: string;
|
|
131
|
+
/** Frontmatter data with dynamic fields */
|
|
132
|
+
data: T;
|
|
133
|
+
/** File slug (filename without extension) */
|
|
134
|
+
slug: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fetch all markdown files from a specific folder/collection
|
|
138
|
+
* @param folderName - The folder name under public/content (e.g., "blog", "pages")
|
|
139
|
+
* @returns Array of collection items with parsed frontmatter and content
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* // Fetch all blog posts
|
|
144
|
+
* const posts = await getCollectionItems("blog");
|
|
145
|
+
* posts.forEach(post => {
|
|
146
|
+
* console.log(post.data.title); // Access any frontmatter field
|
|
147
|
+
* console.log(post.slug); // Filename without .md
|
|
148
|
+
* });
|
|
149
|
+
*
|
|
150
|
+
* // With type inference for known fields
|
|
151
|
+
* interface BlogPost {
|
|
152
|
+
* title: string;
|
|
153
|
+
* date: string;
|
|
154
|
+
* author?: string;
|
|
155
|
+
* [key: string]: any; // Allow any other fields
|
|
156
|
+
* }
|
|
157
|
+
* const typedPosts = await getCollectionItems<BlogPost>("blog");
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function getCollectionItems<T = Record<string, any>>(folderName: string): Promise<CollectionItem<T>[]>;
|
|
161
|
+
/**
|
|
162
|
+
* Fetch a single item from a collection by slug
|
|
163
|
+
* @param folderName - The folder name under public/content
|
|
164
|
+
* @param slug - The filename without .md extension
|
|
165
|
+
* @returns Single collection item or null if not found
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* // Fetch a specific blog post
|
|
170
|
+
* const post = await getCollectionItem("blog", "my-first-post");
|
|
171
|
+
* if (post) {
|
|
172
|
+
* console.log(post.data.title);
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function getCollectionItem<T = Record<string, any>>(folderName: string, slug: string): Promise<CollectionItem<T> | null>;
|
|
177
|
+
/**
|
|
178
|
+
* Get all unique folder names (collections) in the content directory
|
|
179
|
+
* @param baseDir - Base directory to scan (default: "")
|
|
180
|
+
* @returns Array of folder names
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* const collections = await getCollections();
|
|
185
|
+
* // Returns: ["blog", "pages", "docs", ...]
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
declare function getCollections(baseDir?: string): Promise<string[]>;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Upload a file and return its public URL
|
|
192
|
+
* @param fileName - Original filename
|
|
193
|
+
* @param buffer - File content buffer
|
|
194
|
+
* @returns Public URL path to the uploaded file
|
|
195
|
+
*/
|
|
196
|
+
declare function uploadFile(fileName: string, buffer: Buffer): Promise<string>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Session payload structure for JWT
|
|
200
|
+
*/
|
|
201
|
+
interface SessionPayload {
|
|
202
|
+
/** Username of the authenticated user */
|
|
203
|
+
username: string;
|
|
204
|
+
/** Expiration timestamp (Unix time) */
|
|
205
|
+
exp: number;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create a new session token for the given username
|
|
209
|
+
* @param username - The username to create a session for
|
|
210
|
+
* @returns Signed JWT string
|
|
211
|
+
*/
|
|
212
|
+
declare function createSession(username: string): Promise<string>;
|
|
213
|
+
/**
|
|
214
|
+
* Verify and decode a session token
|
|
215
|
+
* @param token - The JWT token to verify
|
|
216
|
+
* @returns Decoded payload if valid, null otherwise
|
|
217
|
+
*/
|
|
218
|
+
declare function verifySession(token: string): Promise<SessionPayload | null>;
|
|
219
|
+
/**
|
|
220
|
+
* Get session from Next.js request cookies
|
|
221
|
+
*/
|
|
222
|
+
declare function getSessionFromCookies(): Promise<SessionPayload | null>;
|
|
223
|
+
/**
|
|
224
|
+
* Set session cookie
|
|
225
|
+
*/
|
|
226
|
+
declare function setSessionCookie(token: string): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Clear session cookie
|
|
229
|
+
*/
|
|
230
|
+
declare function clearSessionCookie(): Promise<void>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Validate username and password against environment variables using timing-safe comparison
|
|
234
|
+
* @param username - Username to validate
|
|
235
|
+
* @param password - Password to validate
|
|
236
|
+
* @returns True if credentials are valid, false otherwise
|
|
237
|
+
* @throws Error if environment variables are not configured
|
|
238
|
+
*/
|
|
239
|
+
declare function validateCredentials(username: string, password: string): boolean;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Login endpoint with rate limiting
|
|
243
|
+
*/
|
|
244
|
+
declare function handleLogin(request: NextRequest): Promise<NextResponse<{
|
|
245
|
+
error: string;
|
|
246
|
+
}> | NextResponse<{
|
|
247
|
+
success: boolean;
|
|
248
|
+
}>>;
|
|
249
|
+
/**
|
|
250
|
+
* Logout endpoint
|
|
251
|
+
*/
|
|
252
|
+
declare function handleLogout(): Promise<NextResponse<{
|
|
253
|
+
success: boolean;
|
|
254
|
+
}>>;
|
|
255
|
+
/**
|
|
256
|
+
* Get content endpoint
|
|
257
|
+
*/
|
|
258
|
+
declare function handleGetContent(_request: NextRequest, filePath: string): Promise<NextResponse<{
|
|
259
|
+
error: string;
|
|
260
|
+
}> | NextResponse<MarkdownData>>;
|
|
261
|
+
/**
|
|
262
|
+
* Save content endpoint
|
|
263
|
+
*/
|
|
264
|
+
declare function handleSaveContent(request: NextRequest, filePath: string): Promise<NextResponse<{
|
|
265
|
+
error: string;
|
|
266
|
+
}> | NextResponse<{
|
|
267
|
+
success: boolean;
|
|
268
|
+
}>>;
|
|
269
|
+
/**
|
|
270
|
+
* Delete content endpoint
|
|
271
|
+
*/
|
|
272
|
+
declare function handleDeleteContent(_request: NextRequest, filePath: string): Promise<NextResponse<{
|
|
273
|
+
error: string;
|
|
274
|
+
}> | NextResponse<{
|
|
275
|
+
success: boolean;
|
|
276
|
+
}>>;
|
|
277
|
+
/**
|
|
278
|
+
* List files endpoint
|
|
279
|
+
*/
|
|
280
|
+
declare function handleListFiles(directory?: string): Promise<NextResponse<{
|
|
281
|
+
error: string;
|
|
282
|
+
}> | NextResponse<{
|
|
283
|
+
entries: FileEntry[];
|
|
284
|
+
}>>;
|
|
285
|
+
/**
|
|
286
|
+
* Create API route handlers for Next.js App Router
|
|
287
|
+
*/
|
|
288
|
+
declare function createContentApiHandlers(): {
|
|
289
|
+
GET(request: NextRequest, { params }: {
|
|
290
|
+
params: {
|
|
291
|
+
path: string[];
|
|
292
|
+
};
|
|
293
|
+
}): Promise<NextResponse<{
|
|
294
|
+
error: string;
|
|
295
|
+
}> | NextResponse<MarkdownData>>;
|
|
296
|
+
POST(request: NextRequest, { params }: {
|
|
297
|
+
params: {
|
|
298
|
+
path: string[];
|
|
299
|
+
};
|
|
300
|
+
}): Promise<NextResponse<{
|
|
301
|
+
error: string;
|
|
302
|
+
}> | NextResponse<{
|
|
303
|
+
success: boolean;
|
|
304
|
+
}>>;
|
|
305
|
+
DELETE(request: NextRequest, { params }: {
|
|
306
|
+
params: {
|
|
307
|
+
path: string[];
|
|
308
|
+
};
|
|
309
|
+
}): Promise<NextResponse<{
|
|
310
|
+
error: string;
|
|
311
|
+
}> | NextResponse<{
|
|
312
|
+
success: boolean;
|
|
313
|
+
}>>;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Create list API handlers
|
|
317
|
+
*/
|
|
318
|
+
declare function createListApiHandlers(): {
|
|
319
|
+
GET(_request: NextRequest, { params }: {
|
|
320
|
+
params: {
|
|
321
|
+
path?: string[];
|
|
322
|
+
};
|
|
323
|
+
}): Promise<NextResponse<{
|
|
324
|
+
error: string;
|
|
325
|
+
}> | NextResponse<{
|
|
326
|
+
entries: FileEntry[];
|
|
327
|
+
}>>;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Handle file upload
|
|
332
|
+
*/
|
|
333
|
+
declare function handleUpload(request: NextRequest): Promise<NextResponse<{
|
|
334
|
+
error: string;
|
|
335
|
+
}> | NextResponse<{
|
|
336
|
+
success: boolean;
|
|
337
|
+
url: string;
|
|
338
|
+
filename: string;
|
|
339
|
+
}>>;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Create middleware to protect admin routes
|
|
343
|
+
*/
|
|
344
|
+
declare function createAuthMiddleware(options?: {
|
|
345
|
+
loginPath?: string;
|
|
346
|
+
protectedPaths?: string[];
|
|
347
|
+
}): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
348
|
+
|
|
349
|
+
interface InitCMSConfig {
|
|
350
|
+
/** Directory where content is stored (default: "public/content") */
|
|
351
|
+
contentDir?: string;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Initialize CMS in a Next.js project
|
|
355
|
+
* Creates the content directory and an example markdown file.
|
|
356
|
+
* @param config - Configuration options
|
|
357
|
+
*/
|
|
358
|
+
declare function initCMS(config?: InitCMSConfig): Promise<void>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Maximum username length (prevents DoS)
|
|
362
|
+
*/
|
|
363
|
+
declare const MAX_USERNAME_LENGTH = 100;
|
|
364
|
+
/**
|
|
365
|
+
* Maximum password length (prevents DoS)
|
|
366
|
+
*/
|
|
367
|
+
declare const MAX_PASSWORD_LENGTH = 1000;
|
|
368
|
+
/**
|
|
369
|
+
* Maximum file size in bytes (10MB)
|
|
370
|
+
*/
|
|
371
|
+
declare const MAX_FILE_SIZE: number;
|
|
372
|
+
/**
|
|
373
|
+
* Validate and sanitize file path to prevent directory traversal
|
|
374
|
+
* @param filePath - The file path to validate
|
|
375
|
+
* @returns Sanitized file path
|
|
376
|
+
* @throws Error if path is invalid or attempts directory traversal
|
|
377
|
+
*/
|
|
378
|
+
declare function validateFilePath(filePath: string): string;
|
|
379
|
+
/**
|
|
380
|
+
* Validate username input
|
|
381
|
+
* @param username - The username to validate
|
|
382
|
+
* @returns True if valid
|
|
383
|
+
* @throws Error if invalid
|
|
384
|
+
*/
|
|
385
|
+
declare function validateUsername(username: string): boolean;
|
|
386
|
+
/**
|
|
387
|
+
* Validate password input
|
|
388
|
+
* @param password - The password to validate
|
|
389
|
+
* @returns True if valid
|
|
390
|
+
* @throws Error if invalid
|
|
391
|
+
*/
|
|
392
|
+
declare function validatePassword(password: string): boolean;
|
|
393
|
+
/**
|
|
394
|
+
* Validate file size
|
|
395
|
+
* @param size - File size in bytes
|
|
396
|
+
* @returns True if valid
|
|
397
|
+
* @throws Error if too large
|
|
398
|
+
*/
|
|
399
|
+
declare function validateFileSize(size: number): boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Sanitize frontmatter data to prevent injection
|
|
402
|
+
* @param data - The data object to sanitize
|
|
403
|
+
* @returns Sanitized data
|
|
404
|
+
*/
|
|
405
|
+
declare function sanitizeFrontmatter(data: Record<string, any>): Record<string, any>;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Simple in-memory rate limiter for login attempts
|
|
409
|
+
*/
|
|
410
|
+
/**
|
|
411
|
+
* Check if IP/identifier is rate limited
|
|
412
|
+
* @param identifier - IP address or other unique identifier
|
|
413
|
+
* @returns Object with isLimited and remaining attempts
|
|
414
|
+
*/
|
|
415
|
+
declare function checkRateLimit(identifier: string): {
|
|
416
|
+
isLimited: boolean;
|
|
417
|
+
remaining: number;
|
|
418
|
+
resetTime: number;
|
|
419
|
+
};
|
|
420
|
+
/**
|
|
421
|
+
* Increment rate limit counter for identifier
|
|
422
|
+
* @param identifier - IP address or other unique identifier
|
|
423
|
+
*/
|
|
424
|
+
declare function incrementRateLimit(identifier: string): void;
|
|
425
|
+
/**
|
|
426
|
+
* Reset rate limit for identifier (use after successful login)
|
|
427
|
+
* @param identifier - IP address or other unique identifier
|
|
428
|
+
*/
|
|
429
|
+
declare function resetRateLimit(identifier: string): void;
|
|
430
|
+
|
|
431
|
+
export { type CollectionItem, type FileEntry, GitHubStorage, type InitCMSConfig, LocalStorage, MAX_FILE_SIZE, MAX_PASSWORD_LENGTH, MAX_USERNAME_LENGTH, type MarkdownData, type StorageProvider, checkRateLimit, clearSessionCookie, createAuthMiddleware, createContentApiHandlers, createListApiHandlers, createSession, deleteMarkdownContent, getCollectionItem, getCollectionItems, getCollections, getMarkdownContent, getSessionFromCookies, getStorageProvider, handleDeleteContent, handleGetContent, handleListFiles, handleLogin, handleLogout, handleSaveContent, handleUpload, incrementRateLimit, initCMS, listDirectory, listMarkdownFiles, markdownFileExists, parseMarkdown, resetRateLimit, sanitizeFrontmatter, saveMarkdownContent, setSessionCookie, stringifyMarkdown, uploadFile, validateCredentials, validateFilePath, validateFileSize, validatePassword, validateUsername, verifySession };
|