@jhits/plugin-images 0.0.6 → 0.0.7

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.
Files changed (170) hide show
  1. package/dist/api/fallback/route.d.ts +7 -0
  2. package/dist/api/fallback/route.d.ts.map +1 -0
  3. package/dist/api/fallback/route.js +65 -0
  4. package/dist/api/index.d.ts +9 -0
  5. package/dist/api/index.d.ts.map +1 -0
  6. package/dist/api/index.js +8 -0
  7. package/dist/api/list/index.d.ts +21 -0
  8. package/dist/api/list/index.d.ts.map +1 -0
  9. package/dist/api/list/index.js +80 -0
  10. package/dist/api/resolve/route.d.ts +39 -0
  11. package/dist/api/resolve/route.d.ts.map +1 -0
  12. package/dist/api/resolve/route.js +213 -0
  13. package/dist/api/router.d.ts +14 -0
  14. package/dist/api/router.d.ts.map +1 -0
  15. package/dist/api/router.js +67 -0
  16. package/dist/api/upload/index.d.ts +20 -0
  17. package/dist/api/upload/index.d.ts.map +1 -0
  18. package/dist/api/upload/index.js +65 -0
  19. package/dist/api/uploads/[filename]/route.d.ts +21 -0
  20. package/dist/api/uploads/[filename]/route.d.ts.map +1 -0
  21. package/dist/api/uploads/[filename]/route.js +80 -0
  22. package/dist/api-server.d.ts +9 -0
  23. package/dist/api-server.d.ts.map +1 -0
  24. package/dist/api-server.js +9 -0
  25. package/dist/components/BackgroundImage.d.ts +11 -0
  26. package/dist/components/BackgroundImage.d.ts.map +1 -0
  27. package/dist/components/BackgroundImage.js +33 -0
  28. package/dist/components/GlobalImageEditor/config.d.ts +9 -0
  29. package/dist/components/GlobalImageEditor/config.d.ts.map +1 -0
  30. package/dist/components/GlobalImageEditor/config.js +17 -0
  31. package/dist/components/GlobalImageEditor/eventHandlers.d.ts +20 -0
  32. package/dist/components/GlobalImageEditor/eventHandlers.d.ts.map +1 -0
  33. package/dist/components/GlobalImageEditor/eventHandlers.js +210 -0
  34. package/dist/components/GlobalImageEditor/imageDetection.d.ts +16 -0
  35. package/dist/components/GlobalImageEditor/imageDetection.d.ts.map +1 -0
  36. package/dist/components/GlobalImageEditor/imageDetection.js +135 -0
  37. package/dist/components/GlobalImageEditor/imageSetup.d.ts +9 -0
  38. package/dist/components/GlobalImageEditor/imageSetup.d.ts.map +1 -0
  39. package/dist/components/GlobalImageEditor/imageSetup.js +260 -0
  40. package/dist/components/GlobalImageEditor/saveLogic.d.ts +26 -0
  41. package/dist/components/GlobalImageEditor/saveLogic.d.ts.map +1 -0
  42. package/dist/components/GlobalImageEditor/saveLogic.js +98 -0
  43. package/dist/components/GlobalImageEditor/stylingDetection.d.ts +9 -0
  44. package/dist/components/GlobalImageEditor/stylingDetection.d.ts.map +1 -0
  45. package/dist/components/GlobalImageEditor/stylingDetection.js +110 -0
  46. package/dist/components/GlobalImageEditor/transformParsing.d.ts +16 -0
  47. package/dist/components/GlobalImageEditor/transformParsing.d.ts.map +1 -0
  48. package/dist/components/GlobalImageEditor/transformParsing.js +68 -0
  49. package/dist/components/GlobalImageEditor/types.d.ts +36 -0
  50. package/dist/components/GlobalImageEditor/types.d.ts.map +1 -0
  51. package/dist/components/GlobalImageEditor/types.js +4 -0
  52. package/dist/components/GlobalImageEditor.d.ts +8 -0
  53. package/dist/components/GlobalImageEditor.d.ts.map +1 -0
  54. package/dist/components/GlobalImageEditor.js +232 -0
  55. package/dist/components/Image.d.ts +22 -0
  56. package/dist/components/Image.d.ts.map +1 -0
  57. package/dist/components/Image.js +227 -0
  58. package/dist/components/ImageBrowserModal.d.ts +13 -0
  59. package/dist/components/ImageBrowserModal.d.ts.map +1 -0
  60. package/dist/components/ImageBrowserModal.js +507 -0
  61. package/dist/components/ImageEditor.d.ts +27 -0
  62. package/dist/components/ImageEditor.d.ts.map +1 -0
  63. package/dist/components/ImageEditor.js +172 -0
  64. package/dist/components/ImageEffectsPanel.d.ts +10 -0
  65. package/dist/components/ImageEffectsPanel.d.ts.map +1 -0
  66. package/dist/components/ImageEffectsPanel.js +11 -0
  67. package/dist/components/ImagePicker.d.ts +3 -0
  68. package/dist/components/ImagePicker.d.ts.map +1 -0
  69. package/dist/components/ImagePicker.js +142 -0
  70. package/dist/components/ImagesPluginInit.d.ts +24 -0
  71. package/dist/components/ImagesPluginInit.d.ts.map +1 -0
  72. package/dist/components/ImagesPluginInit.js +28 -0
  73. package/dist/components/index.d.ts +9 -0
  74. package/dist/components/index.d.ts.map +1 -0
  75. package/dist/components/index.js +7 -0
  76. package/dist/config.d.ts +14 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.js +172 -0
  79. package/dist/hooks/useImagePicker.d.ts +20 -0
  80. package/dist/hooks/useImagePicker.d.ts.map +1 -0
  81. package/dist/hooks/useImagePicker.js +320 -0
  82. package/dist/index.d.ts +23 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +28 -0
  85. package/dist/index.server.d.ts +11 -0
  86. package/dist/index.server.d.ts.map +1 -0
  87. package/dist/index.server.js +10 -0
  88. package/dist/init.d.ts +33 -0
  89. package/dist/init.d.ts.map +1 -0
  90. package/dist/init.js +43 -0
  91. package/dist/types/index.d.ts +80 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/index.js +4 -0
  94. package/dist/utils/fallback.d.ts +27 -0
  95. package/dist/utils/fallback.d.ts.map +1 -0
  96. package/dist/utils/fallback.js +63 -0
  97. package/dist/utils/transforms.d.ts +26 -0
  98. package/dist/utils/transforms.d.ts.map +1 -0
  99. package/dist/utils/transforms.js +38 -0
  100. package/dist/views/ImageManager.d.ts +10 -0
  101. package/dist/views/ImageManager.d.ts.map +1 -0
  102. package/dist/views/ImageManager.js +9 -0
  103. package/package.json +8 -8
  104. package/src/assets/noimagefound.jpg +0 -0
  105. package/src/components/BackgroundImage.d.ts +11 -0
  106. package/src/components/BackgroundImage.d.ts.map +1 -0
  107. package/src/components/BackgroundImage.js +35 -0
  108. package/src/components/GlobalImageEditor/config.d.ts +9 -0
  109. package/src/components/GlobalImageEditor/config.d.ts.map +1 -0
  110. package/src/components/GlobalImageEditor/config.js +18 -0
  111. package/src/components/GlobalImageEditor/eventHandlers.d.ts +20 -0
  112. package/src/components/GlobalImageEditor/eventHandlers.d.ts.map +1 -0
  113. package/src/components/GlobalImageEditor/eventHandlers.js +206 -0
  114. package/src/components/GlobalImageEditor/imageDetection.d.ts +16 -0
  115. package/src/components/GlobalImageEditor/imageDetection.d.ts.map +1 -0
  116. package/src/components/GlobalImageEditor/imageDetection.js +130 -0
  117. package/src/components/GlobalImageEditor/imageSetup.d.ts +9 -0
  118. package/src/components/GlobalImageEditor/imageSetup.d.ts.map +1 -0
  119. package/src/components/GlobalImageEditor/imageSetup.js +261 -0
  120. package/src/components/GlobalImageEditor/saveLogic.d.ts +26 -0
  121. package/src/components/GlobalImageEditor/saveLogic.d.ts.map +1 -0
  122. package/src/components/GlobalImageEditor/saveLogic.js +99 -0
  123. package/src/components/GlobalImageEditor/stylingDetection.d.ts +9 -0
  124. package/src/components/GlobalImageEditor/stylingDetection.d.ts.map +1 -0
  125. package/src/components/GlobalImageEditor/stylingDetection.js +110 -0
  126. package/src/components/GlobalImageEditor/transformParsing.d.ts +16 -0
  127. package/src/components/GlobalImageEditor/transformParsing.d.ts.map +1 -0
  128. package/src/components/GlobalImageEditor/transformParsing.js +68 -0
  129. package/src/components/GlobalImageEditor/types.d.ts +36 -0
  130. package/src/components/GlobalImageEditor/types.d.ts.map +1 -0
  131. package/src/components/GlobalImageEditor/types.js +4 -0
  132. package/src/components/GlobalImageEditor.d.ts +8 -0
  133. package/src/components/GlobalImageEditor.d.ts.map +1 -0
  134. package/src/components/GlobalImageEditor.js +227 -0
  135. package/src/components/Image.d.ts +22 -0
  136. package/src/components/Image.d.ts.map +1 -0
  137. package/src/components/Image.js +229 -0
  138. package/src/components/ImageBrowserModal.d.ts +13 -0
  139. package/src/components/ImageBrowserModal.d.ts.map +1 -0
  140. package/src/components/ImageBrowserModal.js +504 -0
  141. package/src/components/ImageEditor.d.ts +27 -0
  142. package/src/components/ImageEditor.d.ts.map +1 -0
  143. package/src/components/ImageEditor.js +173 -0
  144. package/src/components/ImagePicker.d.ts +3 -0
  145. package/src/components/ImagePicker.d.ts.map +1 -0
  146. package/src/components/ImagePicker.js +143 -0
  147. package/src/components/ImagesPluginInit.d.ts +24 -0
  148. package/src/components/ImagesPluginInit.d.ts.map +1 -0
  149. package/src/components/ImagesPluginInit.js +28 -0
  150. package/src/hooks/useImagePicker.d.ts +20 -0
  151. package/src/hooks/useImagePicker.d.ts.map +1 -0
  152. package/src/hooks/useImagePicker.js +322 -0
  153. package/src/index.d.ts +23 -0
  154. package/src/index.d.ts.map +1 -0
  155. package/src/index.js +28 -0
  156. package/src/init.d.ts +33 -0
  157. package/src/init.d.ts.map +1 -0
  158. package/src/init.js +43 -0
  159. package/src/types/index.d.ts +80 -0
  160. package/src/types/index.d.ts.map +1 -0
  161. package/src/types/index.js +4 -0
  162. package/src/utils/fallback.d.ts +27 -0
  163. package/src/utils/fallback.d.ts.map +1 -0
  164. package/src/utils/fallback.js +63 -0
  165. package/src/utils/transforms.d.ts +26 -0
  166. package/src/utils/transforms.d.ts.map +1 -0
  167. package/src/utils/transforms.js +38 -0
  168. package/src/views/ImageManager.d.ts +10 -0
  169. package/src/views/ImageManager.d.ts.map +1 -0
  170. package/src/views/ImageManager.js +9 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Fallback Image API Route
3
+ * Serves the default "image not found" fallback image
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ export declare function GET(request: NextRequest): Promise<NextResponse<unknown>>;
7
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/api/fallback/route.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,kCA2D7C"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Fallback Image API Route
3
+ * Serves the default "image not found" fallback image
4
+ */
5
+ import { NextResponse } from 'next/server';
6
+ import { readFile } from 'fs/promises';
7
+ import path from 'path';
8
+ export async function GET(request) {
9
+ try {
10
+ // Try multiple possible paths for the fallback image
11
+ const possiblePaths = [
12
+ // Path in workspace root (monorepo development)
13
+ path.join(process.cwd(), 'packages', 'plugin-images', 'src', 'assets', 'noimagefound.jpg'),
14
+ // Path in node_modules (production)
15
+ path.join(process.cwd(), 'node_modules', '@jhits', 'plugin-images', 'src', 'assets', 'noimagefound.jpg'),
16
+ // Fallback to app's public folder if plugin image not found
17
+ path.join(process.cwd(), 'public', 'noimagefound.jpg'),
18
+ ];
19
+ let filePath = null;
20
+ for (const testPath of possiblePaths) {
21
+ try {
22
+ await readFile(testPath);
23
+ filePath = testPath;
24
+ break;
25
+ }
26
+ catch {
27
+ // Continue to next path
28
+ }
29
+ }
30
+ if (!filePath) {
31
+ // If no image found, return a simple placeholder SVG
32
+ const svgPlaceholder = `<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
33
+ <rect width="400" height="300" fill="#f3f4f6"/>
34
+ <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-size="16" fill="#9ca3af">Image not found</text>
35
+ </svg>`;
36
+ return new NextResponse(svgPlaceholder, {
37
+ headers: {
38
+ 'Content-Type': 'image/svg+xml',
39
+ 'Cache-Control': 'public, max-age=3600',
40
+ },
41
+ });
42
+ }
43
+ const fileBuffer = await readFile(filePath);
44
+ return new NextResponse(fileBuffer, {
45
+ headers: {
46
+ 'Content-Type': 'image/jpeg',
47
+ 'Cache-Control': 'public, max-age=31536000, immutable',
48
+ },
49
+ });
50
+ }
51
+ catch (error) {
52
+ console.error('[FallbackImage] Error serving fallback image:', error);
53
+ // Return SVG placeholder on error
54
+ const svgPlaceholder = `<svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
55
+ <rect width="400" height="300" fill="#f3f4f6"/>
56
+ <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-size="16" fill="#9ca3af">Image not found</text>
57
+ </svg>`;
58
+ return new NextResponse(svgPlaceholder, {
59
+ headers: {
60
+ 'Content-Type': 'image/svg+xml',
61
+ 'Cache-Control': 'public, max-age=3600',
62
+ },
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Plugin Images API Exports
3
+ */
4
+ export { handleImagesApi } from './router';
5
+ export { GET as GET_LIST } from './list';
6
+ export { POST as POST_UPLOAD } from './upload';
7
+ export { GET as GET_RESOLVE, POST as POST_RESOLVE } from './resolve/route';
8
+ export { GET as GET_UPLOADS, DELETE as DELETE_UPLOADS } from './uploads/[filename]/route';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Plugin Images API Exports
3
+ */
4
+ export { handleImagesApi } from './router';
5
+ export { GET as GET_LIST } from './list';
6
+ export { POST as POST_UPLOAD } from './upload';
7
+ export { GET as GET_RESOLVE, POST as POST_RESOLVE } from './resolve/route';
8
+ export { GET as GET_UPLOADS, DELETE as DELETE_UPLOADS } from './uploads/[filename]/route';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Image List API Handler
3
+ * GET /api/plugin-images/list - Returns paginated list of uploaded images
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ export declare function GET(request: NextRequest): Promise<NextResponse<{
7
+ images: {
8
+ id: string;
9
+ filename: string;
10
+ url: string;
11
+ size: number;
12
+ mimeType: string;
13
+ uploadedAt: string;
14
+ }[];
15
+ total: number;
16
+ page: number;
17
+ limit: number;
18
+ }> | NextResponse<{
19
+ error: string;
20
+ }>>;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/list/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;IAmF7C"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Image List API Handler
3
+ * GET /api/plugin-images/list - Returns paginated list of uploaded images
4
+ */
5
+ import { NextResponse } from 'next/server';
6
+ import { readdir, stat } from 'fs/promises';
7
+ import path from 'path';
8
+ const uploadsDir = path.join(process.cwd(), 'data', 'uploads');
9
+ export async function GET(request) {
10
+ try {
11
+ const { searchParams } = new URL(request.url);
12
+ const page = parseInt(searchParams.get('page') || '1');
13
+ const limit = parseInt(searchParams.get('limit') || '20');
14
+ const search = searchParams.get('search') || '';
15
+ // Read uploads directory
16
+ let files = [];
17
+ try {
18
+ files = await readdir(uploadsDir);
19
+ }
20
+ catch (error) {
21
+ // Directory doesn't exist yet
22
+ return NextResponse.json({
23
+ images: [],
24
+ total: 0,
25
+ page: 1,
26
+ limit,
27
+ });
28
+ }
29
+ // Filter image files and get metadata
30
+ const imageExtensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif'];
31
+ const imageFiles = files.filter(file => {
32
+ const ext = path.extname(file).toLowerCase();
33
+ return imageExtensions.includes(ext);
34
+ });
35
+ // Apply search filter if provided
36
+ const filteredFiles = search
37
+ ? imageFiles.filter(file => file.toLowerCase().includes(search.toLowerCase()))
38
+ : imageFiles;
39
+ // Sort by modification time (newest first)
40
+ const filesWithStats = await Promise.all(filteredFiles.map(async (filename) => {
41
+ const filePath = path.join(uploadsDir, filename);
42
+ const stats = await stat(filePath);
43
+ return {
44
+ filename,
45
+ mtime: stats.mtime,
46
+ size: stats.size,
47
+ };
48
+ }));
49
+ filesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
50
+ // Paginate
51
+ const start = (page - 1) * limit;
52
+ const end = start + limit;
53
+ const paginatedFiles = filesWithStats.slice(start, end);
54
+ // Build image metadata
55
+ const images = paginatedFiles.map(({ filename, mtime, size }) => {
56
+ const ext = path.extname(filename).toLowerCase();
57
+ const mimeType = ext === '.png' ? 'image/png' :
58
+ ext === '.webp' ? 'image/webp' :
59
+ ext === '.gif' ? 'image/gif' : 'image/jpeg';
60
+ return {
61
+ id: filename,
62
+ filename,
63
+ url: `/api/uploads/${filename}`,
64
+ size,
65
+ mimeType,
66
+ uploadedAt: mtime.toISOString(),
67
+ };
68
+ });
69
+ return NextResponse.json({
70
+ images,
71
+ total: filteredFiles.length,
72
+ page,
73
+ limit,
74
+ });
75
+ }
76
+ catch (error) {
77
+ console.error('List images error:', error);
78
+ return NextResponse.json({ error: 'Failed to list images' }, { status: 500 });
79
+ }
80
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Image ID Resolution API Route
3
+ * Resolves semantic image IDs to actual filenames
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ /**
7
+ * GET /api/plugin-images/resolve?id=home-about-card
8
+ * Resolves a semantic image ID to the actual filename
9
+ */
10
+ export declare function GET(request: NextRequest): Promise<NextResponse<{
11
+ error: string;
12
+ }> | NextResponse<{
13
+ id: string;
14
+ filename: any;
15
+ brightness: any;
16
+ blur: any;
17
+ scale: number;
18
+ positionX: any;
19
+ positionY: any;
20
+ url: string;
21
+ }>>;
22
+ /**
23
+ * POST /api/plugin-images/resolve
24
+ * Sets or updates the mapping between an ID and filename
25
+ * Body: { id: string, filename: string }
26
+ */
27
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
28
+ error: string;
29
+ }> | NextResponse<{
30
+ success: boolean;
31
+ id: any;
32
+ filename: any;
33
+ brightness: any;
34
+ blur: any;
35
+ scale: any;
36
+ positionX: any;
37
+ positionY: any;
38
+ }>>;
39
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../src/api/resolve/route.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA2GxD;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;IAmD7C;AAED;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;IAiE9C"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Image ID Resolution API Route
3
+ * Resolves semantic image IDs to actual filenames
4
+ */
5
+ import { NextResponse } from 'next/server';
6
+ import { readFile, writeFile, mkdir } from 'fs/promises';
7
+ import path from 'path';
8
+ const mappingsPath = path.join(process.cwd(), 'data', 'image-mappings.json');
9
+ // Simple mutex to prevent concurrent writes
10
+ let writeLock = false;
11
+ const writeQueue = [];
12
+ async function acquireLock() {
13
+ return new Promise((resolve) => {
14
+ if (!writeLock) {
15
+ writeLock = true;
16
+ resolve(() => {
17
+ writeLock = false;
18
+ const next = writeQueue.shift();
19
+ if (next)
20
+ next();
21
+ });
22
+ }
23
+ else {
24
+ writeQueue.push(() => {
25
+ writeLock = true;
26
+ resolve(() => {
27
+ writeLock = false;
28
+ const next = writeQueue.shift();
29
+ if (next)
30
+ next();
31
+ });
32
+ });
33
+ }
34
+ });
35
+ }
36
+ async function readMappingsSafely() {
37
+ try {
38
+ const content = await readFile(mappingsPath, 'utf-8');
39
+ // Try to parse, if it fails, try to recover
40
+ try {
41
+ return JSON.parse(content);
42
+ }
43
+ catch (parseError) {
44
+ console.error('JSON parse error, attempting recovery:', parseError);
45
+ // Try to extract valid JSON by finding the last complete object
46
+ const lines = content.split('\n');
47
+ let validContent = '';
48
+ let braceCount = 0;
49
+ let inString = false;
50
+ let escapeNext = false;
51
+ for (const line of lines) {
52
+ for (let i = 0; i < line.length; i++) {
53
+ const char = line[i];
54
+ if (escapeNext) {
55
+ escapeNext = false;
56
+ validContent += char;
57
+ continue;
58
+ }
59
+ if (char === '\\') {
60
+ escapeNext = true;
61
+ validContent += char;
62
+ continue;
63
+ }
64
+ if (char === '"') {
65
+ inString = !inString;
66
+ validContent += char;
67
+ continue;
68
+ }
69
+ if (!inString) {
70
+ if (char === '{')
71
+ braceCount++;
72
+ if (char === '}')
73
+ braceCount--;
74
+ }
75
+ validContent += char;
76
+ }
77
+ validContent += '\n';
78
+ // If we've closed all braces, we might have valid JSON
79
+ if (braceCount === 0 && validContent.trim().endsWith('}')) {
80
+ try {
81
+ return JSON.parse(validContent.trim());
82
+ }
83
+ catch {
84
+ // Continue trying
85
+ }
86
+ }
87
+ }
88
+ // If recovery failed, return empty object
89
+ console.error('Failed to recover JSON, using empty mappings');
90
+ return {};
91
+ }
92
+ }
93
+ catch (error) {
94
+ console.error('Failed to read mappings file:', error);
95
+ return {};
96
+ }
97
+ }
98
+ async function ensureMappingsFile() {
99
+ try {
100
+ const dir = path.dirname(mappingsPath);
101
+ await mkdir(dir, { recursive: true });
102
+ // Check if file exists, if not create empty mappings
103
+ try {
104
+ await readFile(mappingsPath);
105
+ }
106
+ catch {
107
+ await writeFile(mappingsPath, JSON.stringify({}, null, 2));
108
+ }
109
+ }
110
+ catch (error) {
111
+ // Ignore errors
112
+ }
113
+ }
114
+ /**
115
+ * GET /api/plugin-images/resolve?id=home-about-card
116
+ * Resolves a semantic image ID to the actual filename
117
+ */
118
+ export async function GET(request) {
119
+ try {
120
+ await ensureMappingsFile();
121
+ const { searchParams } = new URL(request.url);
122
+ const id = searchParams.get('id');
123
+ if (!id) {
124
+ return NextResponse.json({ error: 'Missing id parameter' }, { status: 400 });
125
+ }
126
+ const mappings = await readMappingsSafely();
127
+ const mapping = mappings[id];
128
+ if (!mapping) {
129
+ return NextResponse.json({ error: 'Image ID not found', id }, { status: 404 });
130
+ }
131
+ // Support both old format (just filename string) and new format (object)
132
+ const filename = typeof mapping === 'string' ? mapping : mapping.filename;
133
+ const brightness = typeof mapping === 'object' ? (mapping.brightness ?? 100) : 100;
134
+ const blur = typeof mapping === 'object' ? (mapping.blur ?? 0) : 0;
135
+ // Ensure scale is within valid range (0.1 to 5.0), default to 1.0 if invalid
136
+ const rawScale = typeof mapping === 'object' ? (mapping.scale ?? 1.0) : 1.0;
137
+ const scale = rawScale > 0 && rawScale <= 5.0 ? Math.max(0.1, rawScale) : 1.0;
138
+ const positionX = typeof mapping === 'object' ? (mapping.positionX ?? 0) : 0;
139
+ const positionY = typeof mapping === 'object' ? (mapping.positionY ?? 0) : 0;
140
+ return NextResponse.json({
141
+ id,
142
+ filename,
143
+ brightness,
144
+ blur,
145
+ scale,
146
+ positionX,
147
+ positionY,
148
+ url: `/api/uploads/${encodeURIComponent(filename)}`,
149
+ });
150
+ }
151
+ catch (error) {
152
+ console.error('Resolve error:', error);
153
+ return NextResponse.json({ error: 'Failed to resolve image ID' }, { status: 500 });
154
+ }
155
+ }
156
+ /**
157
+ * POST /api/plugin-images/resolve
158
+ * Sets or updates the mapping between an ID and filename
159
+ * Body: { id: string, filename: string }
160
+ */
161
+ export async function POST(request) {
162
+ try {
163
+ await ensureMappingsFile();
164
+ const body = await request.json();
165
+ const { id, filename, brightness, blur, scale, positionX, positionY } = body;
166
+ if (!id || !filename) {
167
+ return NextResponse.json({ error: 'Missing id or filename' }, { status: 400 });
168
+ }
169
+ // Acquire write lock to prevent concurrent writes
170
+ const releaseLock = await acquireLock();
171
+ try {
172
+ const mappings = await readMappingsSafely();
173
+ // Store as object with optional brightness, blur, scale, and position
174
+ // Merge with existing values to avoid overwriting fields that aren't provided
175
+ // Ensure scale is within valid range (0.1 to 5.0)
176
+ const safeScale = scale !== undefined ? Math.max(0.1, Math.min(5.0, scale)) : undefined;
177
+ const existing = mappings[id] || {};
178
+ mappings[id] = {
179
+ ...existing, // Preserve existing values
180
+ filename, // Always update filename
181
+ ...(brightness !== undefined && { brightness }),
182
+ ...(blur !== undefined && { blur }),
183
+ ...(safeScale !== undefined && { scale: safeScale }),
184
+ ...(positionX !== undefined && { positionX }),
185
+ ...(positionY !== undefined && { positionY }),
186
+ };
187
+ // Write atomically using a temporary file
188
+ const tempPath = mappingsPath + '.tmp';
189
+ await writeFile(tempPath, JSON.stringify(mappings, null, 2), 'utf-8');
190
+ // Atomic rename (this is atomic on most filesystems)
191
+ const { rename } = await import('fs/promises');
192
+ await rename(tempPath, mappingsPath);
193
+ }
194
+ finally {
195
+ releaseLock();
196
+ }
197
+ console.log('SAVING TO DB:', { id, scale, brightness, blur, positionX, positionY });
198
+ return NextResponse.json({
199
+ success: true,
200
+ id,
201
+ filename,
202
+ brightness: brightness ?? 100,
203
+ blur: blur ?? 0,
204
+ scale: scale ?? 1.0,
205
+ positionX: positionX ?? 0,
206
+ positionY: positionY ?? 0,
207
+ });
208
+ }
209
+ catch (error) {
210
+ console.error('Set mapping error:', error);
211
+ return NextResponse.json({ error: 'Failed to set image mapping' }, { status: 500 });
212
+ }
213
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Plugin Images API Router
3
+ * Centralized API handler for all images plugin routes
4
+ *
5
+ * This router handles requests to /api/plugin-images/*
6
+ * and routes them to the appropriate handler
7
+ */
8
+ import { NextRequest, NextResponse } from 'next/server';
9
+ /**
10
+ * Handle images API requests
11
+ * Routes requests to appropriate handlers based on path
12
+ */
13
+ export declare function handleImagesApi(req: NextRequest, path: string[]): Promise<NextResponse>;
14
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD;;;GAGG;AACH,wBAAsB,eAAe,CACjC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,YAAY,CAAC,CAgEvB"}
@@ -0,0 +1,67 @@
1
+ 'use server';
2
+ /**
3
+ * Plugin Images API Router
4
+ * Centralized API handler for all images plugin routes
5
+ *
6
+ * This router handles requests to /api/plugin-images/*
7
+ * and routes them to the appropriate handler
8
+ */
9
+ import { NextResponse } from 'next/server';
10
+ /**
11
+ * Handle images API requests
12
+ * Routes requests to appropriate handlers based on path
13
+ */
14
+ export async function handleImagesApi(req, path) {
15
+ const method = req.method;
16
+ const route = path[0] || '';
17
+ try {
18
+ // Route: /api/plugin-images/list (list images)
19
+ if (!route || route === 'list') {
20
+ if (method === 'GET') {
21
+ const listModule = await import('./list');
22
+ return await listModule.GET(req);
23
+ }
24
+ }
25
+ // Route: /api/plugin-images/upload (upload image)
26
+ else if (route === 'upload') {
27
+ if (method === 'POST') {
28
+ const uploadModule = await import('./upload');
29
+ return await uploadModule.POST(req);
30
+ }
31
+ }
32
+ // Route: /api/plugin-images/resolve (resolve image ID)
33
+ else if (route === 'resolve') {
34
+ if (method === 'GET' || method === 'POST') {
35
+ const resolveModule = await import('./resolve/route');
36
+ if (method === 'GET') {
37
+ return await resolveModule.GET(req);
38
+ }
39
+ return await resolveModule.POST(req);
40
+ }
41
+ }
42
+ // Route: /api/plugin-images/uploads/[filename] (serve/delete image)
43
+ else if (route === 'uploads' && path[1]) {
44
+ const filename = path[1];
45
+ if (method === 'GET' || method === 'DELETE') {
46
+ const uploadsModule = await import('./uploads/[filename]/route');
47
+ if (method === 'GET') {
48
+ return await uploadsModule.GET(req, { params: Promise.resolve({ filename }) });
49
+ }
50
+ return await uploadsModule.DELETE(req, { params: Promise.resolve({ filename }) });
51
+ }
52
+ }
53
+ // Route: /api/plugin-images/fallback (serve fallback image)
54
+ else if (route === 'fallback') {
55
+ if (method === 'GET') {
56
+ const fallbackModule = await import('./fallback/route');
57
+ return await fallbackModule.GET(req);
58
+ }
59
+ }
60
+ // Method not allowed
61
+ return NextResponse.json({ error: `Method ${method} not allowed for route: ${route || '/'}` }, { status: 405 });
62
+ }
63
+ catch (error) {
64
+ console.error('[ImagesApiRouter] Error:', error);
65
+ return NextResponse.json({ error: error.message || 'Internal server error' }, { status: 500 });
66
+ }
67
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Image Upload API Handler
3
+ * POST /api/plugin-images/upload - Handles image file uploads
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
7
+ success: boolean;
8
+ error: string;
9
+ }> | NextResponse<{
10
+ success: boolean;
11
+ image: {
12
+ id: string;
13
+ filename: string;
14
+ url: string;
15
+ size: number;
16
+ mimeType: string;
17
+ uploadedAt: string;
18
+ };
19
+ }>>;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/upload/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;IAiE9C"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Image Upload API Handler
3
+ * POST /api/plugin-images/upload - Handles image file uploads
4
+ */
5
+ import { NextResponse } from 'next/server';
6
+ import { writeFile, mkdir } from 'fs/promises';
7
+ import path from 'path';
8
+ import { randomBytes } from 'crypto';
9
+ // Ensure uploads directory exists
10
+ const uploadsDir = path.join(process.cwd(), 'data', 'uploads');
11
+ async function ensureUploadsDir() {
12
+ try {
13
+ await mkdir(uploadsDir, { recursive: true });
14
+ }
15
+ catch (error) {
16
+ // Directory might already exist
17
+ }
18
+ }
19
+ export async function POST(request) {
20
+ try {
21
+ await ensureUploadsDir();
22
+ const formData = await request.formData();
23
+ const file = formData.get('file');
24
+ if (!file) {
25
+ return NextResponse.json({ success: false, error: 'No file provided' }, { status: 400 });
26
+ }
27
+ // Validate file type
28
+ const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
29
+ if (!allowedTypes.includes(file.type)) {
30
+ return NextResponse.json({ success: false, error: 'Invalid file type. Only images are allowed.' }, { status: 400 });
31
+ }
32
+ // Validate file size (max 10MB)
33
+ const maxSize = 10 * 1024 * 1024; // 10MB
34
+ if (file.size > maxSize) {
35
+ return NextResponse.json({ success: false, error: 'File size exceeds 10MB limit' }, { status: 400 });
36
+ }
37
+ // Generate unique filename
38
+ const ext = path.extname(file.name);
39
+ const uniqueId = randomBytes(16).toString('hex');
40
+ const timestamp = Date.now();
41
+ const uniqueFilename = `${timestamp}-${uniqueId}${ext}`;
42
+ const filePath = path.join(uploadsDir, uniqueFilename);
43
+ // Save file
44
+ const bytes = await file.arrayBuffer();
45
+ const buffer = Buffer.from(bytes);
46
+ await writeFile(filePath, buffer);
47
+ // Get image dimensions (basic - could use sharp for better handling)
48
+ const imageMetadata = {
49
+ id: uniqueFilename,
50
+ filename: file.name,
51
+ url: `/api/uploads/${uniqueFilename}`,
52
+ size: file.size,
53
+ mimeType: file.type,
54
+ uploadedAt: new Date().toISOString(),
55
+ };
56
+ return NextResponse.json({
57
+ success: true,
58
+ image: imageMetadata,
59
+ });
60
+ }
61
+ catch (error) {
62
+ console.error('Upload error:', error);
63
+ return NextResponse.json({ success: false, error: 'Failed to upload image' }, { status: 500 });
64
+ }
65
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Image Uploads API Route
3
+ * Serves uploaded images for the plugin-images system
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ export declare function GET(request: NextRequest, { params }: {
7
+ params: Promise<{
8
+ filename: string;
9
+ }>;
10
+ }): Promise<NextResponse<unknown>>;
11
+ export declare function DELETE(request: NextRequest, { params }: {
12
+ params: Promise<{
13
+ filename: string;
14
+ }>;
15
+ }): Promise<NextResponse<{
16
+ success: boolean;
17
+ message: string;
18
+ }> | NextResponse<{
19
+ error: string;
20
+ }>>;
21
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/api/uploads/[filename]/route.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIxD,wBAAsB,GAAG,CACrB,OAAO,EAAE,WAAW,EACpB,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,kCAuDxD;AAED,wBAAsB,MAAM,CACxB,OAAO,EAAE,WAAW,EACpB,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE;;;;;IAqBxD"}