diff --git a/build_system.d b/build_system.d index fddc227..80344a8 100755 --- a/build_system.d +++ b/build_system.d @@ -13,19 +13,21 @@ module build_system; import dsh; const DIST = "./src/main/resources/app"; +const DIST_ORIGIN = "./quasar-app/dist/spa"; +const APP_BUILD = "quasar build -m spa"; void main(string[] args) { print("Building RailSignalAPI"); - chdir("railsignal-app"); + chdir("quasar-app"); print("Building app..."); - runOrQuit("npm run build"); - print("Copying dist to %s", DIST); + runOrQuit(APP_BUILD); + print("Copying dist from %s to %s", DIST_ORIGIN, DIST); chdir(".."); removeIfExists(DIST); mkdir(DIST); - copyDir("railsignal-app/dist", DIST); + copyDir(DIST_ORIGIN, DIST); print("Building API..."); - runOrQuit("mvn clean package spring-boot:repackage"); + runOrQuit("mvn clean package spring-boot:repackage -DskipTests=true"); print("Build complete!"); if (args.length > 1 && args[1] == "run") { diff --git a/quasar-app/quasar.config.js b/quasar-app/quasar.config.js index 8078efb..fa10ccb 100644 --- a/quasar-app/quasar.config.js +++ b/quasar-app/quasar.config.js @@ -68,7 +68,7 @@ module.exports = configure(function (ctx) { // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup - // publicPath: '/', + publicPath: "/app/", // analyze: true, env: { API_URL: ctx.dev ? "http://localhost:8080/api" : "http://localhost:8080/api", @@ -76,7 +76,7 @@ module.exports = configure(function (ctx) { }, // rawDefine: {} // ignorePublicFolder: true, - // minify: false, + // minify: "hidden", // polyfillModulePreload: true, // distDir diff --git a/railsignal-app/vite.config.js b/railsignal-app/vite.config.js index 116273f..802da0a 100644 --- a/railsignal-app/vite.config.js +++ b/railsignal-app/vite.config.js @@ -4,11 +4,14 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [vue()], - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)) - } +export default defineConfig(({command, mode}) => { + return { + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + }, + base: mode === "production" ? "/app/" : undefined } }) diff --git a/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java b/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java index 92afd4d..70ccff2 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java @@ -1,19 +1,18 @@ package nl.andrewl.railsignalapi.model; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; +import javax.persistence.*; /** * Represents a closed system that contains a collection of components. */ @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class RailSystem { @Id @GeneratedValue @@ -25,6 +24,11 @@ public class RailSystem { @Column(nullable = false, unique = true) private String name; + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) + @PrimaryKeyJoinColumn + @Setter + private RailSystemSettings settings; + public RailSystem(String name) { this.name = name; } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/RailSystemSettings.java b/src/main/java/nl/andrewl/railsignalapi/model/RailSystemSettings.java new file mode 100644 index 0000000..918eb4d --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/RailSystemSettings.java @@ -0,0 +1,48 @@ +package nl.andrewl.railsignalapi.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +/** + * A single entity that's paired with each rail system, and contains some + * settings that apply globally to the rail system. + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class RailSystemSettings { + @Id + @Column(name = "rail_system_id", unique = true, nullable = false) + private Long id; + + @MapsId + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "rail_system_id") + private RailSystem railSystem; + + /** + * Tells whether the rail system is read-only, meaning it cannot be changed + * by any means. + */ + @Column(nullable = false) @Setter + private boolean readOnly; + + /** + * Whether it is required for users to be authenticated in order to access + * this rail system. If true, the system will check that users are + * authenticated prior to any access or modification of the system. + */ + @Column(nullable = false) @Setter + private boolean authenticationRequired; + + public RailSystemSettings(RailSystem rs) { + this.id = rs.getId(); + this.railSystem = rs; + this.readOnly = false; + this.authenticationRequired = false; + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemSettingsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemSettingsApiController.java new file mode 100644 index 0000000..4cde2f8 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemSettingsApiController.java @@ -0,0 +1,25 @@ +package nl.andrewl.railsignalapi.rest; + +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.rest.dto.RailSystemSettingsPayload; +import nl.andrewl.railsignalapi.service.RailSystemSettingsService; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping(path = "/api/rs/{rsId}/settings") +@RequiredArgsConstructor +public class RailSystemSettingsApiController { + private final RailSystemSettingsService settingsService; + + @GetMapping + public RailSystemSettingsPayload getSettings(@PathVariable long rsId) { + return settingsService.getSettings(rsId); + } + + @PostMapping + public RailSystemSettingsPayload updateSettings(@PathVariable long rsId, @RequestBody @Valid RailSystemSettingsPayload payload) { + return settingsService.setSettings(rsId, payload); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/RailSystemSettingsPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/RailSystemSettingsPayload.java new file mode 100644 index 0000000..3921bbc --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/RailSystemSettingsPayload.java @@ -0,0 +1,20 @@ +package nl.andrewl.railsignalapi.rest.dto; + +import nl.andrewl.railsignalapi.model.RailSystemSettings; + +import javax.validation.constraints.NotNull; + +public record RailSystemSettingsPayload( + @NotNull + boolean readOnly, + + @NotNull + boolean authenticationRequired +) { + public RailSystemSettingsPayload(RailSystemSettings settings) { + this( + settings.isReadOnly(), + settings.isAuthenticationRequired() + ); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/RailSystemSettingsService.java b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemSettingsService.java new file mode 100644 index 0000000..22aa423 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemSettingsService.java @@ -0,0 +1,43 @@ +package nl.andrewl.railsignalapi.service; + +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.dao.RailSystemRepository; +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.RailSystemSettings; +import nl.andrewl.railsignalapi.rest.dto.RailSystemSettingsPayload; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +@Service +@RequiredArgsConstructor +public class RailSystemSettingsService { + private final RailSystemRepository railSystemRepository; + + @Transactional + public RailSystemSettingsPayload getSettings(long rsId) { + var rs = railSystemRepository.findById(rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + return new RailSystemSettingsPayload(getOrCreateSettings(rs)); + } + + @Transactional + public RailSystemSettingsPayload setSettings(long rsId, RailSystemSettingsPayload payload) { + var rs = railSystemRepository.findById(rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + RailSystemSettings settings = getOrCreateSettings(rs); + settings.setReadOnly(payload.readOnly()); + settings.setAuthenticationRequired(payload.authenticationRequired()); + railSystemRepository.save(rs); + return new RailSystemSettingsPayload(settings); + } + + private RailSystemSettings getOrCreateSettings(RailSystem rs) { + if (rs.getSettings() == null) { + rs.setSettings(new RailSystemSettings(rs)); + railSystemRepository.save(rs); + } + return rs.getSettings(); + } +}