Skip to content

Commit 4cd9011

Browse files
authored
improvement: marketplace, sidebar, loading (#221)
1 parent 2df1d13 commit 4cd9011

File tree

21 files changed

+1537
-147
lines changed

21 files changed

+1537
-147
lines changed

sim/app/api/marketplace/[id]/info/route.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
4242
description: marketplaceEntry.description,
4343
category: marketplaceEntry.category,
4444
authorName: marketplaceEntry.authorName,
45-
stars: marketplaceEntry.stars,
4645
views: marketplaceEntry.views,
4746
createdAt: marketplaceEntry.createdAt,
4847
updatedAt: marketplaceEntry.updatedAt,
Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,108 @@
11
import { NextRequest } from 'next/server'
22
import { eq } from 'drizzle-orm'
33
import { createLogger } from '@/lib/logs/console-logger'
4-
import { validateWorkflowAccess } from '@/app/api/workflows/middleware'
54
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
5+
import { getSession } from '@/lib/auth'
66
import { db } from '@/db'
77
import * as schema from '@/db/schema'
88

99
const logger = createLogger('MarketplaceUnpublishAPI')
1010

11+
/**
12+
* API endpoint to unpublish a workflow from the marketplace by its marketplace ID
13+
*
14+
* Security:
15+
* - Requires authentication
16+
* - Validates that the current user is the author of the marketplace entry
17+
* - Only allows the owner to unpublish
18+
*/
1119
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
1220
const requestId = crypto.randomUUID().slice(0, 8)
1321

1422
try {
1523
const { id } = await params
16-
17-
// Validate access to the workflow (must be owner to unpublish)
18-
// Pass false to requireDeployment since unpublishing doesn't require the workflow to be deployed
19-
const validation = await validateWorkflowAccess(request, id, false)
20-
if (validation.error) {
21-
logger.warn(`[${requestId}] Workflow access validation failed: ${validation.error.message}`)
22-
return createErrorResponse(validation.error.message, validation.error.status)
24+
25+
// Get the session first for authorization
26+
const session = await getSession()
27+
if (!session?.user?.id) {
28+
logger.warn(`[${requestId}] Unauthorized unpublish attempt for marketplace ID: ${id}`)
29+
return createErrorResponse('Unauthorized', 401)
2330
}
2431

25-
// Check if workflow is published
32+
const userId = session.user.id
33+
34+
// Get the marketplace entry using the marketplace ID
2635
const marketplaceEntry = await db
27-
.select()
36+
.select({
37+
id: schema.marketplace.id,
38+
workflowId: schema.marketplace.workflowId,
39+
authorId: schema.marketplace.authorId,
40+
name: schema.marketplace.name,
41+
})
2842
.from(schema.marketplace)
29-
.where(eq(schema.marketplace.workflowId, id))
43+
.where(eq(schema.marketplace.id, id))
3044
.limit(1)
3145
.then((rows) => rows[0])
3246

3347
if (!marketplaceEntry) {
34-
logger.warn(`[${requestId}] No marketplace entry found for workflow: ${id}`)
35-
return createErrorResponse('Workflow is not published to marketplace', 404)
48+
logger.warn(`[${requestId}] No marketplace entry found with ID: ${id}`)
49+
return createErrorResponse('Marketplace entry not found', 404)
50+
}
51+
52+
// Check if the user is the author of the marketplace entry
53+
if (marketplaceEntry.authorId !== userId) {
54+
logger.warn(
55+
`[${requestId}] User ${userId} tried to unpublish marketplace entry they don't own: ${id}, author: ${marketplaceEntry.authorId}`
56+
)
57+
return createErrorResponse('You do not have permission to unpublish this workflow', 403)
3658
}
3759

38-
// Delete the marketplace entry
39-
await db.delete(schema.marketplace).where(eq(schema.marketplace.workflowId, id))
40-
41-
// Update the workflow to mark it as unpublished
42-
await db.update(schema.workflow).set({ isPublished: false }).where(eq(schema.workflow.id, id))
43-
44-
logger.info(`[${requestId}] Workflow unpublished from marketplace: ${id}`)
45-
46-
return createSuccessResponse({
47-
success: true,
48-
message: 'Workflow successfully unpublished from marketplace',
49-
})
60+
const workflowId = marketplaceEntry.workflowId
61+
62+
// Verify the workflow exists and belongs to the user
63+
const workflow = await db
64+
.select({
65+
id: schema.workflow.id,
66+
userId: schema.workflow.userId,
67+
})
68+
.from(schema.workflow)
69+
.where(eq(schema.workflow.id, workflowId))
70+
.limit(1)
71+
.then((rows) => rows[0])
72+
73+
if (!workflow) {
74+
logger.warn(`[${requestId}] Associated workflow not found: ${workflowId}`)
75+
// We'll still delete the marketplace entry even if the workflow is missing
76+
} else if (workflow.userId !== userId) {
77+
logger.warn(
78+
`[${requestId}] Workflow ${workflowId} belongs to user ${workflow.userId}, not current user ${userId}`
79+
)
80+
return createErrorResponse('You do not have permission to unpublish this workflow', 403)
81+
}
82+
83+
try {
84+
// Delete the marketplace entry - this is the primary action
85+
await db.delete(schema.marketplace).where(eq(schema.marketplace.id, id))
86+
87+
// Update the workflow to mark it as unpublished if it exists
88+
if (workflow) {
89+
await db.update(schema.workflow)
90+
.set({ isPublished: false })
91+
.where(eq(schema.workflow.id, workflowId))
92+
}
93+
94+
logger.info(`[${requestId}] Workflow "${marketplaceEntry.name}" unpublished from marketplace: ID=${id}, workflowId=${workflowId}`)
95+
96+
return createSuccessResponse({
97+
success: true,
98+
message: 'Workflow successfully unpublished from marketplace',
99+
})
100+
} catch (dbError) {
101+
logger.error(`[${requestId}] Database error unpublishing marketplace entry:`, dbError)
102+
return createErrorResponse('Failed to unpublish workflow due to a database error', 500)
103+
}
50104
} catch (error) {
51-
logger.error(`[${requestId}] Error unpublishing workflow: ${(await params).id}`, error)
105+
logger.error(`[${requestId}] Error unpublishing marketplace entry: ${(await params).id}`, error)
52106
return createErrorResponse('Failed to unpublish workflow', 500)
53107
}
54108
}

sim/app/api/marketplace/publish/route.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ export async function POST(request: NextRequest) {
112112
.values({
113113
...marketplaceEntry,
114114
createdAt: new Date(),
115-
stars: 0,
116115
views: 0,
117116
})
118117
.returning()

sim/app/api/marketplace/workflows/route.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ export async function GET(request: NextRequest) {
5757
authorId: schema.marketplace.authorId,
5858
authorName: schema.marketplace.authorName,
5959
state: schema.marketplace.state,
60-
stars: schema.marketplace.stars,
6160
views: schema.marketplace.views,
6261
category: schema.marketplace.category,
6362
createdAt: schema.marketplace.createdAt,
@@ -77,7 +76,6 @@ export async function GET(request: NextRequest) {
7776
description: schema.marketplace.description,
7877
authorId: schema.marketplace.authorId,
7978
authorName: schema.marketplace.authorName,
80-
stars: schema.marketplace.stars,
8179
views: schema.marketplace.views,
8280
category: schema.marketplace.category,
8381
createdAt: schema.marketplace.createdAt,
@@ -123,7 +121,6 @@ export async function GET(request: NextRequest) {
123121
authorId: schema.marketplace.authorId,
124122
authorName: schema.marketplace.authorName,
125123
state: schema.marketplace.state,
126-
stars: schema.marketplace.stars,
127124
views: schema.marketplace.views,
128125
category: schema.marketplace.category,
129126
createdAt: schema.marketplace.createdAt,
@@ -143,7 +140,6 @@ export async function GET(request: NextRequest) {
143140
description: schema.marketplace.description,
144141
authorId: schema.marketplace.authorId,
145142
authorName: schema.marketplace.authorName,
146-
stars: schema.marketplace.stars,
147143
views: schema.marketplace.views,
148144
category: schema.marketplace.category,
149145
createdAt: schema.marketplace.createdAt,
@@ -192,7 +188,6 @@ export async function GET(request: NextRequest) {
192188
name: schema.marketplace.name,
193189
description: schema.marketplace.description,
194190
authorName: schema.marketplace.authorName,
195-
stars: schema.marketplace.stars,
196191
views: schema.marketplace.views,
197192
category: schema.marketplace.category,
198193
createdAt: schema.marketplace.createdAt,
@@ -212,7 +207,7 @@ export async function GET(request: NextRequest) {
212207
result.popular = await db
213208
.select(selectFields)
214209
.from(schema.marketplace)
215-
.orderBy(desc(schema.marketplace.stars), desc(schema.marketplace.views))
210+
.orderBy(desc(schema.marketplace.views))
216211
.limit(limit)
217212
}
218213

@@ -262,7 +257,7 @@ export async function GET(request: NextRequest) {
262257
.select(selectFields)
263258
.from(schema.marketplace)
264259
.where(eq(schema.marketplace.category, categoryValue))
265-
.orderBy(desc(schema.marketplace.stars), desc(schema.marketplace.views))
260+
.orderBy(desc(schema.marketplace.views))
266261
.limit(limit)
267262

268263
// Always add the category to the result, even if empty
@@ -277,15 +272,17 @@ export async function GET(request: NextRequest) {
277272
// Transform the data if state was included to match the expected format
278273
if (includeState) {
279274
const transformSection = (section: any[]) => {
280-
return section.map((item) =>
281-
'state' in item
282-
? {
283-
...item,
284-
workflowState: item.state,
285-
state: undefined,
286-
}
287-
: item
288-
)
275+
return section.map((item) => {
276+
if ('state' in item) {
277+
// Create a new object without the state field, but with workflowState
278+
const { state, ...rest } = item;
279+
return {
280+
...rest,
281+
workflowState: state
282+
};
283+
}
284+
return item;
285+
});
289286
}
290287

291288
if (result.popular.length > 0) {

sim/app/api/workflows/sync/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const logger = createLogger('WorkflowAPI')
1111
// Define marketplace data schema
1212
const MarketplaceDataSchema = z.object({
1313
id: z.string(),
14-
status: z.enum(['owner', 'temp', 'star'])
14+
status: z.enum(['owner', 'temp'])
1515
}).nullable().optional()
1616

1717
// Schema for workflow data

0 commit comments

Comments
 (0)