- Java Spring Boot 3.2 service that backs the Imgfloat livestream overlays, admin UI, and dashboard.
- Uses Spring MVC controllers, WebSocket/STOMP broadcasts (
SimpMessagingTemplate), OAuth2/Twitch authentication, and scheduled/async services (@EnableScheduling,@EnableAsyncinImgfloatApplication). - Media-heavy assets are stored on disk (
assets/,previews/), processed withffmpeg/ffprobe, and surfaced throughstatic/js/ Thymeleaf templates undersrc/main/resources.
src/main/java/dev/kruhlmann/imgfloat/config: Spring configuration beans (upload limits, security overrides, asset storage wiring).controller: REST + view controllers keep endpoints thin and delegate work to services and repositories.service: business logic (asset management, marketplace seeds, git info, Twitch emote syncing, audit logging, cleanup, media optimization, etc.).service.mediacontains ffmpeg helpers and media detection.repository: Spring Data JPA interfaces for the Imgfloat DB (Asset,Channel,ScriptAsset, etc.) and a separate audit DB underrepository.audit.model:dbpackages mirror schema entities;api.request/responsehold DTOs used by controllers.templates: Thymeleaf views (admin.html,broadcast.html,dashboard.html, etc.).static: frontend JavaScript modules (static/js/admin,static/js/broadcast/*,static/js/customAssets.js, etc.), CSS, icons, and broadcast worker scripts. Keep new assets next to the feature they support.src/main/resources/db/migrationanddb/audit: Flyway migration scripts that must stay in sync with the SQLite schemas referenced byIMGFLOAT_DB_PATHandIMGFLOAT_AUDIT_DB_PATH.
-
.envfiles are only loaded when you runmake run; shellingmvn spring-boot:rundirectly requires manually exporting each variable. -
Required environment variables (set them via
.envlocally or through your deployment tooling):Variable Purpose IMGFLOAT_ASSETS_PATHBase filesystem directory for uploaded assets. AssetStorageServicesanitizes broadcaster segments under this path.IMGFLOAT_PREVIEWS_PATHWhere generated previews (PNG thumbnails) go. IMGFLOAT_DB_PATHSQLite file used by the main Flyway-managed schema ( db/migration).IMGFLOAT_AUDIT_DB_PATHSQLite file for audit logs ( db/audit) and session storage.IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMINTwitch login that becomes the initial sysadmin and can manage all channels. IMGFLOAT_GITHUB_CLIENT_OWNER/REPO/VERSIONGitHub release coordinates used by GithubReleaseServiceto build download links for the client bundle.IMGFLOAT_TOKEN_ENCRYPTION_KEYBase64/Base64URL-encoded 32-byte key used by Spring Security to encrypt OAuth tokens at rest. SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE/MAX_REQUEST_SIZEControl upload limits (defaults to 10MB in Makefile, also read byUploadLimitsConfigwhich exposesuploadLimitBytesto controllers/services).TWITCH_CLIENT_ID&TWITCH_CLIENT_SECRETUsed by Spring Security’s OAuth2 client registration in application.yml. -
Optional/operational variables:
Variable Purpose IMGFLOAT_COMMIT_URL_PREFIXEnables the commit chip in the UI ( GitInfoService) when paired withgit-commit-id-plugin.IMGFLOAT_DOCS_URLBase URL for documentation links rendered in templates. IMGFLOAT_IS_STAGINGSet to 1to surface a staging banner across non-broadcast pages. Defaults to0.IMGFLOAT_MARKETPLACE_SCRIPTS_PATHDirectory containing marketplace seed directories ( MarketplaceScriptSeedLoaderreads metadata, source.js, logo, attachments).doc/marketplace-scriptsholds examples.IMGFLOAT_SYSADMIN_CHANNEL_ACCESS_ENABLEDToggle to let sysadmins manage every channel without being added as an admin. TWITCH_REDIRECT_URIOverrides the default redirect URI for OAuth ( {baseUrl}/login/oauth2/code/twitch).IMGFLOAT_TOKEN_ENCRYPTION_PREVIOUS_KEYSComma-delimited base64 keys to decrypt tokens created before rotating IMGFLOAT_TOKEN_ENCRYPTION_KEY. -
AssetStorageServicewill fall back to system temp dirs (java.io.tmpdir/imgfloat-assets&-previews) ifIMGFLOAT_ASSETS_PATH/PREVIEWS_PATHare unset, but storing data on persistent storage is required for production. -
UploadLimitsConfigdefaults to 50MB/100MB for tests if the environment properties are absent; production should honor thespring.servlet.multipart.*values you expose.
make build/mvn compile: compile the server.make run: sources.env(if present) and runsmvn spring-boot:run. Use this for local dev; hot reload works viaspring-devtools.make watch: compiles continuously usingentr(sleep loop watchessrc/main). Requiresentrinstalled and you must refresh the browser manually after the compile succeeds.make test/mvn test: run the JUnit 5 + Mockito suite (including integration tests that spin up the Spring context). Run this before pushing changes that touch controllers, services, or persistence logic.make package/mvn clean package: clean build that packages the runnable JAR.springdoc-openapiis on the classpath; the automated Swagger UI can be helpful for understanding the available REST endpoints.
- The main SQLite DB (
IMGFLOAT_DB_PATH) holds channel, user, asset, marketplace, and settings tables. Flyway (configured inapplication.yml) runs migrations fromclasspath:db/migration. Don’t bypass these migration scripts when updating schemas. - Audit logs live in the separate
IMGFLOAT_AUDIT_DB_PATHwith migrations undersrc/main/resources/db/audit. The audit DB also stores Spring Session tables;spring.session.jdbc.initialize-schemais set tonever, so the Flyway scripts must include the session table definitions. AuditLogServicesanitizes log entries before persisting them and is used by controllers such asAuditLogApiControllerandAccountService.
ChannelDirectoryServicehandles uploads. It enforcesuploadLimitBytes(derived fromspring.servlet.multipart.max-file-size), normalizes broadcaster names, and records audit events viaAuditLogService.- Uploaded assets go through
MediaDetectionService,MediaOptimizationService, andAssetStorageServicewhich writes toIMGFLOAT_ASSETS_PATH. Previews (PNGs) are written toIMGFLOAT_PREVIEWS_PATH. AssetStorageServicesanitizes broadcaster segments (sanitizeUserSegment) to avoid traversal and auto-creates directories. When deleting,AssetCleanupServiceruns asynchronously after startup to remove orphan files that are not referenced by any DB record.- Media processing uses
ffmpeg/ffprobeviaFfmpegService. Make sure those binaries are available on the system; otherwise video/preview transcodes and APNG → GIF conversions will fail with warnings logged fromFfmpegService.
- Marketplace seeds live under the path configured by
IMGFLOAT_MARKETPLACE_SCRIPTS_PATH. Each subfolder is a listing identifier and must containmetadata.jsonandsource.js. Optional files:logo.png(renders as the marketplace badge) andattachments/for extra files (filenames must be unique per script). metadata.jsoncombinesname, optionaldescription, optionalbroadcaster, and optionalallowedDomains. Allowed domains control thefetchsandbox in the broadcast worker and are normalized viaChannelDirectoryService.normalizeAllowedDomains. Valid entries are hostname or hostname+port (regex^[a-z0-9.-]+(?::[0-9]{1,5})?$), de-duplicated, lower-cased, and capped at 32 entries per script.- Seeds are cached at startup;
MarketplaceScriptSeedLoaderexposeslistEntriesForQueryandfindByIdso the frontend can show the marketplace catalog without hitting Twitch/SevenTV endpoints. - Example scripts are shipped under
doc/marketplace-scripts/.
- Spring Security OAuth2 login is configured in
application.ymlunderspring.security.oauth2.client. Twitch is the only configured provider; scopes includeuser:read:emailandmoderation:read. - OAuth tokens are encrypted using
IMGFLOAT_TOKEN_ENCRYPTION_KEY; rotate keys by settingIMGFLOAT_TOKEN_ENCRYPTION_PREVIOUS_KEYSto a comma list of old keys (oldest last) before swapping in a newIMGFLOAT_TOKEN_ENCRYPTION_KEY. IMGFLOAT_INITIAL_TWITCH_USERNAME_SYSADMINseeds the first sysadmin; subsequent sysadmins can be managed via the UI or API. To allow sysadmins to hop between channels without being channel admins, setIMGFLOAT_SYSADMIN_CHANNEL_ACCESS_ENABLED=true.GitInfoServicewill expose a commit link ifIMGFLOAT_COMMIT_URL_PREFIXis configured andgit.propertiesis present (generated by thegit-commit-id-plugin), otherwise it falls back to runninggitdirectly.GithubReleaseServicefails fast whenIMGFLOAT_GITHUB_CLIENT_OWNER,IMGFLOAT_GITHUB_CLIENT_REPO, orIMGFLOAT_GITHUB_CLIENT_VERSIONare missing; these powers the download link in the UI.
- Broadcast client code lives in
src/main/resources/static/js/broadcast/(renderer, workers, runtime helpers). When editing overlay scripts, keep worker changes inscript-worker.jsand keep main page logic inrenderer.js. - Admin/dashboard JS modules (
customAssets.js,settings.js, etc.) are plain ES modules bundled through Thymeleaf templates, so keep related CSS/HTML understatic/cssandtemplates. - Templates render dynamic data via controllers such as
ViewController, which also injectsuploadLimitBytes, version info (VersionService), and feature flags (staging banner, docs URL, commit chip wrapped inGitInfoService/GithubReleaseServicevalues).
- Tests are under
src/test/java/dev/kruhlmann/imgfloat. Controller integration tests (e.g.,ChannelApiIntegrationTest) boot up the application context—mock external services carefully or rely on the in-memory SQLite test DB created from the Flyway scripts. - Service unit tests (e.g.,
ChannelDirectoryServiceTest) cover asset normalization, allowed domain semantics, and marketplace seed behavior. Add targeted tests for new service logic, API validations, or asset handling (uploads, attachments, downloads). - Run
make testbefore shipping changes; it also generates JaCoCo reports via the Maven plugin.
- Java code uses 4-space indentation,
UpperCamelCasefor types,lowerCamelCasefor fields/methods, andUPPER_SNAKE_CASEfor constants. DTOs often userecords when immutability is feasible (ScriptMarketplaceEntry,MarketplaceScriptSeedLoader.SeedScript, etc.). - Prefer constructor injection for Spring components. Controllers should remain thin and delegate to service-layer beans; services orchestrate repositories, asset storage, storage cleanup, Git/Twitch helpers, and messaging (websockets/events).
- Keep assets close to their feature (e.g.,
static/js/broadcast/for overlay code,templates/for the Thymeleaf view referencing that script). - Avoid path traversal by relying on existing sanitizers (
AssetStorageService.sanitizeUserSegment). When adding new file paths, reusesafeJoin/sanitizeUserSegmentpatterns. - When syncing marketplace favorites or script attachments, obey
ChannelDirectoryService.loadScriptAttachmentssemantics—attachments are stored inscript_asset_attachmentrecords and are cleaned up when orphaned.
AssetCleanupServiceruns asynchronously once per startup (afterApplicationReadyEvent) and purges disk files whose IDs are not referenced by any DB row; do not rely on manual cleanup unless debugging.VersionServicelooks forMETA-INF/maven/dev.kruhlmann/imgfloat/pom.propertiesfirst, falls back to parsingpom.xml(useful when running from source), and throws if no version is available—keep yourpom.versionaccurate for release builds.EmoteSyncScheduler,SevenTvEmoteService,TwitchEmoteService, andTwitchAppAccessTokenServiceorchestrate Twitch/7TV emote catalogs and should be aware of scheduling constraints if you modify emote ingestion.- Static files, marketplace metadata, and assets are best tested locally by running
make runwith the appropriate env variables and by refreshing the admin/broadcast UI manually.