Update project configuration and database setup

- Added H2 in-memory database support, replacing PostgreSQL for local development.
- Updated application properties to reflect new database configuration.
- Modified Vite build output directory to integrate with Quarkus static resources.
- Enhanced entity classes to use @Lob for character and story descriptions, allowing for larger text storage.
- Updated development workflow documentation to reflect changes in database setup and prerequisites.
This commit is contained in:
Konrad Neitzel 2026-02-25 06:10:55 +01:00
parent 7302cc52a7
commit ee6e1c7cb1
13 changed files with 46 additions and 52 deletions

3
.gitignore vendored
View File

@ -31,5 +31,8 @@ out/
# Generated API client (regenerated by mvn generate-sources) # Generated API client (regenerated by mvn generate-sources)
src/main/web/src/api/generated/ src/main/web/src/api/generated/
# Vite build output (built into Quarkus static resources)
src/main/resources/META-INF/resources/
# OS files # OS files
.DS_Store .DS_Store

View File

@ -4,7 +4,7 @@
- **Backend**: Quarkus 3.31.2 (Java 21, Maven). - **Backend**: Quarkus 3.31.2 (Java 21, Maven).
- **Frontend**: React 18 + TypeScript + Vite in `src/main/web`. - **Frontend**: React 18 + TypeScript + Vite in `src/main/web`.
- **Database**: PostgreSQL with Liquibase migrations. - **Database**: H2 in-memory with Liquibase migrations (data resets on restart).
- **Auth**: SmallRye JWT with RSA key signing; BCrypt password hashing. - **Auth**: SmallRye JWT with RSA key signing; BCrypt password hashing.
- **API Contract**: OpenAPI 3.0.3 at `src/main/resources/openapi/story-teller-api.yaml`. - **API Contract**: OpenAPI 3.0.3 at `src/main/resources/openapi/story-teller-api.yaml`.
- **Generated Code**: - **Generated Code**:
@ -13,13 +13,8 @@
### Local Setup ### Local Setup
1. **Prerequisites**: JDK 21, Maven 3.9+, PostgreSQL, Node.js 22 (managed by frontend-maven-plugin). 1. **Prerequisites**: JDK 21, Maven 3.9+, Node.js 22 (managed by frontend-maven-plugin).
2. **Database**: Create a PostgreSQL database: 2. **Database**: H2 in-memory — no external DB setup required. Liquibase creates the schema and seeds the admin user automatically on each startup.
```sql
CREATE DATABASE storyteller;
CREATE USER storyteller WITH PASSWORD 'storyteller';
GRANT ALL PRIVILEGES ON DATABASE storyteller TO storyteller;
```
3. **Build everything**: `mvn package` (generates API code, compiles Java, builds frontend, runs tests). 3. **Build everything**: `mvn package` (generates API code, compiles Java, builds frontend, runs tests).
4. **Run in dev mode**: `mvn quarkus:dev` (hot reload, frontend proxy at http://localhost:5173). 4. **Run in dev mode**: `mvn quarkus:dev` (hot reload, frontend proxy at http://localhost:5173).
5. **Frontend dev**: `cd src/main/web && npm run dev` (standalone Vite dev server, proxies `/api` to Quarkus). 5. **Frontend dev**: `cd src/main/web && npm run dev` (standalone Vite dev server, proxies `/api` to Quarkus).
@ -106,4 +101,4 @@ The AI step generation flow includes automatic context compaction:
- **JWT errors**: Ensure `privateKey.pem` and `publicKey.pem` exist in `src/main/resources/`. - **JWT errors**: Ensure `privateKey.pem` and `publicKey.pem` exist in `src/main/resources/`.
- **Generated code issues**: Run `mvn clean generate-sources` to rebuild from scratch. - **Generated code issues**: Run `mvn clean generate-sources` to rebuild from scratch.
- **TypeScript errors**: Check that `src/main/web/node_modules` exists; run `npm install` in `src/main/web`. - **TypeScript errors**: Check that `src/main/web/node_modules` exists; run `npm install` in `src/main/web`.
- **DB connection**: Verify PostgreSQL is running and the `storyteller` database/user exist. - **DB issues**: H2 in-memory resets on restart. If Liquibase fails, run `mvn clean` and restart.

40
pom.xml
View File

@ -20,7 +20,6 @@
<openapi.generator.version>7.14.0</openapi.generator.version> <openapi.generator.version>7.14.0</openapi.generator.version>
<build.helper.plugin.version>3.6.0</build.helper.plugin.version> <build.helper.plugin.version>3.6.0</build.helper.plugin.version>
<frontend.maven.plugin.version>1.15.1</frontend.maven.plugin.version> <frontend.maven.plugin.version>1.15.1</frontend.maven.plugin.version>
<maven.resources.plugin.version>3.3.1</maven.resources.plugin.version>
<maven.surefire.plugin.version>3.5.3</maven.surefire.plugin.version> <maven.surefire.plugin.version>3.5.3</maven.surefire.plugin.version>
</properties> </properties>
@ -67,7 +66,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId> <artifactId>quarkus-jdbc-h2</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
@ -102,6 +101,17 @@
<artifactId>quarkus-maven-plugin</artifactId> <artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.plugin.version}</version> <version>${quarkus.plugin.version}</version>
<extensions>true</extensions> <extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
<phase>package</phase>
<configuration>
<skip>${quarkus.package.skip}</skip>
</configuration>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
@ -128,6 +138,7 @@
<interfaceOnly>true</interfaceOnly> <interfaceOnly>true</interfaceOnly>
<useJakartaEe>true</useJakartaEe> <useJakartaEe>true</useJakartaEe>
<useTags>true</useTags> <useTags>true</useTags>
<useBeanValidation>false</useBeanValidation>
<dateLibrary>java8</dateLibrary> <dateLibrary>java8</dateLibrary>
<sourceFolder>src/gen/java</sourceFolder> <sourceFolder>src/gen/java</sourceFolder>
</configOptions> </configOptions>
@ -206,7 +217,7 @@
</execution> </execution>
<execution> <execution>
<id>npm-build</id> <id>npm-build</id>
<phase>prepare-package</phase> <phase>compile</phase>
<goals> <goals>
<goal>npm</goal> <goal>npm</goal>
</goals> </goals>
@ -217,29 +228,6 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven.resources.plugin.version}</version>
<executions>
<execution>
<id>copy-web-dist-to-quarkus-static</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/META-INF/resources</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/web-dist</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.plugin.version}</version> <version>${maven.surefire.plugin.version}</version>

View File

@ -5,6 +5,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table; import jakarta.persistence.Table;
/** /**
@ -32,7 +33,7 @@ public class ScenarioCharacterEntity {
private String role; private String role;
/** Character description. */ /** Character description. */
@Column(columnDefinition = "TEXT") @Lob
private String description; private String description;
/** @return primary key */ /** @return primary key */

View File

@ -7,6 +7,7 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -34,11 +35,12 @@ public class ScenarioEntity {
private String title; private String title;
/** Optional overall description. */ /** Optional overall description. */
@Column(columnDefinition = "TEXT") @Lob
private String description; private String description;
/** Initial scene narrative text. */ /** Initial scene narrative text. */
@Column(name = "starting_scene_description", nullable = false, columnDefinition = "TEXT") @Lob
@Column(name = "starting_scene_description", nullable = false)
private String startingSceneDescription; private String startingSceneDescription;
/** Starting characters for this scenario. */ /** Starting characters for this scenario. */

View File

@ -5,6 +5,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table; import jakarta.persistence.Table;
/** /**
@ -32,7 +33,7 @@ public class StoryCharacterEntity {
private String role; private String role;
/** Character description. */ /** Character description. */
@Column(columnDefinition = "TEXT") @Lob
private String description; private String description;
/** @return primary key */ /** @return primary key */

View File

@ -7,6 +7,7 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -38,7 +39,8 @@ public class StoryEntity {
private String title; private String title;
/** Current compacted scene narrative. */ /** Current compacted scene narrative. */
@Column(name = "current_scene_description", nullable = false, columnDefinition = "TEXT") @Lob
@Column(name = "current_scene_description", nullable = false)
private String currentSceneDescription; private String currentSceneDescription;
/** Lifecycle status: ACTIVE, ARCHIVED, DELETED. */ /** Lifecycle status: ACTIVE, ARCHIVED, DELETED. */

View File

@ -5,6 +5,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -29,11 +30,13 @@ public class StoryStepEntity {
private int stepNumber; private int stepNumber;
/** Narrative text for this step. */ /** Narrative text for this step. */
@Column(nullable = false, columnDefinition = "TEXT") @Lob
@Column(nullable = false)
private String content; private String content;
/** The user's direction prompt that produced this step. */ /** The user's direction prompt that produced this step. */
@Column(name = "user_direction", columnDefinition = "TEXT") @Lob
@Column(name = "user_direction")
private String userDirection; private String userDirection;
/** Whether this step has been compacted into the current scene. */ /** Whether this step has been compacted into the current scene. */

View File

@ -1,15 +1,13 @@
quarkus.http.port=8080 quarkus.http.port=8080
quarkus.http.cors=true quarkus.http.cors.enabled=true
quarkus.liquibase.migrate-at-start=true quarkus.liquibase.migrate-at-start=true
quarkus.liquibase.change-log=db/migration/db.changelog-master.yaml quarkus.liquibase.change-log=db/migration/db.changelog-master.yaml
quarkus.datasource.db-kind=postgresql quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/storyteller quarkus.datasource.jdbc.url=jdbc:h2:mem:storyteller;DB_CLOSE_DELAY=-1
quarkus.datasource.username=storyteller quarkus.datasource.username=sa
quarkus.datasource.password=storyteller quarkus.datasource.password=
quarkus.hibernate-orm.database.generation=none
mp.jwt.verify.publickey.location=publicKey.pem mp.jwt.verify.publickey.location=publicKey.pem
mp.jwt.verify.issuer=storyteller mp.jwt.verify.issuer=storyteller

View File

@ -4,6 +4,7 @@
"target": "ES2020", "target": "ES2020",
"lib": ["ES2020"], "lib": ["ES2020"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"types": ["node"] "types": ["node"]

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react";
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
build: { build: {
outDir: "../../../target/web-dist", outDir: "../../../src/main/resources/META-INF/resources",
emptyOutDir: true emptyOutDir: true
}, },
server: { server: {

View File

@ -4,7 +4,7 @@ import react from "@vitejs/plugin-react";
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
build: { build: {
outDir: "../../../target/web-dist", outDir: "../../../src/main/resources/META-INF/resources",
emptyOutDir: true emptyOutDir: true
}, },
server: { server: {