Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[Makefile]
indent_style = tab

[*.java]
indent_size = 4

[*.md]
indent_size = 4
20 changes: 18 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
test:
./gradlew test

start: run

run:
./gradlew run
./gradlew bootRun

update-gradle:
./gradlew wrapper --gradle-version 9.1.0
./gradlew wrapper --gradle-version 9.2.1

update-deps:
./gradlew refreshVersions

install:
./gradlew dependencies

build:
./gradlew build

lint:
./gradlew spotlessCheck

lint-fix:
./gradlew spotlessApply

.PHONY: build
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Project DevOps Deploy

Bulletin board service.

The default `dev` profile uses an in-memory H2 database and seeds 10 sample bulletins through `DataInitializer`, so the API works immediately after startup.

API documentation is available via Swagger UI at `http://localhost:8080/swagger-ui/index.html`.

## Requirements

- JDK 21+.
- Gradle 9.2.1.
- PostgreSQL only if you run the `prod` profile with an external database.
- Make.

## Running

### Local profile
1. Start the application:
```bash

make run
```
3. Explore the API:
- All bulletins: `GET http://localhost:8080/api/bulletins`
- Swagger UI: `http://localhost:8080/swagger-ui/index.html`

### Production

Prepare your database and export connection parameters:

```bash
export SPRING_PROFILES_ACTIVE=prod
export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/bulletins
export SPRING_DATASOURCE_USERNAME=postgres
export SPRING_DATASOURCE_PASSWORD=postgres
```

Build app:

```bash
make build
java -jar build/libs/project-devops-deploy-0.0.1-SNAPSHOT.jar
```

### Useful commands

See [Makefile](./Makefile)

## Frontend

### Development

1. Install Node.js 20 LTS (or newer) and npm 10.
2. Install dependencies and start the Vite dev server:
```bash
cd frontend
make install
make start
```
3. The dev server proxies `/api` requests to `http://localhost:8080`, so keep the backend running via `make run` (or `./gradlew bootRun`) in another terminal.

### Build and serve from the Java app

1. Build the production bundle:
```bash
cd frontend
make install # run once
make build # outputs to frontend/dist
```
2. Copy the compiled assets into Spring Boot’s static resources (served from `src/main/resources/static`):
```bash
rm -rf src/main/resources/static
mkdir -p src/main/resources/static
cp -R frontend/dist/* src/main/resources/static/
```
3. Restart the backend (`make run`) and open `http://localhost:8080/` — the React app will now be served directly by the Java application.
34 changes: 32 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
application
alias(libs.plugins.versions)
alias(libs.plugins.spotless)
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependency.management)
alias(libs.plugins.lombok)
}

group = "com.example"
Expand All @@ -26,25 +30,51 @@ repositories {

dependencies {
implementation(libs.springBootStarterWeb)
implementation(libs.springBootStarterDataJpa)
implementation(libs.springBootStarterValidation)
implementation(libs.mapstruct)
implementation(libs.instancioCore)
implementation(libs.springdocOpenapi)
developmentOnly(libs.springBootDevtools)
annotationProcessor(libs.mapstructProcessor)
compileOnly(libs.lombok)
annotationProcessor(libs.lombok)
annotationProcessor(libs.lombokMapstructBinding)
testCompileOnly(libs.lombok)
testAnnotationProcessor(libs.lombok)
testAnnotationProcessor(libs.lombokMapstructBinding)
// implementation "org.springframework.boot:spring-boot-starter"
// testImplementation "org.springframework.boot:spring-boot-starter-test"
// testRuntimeOnly "org.junit.platform:junit-platform-launcher"
testImplementation(libs.springBootStarterTest)
testImplementation(libs.springSecurityTest)
testImplementation(platform(libs.junitBom))
testImplementation(libs.junitJupiter)
testRuntimeOnly(libs.junitPlatformLauncher)
runtimeOnly("com.h2database:h2")
runtimeOnly(libs.postgresql)

implementation(libs.jacksonDatabindNullable)
implementation(libs.datafaker)
}

tasks.test {
useJUnitPlatform()
testLogging {
exceptionFormat = TestExceptionFormat.SHORT
events = setOf(
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED
)
showStandardStreams = true
}
}

spotless {
java {
importOrder()
removeUnusedImports()
eclipse().sortMembersEnabled(true)
eclipse()
formatAnnotations()
leadingTabsToSpaces(4)
}
Expand Down
24 changes: 24 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
12 changes: 12 additions & 0 deletions frontend/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
setup: install

install:
npm ci

build:
npm run build

start: dev

dev:
npm run dev
26 changes: 26 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# project-devops-deploy

## Installation

Install the application dependencies by running:

```sh
npm install
```

## Development

Start the application in development mode by running:

```sh
npm run dev
```

## Production

Build the application in production mode by running:

```sh
npm run build
```

44 changes: 44 additions & 0 deletions frontend/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import js from "@eslint/js";
import { defineConfig, globalIgnores } from "eslint/config";
import tseslint from "typescript-eslint";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import globals from "globals";

export default defineConfig([
globalIgnores(["**/node_modules", "**/dist"]),
{
name: "eslint-js-recommended-rules",
plugins: {
js,
},
extends: ["js/recommended"],
},
tseslint.configs.recommended.map((conf) => ({
...conf,
files: ["**/*.ts", "**/*.tsx"],
})),
eslintPluginPrettierRecommended,
{
name: "react",
...react.configs.flat.recommended,
},
reactHooks.configs["recommended-latest"],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
rules: {
"react/react-in-jsx-scope": "off",
},
settings: {
react: {
version: "detect",
},
},
},
]);
Loading