feat: set workspace url when creating a new workspace (#172)
* feat: add migration to remove not null constraint from referenceId on subscriptions * feat(cloud): write worspace slug availability checks to db * feat: add migration to add cascade set null to referenceId on subscription * feat: update workspace and subscripton schema * feat: add createWorkspaceSlugCheck repo func * feat: set workspace url on creation * chore: update translations * refactor: various tweaks and fixes * feat: add pro bolt to pricing notice * feat: toggle pro subscription * fix: hide pro subscription notice for self hosters * feat: add pro upgrade message on members page * feat: hide pro upgrade notice on small viewports * feat: update workspace url placeholder
This commit is contained in:
@@ -140,6 +140,12 @@ export const workspaceRouter = createTRPCRouter({
|
||||
.input(
|
||||
z.object({
|
||||
name: z.string().min(1),
|
||||
slug: z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(24)
|
||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/)
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
.output(z.custom<Awaited<ReturnType<typeof workspaceRepo.create>>>())
|
||||
@@ -153,12 +159,43 @@ export const workspaceRouter = createTRPCRouter({
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
// Check if slug is provided in cloud environment
|
||||
if (input.slug && env("NEXT_PUBLIC_KAN_ENV") === "cloud") {
|
||||
throw new TRPCError({
|
||||
message: "Custom URLs are only available for Pro workspaces",
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
}
|
||||
|
||||
const workspacePublicId = generateUID();
|
||||
const workspaceSlug = input.slug ?? workspacePublicId;
|
||||
|
||||
if (input.slug) {
|
||||
const reservedOrPremiumWorkspaceSlug =
|
||||
await workspaceSlugRepo.getWorkspaceSlug(ctx.db, input.slug);
|
||||
|
||||
const isWorkspaceSlugAvailable =
|
||||
await workspaceRepo.isWorkspaceSlugAvailable(ctx.db, input.slug);
|
||||
|
||||
if (reservedOrPremiumWorkspaceSlug) {
|
||||
throw new TRPCError({
|
||||
message: `Workspace slug '${input.slug}' is reserved or premium`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
}
|
||||
|
||||
if (!isWorkspaceSlugAvailable) {
|
||||
throw new TRPCError({
|
||||
message: `Workspace slug '${input.slug}' is already taken`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = await workspaceRepo.create(ctx.db, {
|
||||
publicId: workspacePublicId,
|
||||
name: input.name,
|
||||
slug: workspacePublicId,
|
||||
slug: workspaceSlug,
|
||||
createdBy: userId,
|
||||
createdByEmail: userEmail,
|
||||
});
|
||||
@@ -334,6 +371,14 @@ export const workspaceRouter = createTRPCRouter({
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const userId = ctx.user?.id;
|
||||
|
||||
if (!userId)
|
||||
throw new TRPCError({
|
||||
message: `User not authenticated`,
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
|
||||
const slug = input.workspaceSlug.toLowerCase();
|
||||
// check slug is not reserved
|
||||
const workspaceSlug = await workspaceSlugRepo.getWorkspaceSlug(
|
||||
@@ -345,6 +390,19 @@ export const workspaceRouter = createTRPCRouter({
|
||||
const isWorkspaceSlugAvailable =
|
||||
await workspaceRepo.isWorkspaceSlugAvailable(ctx.db, slug);
|
||||
|
||||
const isAvailable =
|
||||
isWorkspaceSlugAvailable && workspaceSlug?.type !== "reserved";
|
||||
const isReserved = workspaceSlug?.type === "reserved";
|
||||
|
||||
if (env("NEXT_PUBLIC_KAN_ENV") === "cloud") {
|
||||
await workspaceSlugRepo.createWorkspaceSlugCheck(ctx.db, {
|
||||
slug,
|
||||
userId,
|
||||
available: isAvailable,
|
||||
reserved: isReserved,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
isAvailable:
|
||||
isWorkspaceSlugAvailable && workspaceSlug?.type !== "reserved",
|
||||
|
||||
Reference in New Issue
Block a user