Compare commits

..

15 Commits

Author SHA1 Message Date
Daniel Luiz Alves
8f3ad71850 v2.0.0-beta.5 (#36) 2025-05-20 12:03:31 -03:00
Daniel Luiz Alves
a9191d6b54 docs(installation): update environment variable names for consistency
Update environment variable names in the installation documentation to use consistent naming conventions. This includes changing POSTGRES_PASSWORD to POSTGRESQL_PASSWORD and similar adjustments for other variables to align with the expected naming format.
2025-05-20 12:02:14 -03:00
Daniel Luiz Alves
b8465df016 Fix healthcheck and environment variables in docker-compose.yml (#35) 2025-05-20 11:51:44 -03:00
tiltshiftfocus
5a44d79279 Fix healthcheck and environment variables in docker-compose.yml 2025-05-20 14:50:35 +08:00
Daniel Luiz Alves
63d9abfe3e v2.0.0-beta.4 (#34) 2025-05-19 18:10:13 -03:00
Daniel Luiz Alves
d3ae5ea10c build: update Node.js and dependencies in Dockerfiles and package.json
Update the base Node.js image from version 18 to 22-alpine in both web and server Dockerfiles to leverage the latest features and security patches. Additionally, update the @prisma/client dependency to version 6.4.1 in the server package.json and pnpm-lock.yaml to ensure compatibility and stability.
2025-05-19 18:08:19 -03:00
Daniel Luiz Alves
d614820aca add RepoFlow as supporter 2025-05-14 21:44:06 -03:00
Daniel Luiz Alves
3c2d92c630 feat: change download behaviour (#33) 2025-05-14 21:26:52 -03:00
Charly Gley
6ae8436f4b fix: changed download behaviour for the public share as well 2025-05-10 15:03:18 +02:00
Charly Gley
39d5980936 feat: change download behaviour - download directly from minio backend, which makes the browser display the download progress 2025-05-10 03:24:25 +02:00
Daniel Luiz Alves
27e0e7c8da docs: update docker image tags to 'latest' in installation guide (#31) 2025-05-06 11:03:06 -03:00
Daniel Luiz Alves
02bc1df0c1 docs: update docker image tags to 'latest' in installation guide
This change ensures that the installation guide uses the 'latest' tag for docker images instead of the specific 'v2.0.0-beta' version, making it easier for users to always get the most recent version without manually updating the tag.
2025-05-06 11:02:00 -03:00
Daniel Luiz Alves
c1baa3a16d v2.0.0-beta.3 (#30) 2025-05-06 10:06:52 -03:00
Daniel Luiz Alves
cfc103e056 fix: (docker images) (#29) 2025-05-06 09:41:12 -03:00
Charly Gley
1a0b565ae0 fix: (docker images)
changed check-db.mjs to output number in stdout
changed routes.ts to import prisma with relative paths
2025-05-05 21:04:14 +02:00
26 changed files with 166 additions and 67 deletions

View File

@@ -49,6 +49,10 @@
</br> </br>
## 🤝 Supporters
[<img src="https://i.ibb.co/nMN40STL/Repoflow.png" width="200px" alt="Daniel Luiz Alves" />](https://www.repoflow.io/)
## ⭐ Star History ## ⭐ Star History
<a href="https://www.star-history.com/#kyantech/Palmr&Date"> <a href="https://www.star-history.com/#kyantech/Palmr&Date">

View File

@@ -30,7 +30,7 @@ Below is the complete content of our `docker-compose.yaml` that can be copied di
```yaml ```yaml
services: services:
palmr-api: palmr-api:
image: kyantech/palmr-api:v2.0.0-beta # Make sure to use the correct version (latest) of the image image: kyantech/palmr-api:latest # Make sure to use the correct version (latest) of the image
container_name: palmr-api container_name: palmr-api
depends_on: depends_on:
postgres: postgres:
@@ -39,8 +39,8 @@ services:
condition: "service_healthy" condition: "service_healthy"
environment: environment:
- PORT=${API_INTERNAL_PORT:-3333} # Port for the backend service - PORT=${API_INTERNAL_PORT:-3333} # Port for the backend service
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-postgresRootPassword}@postgres:5432/palmr_db?schema=public # Database URL with configurable password through POSTGRES_PASSWORD env var - DATABASE_URL=postgresql://postgres:${POSTGRESQL_PASSWORD:-postgresRootPassword}@postgres:5432/palmr_db?schema=public # Database URL with configurable password through POSTGRESQL_PASSWORD env var
- MINIO_ENDPOINT=minio # This can change if your MinIO is at a different address - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio} # This can change if your MinIO is at a different address
- MINIO_PORT=${MINIO_INTERNAL_API_PORT:-6421} # Default MinIO port (Change if yours is not the default) - MINIO_PORT=${MINIO_INTERNAL_API_PORT:-6421} # Default MinIO port (Change if yours is not the default)
- MINIO_USE_SSL=false # MinIO uses SSL by default, but you can change it to true if needed - MINIO_USE_SSL=false # MinIO uses SSL by default, but you can change it to true if needed
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user} # MinIO credentials can be configured through MINIO_ROOT_USER env vars - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user} # MinIO credentials can be configured through MINIO_ROOT_USER env vars
@@ -54,14 +54,14 @@ services:
- "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping - "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${API_INTERNAL_PORT:-3333}/health"] test: ["CMD", "wget", "--no-verbose", "http://palmr-api:${API_INTERNAL_PORT:-3333}/health"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5
start_period: 30s start_period: 30s
palmr-app: palmr-app:
image: kyantech/palmr-app:v2.0.0-beta # Make sure to use the correct version (latest) of the image image: kyantech/palmr-app:latest # Make sure to use the correct version (latest) of the image
container_name: palmr-web container_name: palmr-web
depends_on: depends_on:
palmr-api: palmr-api:
@@ -121,10 +121,10 @@ services:
container_name: palmr-postgres container_name: palmr-postgres
environment: environment:
# PostgreSQL credentials configurable through environment variables # PostgreSQL credentials configurable through environment variables
# POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB can be set to override defaults # POSTGRESQL_USERNAME, POSTGRESQL_PASSWORD, and POSTGRES_DB can be set to override defaults
- POSTGRESQL_USERNAME=${POSTGRES_USER:-postgres} - POSTGRESQL_USERNAME=${POSTGRESQL_USERNAME:-postgres}
- POSTGRESQL_PASSWORD=${POSTGRES_PASSWORD:-postgresRootPassword} - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD:-postgresRootPassword}
- POSTGRESQL_DATABASE=${POSTGRES_DB:-palmr_db} - POSTGRESQL_DATABASE=${POSTGRES_DATABASE:-palmr_db}
volumes: volumes:
- postgres_data:/bitnami/postgresql - postgres_data:/bitnami/postgresql
ports: ports:

View File

@@ -1,8 +1,8 @@
FROM node:18 FROM node:22-alpine
WORKDIR /app/server WORKDIR /app/server
RUN apt-get update && apt-get install -y netcat-traditional RUN apk add --no-cache netcat-openbsd
RUN npm install -g pnpm RUN npm install -g pnpm
COPY package*.json ./ COPY package*.json ./

View File

@@ -24,7 +24,7 @@
"@fastify/static": "^8.1.1", "@fastify/static": "^8.1.1",
"@fastify/swagger": "^9.4.2", "@fastify/swagger": "^9.4.2",
"@fastify/swagger-ui": "^5.2.1", "@fastify/swagger-ui": "^5.2.1",
"@prisma/client": "^6.3.1", "@prisma/client": "^6.4.1",
"@scalar/fastify-api-reference": "^1.25.116", "@scalar/fastify-api-reference": "^1.25.116",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"fastify": "^5.2.1", "fastify": "^5.2.1",

View File

@@ -30,7 +30,7 @@ importers:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.2 version: 5.2.2
'@prisma/client': '@prisma/client':
specifier: ^6.3.1 specifier: ^6.4.1
version: 6.4.1(prisma@6.4.1(typescript@5.7.3))(typescript@5.7.3) version: 6.4.1(prisma@6.4.1(typescript@5.7.3))(typescript@5.7.3)
'@scalar/fastify-api-reference': '@scalar/fastify-api-reference':
specifier: ^1.25.116 specifier: ^1.25.116

View File

@@ -2,7 +2,10 @@ import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient(); const prisma = new PrismaClient();
async function main() {} async function main() {
const count = await prisma.user.count();
console.log(count);
}
main() main()
.catch((e) => { .catch((e) => {

View File

@@ -1,4 +1,4 @@
import { prisma } from "shared/prisma"; import { prisma } from "../../shared/prisma";
import { AppController } from "./controller"; import { AppController } from "./controller";
import { ConfigResponseSchema, BulkUpdateConfigSchema } from "./dto"; import { ConfigResponseSchema, BulkUpdateConfigSchema } from "./dto";
import { FastifyInstance } from "fastify"; import { FastifyInstance } from "fastify";

View File

@@ -169,9 +169,9 @@ export class FileController {
if (!fileRecord) { if (!fileRecord) {
return reply.status(404).send({ error: "File not found." }); return reply.status(404).send({ error: "File not found." });
} }
const fileName = fileRecord.name;
const expires = 3600; const expires = 3600;
const url = await this.fileService.getPresignedGetUrl(objectName, expires); const url = await this.fileService.getPresignedGetUrl(objectName, expires, fileName);
return reply.send({ url, expiresIn: expires }); return reply.send({ url, expiresIn: expires });
} catch (error) { } catch (error) {
console.error("Error in getDownloadUrl:", error); console.error("Error in getDownloadUrl:", error);

View File

@@ -20,24 +20,43 @@ export class FileService {
}); });
} }
getPresignedGetUrl(objectName: string, expires: number): Promise<string> { getPresignedGetUrl(objectName: string, expires: number, fileName?: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
(minioLocalClient as any).presignedGetObject(bucketName, objectName, expires, (( const reqParams: { [key: string]: any } = {};
err: Error | null, let rcdFileName: string;
presignedUrl?: string if (fileName && fileName.trim() !== '') {
) => { rcdFileName = fileName;
if (err) { } else {
console.error("Erro no presignedGetObject:", err); const lastSlashIndex = objectName.lastIndexOf('/');
reject(err); rcdFileName = lastSlashIndex !== -1
} else if (!presignedUrl) { ? objectName.substring(lastSlashIndex + 1)
reject(new Error("URL não gerada")); : objectName;
} else { if (!rcdFileName) {
resolve(presignedUrl); rcdFileName = 'downloaded_file';
} }
}) as any); }
reqParams['response-content-disposition'] = `attachment; filename="${rcdFileName}"`;
(minioLocalClient as any).presignedGetObject(
bucketName,
objectName,
expires,
reqParams, // Pass the constructed request parameters
((err: Error | null, presignedUrl?: string) => {
if (err) {
console.error("Erro no presignedGetObject:", err);
reject(err);
} else if (!presignedUrl) {
reject(new Error("URL não gerada"));
} else {
resolve(presignedUrl);
}
}) as any // Consider using proper MinIO SDK types for the callback if available
// to avoid 'as any'
);
}); });
} }
deleteObject(objectName: string): Promise<void> { deleteObject(objectName: string): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
(minioClient as any).removeObject(bucketName, objectName, (err: Error | null) => { (minioClient as any).removeObject(bucketName, objectName, (err: Error | null) => {

View File

@@ -1,4 +1,4 @@
import { prisma } from "shared/prisma"; import { prisma } from "../../shared/prisma";
import { createPasswordSchema } from "../auth/dto"; import { createPasswordSchema } from "../auth/dto";
import { UserController } from "./controller"; import { UserController } from "./controller";
import { UpdateUserSchema, UserResponseSchema } from "./dto"; import { UpdateUserSchema, UserResponseSchema } from "./dto";
@@ -13,7 +13,7 @@ export async function userRoutes(app: FastifyInstance) {
try { try {
const usersCount = await prisma.user.count(); const usersCount = await prisma.user.count();
if (usersCount > 0 ) { if (usersCount > 0) {
await request.jwtVerify(); await request.jwtVerify();
if (!request.user.isAdmin) { if (!request.user.isAdmin) {
return reply return reply

View File

@@ -1,6 +1,6 @@
# Use the official Node.js image as the base image # Use the official Node.js image as the base image
FROM node:18-alpine AS base FROM node:22-alpine AS base
# Install dependencies only when needed # Install dependencies only when needed
FROM base AS deps FROM base AS deps

View File

@@ -81,7 +81,13 @@
"uploadFile": "رفع ملف", "uploadFile": "رفع ملف",
"loadError": "فشل في تحميل الملفات", "loadError": "فشل في تحميل الملفات",
"pageTitle": "ملفاتي", "pageTitle": "ملفاتي",
"breadcrumb": "ملفاتي" "breadcrumb": "ملفاتي",
"downloadStart": "بدأ التنزيل",
"downloadError": "فشل في تنزيل الملف",
"updateSuccess": "تم تحديث الملف بنجاح",
"updateError": "فشل في تحديث الملف",
"deleteSuccess": "تم حذف الملف بنجاح",
"deleteError": "فشل في حذف الملف"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "جدول الملفات", "ariaLabel": "جدول الملفات",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Datei hochladen", "uploadFile": "Datei hochladen",
"loadError": "Fehler beim Laden der Dateien", "loadError": "Fehler beim Laden der Dateien",
"pageTitle": "Meine Dateien", "pageTitle": "Meine Dateien",
"breadcrumb": "Meine Dateien" "breadcrumb": "Meine Dateien",
"downloadStart": "Download gestartet",
"downloadError": "Fehler beim Herunterladen der Datei",
"updateSuccess": "Datei erfolgreich aktualisiert",
"updateError": "Fehler beim Aktualisieren der Datei",
"deleteSuccess": "Datei erfolgreich gelöscht",
"deleteError": "Fehler beim Löschen der Datei"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Dateitabelle", "ariaLabel": "Dateitabelle",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Upload File", "uploadFile": "Upload File",
"loadError": "Failed to load files", "loadError": "Failed to load files",
"pageTitle": "My Files", "pageTitle": "My Files",
"breadcrumb": "My Files" "breadcrumb": "My Files",
"downloadStart": "Download started",
"downloadError": "Failed to download file",
"updateSuccess": "File updated successfully",
"updateError": "Failed to update file",
"deleteSuccess": "File deleted successfully",
"deleteError": "Failed to delete file"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Files table", "ariaLabel": "Files table",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Subir archivo", "uploadFile": "Subir archivo",
"loadError": "Error al cargar los archivos", "loadError": "Error al cargar los archivos",
"pageTitle": "Mis archivos", "pageTitle": "Mis archivos",
"breadcrumb": "Mis archivos" "breadcrumb": "Mis archivos",
"downloadStart": "Descarga iniciada",
"downloadError": "Error al descargar el archivo",
"updateSuccess": "Archivo actualizado exitosamente",
"updateError": "Error al actualizar el archivo",
"deleteSuccess": "Archivo eliminado exitosamente",
"deleteError": "Error al eliminar el archivo"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Tabla de archivos", "ariaLabel": "Tabla de archivos",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Envoyer un Fichier", "uploadFile": "Envoyer un Fichier",
"loadError": "Échec du chargement des fichiers", "loadError": "Échec du chargement des fichiers",
"pageTitle": "Mes Fichiers", "pageTitle": "Mes Fichiers",
"breadcrumb": "Mes Fichiers" "breadcrumb": "Mes Fichiers",
"downloadStart": "Téléchargement commencé",
"downloadError": "Échec du téléchargement du fichier",
"updateSuccess": "Fichier mis à jour avec succès",
"updateError": "Échec de la mise à jour du fichier",
"deleteSuccess": "Fichier supprimé avec succès",
"deleteError": "Échec de la suppression du fichier"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Tableau des fichiers", "ariaLabel": "Tableau des fichiers",

View File

@@ -81,7 +81,13 @@
"uploadFile": "फाइल अपलोड करें", "uploadFile": "फाइल अपलोड करें",
"loadError": "फाइलें लोड करने में त्रुटि", "loadError": "फाइलें लोड करने में त्रुटि",
"pageTitle": "मेरी फाइलें", "pageTitle": "मेरी फाइलें",
"breadcrumb": "मेरी फाइलें" "breadcrumb": "मेरी फाइलें",
"downloadStart": "डाउनलोड शुरू हुआ",
"downloadError": "फाइल डाउनलोड करने में त्रुटि",
"updateSuccess": "फाइल सफलतापूर्वक अपडेट हुई",
"updateError": "फाइल अपडेट करने में त्रुटि",
"deleteSuccess": "फाइल सफलतापूर्वक हटाई गई",
"deleteError": "फाइल हटाने में त्रुटि"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "फाइल टेबल", "ariaLabel": "फाइल टेबल",

View File

@@ -81,7 +81,13 @@
"uploadFile": "ファイルをアップロード", "uploadFile": "ファイルをアップロード",
"loadError": "ファイルの読み込みに失敗しました", "loadError": "ファイルの読み込みに失敗しました",
"pageTitle": "マイファイル", "pageTitle": "マイファイル",
"breadcrumb": "マイファイル" "breadcrumb": "マイファイル",
"downloadStart": "ダウンロードが開始されました",
"downloadError": "ファイルのダウンロードに失敗しました",
"updateSuccess": "ファイルが正常に更新されました",
"updateError": "ファイルの更新に失敗しました",
"deleteSuccess": "ファイルが正常に削除されました",
"deleteError": "ファイルの削除に失敗しました"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "ファイルテーブル", "ariaLabel": "ファイルテーブル",

View File

@@ -81,7 +81,13 @@
"uploadFile": "파일 업로드", "uploadFile": "파일 업로드",
"loadError": "파일을 불러오는데 실패했습니다", "loadError": "파일을 불러오는데 실패했습니다",
"pageTitle": "내 파일", "pageTitle": "내 파일",
"breadcrumb": "내 파일" "breadcrumb": "내 파일",
"downloadStart": "다운로드가 시작되었습니다",
"downloadError": "파일 다운로드에 실패했습니다",
"updateSuccess": "파일이 성공적으로 업데이트되었습니다",
"updateError": "파일 업데이트에 실패했습니다",
"deleteSuccess": "파일이 성공적으로 삭제되었습니다",
"deleteError": "파일 삭제에 실패했습니다"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "파일 테이블", "ariaLabel": "파일 테이블",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Enviar Arquivo", "uploadFile": "Enviar Arquivo",
"loadError": "Falha ao carregar arquivos", "loadError": "Falha ao carregar arquivos",
"pageTitle": "Meus Arquivos", "pageTitle": "Meus Arquivos",
"breadcrumb": "Meus Arquivos" "breadcrumb": "Meus Arquivos",
"downloadStart": "Download iniciado",
"downloadError": "Erro ao baixar o arquivo",
"updateSuccess": "Arquivo atualizado com sucesso",
"updateError": "Erro ao atualizar o arquivo",
"deleteSuccess": "Arquivo excluído com sucesso",
"deleteError": "Erro ao excluir o arquivo"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Tabela de arquivos", "ariaLabel": "Tabela de arquivos",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Загрузить файл", "uploadFile": "Загрузить файл",
"loadError": "Ошибка загрузки файлов", "loadError": "Ошибка загрузки файлов",
"pageTitle": "Мои файлы", "pageTitle": "Мои файлы",
"breadcrumb": "Мои файлы" "breadcrumb": "Мои файлы",
"downloadStart": "Скачивание начато",
"downloadError": "Ошибка при скачивании файла",
"updateSuccess": "Файл успешно обновлен",
"updateError": "Ошибка при обновлении файла",
"deleteSuccess": "Файл успешно удален",
"deleteError": "Ошибка при удалении файла"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Таблица файлов", "ariaLabel": "Таблица файлов",

View File

@@ -81,7 +81,13 @@
"uploadFile": "Dosya Yükle", "uploadFile": "Dosya Yükle",
"loadError": "Dosyalar yüklenemedi", "loadError": "Dosyalar yüklenemedi",
"pageTitle": "Benim Dosyalarım", "pageTitle": "Benim Dosyalarım",
"breadcrumb": "Benim Dosyalarım" "breadcrumb": "Benim Dosyalarım",
"downloadStart": "İndirme başladı",
"downloadError": "Dosya indirilemedi",
"updateSuccess": "Dosya başarıyla güncellendi",
"updateError": "Dosya güncellenemedi",
"deleteSuccess": "Dosya başarıyla silindi",
"deleteError": "Dosya silinemedi"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "Dosya Tablosu", "ariaLabel": "Dosya Tablosu",

View File

@@ -81,7 +81,13 @@
"uploadFile": "上传文件", "uploadFile": "上传文件",
"loadError": "加载文件失败", "loadError": "加载文件失败",
"pageTitle": "我的文件", "pageTitle": "我的文件",
"breadcrumb": "我的文件" "breadcrumb": "我的文件",
"downloadStart": "下载已开始",
"downloadError": "下载文件失败",
"updateSuccess": "文件更新成功",
"updateError": "文件更新失败",
"deleteSuccess": "文件删除成功",
"deleteError": "文件删除失败"
}, },
"filesTable": { "filesTable": {
"ariaLabel": "文件表格", "ariaLabel": "文件表格",

View File

@@ -57,12 +57,17 @@ export function usePublicShare() {
const encodedObjectName = encodeURIComponent(objectName); const encodedObjectName = encodeURIComponent(objectName);
const response = await getDownloadUrl(encodedObjectName); const response = await getDownloadUrl(encodedObjectName);
const downloadUrl = response.data.url; const downloadUrl = response.data.url;
console.log(fileName)
await downloadFile(downloadUrl, fileName); const link = document.createElement("a");
toast.success(t("share.messages.downloadStarted")); link.href = downloadUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
toast.success(t("files.downloadStart"));
} catch (error) { } catch (error) {
toast.error(t("share.errors.downloadFailed"));
console.error(error); console.error(error);
toast.success(t("files.downloadError"));
} }
}; };

View File

@@ -1,5 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { useTranslations } from "next-intl";
import { deleteFile, getDownloadUrl, updateFile } from "@/http/endpoints"; import { deleteFile, getDownloadUrl, updateFile } from "@/http/endpoints";
@@ -32,6 +33,7 @@ export interface FileManagerHook {
} }
export function useFileManager(onRefresh: () => Promise<void>) { export function useFileManager(onRefresh: () => Promise<void>) {
const t = useTranslations();
const [previewFile, setPreviewFile] = useState<PreviewFile | null>(null); const [previewFile, setPreviewFile] = useState<PreviewFile | null>(null);
const [fileToRename, setFileToRename] = useState<FileToRename | null>(null); const [fileToRename, setFileToRename] = useState<FileToRename | null>(null);
const [fileToDelete, setFileToDelete] = useState<FileToDelete | null>(null); const [fileToDelete, setFileToDelete] = useState<FileToDelete | null>(null);
@@ -41,23 +43,17 @@ export function useFileManager(onRefresh: () => Promise<void>) {
const encodedObjectName = encodeURIComponent(objectName); const encodedObjectName = encodeURIComponent(objectName);
const response = await getDownloadUrl(encodedObjectName); const response = await getDownloadUrl(encodedObjectName);
const downloadUrl = response.data.url; const downloadUrl = response.data.url;
console.log(fileName)
const fileResponse = await fetch(downloadUrl);
const blob = await fileResponse.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a"); const link = document.createElement("a");
link.href = downloadUrl;
link.href = url;
link.download = fileName; link.download = fileName;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
window.URL.revokeObjectURL(url); toast.success(t("files.downloadStart"));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
toast.error("Failed to download file"); toast.success(t("files.downloadError"));
} }
}; };
@@ -68,11 +64,11 @@ export function useFileManager(onRefresh: () => Promise<void>) {
description: description || null, description: description || null,
}); });
await onRefresh(); await onRefresh();
toast.success("File updated successfully"); toast.success(t("files.updateSuccess"));
setFileToRename(null); setFileToRename(null);
} catch (error) { } catch (error) {
console.error("Failed to update file:", error); console.error("Failed to update file:", error);
toast.error("Failed to update file"); toast.success(t("files.updateError"));
} }
}; };
@@ -80,11 +76,11 @@ export function useFileManager(onRefresh: () => Promise<void>) {
try { try {
await deleteFile(fileId); await deleteFile(fileId);
await onRefresh(); await onRefresh();
toast.success("File deleted successfully"); toast.success(t("files.deleteSuccess"));
setFileToDelete(null); setFileToDelete(null);
} catch (error) { } catch (error) {
console.error("Failed to delete file:", error); console.error("Failed to delete file:", error);
toast.error("Failed to delete file"); toast.success(t("files.deleteError"));
} }
}; };

View File

@@ -10,7 +10,7 @@ services:
environment: environment:
- PORT=${API_INTERNAL_PORT:-3333} # Port for the backend service - PORT=${API_INTERNAL_PORT:-3333} # Port for the backend service
- DATABASE_URL=postgresql://postgres:${POSTGRESQL_PASSWORD:-postgresRootPassword}@postgres:5432/palmr_db?schema=public # Database URL with configurable password through POSTGRESQL_PASSWORD env var - DATABASE_URL=postgresql://postgres:${POSTGRESQL_PASSWORD:-postgresRootPassword}@postgres:5432/palmr_db?schema=public # Database URL with configurable password through POSTGRESQL_PASSWORD env var
- MINIO_ENDPOINT=minio # This can change if your MinIO is at a different address - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio} # This can change if your MinIO is at a different address
- MINIO_PORT=${MINIO_INTERNAL_API_PORT:-6421} # Default MinIO port (Change if yours is not the default) - MINIO_PORT=${MINIO_INTERNAL_API_PORT:-6421} # Default MinIO port (Change if yours is not the default)
- MINIO_USE_SSL=false # MinIO uses SSL by default, but you can change it to true if needed - MINIO_USE_SSL=false # MinIO uses SSL by default, but you can change it to true if needed
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user} # MinIO credentials can be configured through MINIO_ROOT_USER env vars - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user} # MinIO credentials can be configured through MINIO_ROOT_USER env vars
@@ -24,7 +24,7 @@ services:
- "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping - "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${API_INTERNAL_PORT:-3333}/health"] test: ["CMD", "wget", "--no-verbose", "http://palmr-api:${API_INTERNAL_PORT:-3333}/health"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5