mirror of
				https://github.com/kyantech/Palmr.git
				synced 2025-11-04 05:53:23 +00:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			v2.0.0-bet
			...
			v2.0.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8f3ad71850 | ||
| 
						 | 
					a9191d6b54 | ||
| 
						 | 
					b8465df016 | ||
| 
						 | 
					5a44d79279 | ||
| 
						 | 
					63d9abfe3e | ||
| 
						 | 
					d3ae5ea10c | ||
| 
						 | 
					d614820aca | ||
| 
						 | 
					3c2d92c630 | ||
| 
						 | 
					6ae8436f4b | ||
| 
						 | 
					39d5980936 | ||
| 
						 | 
					27e0e7c8da | ||
| 
						 | 
					02bc1df0c1 | ||
| 
						 | 
					c1baa3a16d | ||
| 
						 | 
					cfc103e056 | ||
| 
						 | 
					1a0b565ae0 | 
@@ -49,6 +49,10 @@
 | 
			
		||||
 | 
			
		||||
</br>
 | 
			
		||||
 | 
			
		||||
## 🤝 Supporters
 | 
			
		||||
 | 
			
		||||
[<img src="https://i.ibb.co/nMN40STL/Repoflow.png" width="200px" alt="Daniel Luiz Alves" />](https://www.repoflow.io/)
 | 
			
		||||
 | 
			
		||||
## ⭐ Star History
 | 
			
		||||
 | 
			
		||||
  <a href="https://www.star-history.com/#kyantech/Palmr&Date">
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ Below is the complete content of our `docker-compose.yaml` that can be copied di
 | 
			
		||||
```yaml
 | 
			
		||||
services:
 | 
			
		||||
  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
 | 
			
		||||
    depends_on:
 | 
			
		||||
      postgres:
 | 
			
		||||
@@ -39,8 +39,8 @@ services:
 | 
			
		||||
        condition: "service_healthy"
 | 
			
		||||
    environment:
 | 
			
		||||
      - 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
 | 
			
		||||
      - MINIO_ENDPOINT=minio # This can change if your MinIO is at a different address
 | 
			
		||||
      - 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_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_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
 | 
			
		||||
@@ -54,14 +54,14 @@ services:
 | 
			
		||||
      - "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping 
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    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
 | 
			
		||||
      timeout: 5s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      start_period: 30s
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
    depends_on:
 | 
			
		||||
      palmr-api:
 | 
			
		||||
@@ -121,10 +121,10 @@ services:
 | 
			
		||||
    container_name: palmr-postgres
 | 
			
		||||
    environment:
 | 
			
		||||
      # PostgreSQL credentials configurable through environment variables
 | 
			
		||||
      # POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB can be set to override defaults
 | 
			
		||||
      - POSTGRESQL_USERNAME=${POSTGRES_USER:-postgres}
 | 
			
		||||
      - POSTGRESQL_PASSWORD=${POSTGRES_PASSWORD:-postgresRootPassword}
 | 
			
		||||
      - POSTGRESQL_DATABASE=${POSTGRES_DB:-palmr_db}
 | 
			
		||||
      # POSTGRESQL_USERNAME, POSTGRESQL_PASSWORD, and POSTGRES_DB can be set to override defaults
 | 
			
		||||
      - POSTGRESQL_USERNAME=${POSTGRESQL_USERNAME:-postgres}
 | 
			
		||||
      - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD:-postgresRootPassword}
 | 
			
		||||
      - POSTGRESQL_DATABASE=${POSTGRES_DATABASE:-palmr_db}
 | 
			
		||||
    volumes:
 | 
			
		||||
      - postgres_data:/bitnami/postgresql
 | 
			
		||||
    ports:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
FROM node:18
 | 
			
		||||
FROM node:22-alpine
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
COPY package*.json ./
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
    "@fastify/static": "^8.1.1",
 | 
			
		||||
    "@fastify/swagger": "^9.4.2",
 | 
			
		||||
    "@fastify/swagger-ui": "^5.2.1",
 | 
			
		||||
    "@prisma/client": "^6.3.1",
 | 
			
		||||
    "@prisma/client": "^6.4.1",
 | 
			
		||||
    "@scalar/fastify-api-reference": "^1.25.116",
 | 
			
		||||
    "bcryptjs": "^2.4.3",
 | 
			
		||||
    "fastify": "^5.2.1",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								apps/server/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								apps/server/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -30,7 +30,7 @@ importers:
 | 
			
		||||
        specifier: ^5.2.1
 | 
			
		||||
        version: 5.2.2
 | 
			
		||||
      '@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)
 | 
			
		||||
      '@scalar/fastify-api-reference':
 | 
			
		||||
        specifier: ^1.25.116
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,10 @@ import { PrismaClient } from '@prisma/client';
 | 
			
		||||
 | 
			
		||||
const prisma = new PrismaClient();
 | 
			
		||||
 | 
			
		||||
async function main() {}
 | 
			
		||||
async function main() {
 | 
			
		||||
  const count = await prisma.user.count();
 | 
			
		||||
  console.log(count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
  .catch((e) => {
 | 
			
		||||
@@ -11,4 +14,4 @@ main()
 | 
			
		||||
  })
 | 
			
		||||
  .finally(async () => {
 | 
			
		||||
    await prisma.$disconnect();
 | 
			
		||||
  });
 | 
			
		||||
  });
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { prisma } from "shared/prisma";
 | 
			
		||||
import { prisma } from "../../shared/prisma";
 | 
			
		||||
import { AppController } from "./controller";
 | 
			
		||||
import { ConfigResponseSchema, BulkUpdateConfigSchema } from "./dto";
 | 
			
		||||
import { FastifyInstance } from "fastify";
 | 
			
		||||
 
 | 
			
		||||
@@ -169,9 +169,9 @@ export class FileController {
 | 
			
		||||
      if (!fileRecord) {
 | 
			
		||||
        return reply.status(404).send({ error: "File not found." });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const fileName = fileRecord.name;
 | 
			
		||||
      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 });
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error("Error in getDownloadUrl:", error);
 | 
			
		||||
 
 | 
			
		||||
@@ -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) => {
 | 
			
		||||
      (minioLocalClient as any).presignedGetObject(bucketName, objectName, expires, ((
 | 
			
		||||
        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);
 | 
			
		||||
      const reqParams: { [key: string]: any } = {}; 
 | 
			
		||||
      let rcdFileName: string;
 | 
			
		||||
      if (fileName && fileName.trim() !== '') {
 | 
			
		||||
        rcdFileName = fileName;
 | 
			
		||||
      } else {
 | 
			
		||||
        const lastSlashIndex = objectName.lastIndexOf('/');
 | 
			
		||||
        rcdFileName = lastSlashIndex !== -1
 | 
			
		||||
          ? objectName.substring(lastSlashIndex + 1)
 | 
			
		||||
          : objectName;
 | 
			
		||||
        if (!rcdFileName) {
 | 
			
		||||
          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> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      (minioClient as any).removeObject(bucketName, objectName, (err: Error | null) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { prisma } from "shared/prisma";
 | 
			
		||||
import { prisma } from "../../shared/prisma";
 | 
			
		||||
import { createPasswordSchema } from "../auth/dto";
 | 
			
		||||
import { UserController } from "./controller";
 | 
			
		||||
import { UpdateUserSchema, UserResponseSchema } from "./dto";
 | 
			
		||||
@@ -13,7 +13,7 @@ export async function userRoutes(app: FastifyInstance) {
 | 
			
		||||
    try {
 | 
			
		||||
      const usersCount = await prisma.user.count();
 | 
			
		||||
 | 
			
		||||
      if (usersCount > 0 ) {
 | 
			
		||||
      if (usersCount > 0) {
 | 
			
		||||
        await request.jwtVerify();
 | 
			
		||||
        if (!request.user.isAdmin) {
 | 
			
		||||
          return reply
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
# 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
 | 
			
		||||
FROM base AS deps
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "رفع ملف",
 | 
			
		||||
    "loadError": "فشل في تحميل الملفات",
 | 
			
		||||
    "pageTitle": "ملفاتي",
 | 
			
		||||
    "breadcrumb": "ملفاتي"
 | 
			
		||||
    "breadcrumb": "ملفاتي",
 | 
			
		||||
    "downloadStart": "بدأ التنزيل",
 | 
			
		||||
    "downloadError": "فشل في تنزيل الملف",
 | 
			
		||||
    "updateSuccess": "تم تحديث الملف بنجاح",
 | 
			
		||||
    "updateError": "فشل في تحديث الملف",
 | 
			
		||||
    "deleteSuccess": "تم حذف الملف بنجاح",
 | 
			
		||||
    "deleteError": "فشل في حذف الملف"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "جدول الملفات",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Datei hochladen",
 | 
			
		||||
    "loadError": "Fehler beim Laden der 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": {
 | 
			
		||||
    "ariaLabel": "Dateitabelle",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Upload File",
 | 
			
		||||
    "loadError": "Failed to load 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": {
 | 
			
		||||
    "ariaLabel": "Files table",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Subir archivo",
 | 
			
		||||
    "loadError": "Error al cargar los 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": {
 | 
			
		||||
    "ariaLabel": "Tabla de archivos",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Envoyer un Fichier",
 | 
			
		||||
    "loadError": "Échec du chargement des 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": {
 | 
			
		||||
    "ariaLabel": "Tableau des fichiers",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "फाइल अपलोड करें",
 | 
			
		||||
    "loadError": "फाइलें लोड करने में त्रुटि",
 | 
			
		||||
    "pageTitle": "मेरी फाइलें",
 | 
			
		||||
    "breadcrumb": "मेरी फाइलें"
 | 
			
		||||
    "breadcrumb": "मेरी फाइलें",
 | 
			
		||||
    "downloadStart": "डाउनलोड शुरू हुआ",
 | 
			
		||||
    "downloadError": "फाइल डाउनलोड करने में त्रुटि",
 | 
			
		||||
    "updateSuccess": "फाइल सफलतापूर्वक अपडेट हुई",
 | 
			
		||||
    "updateError": "फाइल अपडेट करने में त्रुटि",
 | 
			
		||||
    "deleteSuccess": "फाइल सफलतापूर्वक हटाई गई",
 | 
			
		||||
    "deleteError": "फाइल हटाने में त्रुटि"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "फाइल टेबल",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "ファイルをアップロード",
 | 
			
		||||
    "loadError": "ファイルの読み込みに失敗しました",
 | 
			
		||||
    "pageTitle": "マイファイル",
 | 
			
		||||
    "breadcrumb": "マイファイル"
 | 
			
		||||
    "breadcrumb": "マイファイル",
 | 
			
		||||
    "downloadStart": "ダウンロードが開始されました",
 | 
			
		||||
    "downloadError": "ファイルのダウンロードに失敗しました",
 | 
			
		||||
    "updateSuccess": "ファイルが正常に更新されました",
 | 
			
		||||
    "updateError": "ファイルの更新に失敗しました",
 | 
			
		||||
    "deleteSuccess": "ファイルが正常に削除されました",
 | 
			
		||||
    "deleteError": "ファイルの削除に失敗しました"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "ファイルテーブル",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "파일 업로드",
 | 
			
		||||
    "loadError": "파일을 불러오는데 실패했습니다",
 | 
			
		||||
    "pageTitle": "내 파일",
 | 
			
		||||
    "breadcrumb": "내 파일"
 | 
			
		||||
    "breadcrumb": "내 파일",
 | 
			
		||||
    "downloadStart": "다운로드가 시작되었습니다",
 | 
			
		||||
    "downloadError": "파일 다운로드에 실패했습니다",
 | 
			
		||||
    "updateSuccess": "파일이 성공적으로 업데이트되었습니다",
 | 
			
		||||
    "updateError": "파일 업데이트에 실패했습니다",
 | 
			
		||||
    "deleteSuccess": "파일이 성공적으로 삭제되었습니다",
 | 
			
		||||
    "deleteError": "파일 삭제에 실패했습니다"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "파일 테이블",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Enviar Arquivo",
 | 
			
		||||
    "loadError": "Falha ao carregar 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": {
 | 
			
		||||
    "ariaLabel": "Tabela de arquivos",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Загрузить файл",
 | 
			
		||||
    "loadError": "Ошибка загрузки файлов",
 | 
			
		||||
    "pageTitle": "Мои файлы",
 | 
			
		||||
    "breadcrumb": "Мои файлы"
 | 
			
		||||
    "breadcrumb": "Мои файлы",
 | 
			
		||||
    "downloadStart": "Скачивание начато",
 | 
			
		||||
    "downloadError": "Ошибка при скачивании файла",
 | 
			
		||||
    "updateSuccess": "Файл успешно обновлен",
 | 
			
		||||
    "updateError": "Ошибка при обновлении файла",
 | 
			
		||||
    "deleteSuccess": "Файл успешно удален",
 | 
			
		||||
    "deleteError": "Ошибка при удалении файла"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "Таблица файлов",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "Dosya Yükle",
 | 
			
		||||
    "loadError": "Dosyalar yüklenemedi",
 | 
			
		||||
    "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": {
 | 
			
		||||
    "ariaLabel": "Dosya Tablosu",
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,13 @@
 | 
			
		||||
    "uploadFile": "上传文件",
 | 
			
		||||
    "loadError": "加载文件失败",
 | 
			
		||||
    "pageTitle": "我的文件",
 | 
			
		||||
    "breadcrumb": "我的文件"
 | 
			
		||||
    "breadcrumb": "我的文件",
 | 
			
		||||
    "downloadStart": "下载已开始",
 | 
			
		||||
    "downloadError": "下载文件失败",
 | 
			
		||||
    "updateSuccess": "文件更新成功",
 | 
			
		||||
    "updateError": "文件更新失败",
 | 
			
		||||
    "deleteSuccess": "文件删除成功",
 | 
			
		||||
    "deleteError": "文件删除失败"
 | 
			
		||||
  },
 | 
			
		||||
  "filesTable": {
 | 
			
		||||
    "ariaLabel": "文件表格",
 | 
			
		||||
 
 | 
			
		||||
@@ -57,12 +57,17 @@ export function usePublicShare() {
 | 
			
		||||
      const encodedObjectName = encodeURIComponent(objectName);
 | 
			
		||||
      const response = await getDownloadUrl(encodedObjectName);
 | 
			
		||||
      const downloadUrl = response.data.url;
 | 
			
		||||
 | 
			
		||||
      await downloadFile(downloadUrl, fileName);
 | 
			
		||||
      toast.success(t("share.messages.downloadStarted"));
 | 
			
		||||
      console.log(fileName)
 | 
			
		||||
      const link = document.createElement("a");
 | 
			
		||||
      link.href = downloadUrl;
 | 
			
		||||
      link.download = fileName;
 | 
			
		||||
      document.body.appendChild(link);
 | 
			
		||||
      link.click();
 | 
			
		||||
      document.body.removeChild(link);
 | 
			
		||||
      toast.success(t("files.downloadStart"));
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      toast.error(t("share.errors.downloadFailed"));
 | 
			
		||||
      console.error(error);
 | 
			
		||||
      toast.success(t("files.downloadError"));
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
import { toast } from "sonner";
 | 
			
		||||
import { useTranslations } from "next-intl";
 | 
			
		||||
 | 
			
		||||
import { deleteFile, getDownloadUrl, updateFile } from "@/http/endpoints";
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +33,7 @@ export interface FileManagerHook {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useFileManager(onRefresh: () => Promise<void>) {
 | 
			
		||||
  const t = useTranslations();
 | 
			
		||||
  const [previewFile, setPreviewFile] = useState<PreviewFile | null>(null);
 | 
			
		||||
  const [fileToRename, setFileToRename] = useState<FileToRename | null>(null);
 | 
			
		||||
  const [fileToDelete, setFileToDelete] = useState<FileToDelete | null>(null);
 | 
			
		||||
@@ -41,23 +43,17 @@ export function useFileManager(onRefresh: () => Promise<void>) {
 | 
			
		||||
      const encodedObjectName = encodeURIComponent(objectName);
 | 
			
		||||
      const response = await getDownloadUrl(encodedObjectName);
 | 
			
		||||
      const downloadUrl = response.data.url;
 | 
			
		||||
 | 
			
		||||
      const fileResponse = await fetch(downloadUrl);
 | 
			
		||||
      const blob = await fileResponse.blob();
 | 
			
		||||
      const url = window.URL.createObjectURL(blob);
 | 
			
		||||
 | 
			
		||||
      console.log(fileName)
 | 
			
		||||
      const link = document.createElement("a");
 | 
			
		||||
 | 
			
		||||
      link.href = url;
 | 
			
		||||
      link.href = downloadUrl;
 | 
			
		||||
      link.download = fileName;
 | 
			
		||||
      document.body.appendChild(link);
 | 
			
		||||
      link.click();
 | 
			
		||||
 | 
			
		||||
      document.body.removeChild(link);
 | 
			
		||||
      window.URL.revokeObjectURL(url);
 | 
			
		||||
      toast.success(t("files.downloadStart"));
 | 
			
		||||
    } catch (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,
 | 
			
		||||
      });
 | 
			
		||||
      await onRefresh();
 | 
			
		||||
      toast.success("File updated successfully");
 | 
			
		||||
      toast.success(t("files.updateSuccess"));
 | 
			
		||||
      setFileToRename(null);
 | 
			
		||||
    } catch (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 {
 | 
			
		||||
      await deleteFile(fileId);
 | 
			
		||||
      await onRefresh();
 | 
			
		||||
      toast.success("File deleted successfully");
 | 
			
		||||
      toast.success(t("files.deleteSuccess"));
 | 
			
		||||
      setFileToDelete(null);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error("Failed to delete file:", error);
 | 
			
		||||
      toast.error("Failed to delete file");
 | 
			
		||||
      toast.success(t("files.deleteError"));
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -99,4 +95,4 @@ export function useFileManager(onRefresh: () => Promise<void>) {
 | 
			
		||||
    handleRename,
 | 
			
		||||
    handleDelete,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -10,7 +10,7 @@ services:
 | 
			
		||||
    environment:
 | 
			
		||||
      - 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
 | 
			
		||||
      - 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_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
 | 
			
		||||
@@ -24,7 +24,7 @@ services:
 | 
			
		||||
      - "${API_EXTERNAL_PORT:-3333}:${API_INTERNAL_PORT:-3333}" # Backend port mapping 
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    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
 | 
			
		||||
      timeout: 5s
 | 
			
		||||
      retries: 5
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user