From dc2b2027973b8f19dca060f5d762f6c535d6175d Mon Sep 17 00:00:00 2001 From: Noro Studio Date: Tue, 30 Jun 2026 21:02:21 +0330 Subject: [PATCH] Update API media endpoints and deploy scripts --- api/.env.example | 4 ++-- api/src/index.js | 33 +++++++++++++++++++++++++++-- deploy/clone-repos.sh | 27 ++++++++++++++++++++++++ deploy/sync-from-server.ps1 | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 deploy/clone-repos.sh create mode 100644 deploy/sync-from-server.ps1 diff --git a/api/.env.example b/api/.env.example index 2265462..1fd639c 100644 --- a/api/.env.example +++ b/api/.env.example @@ -1,7 +1,7 @@ PORT=4000 NODE_ENV=production JWT_SECRET=change-this-to-a-long-random-secret -ADMIN_EMAIL=admin@studionoro.ir -ADMIN_PASSWORD=change-this-strong-password +ADMIN_EMAIL=mohammadbanilohi7@gmail.com +ADMIN_PASSWORD=admin123 API_PUBLIC_URL=https://api.studionoro.ir CORS_ORIGIN=https://studionoro.ir,https://www.studionoro.ir,https://admin.studionoro.ir diff --git a/api/src/index.js b/api/src/index.js index 3b3b448..519177b 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -2,8 +2,8 @@ import 'dotenv/config' import express from 'express' import cors from 'cors' import multer from 'multer' -import { existsSync, mkdirSync } from 'node:fs' -import { dirname, extname, join } from 'node:path' +import { existsSync, mkdirSync, unlinkSync } from 'node:fs' +import { basename, dirname, extname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { randomUUID } from 'node:crypto' import { authMiddleware, loginHandler, meHandler } from './auth.js' @@ -234,6 +234,35 @@ app.post('/api/media/upload', authMiddleware, upload.single('file'), (req, res) res.status(201).json(item) }) +app.put('/api/media/:id', authMiddleware, (req, res) => { + const { filename } = req.body || {} + if (!filename?.trim()) return res.status(400).json({ error: 'نام فایل الزامی است' }) + + let updated = null + updateDb((db) => { + const i = db.media.findIndex((m) => m.id === req.params.id) + if (i === -1) return + db.media[i] = { ...db.media[i], filename: filename.trim() } + updated = db.media[i] + }) + if (!updated) return res.status(404).json({ error: 'فایل یافت نشد' }) + res.json(updated) +}) + +app.delete('/api/media/:id', authMiddleware, (req, res) => { + let removed = null + updateDb((db) => { + const i = db.media.findIndex((m) => m.id === req.params.id) + if (i === -1) return + removed = db.media.splice(i, 1)[0] + }) + if (!removed) return res.status(404).json({ error: 'فایل یافت نشد' }) + + const filePath = join(UPLOADS_DIR, basename(removed.url)) + if (existsSync(filePath)) unlinkSync(filePath) + res.status(204).end() +}) + app.listen(PORT, '127.0.0.1', () => { console.log(`Studio Noro API running at http://127.0.0.1:${PORT}`) }) diff --git a/deploy/clone-repos.sh b/deploy/clone-repos.sh new file mode 100644 index 0000000..95fc696 --- /dev/null +++ b/deploy/clone-repos.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Clone all Studio Noro repos into /var/www/studionoro +set -euo pipefail + +APP_ROOT="${APP_ROOT:-/var/www/studionoro}" +GIT_BASE="${GIT_BASE:-https://get.feep.ir/noro}" + +mkdir -p "$APP_ROOT" +cd "$APP_ROOT" + +if [ ! -d api ]; then + git clone "${GIT_BASE}/server.git" _server-tmp + mv _server-tmp/api api + mv _server-tmp/deploy deploy + rm -rf _server-tmp +fi + +if [ ! -d nuxt-tailwind-app ]; then + git clone "${GIT_BASE}/site.git" nuxt-tailwind-app +fi + +if [ ! -d admin ]; then + git clone "${GIT_BASE}/admin.git" admin +fi + +echo "Repos ready in $APP_ROOT:" +ls -la "$APP_ROOT" diff --git a/deploy/sync-from-server.ps1 b/deploy/sync-from-server.ps1 new file mode 100644 index 0000000..7f53281 --- /dev/null +++ b/deploy/sync-from-server.ps1 @@ -0,0 +1,41 @@ +# Sync production CMS data to local admin (and optional api folder) +# Usage: .\deploy\sync-from-server.ps1 + +param( + [string]$Server = "root@45.94.215.38", + [string]$RemoteApiPath = "/var/www/studionoro/api", + [string]$ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +) + +$ErrorActionPreference = "Stop" + +$targets = @( + @{ + Name = "admin" + DataDir = Join-Path $ProjectRoot "admin\data" + UploadsDir = Join-Path $ProjectRoot "admin\uploads" + }, + @{ + Name = "api" + DataDir = Join-Path $ProjectRoot "api\data" + UploadsDir = Join-Path $ProjectRoot "api\uploads" + } +) + +Write-Host "==> Sync from $Server`:$RemoteApiPath" -ForegroundColor Cyan +Write-Host "" + +foreach ($target in $targets) { + New-Item -ItemType Directory -Force -Path $target.DataDir | Out-Null + New-Item -ItemType Directory -Force -Path $target.UploadsDir | Out-Null + + Write-Host "-> $($target.Name)/data/content.json" -ForegroundColor Yellow + scp "${Server}:${RemoteApiPath}/data/content.json" (Join-Path $target.DataDir "content.json") + + Write-Host "-> $($target.Name)/uploads/" -ForegroundColor Yellow + scp -r "${Server}:${RemoteApiPath}/uploads/*" "$($target.UploadsDir)/" +} + +Write-Host "" +Write-Host "Done. Restart local admin dev server if it is running:" -ForegroundColor Green +Write-Host " cd admin && npm run dev"