Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ next-env.d.ts
skills-lock.json
studio_audit_report.md
server_audit_report.md
github_issues_to_create.md
4 changes: 2 additions & 2 deletions apps/blog-platform/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veriworkly/blog-platform",
"version": "v3.7.1-beta.1",
"version": "3.8.0-beta.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -15,7 +15,7 @@
"fumadocs-core": "^16.8.3",
"fumadocs-mdx": "^14.3.1",
"fumadocs-ui": "^16.8.3",
"lucide-react": "^1.11.0",
"lucide-react": "^1.16.0",
"next": "16.2.6",
"next-themes": "^0.4.6",
"react": "^19.2.5",
Expand Down
23 changes: 9 additions & 14 deletions apps/docs-platform/content/docs/getting-started/local-setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,10 @@ Before initialization, ensure the following dependencies are installed:

</Step>

<Step>
### Install Dependencies
Install all package and workspace dependencies defined in the monorepo root:
```bash
npm install
```
</Step>
<Step>
### Install Dependencies Install all package and workspace dependencies defined in the monorepo
root: ```bash npm install ```
</Step>

<Step>
### Configure Environment Variables
Expand All @@ -76,13 +73,11 @@ Before initialization, ensure the following dependencies are installed:

</Step>

<Step>
### Database Initialization
If you are planning to work on backend API authentication or syncing features, initialize your PostgreSQL database with the Prisma schema:
```bash
npm run db:push -w @veriworkly/server
```
</Step>
<Step>
### Database Initialization If you are planning to work on backend API authentication or syncing
features, initialize your PostgreSQL database with the Prisma schema: ```bash npm run db:push -w
@veriworkly/server ```
</Step>

<Step>
### Start Development Servers
Expand Down
4 changes: 2 additions & 2 deletions apps/docs-platform/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veriworkly/docs-platform",
"version": "v3.7.1-beta.1",
"version": "3.8.0-beta.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -16,7 +16,7 @@
"fumadocs-mdx": "^14.3.1",
"fumadocs-openapi": "^10.8.0",
"fumadocs-ui": "^16.8.3",
"lucide-react": "^1.11.0",
"lucide-react": "^1.16.0",
"next": "16.2.6",
"next-themes": "^0.4.6",
"react": "^19.2.5",
Expand Down
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veriworkly/server",
"version": "v3.7.1-beta.1",
"version": "3.8.0-beta.1",
"description": "VeriWorkly Resume Backend API",
"main": "dist/index.js",
"type": "module",
Expand Down
24 changes: 22 additions & 2 deletions apps/server/src/controllers/shareController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const shareLinkCreateSchema = z.object({
expiresAt: z.string().datetime().nullable().optional(),
noExpiry: z.boolean().optional(),
updateSlug: z.boolean().optional(),
removePassword: z.boolean().optional(),
});

const shareLinkPasswordSchema = z.object({
Expand All @@ -44,6 +45,17 @@ function buildPublicSharePayload(shareLink: PublicShareLink, includeSnapshot: bo
};
}

function formatShareLinkResponse<T extends { passwordHash?: string | null }>(shareLink: T | null) {
if (!shareLink) return shareLink;

const { passwordHash, ...rest } = shareLink;

return {
...rest,
hasPassword: Boolean(passwordHash),
};
}

export class ShareController {
/**
* Create a new share link for a document.
Expand Down Expand Up @@ -75,7 +87,14 @@ export class ShareController {
await cacheDel(`share:public-readable:${shareLink.username}:${shareLink.documentSlug}`);
await cacheDel(`share:public-readable:${shareLink.username}:${shareLink.slug}`);

res.status(201).json(createSuccessResponse(shareLink, "Share link created successfully"));
res
.status(201)
.json(
createSuccessResponse(
formatShareLinkResponse(shareLink),
"Share link created successfully",
),
);
} catch (error) {
if (error instanceof z.ZodError) return next(handleValidationError(error));
next(error);
Expand Down Expand Up @@ -113,7 +132,8 @@ export class ShareController {
pagination,
);

const response = { items, ...createOffsetPaginationMeta(total, pagination) };
const formattedItems = items.map(formatShareLinkResponse);
const response = { items: formattedItems, ...createOffsetPaginationMeta(total, pagination) };

await cacheSet(cacheKey, response, 1800);

Expand Down
10 changes: 6 additions & 4 deletions apps/server/src/services/documentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,13 @@ export class DocumentService {

const document = await prisma.document.create({
data: {
id: input.id,
slug,
title,
userId,
id: input.id,
type: input.type,
title,
slug,
tags: input.tags || [],
lastSyncedAt: new Date(),
content: initialContent || {},
metadata: input.metadata || {},
templateId: input.templateId || "modern",
Expand Down Expand Up @@ -198,7 +199,7 @@ export class DocumentService {
updateData.slug = await this.buildUniqueSlug(userId, nextSlugSource, documentId);
}

if (updateShareSlug && user?.username && updateData.slug) {
if (user?.username && updateData.slug && updateShareSlug) {
const shareLink = await prisma.shareLink.findUnique({
where: { userId_documentId: { userId, documentId } },
select: { id: true, slug: true },
Expand All @@ -210,6 +211,7 @@ export class DocumentService {
id: shareLink.id,
slug: await this.buildUniqueShareSlug(userId, updateData.slug, shareLink.id),
};

readableShareCacheKeys.add(
`share:public-readable:${user.username}:${shareLinkSlugUpdate.slug}`,
);
Expand Down
50 changes: 33 additions & 17 deletions apps/server/src/services/shareService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class ShareService {
noExpiry?: boolean;
expiresAt?: string | Date | null;
updateSlug?: boolean;
removePassword?: boolean;
snapshot: Prisma.InputJsonValue;
},
) {
Expand All @@ -75,14 +76,22 @@ export class ShareService {

const existing = await prisma.shareLink.findUnique({
where: { userId_documentId: { userId, documentId } },
select: { id: true, slug: true },
select: { id: true, slug: true, passwordHash: true },
});

const slug =
existing && !data.updateSlug
? existing.slug
: await this.buildUniqueShareSlug(userId, document.slug, existing?.id);
const passwordHash = data.password ? await this.hashPassword(data.password) : null;

let passwordHash = existing ? existing.passwordHash : null;

if (data.removePassword) {
passwordHash = null;
} else if (data.password) {
passwordHash = await this.hashPassword(data.password);
}

const expiresAt = data.noExpiry ? null : data.expiresAt ? new Date(data.expiresAt) : null;

const shareLink = existing
Expand All @@ -109,6 +118,7 @@ export class ShareService {
return {
shareLink: {
...shareLink,
token: shareLink.slug,
username,
documentSlug: document.slug,
publicPath: `/${username}/${shareLink.slug}`,
Expand All @@ -118,27 +128,33 @@ export class ShareService {
}

static async listShareLinks(userId: string, documentId: string) {
return this.listShareLinksPaginated(userId, documentId, { limit: 100, offset: 0 });
return this.listShareLinksPaginated(userId, documentId);
}

static async listShareLinksPaginated(
userId: string,
documentId: string,
query: { limit: number; offset: number },
query?: { limit: number; offset: number },
) {
const where = { userId, documentId };

const [items, total] = await Promise.all([
prisma.shareLink.findMany({
where,
orderBy: { createdAt: "desc" },
take: query.limit,
skip: query.offset,
}),
prisma.shareLink.count({ where }),
]);

return { items, total };
void query;

const item = await prisma.shareLink.findUnique({
where: { userId_documentId: { userId, documentId } },
});

const username = await UserService.requireUsernameForUser(userId);

const items = item
? [
{
...item,
token: item.slug,
username,
},
]
: [];

return { items, total: items.length };
}

static async revokeShareLink(userId: string, documentId: string, shareLinkId: string) {
Expand Down
4 changes: 2 additions & 2 deletions apps/site/app/roadmap/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function RoadmapLoading() {
<main className="relative flex min-h-screen flex-col overflow-hidden">
<div className="surface-grid pointer-events-none absolute inset-0 -z-10 opacity-[0.25]" />

<div className="bg-accent/5 pointer-events-none absolute top-0 left-1/4 -z-10 h-[600px] w-[600px] rounded-full blur-[130px]" />
<div className="bg-accent/5 pointer-events-none absolute top-0 left-1/4 -z-10 h-150 w-150 rounded-full blur-[130px]" />

<Container className="pt-28 pb-20 lg:pt-36">
<div className="border-border/40 mb-12 space-y-4 border-b pb-8">
Expand Down Expand Up @@ -41,7 +41,7 @@ export default function RoadmapLoading() {
{[1, 2, 3].map((i) => (
<div
key={i}
className="border-border/30 bg-muted/5 flex min-h-[350px] flex-col gap-5 rounded-3xl border p-4 sm:p-5"
className="border-border/30 bg-muted/5 flex min-h-87.5 flex-col gap-5 rounded-3xl border p-4 sm:p-5"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
Expand Down
4 changes: 2 additions & 2 deletions apps/site/app/stats/components/StatsFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const StatsFilters = ({ status, kind, updatedFrom, updatedTo }: StatsFiltersProp
</p>
</div>

<div className="bg-muted/10 border-border/20 scrollbar-none inline-flex w-fit overflow-x-auto rounded-full border p-0.5">
<div className="bg-muted/10 border-border/20 inline-flex w-fit scrollbar-none overflow-x-auto rounded-full border p-0.5">
{statusOptions.map((option) => {
const isActive = status === option.value;

Expand Down Expand Up @@ -65,7 +65,7 @@ const StatsFilters = ({ status, kind, updatedFrom, updatedTo }: StatsFiltersProp
</h2>
</div>

<div className="bg-muted/10 border-border/20 scrollbar-none inline-flex w-fit overflow-x-auto rounded-full border p-0.5">
<div className="bg-muted/10 border-border/20 inline-flex w-fit scrollbar-none overflow-x-auto rounded-full border p-0.5">
{kindOptions.map((option) => {
const isActive = kind === option.value;

Expand Down
2 changes: 1 addition & 1 deletion apps/site/app/stats/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const StatsLoading = () => {
{[1, 2, 3, 4].map((i) => (
<div
key={i}
className="border-border/30 bg-card/40 h-[72px] min-w-[110px] rounded-2xl border px-4 py-3"
className="border-border/30 bg-card/40 h-18 min-w-27.5 rounded-2xl border px-4 py-3"
/>
))}
</div>
Expand Down
3 changes: 1 addition & 2 deletions apps/site/components/roadmap/KanbanColumnView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ const KanbanColumnView = ({
);

return (
<div className="border-border/30 bg-muted/5 flex min-h-[350px] flex-col gap-5 rounded-3xl border p-4 sm:p-5">
{/* Column Header */}
<div className="border-border/30 bg-muted/5 flex min-h-87.5 flex-col gap-5 rounded-3xl border p-4 sm:p-5">
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2.5">
Expand Down
2 changes: 1 addition & 1 deletion apps/site/components/roadmap/KanbanItemCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const KanbanItemCard = ({
}}
>
{isInteractive && (
<div className="bg-accent/80 absolute top-0 right-0 left-0 h-[2px] opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
<div className="bg-accent/80 absolute top-0 right-0 left-0 h-0.5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
)}

<div className="flex flex-col gap-4">
Expand Down
4 changes: 2 additions & 2 deletions apps/site/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veriworkly/site",
"version": "v3.7.1-beta.1",
"version": "3.8.0-beta.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -13,7 +13,7 @@
"dependencies": {
"@veriworkly/ui": "*",
"clsx": "^2.1.1",
"lucide-react": "^1.11.0",
"lucide-react": "^1.16.0",
"next": "16.2.6",
"next-themes": "^0.4.6",
"react": "^19.2.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
"use client";

import { Copy, Cloud, Share2, Trash2, RefreshCw, ExternalLink, MoreHorizontal } from "lucide-react";
import { useRouter } from "next/navigation";
import {
Copy,
Edit2,
Cloud,
Share2,
Trash2,
RefreshCw,
ExternalLink,
MoreHorizontal,
} from "lucide-react";
import { toast } from "sonner";
import { useRouter } from "next/navigation";

import { Menu, MenuItem, MenuSeparator } from "@veriworkly/ui";

Expand All @@ -13,6 +22,7 @@ export interface DocumentActionsMenuProps {
syncing: boolean;
onDeleteAction: (doc: DocumentLibraryItem) => void;
onShareAction: (doc: DocumentLibraryItem) => void;
onRenameAction: (doc: DocumentLibraryItem) => void;
onSyncNowAction: (id: string) => void;
onSyncDetailsAction: (id: string) => void;
}
Expand All @@ -22,6 +32,7 @@ export function DocumentActionsMenu({
syncing,
onDeleteAction,
onShareAction,
onRenameAction,
onSyncNowAction,
onSyncDetailsAction,
}: DocumentActionsMenuProps) {
Expand Down Expand Up @@ -62,6 +73,17 @@ export function DocumentActionsMenu({
Open
</MenuItem>

<MenuItem
className="h-8 rounded-lg text-xs"
onClick={() => {
close();
onRenameAction(doc);
}}
>
<Edit2 className="h-4 w-4" />
Rename
</MenuItem>

<MenuItem
className="h-8 rounded-lg text-xs"
onClick={() => {
Expand Down
Loading
Loading