refactor: update storage-related API and components for improved functionality
This commit is contained in:
parent
7b619e54db
commit
7f93bdeb98
10 changed files with 165 additions and 102 deletions
|
|
@ -65,9 +65,10 @@ ENV TILES_URL="localhost:6379"
|
|||
ENV SKEBBY_USER="mock"
|
||||
ENV SKEBBY_PASS="mock"
|
||||
ENV REVALIDATION_SECRET="mock"
|
||||
ENV STORAGE_URL="mock"
|
||||
ENV STORAGE_TOKEN="mock"
|
||||
|
||||
|
||||
#TODO remove variabili non necessarie al build tanto la validazione viene saltata
|
||||
RUN SKIP_ENV_VALIDATION=1 npm run build
|
||||
|
||||
|
||||
|
|
@ -132,6 +133,8 @@ ENV SKEBBY_USER=$SKEBBY_USER
|
|||
ARG SKEBBY_PASS
|
||||
ENV SKEBBY_PASS=$SKEBBY_PASS
|
||||
ENV REVALIDATION_SECRET="SWKpgaaLfsyeqV1eOT0WG7TUFBewir8kJXure3O37ki8Lt4Z4IiEgBW4zPHDdM5c"
|
||||
ARG STORAGE_URL
|
||||
ENV STORAGE_URL=$STORAGE_URL
|
||||
ARG STORAGE_TOKEN
|
||||
ENV STORAGE_TOKEN=$STORAGE_TOKEN
|
||||
|
||||
|
|
|
|||
|
|
@ -59,10 +59,6 @@ async function createNextConfig(): Promise<NextConfig> {
|
|||
reactStrictMode: true,
|
||||
rewrites: async () => {
|
||||
return [
|
||||
{
|
||||
source: "/storage-api/:slug*",
|
||||
destination: `http://localhost:8080/:slug*?token=${env.STORAGE_TOKEN}`,
|
||||
},
|
||||
{
|
||||
destination: env.NODE_ENV === "production" ? "/404" : "/api/panel",
|
||||
source: "/api/panel",
|
||||
|
|
@ -71,32 +67,6 @@ async function createNextConfig(): Promise<NextConfig> {
|
|||
destination: `${env.BACKENDSERVER_URL}/images/get/:slug*`,
|
||||
source: "/go-api/images/get/:slug*",
|
||||
},
|
||||
{
|
||||
destination: `${env.BACKENDSERVER_URL}/storage/get/:slug*`,
|
||||
has: [
|
||||
{ key: "access_token", type: "cookie" },
|
||||
{
|
||||
key: "token",
|
||||
type: "query",
|
||||
value:
|
||||
"(?<token>^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$)",
|
||||
},
|
||||
],
|
||||
source: "/go-api/storage/get/:slug*",
|
||||
},
|
||||
{
|
||||
destination: `${env.BACKENDSERVER_URL}/storage/upload:slug*`,
|
||||
has: [
|
||||
{ key: "access_token", type: "cookie" },
|
||||
{
|
||||
key: "token",
|
||||
type: "query",
|
||||
value:
|
||||
"(?<token>^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$)",
|
||||
},
|
||||
],
|
||||
source: "/go-api/storage/upload:slug*",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -500,14 +500,15 @@ const ContrattoSection = ({
|
|||
|
||||
const { data: files, isLoading } = api.storage.getAll.useQuery();
|
||||
|
||||
const { mutate: setUserStorage } = api.storage.addUserStorage.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
const { mutate: setUserStorage } =
|
||||
api.storage.addUserStorageEntry.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
handleUpdate({
|
||||
|
|
@ -592,14 +593,15 @@ const RicevutaSection = ({
|
|||
|
||||
const { data: files, isLoading } = api.storage.getAll.useQuery();
|
||||
|
||||
const { mutate: setUserStorage } = api.storage.addUserStorage.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
const { mutate: setUserStorage } =
|
||||
api.storage.addUserStorageEntry.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
handleUpdate({
|
||||
|
|
@ -757,14 +759,15 @@ const DocSection = ({
|
|||
|
||||
const { data: files, isLoading } = api.storage.getAll.useQuery();
|
||||
|
||||
const { mutate: setUserStorage } = api.storage.addUserStorage.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
const { mutate: setUserStorage } =
|
||||
api.storage.addUserStorageEntry.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.storage.retrieveUserFileData.invalidate({
|
||||
userId,
|
||||
});
|
||||
await utils.storage.getAll.invalidate();
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) return <LoadingPage />;
|
||||
if (files === undefined) {
|
||||
|
|
|
|||
|
|
@ -36,13 +36,8 @@ import {
|
|||
} from "~/components/ui/dropdown-menu";
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { useStorageTable } from "~/providers/StorageTableProvider";
|
||||
import {
|
||||
editFile,
|
||||
type FileMetadataWithAdmin,
|
||||
} from "~/server/services/storage.service";
|
||||
|
||||
import type { FileMetadataWithAdmin } from "~/server/services/storage.service";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
export const StorageTable = ({
|
||||
|
|
@ -283,9 +278,24 @@ const RinominaDialog = ({
|
|||
target: { allegatoId: string; filename: string | null } | null,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const utils = api.useUtils();
|
||||
|
||||
const { mutateAsync: renameFile } = api.storage.renameFile.useMutation({
|
||||
onMutate: () => {
|
||||
const toastId = toast.loading("Modifica in corso", {
|
||||
icon: "🪛",
|
||||
});
|
||||
return { toastId };
|
||||
},
|
||||
onSuccess: async (_error, _variables, context) => {
|
||||
await utils.storage.getAll.invalidate();
|
||||
toast.success("File rinominato con successo", {
|
||||
icon: "👍",
|
||||
id: context?.toastId,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handleEditSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const form = e.currentTarget;
|
||||
|
|
@ -293,17 +303,10 @@ const RinominaDialog = ({
|
|||
if (!formData.get("filename")) {
|
||||
return;
|
||||
}
|
||||
const rename = await editFile(
|
||||
renameTarget.allegatoId,
|
||||
formData.get("filename") as string,
|
||||
);
|
||||
if (rename.success) {
|
||||
setRenameTarget(null);
|
||||
toast.success("File rinominato con successo", { icon: "👍" });
|
||||
await utils.storage.getAll.invalidate();
|
||||
} else {
|
||||
toast.error(rename.error);
|
||||
}
|
||||
await renameFile({
|
||||
fileId: renameTarget.allegatoId,
|
||||
name: formData.get("filename") as string,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Dialog
|
||||
|
|
@ -312,8 +315,8 @@ const RinominaDialog = ({
|
|||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t.modifica}</DialogTitle>
|
||||
<DialogDescription className="sr-only">modifica</DialogDescription>
|
||||
<DialogTitle>Rinomina</DialogTitle>
|
||||
<DialogDescription className="sr-only">Rinomina</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form method="post" onSubmit={handleEditSubmit}>
|
||||
|
|
@ -323,10 +326,10 @@ const RinominaDialog = ({
|
|||
defaultValue={renameTarget.filename || undefined}
|
||||
minLength={5}
|
||||
name="filename"
|
||||
placeholder="Modifica messaggio"
|
||||
placeholder="Rinomina File"
|
||||
type="text"
|
||||
/>
|
||||
<Button type="submit">{t.salva}</Button>
|
||||
<Button type="submit">Salva</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export const UploadComponent = ({
|
|||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const { mutateAsync: setUserStorage } =
|
||||
api.storage.addUserStorage.useMutation({
|
||||
api.storage.addUserStorageEntry.useMutation({
|
||||
onMutate: () => {
|
||||
toast(t.allegati.allegato_caricato, { icon: "📤" });
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ export async function uploadFile(
|
|||
file: File,
|
||||
expiresAt: string | undefined,
|
||||
): Promise<UploadResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
if (expiresAt) {
|
||||
formData.append("expires_at", expiresAt);
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
if (expiresAt) {
|
||||
formData.append("expires_at", expiresAt);
|
||||
}
|
||||
|
||||
const response = await fetch(`/storage-api/upload`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { authMiddleware } from "~/middlewares/auth_middleware";
|
||||
import { apisMiddleware } from "./middlewares/api_middleware";
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
//console.log("Middleware triggered for:", request.nextUrl.pathname);
|
||||
const { pathname } = request.nextUrl;
|
||||
if (pathname.startsWith("/storage-api/")) {
|
||||
return await apisMiddleware(request);
|
||||
}
|
||||
|
||||
return (await authMiddleware(request)) ?? NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
"/storage-api/:path*",
|
||||
{
|
||||
source: "/",
|
||||
},
|
||||
|
|
|
|||
82
apps/infoalloggi/src/middlewares/api_middleware.ts
Normal file
82
apps/infoalloggi/src/middlewares/api_middleware.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { env } from "~/env";
|
||||
import { TOKEN_CONFIG } from "~/server/auth/configs";
|
||||
import { verifyToken } from "~/server/auth/jwt";
|
||||
|
||||
export const apisMiddleware = async (req: NextRequest) => {
|
||||
const { pathname, searchParams } = req.nextUrl;
|
||||
console.log("APIs Middleware triggered for:", pathname);
|
||||
const accessToken = req.cookies.get(
|
||||
TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
|
||||
)?.value;
|
||||
if (!accessToken) {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
const payload = await verifyToken(accessToken);
|
||||
if (!payload) {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
// Build the storage API URL correctly
|
||||
const slug = pathname.replace("/storage-api/", "");
|
||||
|
||||
// Preserve existing query params and add token
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("token", env.STORAGE_TOKEN);
|
||||
|
||||
const storageUrl = `${env.STORAGE_URL}/${slug}?${params.toString()}`;
|
||||
|
||||
try {
|
||||
// Forward the request to storage API
|
||||
const headers = new Headers(req.headers);
|
||||
headers.delete("host");
|
||||
headers.delete("cookie");
|
||||
|
||||
const response = await fetch(storageUrl, {
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
body:
|
||||
req.method !== "GET" && req.method !== "HEAD" ? req.body : undefined,
|
||||
// @ts-expect-error - duplex needed for request streaming
|
||||
duplex: "half",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Storage API error:", response.status);
|
||||
return new NextResponse("Storage API error", { status: response.status });
|
||||
}
|
||||
|
||||
// Get response data
|
||||
const data = await response.arrayBuffer();
|
||||
|
||||
// Create response with proper headers
|
||||
const proxyResponse = new NextResponse(data, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
});
|
||||
|
||||
// Copy relevant headers
|
||||
const contentType = response.headers.get("Content-Type");
|
||||
const contentDisposition = response.headers.get("Content-Disposition");
|
||||
const contentLength = response.headers.get("Content-Length");
|
||||
|
||||
if (contentType) proxyResponse.headers.set("Content-Type", contentType);
|
||||
if (contentDisposition)
|
||||
proxyResponse.headers.set("Content-Disposition", contentDisposition);
|
||||
if (contentLength)
|
||||
proxyResponse.headers.set("Content-Length", contentLength);
|
||||
|
||||
// Set CORS headers
|
||||
proxyResponse.headers.set("Access-Control-Allow-Origin", env.BASE_URL);
|
||||
proxyResponse.headers.set("Access-Control-Allow-Credentials", "true");
|
||||
proxyResponse.headers.delete("X-Frame-Options"); // Remove if set by storage API
|
||||
proxyResponse.headers.set(
|
||||
"Content-Security-Policy",
|
||||
"frame-ancestors 'self'",
|
||||
);
|
||||
|
||||
return proxyResponse;
|
||||
} catch (error) {
|
||||
console.error("Storage proxy error:", error);
|
||||
return new NextResponse("Failed to proxy request", { status: 500 });
|
||||
}
|
||||
};
|
||||
|
|
@ -206,7 +206,7 @@ const VisibComponent = ({
|
|||
setOpen: (v: boolean) => void;
|
||||
}) => {
|
||||
const utils = api.useUtils();
|
||||
const { data, isLoading } = api.storage.getStorageVisibility.useQuery({
|
||||
const { data, isLoading } = api.storage.getStorageAccessDetails.useQuery({
|
||||
storageIds,
|
||||
});
|
||||
const { data: users } = api.users.getUsers.useQuery();
|
||||
|
|
@ -219,14 +219,14 @@ const VisibComponent = ({
|
|||
return { toastId };
|
||||
},
|
||||
onSuccess: async (_error, _variables, context) => {
|
||||
await utils.storage.getStorageVisibility.invalidate();
|
||||
await utils.storage.getStorageAccessDetails.invalidate();
|
||||
toast.success("Utente rimosso", {
|
||||
icon: "🗑️",
|
||||
id: context?.toastId,
|
||||
});
|
||||
},
|
||||
});
|
||||
const { mutate: add } = api.storage.addUserStorage.useMutation({
|
||||
const { mutate: add } = api.storage.addUserStorageEntry.useMutation({
|
||||
onMutate: () => {
|
||||
const toastId = toast.loading("Aggiunta utente in corso", {
|
||||
icon: "🪛",
|
||||
|
|
@ -234,7 +234,7 @@ const VisibComponent = ({
|
|||
return { toastId };
|
||||
},
|
||||
onSuccess: async (_error, _variables, context) => {
|
||||
await utils.storage.getStorageVisibility.invalidate();
|
||||
await utils.storage.getStorageAccessDetails.invalidate();
|
||||
toast.success("Utente aggiunto", {
|
||||
icon: "👍",
|
||||
id: context?.toastId,
|
||||
|
|
@ -250,7 +250,7 @@ const VisibComponent = ({
|
|||
return { toastId };
|
||||
},
|
||||
onSuccess: async (_error, _variables, context) => {
|
||||
await utils.storage.getStorageVisibility.invalidate();
|
||||
await utils.storage.getStorageAccessDetails.invalidate();
|
||||
toast.success("Reset effettuato", {
|
||||
icon: "👍",
|
||||
id: context?.toastId,
|
||||
|
|
|
|||
|
|
@ -60,10 +60,15 @@ export const storageRouter = createTRPCRouter({
|
|||
.query(async ({ input }) => {
|
||||
return await retrieveUserFiles(input);
|
||||
}),
|
||||
|
||||
//OLD
|
||||
|
||||
addUserStorage: protectedProcedure
|
||||
getStorageAccessDetails: adminProcedure
|
||||
.input(z.object({ storageIds: z.string().array() }))
|
||||
.query(async ({ input }) => {
|
||||
return await getMultipleWithRelations({
|
||||
db,
|
||||
ids: input.storageIds,
|
||||
});
|
||||
}),
|
||||
addUserStorageEntry: protectedProcedure
|
||||
.input(z.custom<NewUsersStorage>())
|
||||
.mutation(async ({ input }) => {
|
||||
return await addUserStorage(input);
|
||||
|
|
@ -73,15 +78,6 @@ export const storageRouter = createTRPCRouter({
|
|||
return await getAllWithFromAdmin();
|
||||
}),
|
||||
|
||||
getStorageVisibility: adminProcedure
|
||||
.input(z.object({ storageIds: z.string().array() }))
|
||||
.query(async ({ input }) => {
|
||||
return await getMultipleWithRelations({
|
||||
db,
|
||||
ids: input.storageIds,
|
||||
});
|
||||
}),
|
||||
|
||||
renameFile: adminProcedure
|
||||
.input(z.object({ fileId: z.string(), name: z.string() }))
|
||||
.mutation(async ({ input }) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue