mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Configurable filesystem paths
This commit is contained in:
26
README.md
26
README.md
@@ -50,6 +50,32 @@ make docker-build
|
|||||||
TWITCH_CLIENT_ID=your_id TWITCH_CLIENT_SECRET=your_secret docker run -p 8080:8080 imgfloat:latest
|
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
|
### 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`).
|
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`).
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.jcodec.common.model.Picture;
|
|||||||
import org.jcodec.scale.AWTUtil;
|
import org.jcodec.scale.AWTUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -57,19 +58,23 @@ import org.w3c.dom.NodeList;
|
|||||||
public class ChannelDirectoryService {
|
public class ChannelDirectoryService {
|
||||||
private static final int MIN_GIF_DELAY_MS = 20;
|
private static final int MIN_GIF_DELAY_MS = 20;
|
||||||
private static final String PREVIEW_MEDIA_TYPE = "image/png";
|
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 static final Logger logger = LoggerFactory.getLogger(ChannelDirectoryService.class);
|
||||||
private final ChannelRepository channelRepository;
|
private final ChannelRepository channelRepository;
|
||||||
private final AssetRepository assetRepository;
|
private final AssetRepository assetRepository;
|
||||||
private final SimpMessagingTemplate messagingTemplate;
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
private final Path assetRoot;
|
||||||
|
private final Path previewRoot;
|
||||||
|
|
||||||
public ChannelDirectoryService(ChannelRepository channelRepository,
|
public ChannelDirectoryService(ChannelRepository channelRepository,
|
||||||
AssetRepository assetRepository,
|
AssetRepository assetRepository,
|
||||||
SimpMessagingTemplate messagingTemplate) {
|
SimpMessagingTemplate messagingTemplate,
|
||||||
|
@Value("${IMGFLOAT_ASSETS_PATH:assets}") String assetRoot,
|
||||||
|
@Value("${IMGFLOAT_PREVIEWS_PATH:previews}") String previewRoot) {
|
||||||
this.channelRepository = channelRepository;
|
this.channelRepository = channelRepository;
|
||||||
this.assetRepository = assetRepository;
|
this.assetRepository = assetRepository;
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
|
this.assetRoot = Paths.get(assetRoot);
|
||||||
|
this.previewRoot = Paths.get(previewRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Channel getOrCreateChannel(String broadcaster) {
|
public Channel getOrCreateChannel(String broadcaster) {
|
||||||
@@ -408,7 +413,7 @@ public class ChannelDirectoryService {
|
|||||||
if (assetBytes == null || assetBytes.length == 0) {
|
if (assetBytes == null || assetBytes.length == 0) {
|
||||||
throw new IOException("Asset content is empty");
|
throw new IOException("Asset content is empty");
|
||||||
}
|
}
|
||||||
Path directory = ASSET_ROOT.resolve(normalize(broadcaster));
|
Path directory = assetRoot.resolve(normalize(broadcaster));
|
||||||
Files.createDirectories(directory);
|
Files.createDirectories(directory);
|
||||||
String extension = extensionForMediaType(mediaType);
|
String extension = extensionForMediaType(mediaType);
|
||||||
Path assetFile = directory.resolve(assetId + extension);
|
Path assetFile = directory.resolve(assetId + extension);
|
||||||
@@ -460,7 +465,7 @@ public class ChannelDirectoryService {
|
|||||||
if (previewBytes == null || previewBytes.length == 0) {
|
if (previewBytes == null || previewBytes.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Path directory = PREVIEW_ROOT.resolve(normalize(broadcaster));
|
Path directory = previewRoot.resolve(normalize(broadcaster));
|
||||||
Files.createDirectories(directory);
|
Files.createDirectories(directory);
|
||||||
Path previewFile = directory.resolve(assetId + ".png");
|
Path previewFile = directory.resolve(assetId + ".png");
|
||||||
Files.write(previewFile, previewBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
Files.write(previewFile, previewBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spring:
|
|||||||
thymeleaf:
|
thymeleaf:
|
||||||
cache: false
|
cache: false
|
||||||
datasource:
|
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
|
driver-class-name: org.sqlite.JDBC
|
||||||
hikari:
|
hikari:
|
||||||
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"
|
connection-init-sql: "PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;"
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import org.springframework.mock.web.MockMultipartFile;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -40,12 +42,15 @@ class ChannelDirectoryServiceTest {
|
|||||||
private AssetRepository assetRepository;
|
private AssetRepository assetRepository;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() throws Exception {
|
||||||
messagingTemplate = mock(SimpMessagingTemplate.class);
|
messagingTemplate = mock(SimpMessagingTemplate.class);
|
||||||
channelRepository = mock(ChannelRepository.class);
|
channelRepository = mock(ChannelRepository.class);
|
||||||
assetRepository = mock(AssetRepository.class);
|
assetRepository = mock(AssetRepository.class);
|
||||||
setupInMemoryPersistence();
|
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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user