mirror of
https://github.com/kyantech/Palmr.git
synced 2025-10-23 06:11:58 +00:00
refactor: remove demo functionality and related components (#182)
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
|||||||
LayoutIcon,
|
LayoutIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
MousePointer,
|
MousePointer,
|
||||||
RadioIcon,
|
|
||||||
RocketIcon,
|
RocketIcon,
|
||||||
SearchIcon,
|
SearchIcon,
|
||||||
TimerIcon,
|
TimerIcon,
|
||||||
@@ -82,23 +81,6 @@ function Hero() {
|
|||||||
<Link href={docsLink}>Documentation</Link>
|
<Link href={docsLink}>Documentation</Link>
|
||||||
</div>
|
</div>
|
||||||
</PulsatingButton>
|
</PulsatingButton>
|
||||||
<RippleButton
|
|
||||||
onClick={() => {
|
|
||||||
const demoId = `${Math.random().toString(36).substr(2, 9)}`;
|
|
||||||
const token = `${Math.random().toString(36).substr(2, 12)}`;
|
|
||||||
|
|
||||||
sessionStorage.setItem("demo_token", token);
|
|
||||||
sessionStorage.setItem("demo_id", demoId);
|
|
||||||
sessionStorage.setItem("demo_expires", (Date.now() + 5 * 60 * 1000).toString());
|
|
||||||
|
|
||||||
window.location.href = `/demo?id=${demoId}&token=${token}`;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<RadioIcon size={18} />
|
|
||||||
Live Demo
|
|
||||||
</div>
|
|
||||||
</RippleButton>
|
|
||||||
<RippleButton>
|
<RippleButton>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/kyantech/Palmr"
|
href="https://github.com/kyantech/Palmr"
|
||||||
|
@@ -1,225 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
import { Palmtree } from "lucide-react";
|
|
||||||
import { motion } from "motion/react";
|
|
||||||
|
|
||||||
import { BackgroundLights } from "@/components/ui/background-lights";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
interface DemoStatus {
|
|
||||||
status: "waiting" | "ready";
|
|
||||||
url: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateDemoResponse {
|
|
||||||
message: string;
|
|
||||||
url: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DemoClientInner() {
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const demoId = searchParams.get("id");
|
|
||||||
const token = searchParams.get("token");
|
|
||||||
|
|
||||||
const [status, setStatus] = useState<DemoStatus | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const validateAccess = () => {
|
|
||||||
const storedToken = sessionStorage.getItem("demo_token");
|
|
||||||
const storedId = sessionStorage.getItem("demo_id");
|
|
||||||
const expiresAt = sessionStorage.getItem("demo_expires");
|
|
||||||
|
|
||||||
if (!demoId || !token || !storedToken || !storedId || !expiresAt) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token !== storedToken || demoId !== storedId || Date.now() > parseInt(expiresAt)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!validateAccess()) {
|
|
||||||
setError("Unauthorized access. Please use the Live Demo button to access this page.");
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createDemo = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch("https://palmr-demo-manager.kyantech.com.br/create-demo", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
palmr_demo_instance_id: demoId,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to create demo");
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: CreateDemoResponse = await response.json();
|
|
||||||
console.log("Demo creation response:", data);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error creating demo:", err);
|
|
||||||
setError("Failed to create demo. Please try again.");
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkStatus = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`https://palmr-demo-manager.kyantech.com.br/status/${demoId}`);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to check demo status");
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: DemoStatus = await response.json();
|
|
||||||
setStatus(data);
|
|
||||||
|
|
||||||
if (data.status === "ready" && data.url) {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error checking status:", err);
|
|
||||||
setError("Failed to check demo status. Please try again.");
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
createDemo();
|
|
||||||
|
|
||||||
const interval = setInterval(checkStatus, 5000); // Check every 5 seconds
|
|
||||||
|
|
||||||
checkStatus();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
sessionStorage.removeItem("demo_token");
|
|
||||||
sessionStorage.removeItem("demo_id");
|
|
||||||
sessionStorage.removeItem("demo_expires");
|
|
||||||
};
|
|
||||||
}, [demoId, token]);
|
|
||||||
|
|
||||||
const handleGoToDemo = () => {
|
|
||||||
if (status?.url) {
|
|
||||||
window.open(status.url, "_blank");
|
|
||||||
}
|
|
||||||
window.location.href = "/";
|
|
||||||
};
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-background">
|
|
||||||
<BackgroundLights />
|
|
||||||
<div className="relative flex flex-col items-center justify-center h-full">
|
|
||||||
<div className="text-center space-y-6 max-w-md">
|
|
||||||
<h1 className="text-2xl font-bold text-destructive">Error</h1>
|
|
||||||
<p className="text-muted-foreground">{error}</p>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
sessionStorage.removeItem("demo_token");
|
|
||||||
sessionStorage.removeItem("demo_id");
|
|
||||||
sessionStorage.removeItem("demo_expires");
|
|
||||||
window.location.href = "/";
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Go Back
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-background">
|
|
||||||
<BackgroundLights />
|
|
||||||
<div className="flex flex-col items-center gap-6 text-center h-full justify-center">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h1 className="text-2xl font-bold">Your demo is being generated, please wait...</h1>
|
|
||||||
<p className="text-muted-foreground max-w-lg">
|
|
||||||
This demo will be available for 30 minutes for testing. After that, all data will be permanently deleted
|
|
||||||
and become inaccessible. You can test Palmr. with a 200MB storage limit.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-background">
|
|
||||||
<BackgroundLights />
|
|
||||||
<div className="relative flex flex-col items-center justify-center h-full">
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.5 }}
|
|
||||||
className="container mx-auto max-w-7xl px-6 flex-grow"
|
|
||||||
>
|
|
||||||
<section className="relative flex flex-col items-center justify-center gap-6 m-auto h-full">
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.5, delay: 0.2 }}
|
|
||||||
className="inline-block max-w-xl text-center justify-center"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col gap-8">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<motion.span
|
|
||||||
initial={{ opacity: 0, x: -20 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ delay: 0.4, duration: 0.5 }}
|
|
||||||
className="text-4xl lg:text-3xl font-semibold tracking-tight text-primary"
|
|
||||||
>
|
|
||||||
Your demo is ready!
|
|
||||||
</motion.span>
|
|
||||||
<motion.span
|
|
||||||
initial={{ opacity: 0, x: 20 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ delay: 0.6, duration: 0.5 }}
|
|
||||||
className="text-3xl leading-9 font-semibold tracking-tight"
|
|
||||||
>
|
|
||||||
Click the button below to test
|
|
||||||
</motion.span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.5, delay: 0.8 }}
|
|
||||||
className="flex flex-col items-center gap-6"
|
|
||||||
>
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
|
||||||
transition={{ delay: 1.2, duration: 0.5 }}
|
|
||||||
>
|
|
||||||
<Button onClick={handleGoToDemo} className="flex items-center gap-2 px-8 py-4 text-lg">
|
|
||||||
<Palmtree className="h-5 w-5" />
|
|
||||||
Go to Palmr. Demo
|
|
||||||
</Button>
|
|
||||||
</motion.div>
|
|
||||||
</motion.div>
|
|
||||||
</section>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DemoClient() {
|
|
||||||
return <DemoClientInner />;
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { Suspense } from "react";
|
|
||||||
|
|
||||||
import DemoClient from "./components/demo-client";
|
|
||||||
|
|
||||||
export default function DemoPage() {
|
|
||||||
return (
|
|
||||||
<Suspense>
|
|
||||||
<DemoClient />
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
}
|
|
@@ -14,7 +14,6 @@ const envSchema = z.object({
|
|||||||
S3_FORCE_PATH_STYLE: z.union([z.literal("true"), z.literal("false")]).default("false"),
|
S3_FORCE_PATH_STYLE: z.union([z.literal("true"), z.literal("false")]).default("false"),
|
||||||
SECURE_SITE: z.union([z.literal("true"), z.literal("false")]).default("false"),
|
SECURE_SITE: z.union([z.literal("true"), z.literal("false")]).default("false"),
|
||||||
DATABASE_URL: z.string().optional().default("file:/app/server/prisma/palmr.db"),
|
DATABASE_URL: z.string().optional().default("file:/app/server/prisma/palmr.db"),
|
||||||
DEMO_MODE: z.union([z.literal("true"), z.literal("false")]).default("false"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const env = envSchema.parse(process.env);
|
export const env = envSchema.parse(process.env);
|
||||||
|
@@ -56,17 +56,7 @@ export class FileController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if DEMO_MODE is enabled
|
const maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
||||||
const isDemoMode = env.DEMO_MODE === "true";
|
|
||||||
|
|
||||||
let maxTotalStorage: bigint;
|
|
||||||
if (isDemoMode) {
|
|
||||||
// In demo mode, limit all users to 200MB
|
|
||||||
maxTotalStorage = BigInt(200 * 1024 * 1024); // 200MB in bytes
|
|
||||||
} else {
|
|
||||||
// Normal behavior - use maxTotalStoragePerUser configuration
|
|
||||||
maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
const userFiles = await prisma.file.findMany({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
@@ -138,17 +128,7 @@ export class FileController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if DEMO_MODE is enabled
|
const maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
||||||
const isDemoMode = env.DEMO_MODE === "true";
|
|
||||||
|
|
||||||
let maxTotalStorage: bigint;
|
|
||||||
if (isDemoMode) {
|
|
||||||
// In demo mode, limit all users to 200MB
|
|
||||||
maxTotalStorage = BigInt(200 * 1024 * 1024); // 200MB in bytes
|
|
||||||
} else {
|
|
||||||
// Normal behavior - use maxTotalStoragePerUser configuration
|
|
||||||
maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
const userFiles = await prisma.file.findMany({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
|
@@ -533,17 +533,7 @@ export class ReverseShareService {
|
|||||||
throw new Error(`File size exceeds the maximum allowed size of ${maxSizeMB}MB`);
|
throw new Error(`File size exceeds the maximum allowed size of ${maxSizeMB}MB`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if DEMO_MODE is enabled
|
const maxTotalStorage = BigInt(await configService.getValue("maxTotalStoragePerUser"));
|
||||||
const isDemoMode = env.DEMO_MODE === "true";
|
|
||||||
|
|
||||||
let maxTotalStorage: bigint;
|
|
||||||
if (isDemoMode) {
|
|
||||||
// In demo mode, limit all users to 200MB
|
|
||||||
maxTotalStorage = BigInt(200 * 1024 * 1024); // 200MB in bytes
|
|
||||||
} else {
|
|
||||||
// Normal behavior - use maxTotalStoragePerUser configuration
|
|
||||||
maxTotalStorage = BigInt(await configService.getValue("maxTotalStoragePerUser"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
const userFiles = await prisma.file.findMany({
|
||||||
where: { userId: creatorId },
|
where: { userId: creatorId },
|
||||||
|
@@ -324,89 +324,45 @@ export class StorageService {
|
|||||||
uploadAllowed: boolean;
|
uploadAllowed: boolean;
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const isDemoMode = env.DEMO_MODE === "true";
|
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
if (isDemoMode) {
|
const diskInfo = await this._getDiskSpaceMultiplePaths();
|
||||||
const demoMaxStorage = 200 * 1024 * 1024;
|
|
||||||
const demoMaxStorageGB = this._ensureNumber(demoMaxStorage / (1024 * 1024 * 1024), 0);
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
if (!diskInfo) {
|
||||||
where: { userId },
|
throw new Error("Unable to determine actual disk space - system configuration issue");
|
||||||
select: { size: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const totalUsedStorage = userFiles.reduce((acc, file) => acc + file.size, BigInt(0));
|
|
||||||
const usedStorageGB = this._ensureNumber(Number(totalUsedStorage) / (1024 * 1024 * 1024), 0);
|
|
||||||
const availableStorageGB = this._ensureNumber(demoMaxStorageGB - usedStorageGB, 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
diskSizeGB: Number(demoMaxStorageGB.toFixed(2)),
|
|
||||||
diskUsedGB: Number(usedStorageGB.toFixed(2)),
|
|
||||||
diskAvailableGB: Number(availableStorageGB.toFixed(2)),
|
|
||||||
uploadAllowed: availableStorageGB > 0,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const diskInfo = await this._getDiskSpaceMultiplePaths();
|
|
||||||
|
|
||||||
if (!diskInfo) {
|
|
||||||
throw new Error("Unable to determine actual disk space - system configuration issue");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { total, available } = diskInfo;
|
|
||||||
const used = total - available;
|
|
||||||
|
|
||||||
const diskSizeGB = this._ensureNumber(total / (1024 * 1024 * 1024), 0);
|
|
||||||
const diskUsedGB = this._ensureNumber(used / (1024 * 1024 * 1024), 0);
|
|
||||||
const diskAvailableGB = this._ensureNumber(available / (1024 * 1024 * 1024), 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
diskSizeGB: Number(diskSizeGB.toFixed(2)),
|
|
||||||
diskUsedGB: Number(diskUsedGB.toFixed(2)),
|
|
||||||
diskAvailableGB: Number(diskAvailableGB.toFixed(2)),
|
|
||||||
uploadAllowed: diskAvailableGB > 0.1,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { total, available } = diskInfo;
|
||||||
|
const used = total - available;
|
||||||
|
|
||||||
|
const diskSizeGB = this._ensureNumber(total / (1024 * 1024 * 1024), 0);
|
||||||
|
const diskUsedGB = this._ensureNumber(used / (1024 * 1024 * 1024), 0);
|
||||||
|
const diskAvailableGB = this._ensureNumber(available / (1024 * 1024 * 1024), 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
diskSizeGB: Number(diskSizeGB.toFixed(2)),
|
||||||
|
diskUsedGB: Number(diskUsedGB.toFixed(2)),
|
||||||
|
diskAvailableGB: Number(diskAvailableGB.toFixed(2)),
|
||||||
|
uploadAllowed: diskAvailableGB > 0.1,
|
||||||
|
};
|
||||||
} else if (userId) {
|
} else if (userId) {
|
||||||
if (isDemoMode) {
|
const maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
||||||
const demoMaxStorage = 200 * 1024 * 1024;
|
const maxStorageGB = this._ensureNumber(Number(maxTotalStorage) / (1024 * 1024 * 1024), 10);
|
||||||
const demoMaxStorageGB = this._ensureNumber(demoMaxStorage / (1024 * 1024 * 1024), 0);
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
const userFiles = await prisma.file.findMany({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
select: { size: true },
|
select: { size: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalUsedStorage = userFiles.reduce((acc, file) => acc + file.size, BigInt(0));
|
const totalUsedStorage = userFiles.reduce((acc, file) => acc + file.size, BigInt(0));
|
||||||
const usedStorageGB = this._ensureNumber(Number(totalUsedStorage) / (1024 * 1024 * 1024), 0);
|
const usedStorageGB = this._ensureNumber(Number(totalUsedStorage) / (1024 * 1024 * 1024), 0);
|
||||||
const availableStorageGB = this._ensureNumber(demoMaxStorageGB - usedStorageGB, 0);
|
const availableStorageGB = this._ensureNumber(maxStorageGB - usedStorageGB, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
diskSizeGB: Number(demoMaxStorageGB.toFixed(2)),
|
diskSizeGB: Number(maxStorageGB.toFixed(2)),
|
||||||
diskUsedGB: Number(usedStorageGB.toFixed(2)),
|
diskUsedGB: Number(usedStorageGB.toFixed(2)),
|
||||||
diskAvailableGB: Number(availableStorageGB.toFixed(2)),
|
diskAvailableGB: Number(availableStorageGB.toFixed(2)),
|
||||||
uploadAllowed: availableStorageGB > 0,
|
uploadAllowed: availableStorageGB > 0,
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
const maxTotalStorage = BigInt(await this.configService.getValue("maxTotalStoragePerUser"));
|
|
||||||
const maxStorageGB = this._ensureNumber(Number(maxTotalStorage) / (1024 * 1024 * 1024), 10);
|
|
||||||
|
|
||||||
const userFiles = await prisma.file.findMany({
|
|
||||||
where: { userId },
|
|
||||||
select: { size: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const totalUsedStorage = userFiles.reduce((acc, file) => acc + file.size, BigInt(0));
|
|
||||||
const usedStorageGB = this._ensureNumber(Number(totalUsedStorage) / (1024 * 1024 * 1024), 0);
|
|
||||||
const availableStorageGB = this._ensureNumber(maxStorageGB - usedStorageGB, 0);
|
|
||||||
|
|
||||||
return {
|
|
||||||
diskSizeGB: Number(maxStorageGB.toFixed(2)),
|
|
||||||
diskUsedGB: Number(usedStorageGB.toFixed(2)),
|
|
||||||
diskAvailableGB: Number(availableStorageGB.toFixed(2)),
|
|
||||||
uploadAllowed: availableStorageGB > 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("User ID is required for non-admin users");
|
throw new Error("User ID is required for non-admin users");
|
||||||
|
Reference in New Issue
Block a user