Configurable filesystem paths

This commit is contained in:
2025-12-11 02:09:50 +01:00
parent f21fb1cd13
commit 1ffcec1029
4 changed files with 44 additions and 8 deletions

View File

@@ -50,6 +50,32 @@ make docker-build
TWITCH_CLIENT_ID=your_id TWITCH_CLIENT_SECRET=your_secret docker run -p 8080:8080 imgfloat:latest
```
### Data storage configuration
- `IMGFLOAT_DB_PATH` filesystem location for the SQLite database (default: `imgfloat.db`).
- `IMGFLOAT_ASSETS_PATH` root directory where uploaded assets are persisted (default: `assets`).
- `IMGFLOAT_PREVIEWS_PATH` root directory for generated preview images (default: `previews`).
### Docker Compose example with persistent storage
```yaml
services:
imgfloat:
image: imgfloat:latest
environment:
TWITCH_CLIENT_ID: your_id
TWITCH_CLIENT_SECRET: your_secret
TWITCH_REDIRECT_URI: https://yourdomain/login/oauth2/code/twitch
IMGFLOAT_DB_PATH: /var/lib/imgfloat/imgfloat.db
IMGFLOAT_ASSETS_PATH: /var/lib/imgfloat/assets
IMGFLOAT_PREVIEWS_PATH: /var/lib/imgfloat/previews
ports:
- "8080:8080"
volumes:
- ./data/db:/var/lib/imgfloat
- ./data/assets:/var/lib/imgfloat/assets
- ./data/previews:/var/lib/imgfloat/previews
```
The `volumes` map local folders into the container so database and uploaded files survive container restarts.
### OAuth configuration
Spring Boot reads Twitch credentials from `TWITCH_CLIENT_ID` and `TWITCH_CLIENT_SECRET`. The redirect URI comes from `TWITCH_REDIRECT_URI` (defaulting to `{baseUrl}/login/oauth2/code/twitch`).

View File

@@ -19,6 +19,7 @@ import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -57,19 +58,23 @@ import org.w3c.dom.NodeList;
public class ChannelDirectoryService {
private static final int MIN_GIF_DELAY_MS = 20;
private static final String PREVIEW_MEDIA_TYPE = "image/png";
private static final Path ASSET_ROOT = Paths.get("assets");
private static final Path PREVIEW_ROOT = Paths.get("previews");
private static final Logger logger = LoggerFactory.getLogger(ChannelDirectoryService.class);
private final ChannelRepository channelRepository;
private final AssetRepository assetRepository;
private final SimpMessagingTemplate messagingTemplate;
private final Path assetRoot;
private final Path previewRoot;
public ChannelDirectoryService(ChannelRepository channelRepository,
AssetRepository assetRepository,
SimpMessagingTemplate messagingTemplate) {
SimpMessagingTemplate messagingTemplate,
@Value("${IMGFLOAT_ASSETS_PATH:assets}") String assetRoot,
@Value("${IMGFLOAT_PREVIEWS_PATH:previews}") String previewRoot) {
this.channelRepository = channelRepository;
this.assetRepository = assetRepository;
this.messagingTemplate = messagingTemplate;
this.assetRoot = Paths.get(assetRoot);
this.previewRoot = Paths.get(previewRoot);
}
public Channel getOrCreateChannel(String broadcaster) {
@@ -408,7 +413,7 @@ public class ChannelDirectoryService {
if (assetBytes == null || assetBytes.length == 0) {
throw new IOException("Asset content is empty");
}
Path directory = ASSET_ROOT.resolve(normalize(broadcaster));
Path directory = assetRoot.resolve(normalize(broadcaster));
Files.createDirectories(directory);
String extension = extensionForMediaType(mediaType);
Path assetFile = directory.resolve(assetId + extension);
@@ -460,7 +465,7 @@ public class ChannelDirectoryService {
if (previewBytes == null || previewBytes.length == 0) {
return null;
}
Path directory = PREVIEW_ROOT.resolve(normalize(broadcaster));
Path directory = previewRoot.resolve(normalize(broadcaster));
Files.createDirectories(directory);
Path previewFile = directory.resolve(assetId + ".png");
Files.write(previewFile, previewBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);

View File

@@ -23,7 +23,7 @@ spring:
thymeleaf:
cache: false
datasource:
url: jdbc:sqlite:imgfloat.db?busy_timeout=5000&journal_mode=WAL
url: jdbc:sqlite:${IMGFLOAT_DB_PATH:imgfloat.db}?busy_timeout=5000&journal_mode=WAL
driver-class-name: org.sqlite.JDBC
hikari:
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"

View File

@@ -17,6 +17,8 @@ import org.springframework.mock.web.MockMultipartFile;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -40,12 +42,15 @@ class ChannelDirectoryServiceTest {
private AssetRepository assetRepository;
@BeforeEach
void setup() {
void setup() throws Exception {
messagingTemplate = mock(SimpMessagingTemplate.class);
channelRepository = mock(ChannelRepository.class);
assetRepository = mock(AssetRepository.class);
setupInMemoryPersistence();
service = new ChannelDirectoryService(channelRepository, assetRepository, messagingTemplate);
Path assetRoot = Files.createTempDirectory("imgfloat-assets-test");
Path previewRoot = Files.createTempDirectory("imgfloat-previews-test");
service = new ChannelDirectoryService(channelRepository, assetRepository, messagingTemplate,
assetRoot.toString(), previewRoot.toString());
}
@Test