fix: improve auth
This commit is contained in:
parent
26ac7c1003
commit
227be9ac41
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"culori": "^4.0.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"drizzle-orm": "^0.44.2",
|
||||
"embla-carousel-react": "^8.5.1",
|
||||
|
|
@ -51,6 +52,7 @@
|
|||
"react-markdown": "^10.1.0",
|
||||
"react-phone-number-input": "^3.4.11",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-window": "^2.2.7",
|
||||
"recharts": "^2.14.1",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
|
|
@ -5868,6 +5870,15 @@
|
|||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/culori": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/culori/-/culori-4.0.2.tgz",
|
||||
"integrity": "sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
|
|
@ -9705,6 +9716,16 @@
|
|||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-window": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-2.2.7.tgz",
|
||||
"integrity": "sha512-SH5nvfUQwGHYyriDUAOt7wfPsfG9Qxd6OdzQxl5oQ4dsSsUicqQvjV7dR+NqZ4coY0fUn3w1jnC5PwzIUWEg5w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recast": {
|
||||
"version": "0.23.11",
|
||||
"resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"culori": "^4.0.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"drizzle-orm": "^0.44.2",
|
||||
"embla-carousel-react": "^8.5.1",
|
||||
|
|
@ -68,6 +69,7 @@
|
|||
"react-markdown": "^10.1.0",
|
||||
"react-phone-number-input": "^3.4.11",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-window": "^2.2.7",
|
||||
"recharts": "^2.14.1",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
|
|
@ -98,8 +100,8 @@
|
|||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"cross-env": "^7.0.3",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"dotenv": "^16.5.0",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"postcss": "^8",
|
||||
"storybook": "^8.6.14",
|
||||
"tailwindcss": "^4.1.11",
|
||||
|
|
|
|||
|
|
@ -84,7 +84,15 @@ const VIEW_MODE_OPTIONS = [
|
|||
];
|
||||
|
||||
export default function ProjectsPage() {
|
||||
const { searchQuery, sortKey, sortOrder, viewMode } = useProjectsStore();
|
||||
const {
|
||||
searchQuery,
|
||||
sortKey,
|
||||
sortOrder,
|
||||
viewMode,
|
||||
selectedProjectIds,
|
||||
setSelectedProjects,
|
||||
clearSelectedProjects,
|
||||
} = useProjectsStore();
|
||||
const editor = useEditor();
|
||||
const authStatus = useAuthStore((state) => state.status);
|
||||
|
||||
|
|
@ -107,6 +115,31 @@ export default function ProjectsPage() {
|
|||
const isLoading = editor.project.getIsLoading();
|
||||
const isInitialized = editor.project.getIsInitialized();
|
||||
|
||||
useEffect(() => {
|
||||
if (authStatus !== "authenticated") {
|
||||
clearSelectedProjects();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLoading || !isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedProjectIds.length > 0 || projectsToDisplay.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedProjects({ projectIds: [projectsToDisplay[0].id] });
|
||||
}, [
|
||||
authStatus,
|
||||
clearSelectedProjects,
|
||||
isInitialized,
|
||||
isLoading,
|
||||
projectsToDisplay,
|
||||
selectedProjectIds.length,
|
||||
setSelectedProjects,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="bg-background min-h-screen">
|
||||
<MigrationDialog />
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import {
|
|||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { OcSlidersVerticalIcon } from "@opencut/ui/icons";
|
||||
import { OcSlidersVerticalIcon } from "@/ui/icons";
|
||||
import type { StickerCategory } from "@/types/stickers";
|
||||
import { STICKER_CATEGORIES } from "@/constants/sticker-constants";
|
||||
import { parseStickerId } from "@/lib/stickers/sticker-id";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
PlayIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { OcSocialIcon } from "@opencut/ui/icons";
|
||||
import { OcSocialIcon } from "@/ui/icons";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
export function PreviewToolbar({
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
DEFAULT_BLEND_MODE,
|
||||
DEFAULT_OPACITY,
|
||||
} from "@/constants/timeline-constants";
|
||||
import { OcCheckerboardIcon } from "@opencut/ui/icons";
|
||||
import { OcCheckerboardIcon } from "@/ui/icons";
|
||||
import { Fragment, useRef } from "react";
|
||||
import { Section, SectionContent, SectionHeader } from "../section";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
SelectContent,
|
||||
SelectItem,
|
||||
} from "@/components/ui/select";
|
||||
import { OcFontWeightIcon } from "@opencut/ui/icons";
|
||||
import { OcFontWeightIcon } from "@/ui/icons";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
export function TextProperties({
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ export const backendAuthApi = {
|
|||
body: { username, password },
|
||||
}),
|
||||
register: ({
|
||||
username,
|
||||
email,
|
||||
firstname,
|
||||
lastname,
|
||||
|
|
@ -154,6 +155,7 @@ export const backendAuthApi = {
|
|||
role = 1,
|
||||
organisation_name,
|
||||
}: {
|
||||
username: string;
|
||||
email: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
|
|
@ -164,7 +166,7 @@ export const backendAuthApi = {
|
|||
request<{ message: string; token: string; user: TBackendUser }>({
|
||||
path: "/auth/user/register",
|
||||
method: "POST",
|
||||
body: { email, firstname, lastname, password, role, organisation_name },
|
||||
body: { username, email, firstname, lastname, password, role, organisation_name },
|
||||
}),
|
||||
refresh: () =>
|
||||
request<{ token: string; user: TBackendUser }>({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|||
const PUBLIC_PATH_PREFIXES = ["/auth", "/privacy", "/terms"];
|
||||
const PUBLIC_EXACT_PATHS = ["/robots.txt", "/sitemap.xml", "/manifest.json"];
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
export async function proxy(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
const isPublicPath =
|
||||
PUBLIC_EXACT_PATHS.includes(pathname) ||
|
||||
|
|
|
|||
|
|
@ -363,8 +363,16 @@ class StorageService {
|
|||
}
|
||||
|
||||
async clearAllData(): Promise<void> {
|
||||
await this.projectsAdapter.clear();
|
||||
// project-specific media and timelines cleaned up when projects are deleted
|
||||
const projectIds = await this.projectsAdapter.list();
|
||||
|
||||
await Promise.all(
|
||||
projectIds.map((projectId) => this.deleteProjectMedia({ projectId })),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
this.projectsAdapter.clear(),
|
||||
this.savedSoundsAdapter.clear(),
|
||||
]);
|
||||
}
|
||||
|
||||
async getStorageInfo(): Promise<{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { create } from "zustand";
|
||||
import { backendAuthApi, type TBackendUser } from "@/lib/auth/api-client";
|
||||
import { authSession } from "@/lib/auth/session";
|
||||
import { storageService } from "@/services/storage/service";
|
||||
|
||||
type AuthStatus = "idle" | "loading" | "authenticated" | "unauthenticated";
|
||||
|
||||
|
|
@ -125,6 +126,11 @@ export const useAuthStore = create<AuthState>()((set, get) => ({
|
|||
} catch {
|
||||
// Clear local state even if backend logout is unavailable/misconfigured.
|
||||
} finally {
|
||||
try {
|
||||
await storageService.clearAllData();
|
||||
} catch (error) {
|
||||
console.error("Failed to clear local data during logout:", error);
|
||||
}
|
||||
clearAuthState({ set });
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,3 +15,19 @@ export function OcDataBuddyIcon(props: IconProps) {
|
|||
return <ChartArea {...props} />;
|
||||
}
|
||||
|
||||
// Compatibility exports for upstream UI components.
|
||||
export function OcSocialIcon(props: IconProps) {
|
||||
return <Video {...props} />;
|
||||
}
|
||||
|
||||
export function OcFontWeightIcon(props: IconProps) {
|
||||
return <ChartArea {...props} />;
|
||||
}
|
||||
|
||||
export function OcSlidersVerticalIcon(props: IconProps) {
|
||||
return <ChartArea {...props} />;
|
||||
}
|
||||
|
||||
export function OcCheckerboardIcon(props: IconProps) {
|
||||
return <Gem {...props} />;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue