kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add private/public to projects and expose public projects for marketplace
rodzic
844453584a
commit
ee5783e780
|
@ -4,7 +4,7 @@ import { assert } from '@agentic/platform-core'
|
|||
|
||||
import { authStorage } from './utils'
|
||||
|
||||
export function registerV1AuthGitHubOAuthCallback(
|
||||
export function registerV1GitHubOAuthCallback(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
return app.get('auth/github/callback', async (c) => {
|
||||
|
|
|
@ -51,7 +51,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AuthGitHubOAuthExchange(
|
||||
export function registerV1GitHubOAuthExchange(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -35,7 +35,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AuthGitHubOAuthInitFlow(
|
||||
export function registerV1GitHubOAuthInitFlow(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -49,9 +49,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AuthSignInWithPassword(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
export function registerV1SignInWithPassword(app: OpenAPIHono<DefaultHonoEnv>) {
|
||||
return app.openapi(route, trySignIn)
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AuthSignUpWithPassword(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
export function registerV1SignUpWithPassword(app: OpenAPIHono<DefaultHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
try {
|
||||
// try signing in to see if the user already exists
|
||||
|
|
|
@ -42,7 +42,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AdminConsumersActivateConsumer(
|
||||
export function registerV1AdminActivateConsumer(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AdminConsumersGetConsumerByToken(
|
||||
export function registerV1AdminGetConsumerByToken(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -45,7 +45,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersCreateConsumer(
|
||||
export function registerV1CreateConsumer(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -37,9 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersGetConsumer(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1GetConsumer(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { consumerId } = c.req.valid('param')
|
||||
const { populate = [] } = c.req.valid('query')
|
||||
|
|
|
@ -36,7 +36,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersListConsumers(
|
||||
export function registerV1ListConsumers(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersListForProject(
|
||||
export function registerV1ListConsumersForProject(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -37,7 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersRefreshConsumerToken(
|
||||
export function registerV1RefreshConsumerToken(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -49,7 +49,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ConsumersUpdateConsumer(
|
||||
export function registerV1UpdateConsumer(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -39,7 +39,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1AdminDeploymentsGetDeploymentByIdentifier(
|
||||
export function registerV1AdminGetDeploymentByIdentifier(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { resolveAgenticProjectConfig } from '@agentic/platform'
|
|||
import { assert, parseZodSchema, sha256 } from '@agentic/platform-core'
|
||||
import {
|
||||
isValidDeploymentIdentifier,
|
||||
isValidProjectIdentifier
|
||||
parseProjectIdentifier
|
||||
} from '@agentic/platform-validators'
|
||||
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||
|
||||
|
@ -54,7 +54,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsCreateDeployment(
|
||||
export function registerV1CreateDeployment(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
@ -64,13 +64,10 @@ export function registerV1DeploymentsCreateDeployment(
|
|||
const teamMember = c.get('teamMember')
|
||||
const logger = c.get('logger')
|
||||
|
||||
const namespace = teamMember ? teamMember.teamSlug : user.username
|
||||
const projectIdentifier = `@${namespace}/${body.name}`
|
||||
assert(
|
||||
isValidProjectIdentifier(projectIdentifier),
|
||||
400,
|
||||
`Invalid project identifier "${projectIdentifier}"`
|
||||
)
|
||||
const inputNamespace = teamMember ? teamMember.teamSlug : user.username
|
||||
const inputProjectIdentifier = `@${inputNamespace}/${body.name}`
|
||||
const { projectIdentifier, projectNamespace, projectName } =
|
||||
parseProjectIdentifier(inputProjectIdentifier)
|
||||
|
||||
let project = await db.query.projects.findFirst({
|
||||
where: eq(schema.projects.identifier, projectIdentifier),
|
||||
|
@ -88,8 +85,9 @@ export function registerV1DeploymentsCreateDeployment(
|
|||
await db
|
||||
.insert(schema.projects)
|
||||
.values({
|
||||
name: body.name,
|
||||
identifier: projectIdentifier,
|
||||
namespace: projectNamespace,
|
||||
name: projectName,
|
||||
userId: user.id,
|
||||
teamId: teamMember?.teamId,
|
||||
_secret: await sha256()
|
||||
|
|
|
@ -37,7 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsGetDeploymentByIdentifier(
|
||||
export function registerV1GetDeploymentByIdentifier(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsGetDeployment(
|
||||
export function registerV1GetDeployment(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -37,7 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsListDeployments(
|
||||
export function registerV1ListDeployments(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -46,7 +46,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsPublishDeployment(
|
||||
export function registerV1PublishDeployment(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -45,7 +45,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1DeploymentsUpdateDeployment(
|
||||
export function registerV1UpdateDeployment(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -6,42 +6,45 @@ import type { AuthenticatedHonoEnv } from '@/lib/types'
|
|||
import * as middleware from '@/lib/middleware'
|
||||
import { registerOpenAPIErrorResponses } from '@/lib/openapi-utils'
|
||||
|
||||
import { registerV1AuthGitHubOAuthCallback } from './auth/github-callback'
|
||||
import { registerV1AuthGitHubOAuthExchange } from './auth/github-exchange'
|
||||
import { registerV1AuthGitHubOAuthInitFlow } from './auth/github-init'
|
||||
import { registerV1AuthSignInWithPassword } from './auth/sign-in-with-password'
|
||||
import { registerV1AuthSignUpWithPassword } from './auth/sign-up-with-password'
|
||||
import { registerV1AdminConsumersActivateConsumer } from './consumers/admin-activate-consumer'
|
||||
import { registerV1AdminConsumersGetConsumerByToken } from './consumers/admin-get-consumer-by-token'
|
||||
import { registerV1ConsumersCreateConsumer } from './consumers/create-consumer'
|
||||
import { registerV1ConsumersGetConsumer } from './consumers/get-consumer'
|
||||
import { registerV1ConsumersListConsumers } from './consumers/list-consumers'
|
||||
import { registerV1ConsumersListForProject } from './consumers/list-project-consumers'
|
||||
import { registerV1ConsumersRefreshConsumerToken } from './consumers/refresh-consumer-token'
|
||||
import { registerV1ConsumersUpdateConsumer } from './consumers/update-consumer'
|
||||
import { registerV1AdminDeploymentsGetDeploymentByIdentifier } from './deployments/admin-get-deployment-by-identifier'
|
||||
import { registerV1DeploymentsCreateDeployment } from './deployments/create-deployment'
|
||||
import { registerV1DeploymentsGetDeployment } from './deployments/get-deployment'
|
||||
import { registerV1DeploymentsGetDeploymentByIdentifier } from './deployments/get-deployment-by-identifier'
|
||||
import { registerV1DeploymentsListDeployments } from './deployments/list-deployments'
|
||||
import { registerV1DeploymentsPublishDeployment } from './deployments/publish-deployment'
|
||||
import { registerV1DeploymentsUpdateDeployment } from './deployments/update-deployment'
|
||||
import { registerV1GitHubOAuthCallback } from './auth/github-callback'
|
||||
import { registerV1GitHubOAuthExchange } from './auth/github-exchange'
|
||||
import { registerV1GitHubOAuthInitFlow } from './auth/github-init'
|
||||
import { registerV1SignInWithPassword } from './auth/sign-in-with-password'
|
||||
import { registerV1SignUpWithPassword } from './auth/sign-up-with-password'
|
||||
import { registerV1AdminActivateConsumer } from './consumers/admin-activate-consumer'
|
||||
import { registerV1AdminGetConsumerByToken } from './consumers/admin-get-consumer-by-token'
|
||||
import { registerV1CreateConsumer } from './consumers/create-consumer'
|
||||
import { registerV1GetConsumer } from './consumers/get-consumer'
|
||||
import { registerV1ListConsumers } from './consumers/list-consumers'
|
||||
import { registerV1ListConsumersForProject } from './consumers/list-project-consumers'
|
||||
import { registerV1RefreshConsumerToken } from './consumers/refresh-consumer-token'
|
||||
import { registerV1UpdateConsumer } from './consumers/update-consumer'
|
||||
import { registerV1AdminGetDeploymentByIdentifier } from './deployments/admin-get-deployment-by-identifier'
|
||||
import { registerV1CreateDeployment } from './deployments/create-deployment'
|
||||
import { registerV1GetDeployment } from './deployments/get-deployment'
|
||||
import { registerV1GetDeploymentByIdentifier } from './deployments/get-deployment-by-identifier'
|
||||
import { registerV1ListDeployments } from './deployments/list-deployments'
|
||||
import { registerV1PublishDeployment } from './deployments/publish-deployment'
|
||||
import { registerV1UpdateDeployment } from './deployments/update-deployment'
|
||||
import { registerHealthCheck } from './health-check'
|
||||
import { registerV1ProjectsCreateProject } from './projects/create-project'
|
||||
import { registerV1ProjectsGetProject } from './projects/get-project'
|
||||
import { registerV1ProjectsGetProjectByIdentifier } from './projects/get-project-by-identifier'
|
||||
import { registerV1ProjectsListProjects } from './projects/list-projects'
|
||||
import { registerV1ProjectsUpdateProject } from './projects/update-project'
|
||||
import { registerV1TeamsCreateTeam } from './teams/create-team'
|
||||
import { registerV1TeamsDeleteTeam } from './teams/delete-team'
|
||||
import { registerV1TeamsGetTeam } from './teams/get-team'
|
||||
import { registerV1TeamsListTeams } from './teams/list-teams'
|
||||
import { registerV1TeamsMembersCreateTeamMember } from './teams/members/create-team-member'
|
||||
import { registerV1TeamsMembersDeleteTeamMember } from './teams/members/delete-team-member'
|
||||
import { registerV1TeamsMembersUpdateTeamMember } from './teams/members/update-team-member'
|
||||
import { registerV1TeamsUpdateTeam } from './teams/update-team'
|
||||
import { registerV1UsersGetUser } from './users/get-user'
|
||||
import { registerV1UsersUpdateUser } from './users/update-user'
|
||||
import { registerV1CreateProject } from './projects/create-project'
|
||||
import { registerV1GetProject } from './projects/get-project'
|
||||
import { registerV1GetProjectByIdentifier } from './projects/get-project-by-identifier'
|
||||
import { registerV1GetPublicProject } from './projects/get-public-project'
|
||||
import { registerV1GetPublicProjectByIdentifier } from './projects/get-public-project-by-identifier'
|
||||
import { registerV1ListProjects } from './projects/list-projects'
|
||||
import { registerV1ListPublicProjects } from './projects/list-public-projects'
|
||||
import { registerV1UpdateProject } from './projects/update-project'
|
||||
import { registerV1CreateTeam } from './teams/create-team'
|
||||
import { registerV1DeleteTeam } from './teams/delete-team'
|
||||
import { registerV1GetTeam } from './teams/get-team'
|
||||
import { registerV1ListTeams } from './teams/list-teams'
|
||||
import { registerV1CreateTeamMember } from './teams/members/create-team-member'
|
||||
import { registerV1DeleteTeamMember } from './teams/members/delete-team-member'
|
||||
import { registerV1UpdateTeamMember } from './teams/members/update-team-member'
|
||||
import { registerV1UpdateTeam } from './teams/update-team'
|
||||
import { registerV1GetUser } from './users/get-user'
|
||||
import { registerV1UpdateUser } from './users/update-user'
|
||||
import { registerV1StripeWebhook } from './webhooks/stripe-webhook'
|
||||
|
||||
// Note that the order of some of these routes is important because of
|
||||
|
@ -79,55 +82,60 @@ const privateRouter = new OpenAPIHono<AuthenticatedHonoEnv>()
|
|||
registerHealthCheck(publicRouter)
|
||||
|
||||
// Auth
|
||||
registerV1AuthSignInWithPassword(publicRouter)
|
||||
registerV1AuthSignUpWithPassword(publicRouter)
|
||||
registerV1AuthGitHubOAuthExchange(publicRouter)
|
||||
registerV1AuthGitHubOAuthInitFlow(publicRouter)
|
||||
registerV1AuthGitHubOAuthCallback(publicRouter)
|
||||
registerV1SignInWithPassword(publicRouter)
|
||||
registerV1SignUpWithPassword(publicRouter)
|
||||
registerV1GitHubOAuthExchange(publicRouter)
|
||||
registerV1GitHubOAuthInitFlow(publicRouter)
|
||||
registerV1GitHubOAuthCallback(publicRouter)
|
||||
|
||||
// Users
|
||||
registerV1UsersGetUser(privateRouter)
|
||||
registerV1UsersUpdateUser(privateRouter)
|
||||
registerV1GetUser(privateRouter)
|
||||
registerV1UpdateUser(privateRouter)
|
||||
|
||||
// Teams
|
||||
registerV1TeamsCreateTeam(privateRouter)
|
||||
registerV1TeamsListTeams(privateRouter)
|
||||
registerV1TeamsGetTeam(privateRouter)
|
||||
registerV1TeamsDeleteTeam(privateRouter)
|
||||
registerV1TeamsUpdateTeam(privateRouter)
|
||||
registerV1CreateTeam(privateRouter)
|
||||
registerV1ListTeams(privateRouter)
|
||||
registerV1GetTeam(privateRouter)
|
||||
registerV1DeleteTeam(privateRouter)
|
||||
registerV1UpdateTeam(privateRouter)
|
||||
|
||||
// Team members
|
||||
registerV1TeamsMembersCreateTeamMember(privateRouter)
|
||||
registerV1TeamsMembersUpdateTeamMember(privateRouter)
|
||||
registerV1TeamsMembersDeleteTeamMember(privateRouter)
|
||||
registerV1CreateTeamMember(privateRouter)
|
||||
registerV1UpdateTeamMember(privateRouter)
|
||||
registerV1DeleteTeamMember(privateRouter)
|
||||
|
||||
// Projects
|
||||
registerV1ProjectsCreateProject(privateRouter)
|
||||
registerV1ProjectsListProjects(privateRouter)
|
||||
registerV1ProjectsGetProjectByIdentifier(privateRouter) // must be before `registerV1ProjectsGetProject`
|
||||
registerV1ProjectsGetProject(privateRouter)
|
||||
registerV1ProjectsUpdateProject(privateRouter)
|
||||
// Public projects
|
||||
registerV1ListPublicProjects(publicRouter)
|
||||
registerV1GetPublicProjectByIdentifier(publicRouter) // must be before `registerV1GetPublicProject`
|
||||
registerV1GetPublicProject(publicRouter)
|
||||
|
||||
// Private projects
|
||||
registerV1CreateProject(privateRouter)
|
||||
registerV1ListProjects(privateRouter)
|
||||
registerV1GetProjectByIdentifier(privateRouter) // must be before `registerV1GetProject`
|
||||
registerV1GetProject(privateRouter)
|
||||
registerV1UpdateProject(privateRouter)
|
||||
|
||||
// Consumers
|
||||
registerV1ConsumersGetConsumer(privateRouter)
|
||||
registerV1ConsumersCreateConsumer(privateRouter)
|
||||
registerV1ConsumersUpdateConsumer(privateRouter)
|
||||
registerV1ConsumersRefreshConsumerToken(privateRouter)
|
||||
registerV1ConsumersListConsumers(privateRouter)
|
||||
registerV1ConsumersListForProject(privateRouter)
|
||||
registerV1GetConsumer(privateRouter)
|
||||
registerV1CreateConsumer(privateRouter)
|
||||
registerV1UpdateConsumer(privateRouter)
|
||||
registerV1RefreshConsumerToken(privateRouter)
|
||||
registerV1ListConsumers(privateRouter)
|
||||
registerV1ListConsumersForProject(privateRouter)
|
||||
|
||||
// Deployments
|
||||
registerV1DeploymentsGetDeploymentByIdentifier(privateRouter) // must be before `registerV1DeploymentsGetDeployment`
|
||||
registerV1DeploymentsGetDeployment(privateRouter)
|
||||
registerV1DeploymentsCreateDeployment(privateRouter)
|
||||
registerV1DeploymentsUpdateDeployment(privateRouter)
|
||||
registerV1DeploymentsListDeployments(privateRouter)
|
||||
registerV1DeploymentsPublishDeployment(privateRouter)
|
||||
registerV1GetDeploymentByIdentifier(privateRouter) // must be before `registerV1GetDeployment`
|
||||
registerV1GetDeployment(privateRouter)
|
||||
registerV1CreateDeployment(privateRouter)
|
||||
registerV1UpdateDeployment(privateRouter)
|
||||
registerV1ListDeployments(privateRouter)
|
||||
registerV1PublishDeployment(privateRouter)
|
||||
|
||||
// Internal admin routes
|
||||
registerV1AdminConsumersGetConsumerByToken(privateRouter)
|
||||
registerV1AdminConsumersActivateConsumer(privateRouter)
|
||||
registerV1AdminDeploymentsGetDeploymentByIdentifier(privateRouter)
|
||||
registerV1AdminGetConsumerByToken(privateRouter)
|
||||
registerV1AdminActivateConsumer(privateRouter)
|
||||
registerV1AdminGetDeploymentByIdentifier(privateRouter)
|
||||
|
||||
// Webhook event handlers
|
||||
registerV1StripeWebhook(publicRouter)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { assert, parseZodSchema, sha256 } from '@agentic/platform-core'
|
||||
import { isValidProjectIdentifier } from '@agentic/platform-validators'
|
||||
import { parseProjectIdentifier } from '@agentic/platform-validators'
|
||||
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||
|
||||
import type { AuthenticatedHonoEnv } from '@/lib/types'
|
||||
|
@ -40,7 +40,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ProjectsCreateProject(
|
||||
export function registerV1CreateProject(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
@ -54,8 +54,9 @@ export function registerV1ProjectsCreateProject(
|
|||
const teamMember = c.get('teamMember')
|
||||
const namespace = teamMember ? teamMember.teamSlug : user.username
|
||||
const identifier = `@${namespace}/${body.name}`
|
||||
const parsedProjectIdentifier = parseProjectIdentifier(identifier)
|
||||
assert(
|
||||
isValidProjectIdentifier(identifier),
|
||||
parsedProjectIdentifier,
|
||||
400,
|
||||
`Invalid project identifier "${identifier}"`
|
||||
)
|
||||
|
@ -64,7 +65,9 @@ export function registerV1ProjectsCreateProject(
|
|||
.insert(schema.projects)
|
||||
.values({
|
||||
...body,
|
||||
identifier,
|
||||
identifier: parsedProjectIdentifier.projectIdentifier,
|
||||
namespace: parsedProjectIdentifier.projectNamespace,
|
||||
name: parsedProjectIdentifier.projectName,
|
||||
teamId: teamMember?.teamId,
|
||||
userId: user.id,
|
||||
_secret: await sha256()
|
||||
|
|
|
@ -37,7 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ProjectsGetProjectByIdentifier(
|
||||
export function registerV1GetProjectByIdentifier(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -37,9 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ProjectsGetProject(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1GetProject(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { projectId } = c.req.valid('param')
|
||||
const { populate = [] } = c.req.valid('query')
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import type { DefaultHonoEnv } from '@agentic/platform-hono'
|
||||
import { assert, parseZodSchema } from '@agentic/platform-core'
|
||||
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||
|
||||
import { db, eq, schema } from '@/db'
|
||||
import {
|
||||
openapiAuthenticatedSecuritySchemas,
|
||||
openapiErrorResponse404,
|
||||
openapiErrorResponses
|
||||
} from '@/lib/openapi-utils'
|
||||
|
||||
import { projectIdentifierAndPopulateSchema } from './schemas'
|
||||
|
||||
const route = createRoute({
|
||||
description:
|
||||
'Gets a public project by its public identifier (eg, "@username/project-name").',
|
||||
tags: ['projects'],
|
||||
operationId: 'getPublicProjectByIdentifier',
|
||||
method: 'get',
|
||||
path: 'projects/public/by-identifier',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
query: projectIdentifierAndPopulateSchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'A project',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: schema.projectSelectSchema
|
||||
}
|
||||
}
|
||||
},
|
||||
...openapiErrorResponses,
|
||||
...openapiErrorResponse404
|
||||
}
|
||||
})
|
||||
|
||||
export function registerV1GetPublicProjectByIdentifier(
|
||||
app: OpenAPIHono<DefaultHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { projectIdentifier, populate = [] } = c.req.valid('query')
|
||||
|
||||
const project = await db.query.projects.findFirst({
|
||||
where: eq(schema.projects.identifier, projectIdentifier),
|
||||
with: {
|
||||
lastPublishedDeployment: true,
|
||||
...Object.fromEntries(populate.map((field) => [field, true]))
|
||||
}
|
||||
})
|
||||
assert(
|
||||
project && project.private && project.lastPublishedDeploymentId,
|
||||
404,
|
||||
`Public project not found "${projectIdentifier}"`
|
||||
)
|
||||
|
||||
return c.json(parseZodSchema(schema.projectSelectSchema, project))
|
||||
})
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import type { DefaultHonoEnv } from '@agentic/platform-hono'
|
||||
import { assert, parseZodSchema } from '@agentic/platform-core'
|
||||
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||
|
||||
import { db, eq, schema } from '@/db'
|
||||
import {
|
||||
openapiAuthenticatedSecuritySchemas,
|
||||
openapiErrorResponse404,
|
||||
openapiErrorResponses
|
||||
} from '@/lib/openapi-utils'
|
||||
|
||||
import { populateProjectSchema, projectIdParamsSchema } from './schemas'
|
||||
|
||||
const route = createRoute({
|
||||
description: 'Gets a public project by ID.',
|
||||
tags: ['projects'],
|
||||
operationId: 'getPublicProject',
|
||||
method: 'get',
|
||||
path: 'projects/public/{projectId}',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
params: projectIdParamsSchema,
|
||||
query: populateProjectSchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'A project',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: schema.projectSelectSchema
|
||||
}
|
||||
}
|
||||
},
|
||||
...openapiErrorResponses,
|
||||
...openapiErrorResponse404
|
||||
}
|
||||
})
|
||||
|
||||
export function registerV1GetPublicProject(app: OpenAPIHono<DefaultHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { projectId } = c.req.valid('param')
|
||||
const { populate = [] } = c.req.valid('query')
|
||||
|
||||
const project = await db.query.projects.findFirst({
|
||||
where: eq(schema.projects.id, projectId),
|
||||
with: {
|
||||
lastPublishedDeployment: true,
|
||||
...Object.fromEntries(populate.map((field) => [field, true]))
|
||||
}
|
||||
})
|
||||
assert(
|
||||
project && project.private && project.lastPublishedDeploymentId,
|
||||
404,
|
||||
`Public project not found "${projectId}"`
|
||||
)
|
||||
|
||||
return c.json(parseZodSchema(schema.projectSelectSchema, project))
|
||||
})
|
||||
}
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { paginationAndPopulateProjectSchema } from './schemas'
|
||||
|
||||
const route = createRoute({
|
||||
description: 'Lists projects the authenticated user has access to.',
|
||||
description: 'Lists projects owned by the authenticated user or team.',
|
||||
tags: ['projects'],
|
||||
operationId: 'listProjects',
|
||||
method: 'get',
|
||||
|
@ -34,9 +34,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ProjectsListProjects(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1ListProjects(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const {
|
||||
offset = 0,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import type { DefaultHonoEnv } from '@agentic/platform-hono'
|
||||
import { parseZodSchema } from '@agentic/platform-core'
|
||||
import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||
|
||||
import { and, db, eq, isNotNull, schema } from '@/db'
|
||||
import {
|
||||
openapiAuthenticatedSecuritySchemas,
|
||||
openapiErrorResponses
|
||||
} from '@/lib/openapi-utils'
|
||||
|
||||
import { paginationAndPopulateProjectSchema } from './schemas'
|
||||
|
||||
const route = createRoute({
|
||||
description:
|
||||
'Lists projects that have been published publicly to the marketplace.',
|
||||
tags: ['projects'],
|
||||
operationId: 'listPublicProjects',
|
||||
method: 'get',
|
||||
path: 'projects/public',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
query: paginationAndPopulateProjectSchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'A list of projects',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.array(schema.projectSelectSchema)
|
||||
}
|
||||
}
|
||||
},
|
||||
...openapiErrorResponses
|
||||
}
|
||||
})
|
||||
|
||||
export function registerV1ListPublicProjects(app: OpenAPIHono<DefaultHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const {
|
||||
offset = 0,
|
||||
limit = 10,
|
||||
sort = 'desc',
|
||||
sortBy = 'createdAt',
|
||||
populate = []
|
||||
} = c.req.valid('query')
|
||||
|
||||
const projects = await db.query.projects.findMany({
|
||||
// List projects that are not private and have at least one published deployment
|
||||
where: and(
|
||||
eq(schema.projects.private, false),
|
||||
isNotNull(schema.projects.lastPublishedDeploymentId)
|
||||
),
|
||||
with: {
|
||||
lastPublishedDeployment: true,
|
||||
...Object.fromEntries(populate.map((field) => [field, true]))
|
||||
},
|
||||
orderBy: (projects, { asc, desc }) => [
|
||||
sort === 'desc' ? desc(projects[sortBy]) : asc(projects[sortBy])
|
||||
],
|
||||
offset,
|
||||
limit
|
||||
})
|
||||
|
||||
return c.json(parseZodSchema(z.array(schema.projectSelectSchema), projects))
|
||||
})
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// import { isValidNamespace } from '@agentic/platform-validators'
|
||||
import { z } from '@hono/zod-openapi'
|
||||
|
||||
import {
|
||||
|
@ -17,6 +18,21 @@ export const projectIdParamsSchema = z.object({
|
|||
})
|
||||
})
|
||||
|
||||
// export const namespaceParamsSchema = z.object({
|
||||
// namespace: z
|
||||
// .string()
|
||||
// .refine((namespace) => isValidNamespace(namespace), {
|
||||
// message: 'Invalid namespace'
|
||||
// })
|
||||
// .openapi({
|
||||
// param: {
|
||||
// description: 'Namespace',
|
||||
// name: 'namespace',
|
||||
// in: 'path'
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
export const projectIdentifierQuerySchema = z.object({
|
||||
projectIdentifier: projectIdentifierSchema
|
||||
})
|
||||
|
|
|
@ -44,7 +44,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1ProjectsUpdateProject(
|
||||
export function registerV1UpdateProject(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -40,9 +40,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsCreateTeam(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1CreateTeam(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const user = await ensureAuthUser(c)
|
||||
const body = c.req.valid('json')
|
||||
|
|
|
@ -36,9 +36,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsDeleteTeam(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1DeleteTeam(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { teamId } = c.req.valid('param')
|
||||
await aclTeamAdmin(c, { teamId })
|
||||
|
|
|
@ -36,7 +36,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsGetTeam(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
export function registerV1GetTeam(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { teamId } = c.req.valid('param')
|
||||
await aclTeamMember(c, { teamId })
|
||||
|
|
|
@ -31,9 +31,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsListTeams(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1ListTeams(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const {
|
||||
offset = 0,
|
||||
|
|
|
@ -46,7 +46,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsMembersCreateTeamMember(
|
||||
export function registerV1CreateTeamMember(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -37,7 +37,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsMembersDeleteTeamMember(
|
||||
export function registerV1DeleteTeamMember(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -45,7 +45,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsMembersUpdateTeamMember(
|
||||
export function registerV1UpdateTeamMember(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
return app.openapi(route, async (c) => {
|
||||
|
|
|
@ -44,9 +44,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1TeamsUpdateTeam(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1UpdateTeam(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { teamId } = c.req.valid('param')
|
||||
const body = c.req.valid('json')
|
||||
|
|
|
@ -36,7 +36,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1UsersGetUser(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
export function registerV1GetUser(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { userId } = c.req.valid('param')
|
||||
await acl(c, { userId }, { label: 'User' })
|
||||
|
|
|
@ -44,9 +44,7 @@ const route = createRoute({
|
|||
}
|
||||
})
|
||||
|
||||
export function registerV1UsersUpdateUser(
|
||||
app: OpenAPIHono<AuthenticatedHonoEnv>
|
||||
) {
|
||||
export function registerV1UpdateUser(app: OpenAPIHono<AuthenticatedHonoEnv>) {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { userId } = c.req.valid('param')
|
||||
await acl(c, { userId }, { label: 'User' })
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { isValidProjectName } from '@agentic/platform-validators'
|
||||
import { relations } from '@fisch0920/drizzle-orm'
|
||||
import {
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
|
@ -62,8 +63,16 @@ export const projects = pgTable(
|
|||
...timestamps,
|
||||
|
||||
identifier: projectIdentifier().unique().notNull(),
|
||||
namespace: text().notNull(),
|
||||
name: text().notNull(),
|
||||
alias: text(),
|
||||
|
||||
// Defaulting to `true` for now to hide all projects from the marketplace
|
||||
// by default. Will need to manually set to `true` to allow projects to be
|
||||
// visible on the marketplace.
|
||||
private: boolean().default(true).notNull(),
|
||||
|
||||
// TODO: allow for multiple aliases like vercel
|
||||
// alias: text(),
|
||||
|
||||
userId: userId()
|
||||
.notNull()
|
||||
|
@ -144,9 +153,14 @@ export const projects = pgTable(
|
|||
},
|
||||
(table) => [
|
||||
uniqueIndex('project_identifier_idx').on(table.identifier),
|
||||
index('project_namespace_idx').on(table.namespace),
|
||||
index('project_userId_idx').on(table.userId),
|
||||
index('project_teamId_idx').on(table.teamId),
|
||||
index('project_alias_idx').on(table.alias),
|
||||
// index('project_alias_idx').on(table.alias),
|
||||
index('project_private_idx').on(table.private),
|
||||
index('project_lastPublishedDeploymentId_idx').on(
|
||||
table.lastPublishedDeploymentId
|
||||
),
|
||||
index('project_createdAt_idx').on(table.createdAt),
|
||||
index('project_updatedAt_idx').on(table.updatedAt),
|
||||
index('project_deletedAt_idx').on(table.deletedAt)
|
||||
|
@ -250,8 +264,8 @@ export const projectInsertSchema = createInsertSchema(projects, {
|
|||
|
||||
export const projectUpdateSchema = createUpdateSchema(projects)
|
||||
.pick({
|
||||
name: true,
|
||||
alias: true
|
||||
name: true
|
||||
// alias: true
|
||||
})
|
||||
.strict()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AgenticApiClient } from '@agentic/platform-api-client'
|
||||
|
||||
import { env } from '../src/env'
|
||||
import { deployFixtures } from '../src/deploy-fixtures'
|
||||
import { env } from '../src/env'
|
||||
|
||||
export const client = new AgenticApiClient({
|
||||
apiBaseUrl: env.AGENTIC_API_BASE_URL
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
'use client'
|
||||
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
import Link from 'next/link'
|
||||
import useInfiniteScroll from 'react-infinite-scroll-hook'
|
||||
|
||||
import { useAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { toastError } from '@/lib/notifications'
|
||||
|
||||
export function MarketplaceIndex() {
|
||||
const ctx = useAgentic()
|
||||
const limit = 10
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['projects'],
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
ctx!.api
|
||||
.listPublicProjects({
|
||||
populate: ['lastPublishedDeployment'],
|
||||
offset: pageParam,
|
||||
limit
|
||||
})
|
||||
.then(async (projects) => {
|
||||
return {
|
||||
projects,
|
||||
offset: pageParam,
|
||||
limit,
|
||||
nextOffset:
|
||||
projects.length >= limit ? pageParam + projects.length : undefined
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
void toastError('Failed to fetch projects')
|
||||
throw err
|
||||
}),
|
||||
getNextPageParam: (lastGroup) => lastGroup?.nextOffset,
|
||||
enabled: !!ctx,
|
||||
initialPageParam: 0
|
||||
})
|
||||
|
||||
const [sentryRef] = useInfiniteScroll({
|
||||
loading: isLoading || isFetchingNextPage,
|
||||
hasNextPage,
|
||||
onLoadMore: fetchNextPage,
|
||||
disabled: !ctx || isError,
|
||||
rootMargin: '0px 0px 200px 0px'
|
||||
})
|
||||
|
||||
const projects = data ? data.pages.flatMap((p) => p.projects) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
>
|
||||
Dashboard
|
||||
</h1>
|
||||
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='mt-8'>
|
||||
<h2 className='text-xl font-semibold mb-4'>Your Projects</h2>
|
||||
|
||||
{isError ? (
|
||||
<p>Error fetching projects</p>
|
||||
) : !projects.length ? (
|
||||
<p>
|
||||
No projects found. Create your first project to get started!
|
||||
</p>
|
||||
) : (
|
||||
<div className='grid gap-4'>
|
||||
{projects.map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
className='p-4 border rounded-lg hover:border-gray-400 transition-colors'
|
||||
href={`/app/projects/${project.identifier}`}
|
||||
>
|
||||
<h3 className='font-medium'>{project.name}</h3>
|
||||
|
||||
<p className='text-sm text-gray-500'>
|
||||
{project.identifier}
|
||||
</p>
|
||||
|
||||
{project.lastPublishedDeployment && (
|
||||
<p className='text-sm text-gray-500 mt-1'>
|
||||
Last published:{' '}
|
||||
{project.lastPublishedDeployment.version ||
|
||||
project.lastPublishedDeployment.hash}
|
||||
</p>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div ref={sentryRef} className=''>
|
||||
{isLoading || (isFetchingNextPage && <LoadingIndicator />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { MarketplaceIndex } from './marketplace-index'
|
||||
|
||||
export default function MarketplaceIndexPage() {
|
||||
return <MarketplaceIndex />
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
import { useAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { toastError } from '@/lib/notifications'
|
||||
|
||||
export function MarketplaceProjectIndex({
|
||||
projectIdentifier
|
||||
}: {
|
||||
projectIdentifier: string
|
||||
}) {
|
||||
const ctx = useAgentic()
|
||||
const {
|
||||
data: project,
|
||||
isLoading,
|
||||
isError
|
||||
} = useQuery({
|
||||
queryKey: ['project', projectIdentifier],
|
||||
queryFn: () =>
|
||||
ctx!.api
|
||||
.getPublicProjectByIdentifier({
|
||||
projectIdentifier,
|
||||
populate: ['lastPublishedDeployment']
|
||||
})
|
||||
.catch((err: any) => {
|
||||
void toastError(`Failed to fetch project "${projectIdentifier}"`)
|
||||
throw err
|
||||
}),
|
||||
enabled: !!ctx
|
||||
})
|
||||
|
||||
return (
|
||||
<section>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : isError ? (
|
||||
<p>Error fetching project</p>
|
||||
) : !project ? (
|
||||
<p>Project "{projectIdentifier}" not found</p>
|
||||
) : (
|
||||
<>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
>
|
||||
Project {project.name}
|
||||
</h1>
|
||||
|
||||
<div className='mt-8'>
|
||||
<pre className='max-w-lg'>{JSON.stringify(project, null, 2)}</pre>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { parseProjectIdentifier } from '@agentic/platform-validators'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { toastError } from '@/lib/notifications'
|
||||
|
||||
import { MarketplaceProjectIndex } from './marketplace-project-index'
|
||||
|
||||
export default async function MarketplaceProjectIndexPage({
|
||||
params
|
||||
}: {
|
||||
params: Promise<{
|
||||
namespace: string
|
||||
'project-name': string
|
||||
}>
|
||||
}) {
|
||||
const { namespace: rawNamespace, 'project-name': rawProjectName } =
|
||||
await params
|
||||
|
||||
try {
|
||||
const namespace = decodeURIComponent(rawNamespace)
|
||||
const projectName = decodeURIComponent(rawProjectName)
|
||||
|
||||
const { projectIdentifier } = parseProjectIdentifier(
|
||||
`${namespace}/${projectName}`,
|
||||
{ strict: true }
|
||||
)
|
||||
|
||||
return <MarketplaceProjectIndex projectIdentifier={projectIdentifier} />
|
||||
} catch (err: any) {
|
||||
void toastError(err, { label: 'Invalid project identifier' })
|
||||
|
||||
return notFound()
|
||||
}
|
||||
}
|
|
@ -230,9 +230,10 @@ export class AgenticApiClient {
|
|||
/** Gets the currently authenticated user. */
|
||||
async getMe(): Promise<User> {
|
||||
// const user = await this.verifyAuthAndRefreshIfNecessary()
|
||||
assert(this._authSession)
|
||||
const userId = this._authSession?.user.id
|
||||
assert(userId, 'This method requires authentication.')
|
||||
|
||||
return this.ky.get(`v1/users/${this._authSession.user.id}`).json()
|
||||
return this.ky.get(`v1/users/${userId}`).json()
|
||||
}
|
||||
|
||||
/** Gets a user by ID. */
|
||||
|
@ -335,7 +336,65 @@ export class AgenticApiClient {
|
|||
.json()
|
||||
}
|
||||
|
||||
/** Lists projects the authenticated user has access to. */
|
||||
/** Lists projects that have been published publicly to the marketplace. */
|
||||
async listPublicProjects<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'listPublicProjects'>['populate']
|
||||
>[number]
|
||||
>(
|
||||
searchParams: OperationParameters<'listPublicProjects'> & {
|
||||
populate?: TPopulate[]
|
||||
} = {}
|
||||
): Promise<Array<PopulateProject<TPopulate>>> {
|
||||
return this.ky
|
||||
.get('v1/projects', { searchParams: sanitizeSearchParams(searchParams) })
|
||||
.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a public project by ID. The project must be publicly available on
|
||||
* the marketplace.
|
||||
*/
|
||||
async getPublicProject<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'getPublicProject'>['populate']
|
||||
>[number]
|
||||
>({
|
||||
projectId,
|
||||
...searchParams
|
||||
}: OperationParameters<'getPublicProject'> & {
|
||||
populate?: TPopulate[]
|
||||
}): Promise<PopulateProject<TPopulate>> {
|
||||
return this.ky
|
||||
.get(`v1/projects/public/${projectId}`, {
|
||||
searchParams: sanitizeSearchParams(searchParams)
|
||||
})
|
||||
.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a public project by its identifier. The project must be publicly
|
||||
* available on the marketplace.
|
||||
*/
|
||||
async getPublicProjectByIdentifier<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'getPublicProjectByIdentifier'>['populate']
|
||||
>[number]
|
||||
>(
|
||||
searchParams: OperationParameters<'getPublicProjectByIdentifier'> & {
|
||||
populate?: TPopulate[]
|
||||
}
|
||||
): Promise<PopulateProject<TPopulate>> {
|
||||
return this.ky
|
||||
.get('v1/projects/public/by-identifier', {
|
||||
searchParams: sanitizeSearchParams(searchParams)
|
||||
})
|
||||
.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists projects the authenticated user has access to.
|
||||
*/
|
||||
async listProjects<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'listProjects'>['populate']
|
||||
|
@ -346,7 +405,9 @@ export class AgenticApiClient {
|
|||
} = {}
|
||||
): Promise<Array<PopulateProject<TPopulate>>> {
|
||||
return this.ky
|
||||
.get('v1/projects', { searchParams: sanitizeSearchParams(searchParams) })
|
||||
.get(`v1/projects`, {
|
||||
searchParams: sanitizeSearchParams(searchParams)
|
||||
})
|
||||
.json()
|
||||
}
|
||||
|
||||
|
@ -358,7 +419,9 @@ export class AgenticApiClient {
|
|||
return this.ky.post('v1/projects', { json: project, searchParams }).json()
|
||||
}
|
||||
|
||||
/** Gets a project by ID. */
|
||||
/**
|
||||
* Gets a project by ID. Authenticated user must have access to the project.
|
||||
*/
|
||||
async getProject<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'getProject'>['populate']
|
||||
|
@ -376,7 +439,10 @@ export class AgenticApiClient {
|
|||
.json()
|
||||
}
|
||||
|
||||
/** Gets a project by its public identifier. */
|
||||
/**
|
||||
* Gets a project by its identifier. Authenticated user must have access to
|
||||
* the project.
|
||||
*/
|
||||
async getProjectByIdentifier<
|
||||
TPopulate extends NonNullable<
|
||||
OperationParameters<'getProjectByIdentifier'>['populate']
|
||||
|
@ -393,7 +459,9 @@ export class AgenticApiClient {
|
|||
.json()
|
||||
}
|
||||
|
||||
/** Updates a project. */
|
||||
/**
|
||||
* Updates a project. Authenticated user must have access to the project.
|
||||
*/
|
||||
async updateProject(
|
||||
project: OperationBody<'updateProject'>,
|
||||
{ projectId, ...searchParams }: OperationParameters<'updateProject'>
|
||||
|
|
|
@ -89,6 +89,57 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/projects/public": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Lists projects that have been published publicly to the marketplace. */
|
||||
get: operations["listPublicProjects"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/projects/public/by-identifier": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Gets a public project by its public identifier (eg, "@username/project-name"). */
|
||||
get: operations["getPublicProjectByIdentifier"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/projects/public/{projectId}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Gets a public project by ID. */
|
||||
get: operations["getPublicProject"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/users/{userId}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
@ -186,7 +237,7 @@ export interface paths {
|
|||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description Lists projects the authenticated user has access to. */
|
||||
/** @description Lists projects owned by the authenticated user or team. */
|
||||
get: operations["listProjects"];
|
||||
put?: never;
|
||||
/** @description Creates a new project. */
|
||||
|
@ -445,6 +496,39 @@ export interface components {
|
|||
token: string;
|
||||
user: components["schemas"]["User"];
|
||||
};
|
||||
/** @description Public project identifier (e.g. "@namespace/project-name") */
|
||||
ProjectIdentifier: string;
|
||||
/** @description The frequency at which a subscription is billed. */
|
||||
PricingInterval: "day" | "week" | "month" | "year";
|
||||
/** @description A Project represents a single Agentic API product. A Project is comprised of a series of immutable Deployments, each of which contains pricing data, origin API config, OpenAPI or MCP specs, tool definitions, and various metadata.
|
||||
*
|
||||
* You can think of Agentic Projects as similar to Vercel projects. They both hold some common configuration and are comprised of a series of immutable Deployments.
|
||||
*
|
||||
* Internally, Projects manage all of the Stripe billing resources across Deployments (Stripe Products, Prices, and Meters for usage-based billing). */
|
||||
Project: {
|
||||
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt?: string;
|
||||
identifier: components["schemas"]["ProjectIdentifier"];
|
||||
namespace: string;
|
||||
name: string;
|
||||
private: boolean;
|
||||
/** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */
|
||||
userId: string;
|
||||
/** @description Team id (e.g. "team_tz4a98xxat96iws9zmbrgj3a") */
|
||||
teamId?: string;
|
||||
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
||||
lastPublishedDeploymentId?: string;
|
||||
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
||||
lastDeploymentId?: string;
|
||||
lastPublishedDeploymentVersion?: string;
|
||||
applicationFeePercent: number;
|
||||
defaultPricingInterval: components["schemas"]["PricingInterval"];
|
||||
/** @enum {string} */
|
||||
pricingCurrency: "usd";
|
||||
};
|
||||
Team: {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
|
@ -466,38 +550,6 @@ export interface components {
|
|||
confirmed: boolean;
|
||||
confirmedAt?: string;
|
||||
};
|
||||
/** @description Public project identifier (e.g. "@namespace/project-name") */
|
||||
ProjectIdentifier: string;
|
||||
/** @description The frequency at which a subscription is billed. */
|
||||
PricingInterval: "day" | "week" | "month" | "year";
|
||||
/** @description A Project represents a single Agentic API product. A Project is comprised of a series of immutable Deployments, each of which contains pricing data, origin API config, OpenAPI or MCP specs, tool definitions, and various metadata.
|
||||
*
|
||||
* You can think of Agentic Projects as similar to Vercel projects. They both hold some common configuration and are comprised of a series of immutable Deployments.
|
||||
*
|
||||
* Internally, Projects manage all of the Stripe billing resources across Deployments (Stripe Products, Prices, and Meters for usage-based billing). */
|
||||
Project: {
|
||||
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt?: string;
|
||||
identifier: components["schemas"]["ProjectIdentifier"];
|
||||
name: string;
|
||||
alias?: string;
|
||||
/** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */
|
||||
userId: string;
|
||||
/** @description Team id (e.g. "team_tz4a98xxat96iws9zmbrgj3a") */
|
||||
teamId?: string;
|
||||
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
||||
lastPublishedDeploymentId?: string;
|
||||
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
||||
lastDeploymentId?: string;
|
||||
lastPublishedDeploymentVersion?: string;
|
||||
applicationFeePercent: number;
|
||||
defaultPricingInterval: components["schemas"]["PricingInterval"];
|
||||
/** @enum {string} */
|
||||
pricingCurrency: "usd";
|
||||
};
|
||||
/** @description A Consumer represents a user who has subscribed to a Project and is used
|
||||
* to track usage and billing.
|
||||
*
|
||||
|
@ -1065,6 +1117,92 @@ export interface operations {
|
|||
404: components["responses"]["404"];
|
||||
};
|
||||
};
|
||||
listPublicProjects: {
|
||||
parameters: {
|
||||
query?: {
|
||||
offset?: number | null;
|
||||
limit?: number;
|
||||
sort?: "asc" | "desc";
|
||||
sortBy?: "createdAt" | "updatedAt";
|
||||
populate?: ("user" | "team" | "lastPublishedDeployment" | "lastDeployment") | ("user" | "team" | "lastPublishedDeployment" | "lastDeployment")[];
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description A list of projects */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Project"][];
|
||||
};
|
||||
};
|
||||
400: components["responses"]["400"];
|
||||
401: components["responses"]["401"];
|
||||
403: components["responses"]["403"];
|
||||
};
|
||||
};
|
||||
getPublicProjectByIdentifier: {
|
||||
parameters: {
|
||||
query: {
|
||||
populate?: ("user" | "team" | "lastPublishedDeployment" | "lastDeployment") | ("user" | "team" | "lastPublishedDeployment" | "lastDeployment")[];
|
||||
/** @description Public project identifier (e.g. "@namespace/project-name") */
|
||||
projectIdentifier: components["schemas"]["ProjectIdentifier"];
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description A project */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Project"];
|
||||
};
|
||||
};
|
||||
400: components["responses"]["400"];
|
||||
401: components["responses"]["401"];
|
||||
403: components["responses"]["403"];
|
||||
404: components["responses"]["404"];
|
||||
};
|
||||
};
|
||||
getPublicProject: {
|
||||
parameters: {
|
||||
query?: {
|
||||
populate?: ("user" | "team" | "lastPublishedDeployment" | "lastDeployment") | ("user" | "team" | "lastPublishedDeployment" | "lastDeployment")[];
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Project ID */
|
||||
projectId: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description A project */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Project"];
|
||||
};
|
||||
};
|
||||
400: components["responses"]["400"];
|
||||
401: components["responses"]["401"];
|
||||
403: components["responses"]["403"];
|
||||
404: components["responses"]["404"];
|
||||
};
|
||||
};
|
||||
getUser: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
@ -1505,7 +1643,6 @@ export interface operations {
|
|||
content: {
|
||||
"application/json": {
|
||||
name?: string;
|
||||
alias?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
- hosted docs
|
||||
- merge with current agentic repo
|
||||
- publish packages to npm
|
||||
- api
|
||||
- deploy to prod
|
||||
- database
|
||||
- consider using [neon serverless driver](https://orm.drizzle.team/docs/connect-neon) for production
|
||||
- can this also be used locally?
|
||||
|
|
Ładowanie…
Reference in New Issue