Changeset e2c3d09 in Klonkt


Ignore:
Timestamp:
07/31/2026 12:33:18 PM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
2dd1dc4
Parents:
859b1707
Message:

Media-submappen volgen nu MEDIA_PATH

De submappen voor avatars, post-images, reply-media, hero en audio-covers hadden
elk hun eigen env-variabele met een fallback naar <app>/storage/media/<sub>.
Daardoor negeerden ze MEDIA_PATH: wie zijn data buiten de checkout zette kreeg
alsnog een storage/-map in de work-tree, en uploads landden naast de code. Een
opruimstap bij een volgende deploy kan die vervolgens weggooien.

Nu leiden ze allemaal af van MEDIA_PATH via een gedeelde helper. Een eigen
override per submap wint nog steeds, dus bestaande installaties merken niets.
Dit maakt de scheiding van gebruikersdata en programmadata mogelijk met drie
regels in .env in plaats van acht.

Changed files:
src/routes/account.js

  • AVATAR_DIR via mediaDir(); dode dirname en fileURLToPath-import weg

src/routes/posts.js

  • POST_IMAGES_DIR en REPLY_MEDIA_DIR via mediaDir(); dode declaraties weg

src/routes/admin-media.js

  • POST_IMAGES_DIR en REPLY_MEDIA_DIR via mediaDir(); dode declaraties weg

src/routes/admin-settings.js

  • HERO_DIR via mediaDir(); dode declaraties weg

src/routes/admin-playlists.js

  • COVER_DIR via mediaDir(); dode dirname weg

src/routes/admin-audio.js

  • COVER_DIR via mediaDir(); AUDIO_DIR ongewijzigd (eigen wortel)

src/routes/admin-sites.js

  • PHOTO_DIR via mediaDir(), deelt bewust de avatars-map

src/routes/activitypub.js

  • AP_MEDIA_DIR via mediaDir(); ongebruikte fileURLToPath-import weg

New file:
src/config/paths.js

  • MEDIA_ROOT afgeleid van MEDIA_PATH
  • mediaDir(envVar, sub) voor submappen, met behoud van per-map overrides

DATABASE_PATH en AUDIO_PATH zijn eigen wortels en bewust ongemoeid gelaten.
Geverifieerd: 356 tests groen, en een server met externe MEDIA_PATH maakt al
zijn mappen buiten de checkout aan zonder de work-tree te raken.

-robo
Co-Authored-By: Claude Opus 5 <noreply@…>

Location:
src
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • src/routes/account.js

    r859b1707 re2c3d09  
    1616import path from 'path';
    1717import fs from 'fs';
    18 import { fileURLToPath } from 'url';
    1918import bcrypt from 'bcryptjs';
    2019import multer from 'multer';
     
    2827import { toWebp } from '../services/ImageWebpService.js';
    2928import { SUPPORTED } from '../services/i18n.js';
    30 
    31 const __dirname = path.dirname(fileURLToPath(import.meta.url));
    32 const AVATAR_DIR = path.resolve(
    33   process.env.AVATAR_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'avatars')
    34 );
     29import { mediaDir } from '../config/paths.js';
     30
     31const AVATAR_DIR = mediaDir('AVATAR_PATH', 'avatars');
    3532fs.mkdirSync(AVATAR_DIR, { recursive: true });
    3633
  • src/routes/activitypub.js

    r859b1707 re2c3d09  
    2323import path from 'path';
    2424import fs from 'fs';
    25 import { fileURLToPath } from 'url';
    2625import { randomUUID } from 'crypto';
     26import { mediaDir } from '../config/paths.js';
    2727
    2828const router = express.Router();
     
    422422// AP convention) into the same store the reply editor uses, and gets back
    423423// { url, mediaType, name } to attach on a note (e.g. the help-buoy capture).
    424 const AP_MEDIA_DIR = path.resolve(
    425   process.env.REPLY_MEDIA_PATH ||
    426   path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'storage', 'media', 'reply-media')
    427 );
     424const AP_MEDIA_DIR = mediaDir('REPLY_MEDIA_PATH', 'reply-media');
    428425fs.mkdirSync(AP_MEDIA_DIR, { recursive: true });
    429426const AP_MEDIA_EXT = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif', '.mp3', '.m4a', '.ogg', '.opus', '.flac', '.wav', '.mp4', '.webm', '.mov']);
  • src/routes/admin-audio.js

    r859b1707 re2c3d09  
    2222import { transcodeToMp3, retagMp3 } from '../services/AudioTranscoder.js';
    2323import { audioUrl } from '../services/AudioStreamService.js';
     24import { mediaDir } from '../config/paths.js';
    2425
    2526const __dirname = path.dirname(fileURLToPath(import.meta.url));
     
    3031  process.env.AUDIO_PATH || path.join(__dirname, '..', '..', 'storage', 'audio')
    3132);
    32 const COVER_DIR = path.resolve(
    33   process.env.COVER_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'audio-covers')
    34 );
     33const COVER_DIR = mediaDir('COVER_PATH', 'audio-covers');
    3534fs.mkdirSync(AUDIO_DIR, { recursive: true });
    3635fs.mkdirSync(COVER_DIR, { recursive: true });
  • src/routes/admin-media.js

    r859b1707 re2c3d09  
    1010import path from 'path';
    1111import fs from 'fs';
    12 import { fileURLToPath } from 'url';
    1312import db from '../config/database.js';
    1413import { renderPage } from '../middleware/render.js';
    1514import { requireGod } from '../middleware/auth.js';
    1615import { audioEnabled } from '../config/features.js';
     16import { mediaDir } from '../config/paths.js';
    1717
    18 const __dirname = path.dirname(fileURLToPath(import.meta.url));
    19 const POST_IMAGES_DIR = path.resolve(
    20   process.env.POST_IMAGES_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'post-images')
    21 );
     18const POST_IMAGES_DIR = mediaDir('POST_IMAGES_PATH', 'post-images');
    2219
    2320const router = express.Router();
     
    2724// C2S uploads (Shaer's composer and the help buoy) land here; the videos among
    2825// them are what the Video tab shows.
    29 const REPLY_MEDIA_DIR = path.resolve(
    30   process.env.REPLY_MEDIA_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'reply-media')
    31 );
     26const REPLY_MEDIA_DIR = mediaDir('REPLY_MEDIA_PATH', 'reply-media');
    3227const isSibling = (f) => /-v\.(mp4|jpg)$/i.test(f); // an animated cover's video/poster sibling
    3328
  • src/routes/admin-playlists.js

    r859b1707 re2c3d09  
    2323import { requireGod } from '../middleware/auth.js';
    2424import PlaylistService from '../services/PlaylistService.js';
     25import { mediaDir } from '../config/paths.js';
    2526
    2627// Cover storage — same convention as track covers so a single physical
    2728// directory holds all album/track artwork. Existing covers in the DB
    2829// already point at /media/audio-covers/<filename> so we reuse the path.
    29 const __dirname = path.dirname(new URL(import.meta.url).pathname);
    30 const COVER_DIR = path.resolve(
    31   process.env.COVER_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'audio-covers')
    32 );
     30const COVER_DIR = mediaDir('COVER_PATH', 'audio-covers');
    3331fs.mkdirSync(COVER_DIR, { recursive: true });
    3432
  • src/routes/admin-settings.js

    r859b1707 re2c3d09  
    1616import path from 'path';
    1717import fs from 'fs';
    18 import { fileURLToPath } from 'url';
    1918import multer from 'multer';
    2019import { v4 as uuid } from 'uuid';
     
    2625import { entitlementStatus, premiumUnlocked } from '../services/PatreonService.js';
    2726import { toWebp } from '../services/ImageWebpService.js';
     27import { mediaDir } from '../config/paths.js';
    2828
    2929const router = express.Router();
     
    3636}
    3737
    38 const __dirname = path.dirname(fileURLToPath(import.meta.url));
    3938// Hero uploads land in storage/media/hero → accessible as /media/hero/<file>
    4039// (the /media static handler serves storage/media). Same model as avatars.
    41 const HERO_DIR = path.resolve(
    42   process.env.HERO_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'hero')
    43 );
     40const HERO_DIR = mediaDir('HERO_PATH', 'hero');
    4441fs.mkdirSync(HERO_DIR, { recursive: true });
    4542
  • src/routes/admin-sites.js

    r859b1707 re2c3d09  
    1717import path from 'path';
    1818import fs from 'fs';
    19 import { fileURLToPath } from 'url';
    2019import multer from 'multer';
    2120import { v4 as uuid } from 'uuid';
     
    2625import { listPlatforms, PLATFORMS } from '../services/PlatformIcons.js';
    2726import { toWebp } from '../services/ImageWebpService.js';
    28 
    29 const __dirname = path.dirname(fileURLToPath(import.meta.url));
     27import { mediaDir } from '../config/paths.js';
     28
    3029
    3130// Profile photos share the avatar directory with user avatars — same physical
    3231// folder, same URL prefix. Filenames are uuid-prefixed so site photos and
    3332// user avatars never collide.
    34 const PHOTO_DIR = path.resolve(
    35   process.env.AVATAR_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'avatars')
    36 );
     33const PHOTO_DIR = mediaDir('AVATAR_PATH', 'avatars');
    3734fs.mkdirSync(PHOTO_DIR, { recursive: true });
    3835
  • src/routes/posts.js

    r859b1707 re2c3d09  
    33import path from 'path';
    44import fs from 'fs';
    5 import { fileURLToPath } from 'url';
    65import multer from 'multer';
    76import ejs from 'ejs';
     
    2524import { verifyBlob } from '../services/CryptoBox.js';
    2625import MusicMeta from '../services/MusicMeta.js';
    27 
    28 const __dirname = path.dirname(fileURLToPath(import.meta.url));
    29 const POST_IMAGES_DIR = path.resolve(
    30   process.env.POST_IMAGES_PATH ||
    31   path.join(__dirname, '..', '..', 'storage', 'media', 'post-images')
    32 );
     26import { mediaDir } from '../config/paths.js';
     27
     28const POST_IMAGES_DIR = mediaDir('POST_IMAGES_PATH', 'post-images');
    3329fs.mkdirSync(POST_IMAGES_DIR, { recursive: true });
    3430
     
    3834// Rich replies: media dropped/pasted into the reply editor. Images, audio and
    3935// video, stored as-is (no transcode; a reply attachment is not a track).
    40 const REPLY_MEDIA_DIR = path.resolve(
    41   process.env.REPLY_MEDIA_PATH ||
    42   path.join(__dirname, '..', '..', 'storage', 'media', 'reply-media')
    43 );
     36const REPLY_MEDIA_DIR = mediaDir('REPLY_MEDIA_PATH', 'reply-media');
    4437fs.mkdirSync(REPLY_MEDIA_DIR, { recursive: true });
    4538const ALLOWED_REPLY_MEDIA_EXT = new Set([
Note: See TracChangeset for help on using the changeset viewer.