Skip to content

Commit 0baabac

Browse files
authored
refactor & fix api for get photographs (#2)
1 parent 37715b2 commit 0baabac

File tree

15 files changed

+930
-1403
lines changed

15 files changed

+930
-1403
lines changed

server/.env.example

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
# Re:Earth CMS Integration
2-
REEARTH_CMS_INTEGRATION_API_BASE_URL=https://api.cms.reearth.dev
3-
REEARTH_CMS_INTEGRATION_API_ACCESS_TOKEN=your-reearth-cms-integration-token
4-
REEARTH_CMS_PROJECT_ID=your-reearth-cms-project-id
5-
REEARTH_CMS_PROJECT_PHOTOGRAPHS_MODEL_ID=your-cms-model-id-for-photographs
2+
REEARTH_CMS_INTEGRATION_API_BASE_URL=https://api.cms.reearth.dev/api
3+
REEARTH_CMS_INTEGRATION_API_ACCESS_TOKEN=your_access_token
4+
REEARTH_CMS_PROJECT_ID=your_project_id
5+
REEARTH_CMS_PROJECT_PHOTOGRAPHS_MODEL_ID=your_photographs_model_id
66

77
# Authentication
8-
API_SECRET_KEY=your-default-api-key
8+
API_SECRET_KEY=your_secret_key
99

1010
# CORS Configuration
11-
CORS_ORIGIN=http://localhost:5173
12-
13-
# Upload Configuration
14-
MAX_FILE_SIZE=10485760
15-
UPLOAD_PATH=./uploads
11+
CORS_ORIGIN=http://localhost:5200

server/.yarn/install-state.gz

-35.5 KB
Binary file not shown.

server/api/assets/upload.ts

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { VercelRequest, VercelResponse } from '@vercel/node';
2-
import cors from 'cors';
3-
import multer from 'multer';
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import cors from "cors";
3+
import multer from "multer";
44

5-
import { cmsService } from '../../src/services/cms';
6-
import { authenticate, AuthenticatedRequest } from '../../src/utils/auth';
7-
import { sendSuccess, sendError } from '../../src/utils/response';
5+
import { cmsService } from "../../src/services/cms.js";
6+
import { authenticate, AuthenticatedRequest } from "../../src/utils/auth.js";
7+
import { sendSuccess, sendError } from "../../src/utils/response.js";
88

99
// CORS configuration
1010
const corsOptions = {
11-
origin: process.env.CORS_ORIGIN || '*',
12-
methods: ['POST', 'OPTIONS'],
13-
allowedHeaders: ['Content-Type', 'Authorization']
11+
origin: process.env.CORS_ORIGIN || "*",
12+
methods: ["POST", "OPTIONS"],
13+
allowedHeaders: ["Content-Type", "Authorization"],
1414
};
1515

1616
// Apply CORS middleware
@@ -25,7 +25,6 @@ function runCors(req: VercelRequest, res: VercelResponse) {
2525
});
2626
}
2727

28-
2928
// Multer configuration for memory storage
3029
const upload = multer({
3130
storage: multer.memoryStorage(),
@@ -34,19 +33,25 @@ const upload = multer({
3433
},
3534
fileFilter: (_req, file, cb) => {
3635
// Check if file is an image
37-
const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
36+
const allowedMimes = [
37+
"image/jpeg",
38+
"image/jpg",
39+
"image/png",
40+
"image/webp",
41+
"image/gif",
42+
];
3843
if (allowedMimes.includes(file.mimetype)) {
3944
cb(null, true);
4045
} else {
41-
cb(new Error('Only image files are allowed'));
46+
cb(new Error("Only image files are allowed"));
4247
}
43-
}
48+
},
4449
});
4550

4651
// Wrapper to promisify multer
4752
function runMulter(req: VercelRequest, res: VercelResponse) {
4853
return new Promise((resolve, reject) => {
49-
upload.single('image')(req as any, res as any, (error: unknown) => {
54+
upload.single("image")(req as any, res as any, (error: unknown) => {
5055
if (error) {
5156
return reject(error);
5257
}
@@ -55,53 +60,82 @@ function runMulter(req: VercelRequest, res: VercelResponse) {
5560
});
5661
}
5762

58-
59-
export default async function handler(req: AuthenticatedRequest, res: VercelResponse) {
63+
export default async function handler(
64+
req: AuthenticatedRequest,
65+
res: VercelResponse
66+
) {
6067
await runCors(req, res);
6168

6269
// Handle preflight requests
63-
if (req.method === 'OPTIONS') {
70+
if (req.method === "OPTIONS") {
6471
return res.status(200).end();
6572
}
6673

67-
if (req.method !== 'POST') {
68-
return sendError(res, 'METHOD_NOT_ALLOWED', `Method ${req.method} not allowed`, 405);
74+
if (req.method !== "POST") {
75+
return sendError(
76+
res,
77+
"METHOD_NOT_ALLOWED",
78+
`Method ${req.method} not allowed`,
79+
405
80+
);
6981
}
7082

7183
// Check authentication
7284
if (!authenticate(req)) {
73-
return sendError(res, 'UNAUTHORIZED', 'Invalid or missing authentication token', 401);
85+
return sendError(
86+
res,
87+
"UNAUTHORIZED",
88+
"Invalid or missing authentication token",
89+
401
90+
);
7491
}
7592

76-
const projectId = process.env.REEARTH_CMS_PROJECT_ID || 'default-project-id';
93+
const projectId = process.env.REEARTH_CMS_PROJECT_ID || "default-project-id";
7794

7895
try {
7996
// Process file upload
8097
await runMulter(req, res);
81-
98+
8299
const file = (req as any).file;
83-
100+
84101
if (!file) {
85-
return sendError(res, 'NO_FILE', 'No image file provided', 400);
102+
return sendError(res, "NO_FILE", "No image file provided", 400);
86103
}
87104

88105
// Upload to CMS
89106
const uploadedAsset = await cmsService.uploadAsset(projectId, file);
90107

91108
return sendSuccess(res, uploadedAsset, 201);
92-
93109
} catch (error: unknown) {
94-
console.error('Upload error:', error);
95-
96-
if (error instanceof Error && error.message === 'Only image files are allowed') {
97-
return sendError(res, 'INVALID_FILE_TYPE', 'Only image files are allowed', 400);
110+
console.error("Upload error:", error);
111+
112+
if (
113+
error instanceof Error &&
114+
error.message === "Only image files are allowed"
115+
) {
116+
return sendError(
117+
res,
118+
"INVALID_FILE_TYPE",
119+
"Only image files are allowed",
120+
400
121+
);
98122
}
99-
100-
if (error && typeof error === 'object' && 'code' in error && error.code === 'LIMIT_FILE_SIZE') {
101-
return sendError(res, 'FILE_TOO_LARGE', 'File size exceeds 10MB limit', 413);
123+
124+
if (
125+
error &&
126+
typeof error === "object" &&
127+
"code" in error &&
128+
error.code === "LIMIT_FILE_SIZE"
129+
) {
130+
return sendError(
131+
res,
132+
"FILE_TOO_LARGE",
133+
"File size exceeds 10MB limit",
134+
413
135+
);
102136
}
103-
104-
return sendError(res, 'UPLOAD_FAILED', 'File upload failed', 500);
137+
138+
return sendError(res, "UPLOAD_FAILED", "File upload failed", 500);
105139
}
106140
}
107141

@@ -110,4 +144,4 @@ export const config = {
110144
api: {
111145
bodyParser: false,
112146
},
113-
};
147+
};

server/api/photographs.ts

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { VercelRequest, VercelResponse } from '@vercel/node';
2-
import cors from 'cors';
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import cors from "cors";
33

4-
import { cmsService } from '../src/services/cms';
5-
import { CreatePhotographRequest } from '../src/types';
6-
import { authenticate, AuthenticatedRequest } from '../src/utils/auth';
7-
import { sendSuccess, sendError } from '../src/utils/response';
8-
import { validateRequest, PhotographSchema } from '../src/utils/validation';
4+
import { cmsService } from "../src/services/cms.js";
5+
import { CreatePhotographRequest } from "../src/types";
6+
import { authenticate, AuthenticatedRequest } from "../src/utils/auth.js";
7+
import { sendSuccess, sendError } from "../src/utils/response.js";
8+
import { validateRequest, PhotographSchema } from "../src/utils/validation.js";
99

1010
// CORS configuration
1111
const corsOptions = {
12-
origin: process.env.CORS_ORIGIN || '*',
13-
methods: ['GET', 'POST', 'OPTIONS'],
14-
allowedHeaders: ['Content-Type', 'Authorization']
12+
origin: process.env.CORS_ORIGIN || "*",
13+
methods: ["GET", "POST", "OPTIONS"],
14+
allowedHeaders: ["Content-Type", "Authorization"],
1515
};
1616

1717
// Apply CORS middleware
@@ -26,58 +26,89 @@ function runCors(req: VercelRequest, res: VercelResponse) {
2626
});
2727
}
2828

29-
export default async function handler(req: AuthenticatedRequest, res: VercelResponse) {
29+
export default async function handler(
30+
req: AuthenticatedRequest,
31+
res: VercelResponse
32+
) {
3033
await runCors(req, res);
3134

3235
// Handle preflight requests
33-
if (req.method === 'OPTIONS') {
36+
if (req.method === "OPTIONS") {
3437
return res.status(200).end();
3538
}
3639

3740
// Check authentication
3841
if (!authenticate(req)) {
39-
return sendError(res, 'UNAUTHORIZED', 'Invalid or missing authentication token', 401);
42+
return sendError(
43+
res,
44+
"UNAUTHORIZED",
45+
"Invalid or missing authentication token",
46+
401
47+
);
4048
}
4149

42-
const modelId = process.env.REEARTH_CMS_PROJECT_PHOTOGRAPHS_MODEL_ID || 'default-model-id';
50+
const modelId =
51+
process.env.REEARTH_CMS_PROJECT_PHOTOGRAPHS_MODEL_ID || "default-model-id";
4352

4453
try {
4554
switch (req.method) {
46-
case 'GET':
55+
case "GET":
4756
return await handleGetPhotographs(req, res, modelId);
48-
49-
case 'POST':
57+
58+
case "POST":
5059
return await handleCreatePhotograph(req, res, modelId);
51-
60+
5261
default:
53-
return sendError(res, 'METHOD_NOT_ALLOWED', `Method ${req.method} not allowed`, 405);
62+
return sendError(
63+
res,
64+
"METHOD_NOT_ALLOWED",
65+
`Method ${req.method} not allowed`,
66+
405
67+
);
5468
}
5569
} catch (error) {
56-
console.error('API Error:', error);
57-
return sendError(res, 'INTERNAL_ERROR', 'Internal server error', 500);
70+
console.error("API Error:", error);
71+
return sendError(res, "INTERNAL_ERROR", "Internal server error", 500);
5872
}
5973
}
6074

61-
async function handleGetPhotographs(_req: VercelRequest, res: VercelResponse, modelId: string) {
75+
async function handleGetPhotographs(
76+
_req: VercelRequest,
77+
res: VercelResponse,
78+
modelId: string
79+
) {
6280
try {
6381
const result = await cmsService.getPhotographs(modelId);
6482
return sendSuccess(res, result.items, 200, result.total);
6583
} catch (_error) {
66-
return sendError(res, 'FETCH_FAILED', 'Failed to fetch photographs', 500);
84+
return sendError(res, "FETCH_FAILED", "Failed to fetch photographs", 500);
6785
}
6886
}
6987

70-
async function handleCreatePhotograph(req: VercelRequest, res: VercelResponse, modelId: string) {
88+
async function handleCreatePhotograph(
89+
req: VercelRequest,
90+
res: VercelResponse,
91+
modelId: string
92+
) {
7193
const validation = validateRequest(PhotographSchema, req.body);
72-
94+
7395
if (!validation.success) {
74-
return sendError(res, 'VALIDATION_ERROR', 'Invalid request data', 400, validation.errors);
96+
return sendError(
97+
res,
98+
"VALIDATION_ERROR",
99+
"Invalid request data",
100+
400,
101+
validation.errors
102+
);
75103
}
76104

77105
try {
78-
const photograph = await cmsService.createPhotograph(modelId, validation.data as CreatePhotographRequest);
106+
const photograph = await cmsService.createPhotograph(
107+
modelId,
108+
validation.data as CreatePhotographRequest
109+
);
79110
return sendSuccess(res, photograph, 201);
80111
} catch (_error) {
81-
return sendError(res, 'CREATE_FAILED', 'Failed to create photograph', 500);
112+
return sendError(res, "CREATE_FAILED", "Failed to create photograph", 500);
82113
}
83-
}
114+
}

0 commit comments

Comments
 (0)