mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-07 20:45:17 +00:00
feat(user): add bookmarkedTemplates column to user table and update related API methods
- Introduced a new column `bookmarkedTemplates` to the user table to store user-specific template bookmarks. - Updated API methods to manage bookmarked templates, replacing the deprecated user_template_bookmarks table. - Adjusted queries to retrieve and toggle bookmarks directly from the user record.
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "user" ADD COLUMN "bookmarkedTemplates" text[] DEFAULT ARRAY[]::text[];
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1114,6 +1114,13 @@
|
|||||||
"when": 1775270343231,
|
"when": 1775270343231,
|
||||||
"tag": "0158_amused_synch",
|
"tag": "0158_amused_synch",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 159,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1775274158009,
|
||||||
|
"tag": "0159_polite_puppet_master",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
apiUpdateUser,
|
apiUpdateUser,
|
||||||
invitation,
|
invitation,
|
||||||
member,
|
member,
|
||||||
userTemplateBookmarks,
|
user,
|
||||||
} from "@dokploy/server/db/schema";
|
} from "@dokploy/server/db/schema";
|
||||||
import {
|
import {
|
||||||
hasPermission,
|
hasPermission,
|
||||||
@@ -642,12 +642,12 @@ export const userRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
getBookmarkedTemplates: protectedProcedure.query(async ({ ctx }) => {
|
getBookmarkedTemplates: protectedProcedure.query(async ({ ctx }) => {
|
||||||
const bookmarked = await db.query.userTemplateBookmarks.findMany({
|
const result = await db.query.user.findFirst({
|
||||||
where: eq(userTemplateBookmarks.userId, ctx.user.id),
|
where: eq(user.id, ctx.user.id),
|
||||||
orderBy: [asc(userTemplateBookmarks.createdAt)],
|
columns: { bookmarkedTemplates: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
return bookmarked.map((b) => b.templateId);
|
return result?.bookmarkedTemplates ?? [];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
toggleTemplateBookmark: protectedProcedure
|
toggleTemplateBookmark: protectedProcedure
|
||||||
@@ -657,24 +657,23 @@ export const userRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const existing = await db.query.userTemplateBookmarks.findFirst({
|
const result = await db.query.user.findFirst({
|
||||||
where: and(
|
where: eq(user.id, ctx.user.id),
|
||||||
eq(userTemplateBookmarks.userId, ctx.user.id),
|
columns: { bookmarkedTemplates: true },
|
||||||
eq(userTemplateBookmarks.templateId, input.templateId),
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
const current = result?.bookmarkedTemplates ?? [];
|
||||||
await db
|
const isBookmarked = current.includes(input.templateId);
|
||||||
.delete(userTemplateBookmarks)
|
|
||||||
.where(eq(userTemplateBookmarks.id, existing.id));
|
|
||||||
return { isBookmarked: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.insert(userTemplateBookmarks).values({
|
const updated = isBookmarked
|
||||||
userId: ctx.user.id,
|
? current.filter((id) => id !== input.templateId)
|
||||||
templateId: input.templateId,
|
: [...current, input.templateId];
|
||||||
});
|
|
||||||
return { isBookmarked: true };
|
await db
|
||||||
|
.update(user)
|
||||||
|
.set({ bookmarkedTemplates: updated })
|
||||||
|
.where(eq(user.id, ctx.user.id));
|
||||||
|
|
||||||
|
return { isBookmarked: !isBookmarked };
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import { relations } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
integer,
|
integer,
|
||||||
pgTable,
|
pgTable,
|
||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
unique,
|
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
@@ -67,26 +66,11 @@ export const user = pgTable("user", {
|
|||||||
stripeSubscriptionId: text("stripeSubscriptionId"),
|
stripeSubscriptionId: text("stripeSubscriptionId"),
|
||||||
serversQuantity: integer("serversQuantity").notNull().default(0),
|
serversQuantity: integer("serversQuantity").notNull().default(0),
|
||||||
trustedOrigins: text("trustedOrigins").array(),
|
trustedOrigins: text("trustedOrigins").array(),
|
||||||
|
bookmarkedTemplates: text("bookmarkedTemplates")
|
||||||
|
.array()
|
||||||
|
.default(sql`ARRAY[]::text[]`),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userTemplateBookmarks = pgTable(
|
|
||||||
"user_template_bookmarks",
|
|
||||||
{
|
|
||||||
id: text("id")
|
|
||||||
.notNull()
|
|
||||||
.primaryKey()
|
|
||||||
.$defaultFn(() => nanoid()),
|
|
||||||
userId: text("userId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => user.id, { onDelete: "cascade" }),
|
|
||||||
templateId: text("templateId").notNull(),
|
|
||||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
|
||||||
},
|
|
||||||
(table) => ({
|
|
||||||
uniqueUserTemplate: unique().on(table.userId, table.templateId),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const usersRelations = relations(user, ({ one, many }) => ({
|
export const usersRelations = relations(user, ({ one, many }) => ({
|
||||||
account: one(account, {
|
account: one(account, {
|
||||||
fields: [user.id],
|
fields: [user.id],
|
||||||
@@ -98,25 +82,15 @@ export const usersRelations = relations(user, ({ one, many }) => ({
|
|||||||
ssoProviders: many(ssoProvider),
|
ssoProviders: many(ssoProvider),
|
||||||
backups: many(backups),
|
backups: many(backups),
|
||||||
schedules: many(schedules),
|
schedules: many(schedules),
|
||||||
templateBookmarks: many(userTemplateBookmarks),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const userTemplateBookmarksRelations = relations(
|
|
||||||
userTemplateBookmarks,
|
|
||||||
({ one }) => ({
|
|
||||||
user: one(user, {
|
|
||||||
fields: [userTemplateBookmarks.userId],
|
|
||||||
references: [user.id],
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const createSchema = createInsertSchema(user, {
|
const createSchema = createInsertSchema(user, {
|
||||||
id: z.string().min(1),
|
id: z.string().min(1),
|
||||||
isRegistered: z.boolean().optional(),
|
isRegistered: z.boolean().optional(),
|
||||||
}).omit({
|
}).omit({
|
||||||
role: true,
|
role: true,
|
||||||
trustedOrigins: true,
|
trustedOrigins: true,
|
||||||
|
bookmarkedTemplates: true,
|
||||||
isValidEnterpriseLicense: true,
|
isValidEnterpriseLicense: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ const parseSizeToBytes = (size: string): number => {
|
|||||||
const match = size.match(/^([\d.]+)\s*([KMGT]?B)$/i);
|
const match = size.match(/^([\d.]+)\s*([KMGT]?B)$/i);
|
||||||
if (!match) return 0;
|
if (!match) return 0;
|
||||||
const value = Number.parseFloat(match[1] as string);
|
const value = Number.parseFloat(match[1] as string);
|
||||||
const unit = match[2].toUpperCase();
|
const unit = (match[2] as string).toUpperCase();
|
||||||
const multipliers: Record<string, number> = {
|
const multipliers: Record<string, number> = {
|
||||||
B: 1,
|
B: 1,
|
||||||
KB: 1024,
|
KB: 1024,
|
||||||
|
|||||||
Reference in New Issue
Block a user