{"id":46628777,"url":"https://github.com/stivenjs/savecloud","last_synced_at":"2026-06-07T02:01:29.609Z","repository":{"id":341275497,"uuid":"1169494314","full_name":"Stivenjs/savecloud","owner":"Stivenjs","description":"Automated game save synchronization. Cloud-hosted, locally backed up, and platform-agnostic.","archived":false,"fork":false,"pushed_at":"2026-05-28T19:47:15.000Z","size":13950,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T21:21:45.784Z","etag":null,"topics":["cloud","cloud-gaming","cpp","games","lambda","rust-lang","s3","tauri"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Stivenjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-28T19:16:00.000Z","updated_at":"2026-05-28T19:47:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Stivenjs/savecloud","commit_stats":null,"previous_names":["stivenjs/sync-games","stivenjs/savecloud"],"tags_count":191,"template":false,"template_full_name":null,"purl":"pkg:github/Stivenjs/savecloud","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stivenjs%2Fsavecloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stivenjs%2Fsavecloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stivenjs%2Fsavecloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stivenjs%2Fsavecloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Stivenjs","download_url":"https://codeload.github.com/Stivenjs/savecloud/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Stivenjs%2Fsavecloud/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33884734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloud","cloud-gaming","cpp","games","lambda","rust-lang","s3","tauri"],"created_at":"2026-03-07T23:11:03.657Z","updated_at":"2026-06-04T00:00:42.669Z","avatar_url":"https://github.com/Stivenjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1\u003e\n  \u003cimg src=\"/assets/icon.ico\" width=\"60\" style=\"vertical-align: middle; margin-right: 10px;\" /\u003e\n  SaveCloud\n\u003c/h1\u003e\n\nServidor de guardado en la nube para juegos (S3 + Lambda) y app de escritorio para sincronizar guardados.\n\n![Bun](https://img.shields.io/badge/runtime-bun-black)\n![Node](https://img.shields.io/badge/node-20-green)\n![TypeScript](https://img.shields.io/badge/language-typescript-blue)\n![Fastify](https://img.shields.io/badge/api-fastify-black)\n![AWS](https://img.shields.io/badge/deploy-AWS-orange)\n![S3](https://img.shields.io/badge/storage-S3-red)\n![Tauri](https://img.shields.io/badge/desktop-tauri-blue)\n![React](https://img.shields.io/badge/frontend-react-61dafb)\n![Rust](https://img.shields.io/badge/backend-rust-orange)\n\n---\n\n## Vista de la aplicación\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"/doc/images/preview.png\" width=\"800\" /\u003e\u003c/p\u003e\n\n---\n\n## Guía de despliegue\n\nLa infraestructura backend y la configuración completa del proyecto se explican en la guía de despliegue disponible en:\n\n[Guía de despliegue](./doc/DEPLOYMENT.md)\n\nEsta guía cubre la preparación del entorno, generación de API keys, configuración del archivo `.env`, despliegue en AWS (dev y live), configuración de la aplicación de escritorio y verificación del sistema.\n\n## Contenido del repositorio\n\n- **Backend (API + CLI):** raíz del repo — Fastify, TypeScript, despliegue en AWS Lambda + API Gateway, almacenamiento en S3.\n- **App de escritorio:** `apps/savecloud-desktop` — Tauri 2 (React + Vite + Rust), interfaz gráfica para gestionar juegos, subir/descargar guardados, amigos y configuración.\n\n## Stack del backend\n\n- **Runtime:** Bun (local) / Node.js 20 (Lambda)\n- **Lenguaje:** TypeScript\n- **API:** Fastify\n- **Deploy:** Serverless Framework → AWS Lambda + API Gateway\n- **Almacenamiento:** AWS S3\n\n## Stack de la app de escritorio\n\n- **Frontend:** React 19, Vite, HeroUI, TanStack Query, Framer Motion\n- **Backend local:** Tauri 2 (Rust) — comandos para config, sincronización, Steam, etc.\n- **Plugins Tauri:** dialog, autostart, notification, updater, opener\n\n## Estructura del backend (Clean Architecture)\n\n```\napps/api/src/\n├── domain/                 # Entidades y reglas de negocio\n│   ├── entities/\n│   └── ports/\n├── application/            # Casos de uso (orquestación)\n│   └── use-cases/\n├── infrastructure/         # Implementaciones concretas (S3, etc.)\n│   └── persistence/\n└── interfaces/             # Entrada/salida (HTTP, Lambda)\n    ├── http/               # Fastify app, rutas\n    └── lambda/             # Handler para AWS Lambda\n```\n\nLas dependencias apuntan hacia dentro: `interfaces` → `application` → `domain`; `infrastructure` implementa los puertos definidos en `domain`.\n\n## Scripts (raíz del repo)\n\n| Script                           | Descripción                                         |\n| -------------------------------- | --------------------------------------------------- |\n| `bun run build`                  | Compila TypeScript del backend                      |\n| `bun run dev`                    | API en local con hot-reload (puerto 3000)           |\n| `bun run deploy:dev`             | Despliega a AWS (stage `dev`)                       |\n| `bun run deploy:live`            | Despliega a AWS (stage `live`)                      |\n| `bun run invoke:local`           | Invoca la función Lambda en local                   |\n| `bun run cli`                    | Ejecuta el CLI (menú interactivo)                   |\n| `bun run cli -- add \u003cid\u003e \u003cruta\u003e` | Añade un juego desde la CLI                         |\n| `bun run build:cli`              | Genera ejecutable en `dist/` (ej. `savecloud.exe`)  |\n| `bun run desktop`                | App de escritorio en modo desarrollo (Tauri + Vite) |\n| `bun run desktop:dev`            | Solo frontend (Vite)                                |\n| `bun run desktop:build`          | Build de instalador de la app de escritorio         |\n| `bun run api-key`                | Genera API key para el backend                      |\n| `bun run api-key:live`           | Genera API key para el backend (live)               |\n| `bun run desktop:latest-json`    | Genera latest.json para la app de escritorio        |\n| `bun run desktop:icon`           | Genera icono para la app de escritorio              |\n\nInstalación: `bun install` en la raíz.\n\n## Cómo ejecutar el CLI\n\n- **Menú interactivo:** `bun run cli` (o `savecloud` si hiciste `bun link`) → menú para añadir juego, listar, subir/descargar, config, etc.\n- **Modo comando:** `bun run cli -- add elden-ring \"%APPDATA%/EldenRing\"` o `bun run cli -- upload`\n\nConfig por defecto: `%APPDATA%/savecloud/config.json` (Windows) o `~/.config/savecloud/config.json` (Linux/macOS).\n\n## App de escritorio\n\nDesde la raíz: `bun run desktop`. Requiere Rust y dependencias de Tauri instaladas.\n\n- **Juegos:** listado, añadir/editar/eliminar, subir a la nube, descargar, “Subir todos” / “Descargar todos” (con operaciones batch y paralelismo). Archivos ≥ 5 MB se suben por **multipart** (pausar, cancelar, reanudar); archivos pequeños en **lotes de hasta 16 PUTs en paralelo** y hasta 500 URLs por petición a la API. El tamaño en la nube se muestra correctamente en GB/TB. Al eliminar un juego se borra también de la nube (S3). Al editar se puede cambiar el nombre/ID del juego; se actualiza en la app, en el config y en S3 (los guardados se migran al nuevo nombre).\n- **Diagnóstico:** en caso de errores de subida, se escribe un log en el directorio de configuración (`sync-debug.log`); el comando `get_sync_debug_log_path` devuelve su ruta para poder abrirlo.\n- **Amigos:** importar por link compartido, ver perfil por User ID, copiar guardados de un amigo.\n- **Configuración:** API URL, User ID, API Key, autostart, notificaciones, respaldo/restauración del config en la nube (con subida automática tras cambios). Gestión de backups locales: elegir cuántos backups mantener por juego (3, 5, 10 o 20) y liberar espacio; la preferencia se guarda en config y se aplica también a la auto-limpieza tras cada descarga.\n- **Historial:** operaciones de sync recientes.\n\n## Variables de entorno (backend / Lambda)\n\n- `BUCKET_NAME` — Nombre del bucket S3 (Serverless lo inyecta en Lambda).\n- `API_KEY` — (opcional) Si está definido, la API exige header `x-api-key`.\n- Desarrollo local: `BUCKET_NAME`, `AWS_REGION`, `PORT` (opcional).\n\n## API\n\n| Método y ruta                               | Descripción                                                                    |\n| ------------------------------------------- | ------------------------------------------------------------------------------ |\n| `GET /health`                               | Health check                                                                   |\n| `GET /saves`                                | Lista guardados del usuario (headers: `x-user-id`, `x-api-key` si aplica)      |\n| `POST /saves/upload-url`                    | Una URL de subida. Body: `{ \"gameId\", \"filename\" }` → `{ \"uploadUrl\", \"key\" }` |\n| `POST /saves/upload-urls`                   | Batch: varias URLs de subida (máx. 500 ítems por petición).                    |\n| `POST /saves/download-url`                  | Una URL de descarga. Body: `{ \"gameId\", \"key\" }`                               |\n| `POST /saves/download-urls`                 | Batch: varias URLs de descarga.                                                |\n| `POST /saves/multipart/init`                | Inicia subida multipart (archivos grandes).                                    |\n| `POST /saves/multipart/init-with-part-urls` | Init + todas las URLs de partes en una llamada.                                |\n| `POST /saves/multipart/part-urls`           | URLs firmadas para partes.                                                     |\n| `POST /saves/multipart/complete`            | Completa subida multipart.                                                     |\n| `POST /saves/multipart/abort`               | Aborta subida multipart.                                                       |\n| `POST /saves/delete-game`                   | Borra todos los guardados del juego en S3.                                     |\n| `POST /saves/rename-game`                   | Renombra un juego en S3 (copia a nuevo prefijo y borra el antiguo).            |\n\nEl cliente sube y descarga archivos directamente desde S3 utilizando URLs firmadas. La app de escritorio usa endpoints batch y multipart para reducir llamadas a la API.\n\n## Probar que los guardados se suben a S3\n\n1. **API en local**\n\n```bash\nexport BUCKET_NAME=tu-bucket-savecloud\nexport AWS_REGION=us-east-2\nbun run dev\n```\n\nLa API quedará disponible en `http://localhost:3000`.\n\n2. **Configurar el CLI o la app de escritorio** con la URL de la API y un `userId` en el archivo de configuración.\n\n3. **Subir guardados**\n\n```\nbun run cli -- upload \u003cgame-id\u003e\n```\n\nTambién puede hacerse desde la app de escritorio.\n\nLuego verifica en S3 la ruta:\n\n```\nuserId/gameId/\u003carchivo\u003e\n```\n\nSi desplegaste en AWS (`bun run deploy:dev`), usa en la configuración la URL del API Gateway (por ejemplo `https://xxxx.execute-api.us-east-2.amazonaws.com`).\n\n## Arquitectura del sistema\n\n```\nDesktop App (Tauri + React)\n          │\n          │ HTTPS\n          ▼\n      API Gateway\n          │\n          ▼\n      AWS Lambda\n          │\n          ▼\n        Amazon S3\n          │\n          ▼\n     CloudFront (solo en live)\n```\n\nLa aplicación de escritorio solicita **URLs firmadas** al backend. Luego sube y descarga archivos **directamente desde S3**, evitando que Lambda procese los archivos y reduciendo costos y latencia.\n\n## Comandos rápidos\n\nComandos más comunes durante desarrollo:\n\n```bash\n# instalar dependencias\nbun install\n\n# ejecutar API local\nbun run dev\n\n# desplegar entorno de desarrollo\nbun run deploy:dev\n\n# desplegar entorno de producción\nbun run deploy:live\n\n# ejecutar app de escritorio\nbun run desktop\n```\n\n## Troubleshooting\n\n### Error de autenticación en la API\n\nVerifica que el header `x-api-key` coincida con la variable `API_KEY` configurada en el backend.\n\n### Archivos no aparecen en S3\n\nComprueba:\n\n- que `BUCKET_NAME` esté configurado correctamente\n- que el usuario tenga permisos `s3:PutObject`\n- que el `userId` usado por el cliente sea correcto\n\n### Fallos en subida multipart\n\nSi una subida grande falla:\n\n- revisa el archivo `sync-debug.log`\n- cancela la subida en la app\n- vuelve a iniciar el proceso\n\n### Problemas con CloudFront\n\nSi los archivos no se descargan correctamente en `live`:\n\n- espera unos minutos a que la distribución se propague\n- invalida la cache de CloudFront si cambiaste objetos existentes\n\n---\n\n## Licencia\n\nEste proyecto está licenciado bajo. [**MIT**](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstivenjs%2Fsavecloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstivenjs%2Fsavecloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstivenjs%2Fsavecloud/lists"}