Refactor & Enhancements:
- Refactored `validateApp` in `AppsService` to return `Optional<App>` instead of `null`.
- Updated `JwtTokenService` to handle `expiration` parameter and use `App` object for token creation.
- Improved `TokenController` to work with the updated service layer.
- Fixed typo in `Report.vue` ("Airfreight" to "Air freight").
- Updated application properties to use `SPRING_PROFILES_ACTIVE`.
- Added `.dockerignore`, `dockerfile`, and `docker-compose.yml`, enabling Docker support.
- Removed unused Maven plugins and updated `vite.config.js` build directory.
- Introduced Gitea CI workflows for building and pushing Docker images.
This commit is contained in:
parent
0c51bf7c3d
commit
c071609eb2
13 changed files with 157 additions and 72 deletions
6
.dockerignore
Normal file
6
.dockerignore
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
target/
|
||||
src/frontend/node_modules/
|
||||
src/frontend/dist/
|
||||
.git/
|
||||
.gitignore
|
||||
*.md
|
||||
51
.gitea/workflows/build.yml
Normal file
51
.gitea/workflows/build.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.avatic.de
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: git.avatic.de/${{ gitea.repository_owner }}/lcc
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha,prefix={{branch}}-
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=registry,ref=git.avatic.de/${{ gitea.repository_owner }}/lcc:buildcache
|
||||
cache-to: type=registry,ref=git.avatic.de/${{ gitea.repository_owner }}/lcc:buildcache,mode=max
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -12,6 +12,8 @@ target/
|
|||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
.env.example
|
||||
.env
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
|
|
|
|||
54
docker-compose.yml
Normal file
54
docker-compose.yml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: lcc-mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||
MYSQL_DATABASE: ${DB_DATABASE}
|
||||
MYSQL_USER: ${DB_USER}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
networks:
|
||||
- lcc-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
lcc-app:
|
||||
#image: git.avatic.de/avatic/lcc:latest
|
||||
build: .
|
||||
container_name: lcc-app
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DB_DATABASE: ${DB_DATABASE}
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
ALLOWED_CORS_DOMAIN: ${ALLOWED_CORS_DOMAIN}
|
||||
LCC_BASE_URL: ${LCC_BASE_URL}
|
||||
AZURE_MAPS_CLIENT_ID: ${AZURE_MAPS_CLIENT_ID}
|
||||
AZURE_MAPS_SUBSCRIPTION_KEY: ${AZURE_MAPS_SUBSCRIPTION_KEY}
|
||||
AZURE_TENANT_ID: ${AZURE_TENANT_ID}
|
||||
AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}
|
||||
AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/${DB_DATABASE}
|
||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
- lcc-network
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
|
||||
networks:
|
||||
lcc-network:
|
||||
driver: bridge
|
||||
20
dockerfile
Normal file
20
dockerfile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
FROM node:20-alpine AS frontend-build
|
||||
WORKDIR /app/frontend
|
||||
COPY src/frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY src/frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
FROM maven:3.9-eclipse-temurin-23 AS backend-build
|
||||
WORKDIR /app
|
||||
COPY pom.xml ./
|
||||
COPY src ./src
|
||||
# copy frontend
|
||||
COPY --from=frontend-build /app/frontend/dist ./src/main/resources/static
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
FROM eclipse-temurin:23-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=backend-build /app/target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||
50
pom.xml
50
pom.xml
|
|
@ -174,56 +174,6 @@
|
|||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>npm build the vue app</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<workingDirectory>src/frontend</workingDirectory>
|
||||
<executable>npm</executable>
|
||||
<arguments>
|
||||
<argument>run</argument>
|
||||
<argument>build</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>azure-container-apps-maven-plugin</artifactId>
|
||||
<version>0.1.0</version>
|
||||
<configuration>
|
||||
<subscriptionId>your-subscription-id</subscriptionId>
|
||||
<resourceGroup>your-resource-group</resourceGroup>
|
||||
<appEnvironmentName>your-app-environment-name</appEnvironmentName>
|
||||
<region>your-region</region>
|
||||
<appName>lcc</appName>
|
||||
<containers>
|
||||
<container>
|
||||
<type>code</type>
|
||||
<directory>${project.basedir}</directory>
|
||||
</container>
|
||||
</containers>
|
||||
<ingress>
|
||||
<external>true</external>
|
||||
<targetPort>8080</targetPort>
|
||||
</ingress>
|
||||
<scale>
|
||||
<minReplicas>0</minReplicas>
|
||||
<maxReplicas>10</maxReplicas>
|
||||
</scale>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
|
||||
<div class="report-content-row" v-if="((report.costs.air_freight_cost ?? null) !== null)">
|
||||
<div>Airfreight costs</div>
|
||||
<div>Air freight costs</div>
|
||||
<div class="report-content-data-cell">{{ report.costs.air_freight_cost.total.toFixed(2) }}</div>
|
||||
<div class="report-content-data-cell">{{ (report.costs.air_freight_cost.percentage * 100).toFixed(2) }}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export default defineConfig({
|
|||
],
|
||||
base: '/',
|
||||
build: {
|
||||
outDir: '../../src/main/resources/static',
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true,
|
||||
assetsDir: 'assets',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
package de.avatic.lcc.controller.token;
|
||||
|
||||
import de.avatic.lcc.dto.users.AppDTO;
|
||||
import de.avatic.lcc.model.db.users.App;
|
||||
import de.avatic.lcc.model.db.users.Group;
|
||||
import de.avatic.lcc.repositories.users.JwtTokenService;
|
||||
import de.avatic.lcc.service.apps.AppsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
|
@ -14,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/oauth2/token")
|
||||
|
|
@ -35,26 +33,25 @@ public class TokenController {
|
|||
@RequestParam("client_secret") String clientSecret,
|
||||
@RequestParam(value = "scope", required = false) String scope) {
|
||||
|
||||
long expiration = 3600;
|
||||
|
||||
if (!"client_credentials".equals(grantType)) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(Map.of("error", "unsupported_grant_type"));
|
||||
}
|
||||
|
||||
App app = appService.validateApp(clientId, clientSecret);
|
||||
if (app == null) {
|
||||
Optional<App> app = appService.validateApp(clientId, clientSecret);
|
||||
if (app.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body(Map.of("error", "invalid_client"));
|
||||
}
|
||||
|
||||
String token = jwtTokenService.createApplicationToken(
|
||||
clientId,
|
||||
app.getGroups().stream().map(Group::getName).toList()
|
||||
);
|
||||
String token = jwtTokenService.createApplicationToken(app.get(), expiration);
|
||||
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"access_token", token,
|
||||
"token_type", "Bearer",
|
||||
"expires_in", 3600
|
||||
"expires_in", expiration
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package de.avatic.lcc.repositories.users;
|
||||
|
||||
import de.avatic.lcc.model.db.users.App;
|
||||
import de.avatic.lcc.model.db.users.Group;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
|
|
@ -24,17 +26,17 @@ public class JwtTokenService {
|
|||
}
|
||||
|
||||
|
||||
public String createApplicationToken(String clientId, List<String> groups) {
|
||||
long expirationMs = 3600000;
|
||||
public String createApplicationToken(App app, long expiration) {
|
||||
|
||||
|
||||
return Jwts.builder()
|
||||
.issuer(baseUrl)
|
||||
.subject(clientId)
|
||||
.subject(app.getClientId())
|
||||
.audience().add(baseUrl + "/api").and()
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + expirationMs))
|
||||
.claim("client_id", clientId)
|
||||
.claim("groups", groups)
|
||||
.expiration(new Date(System.currentTimeMillis() + (expiration*1000)))
|
||||
.claim("client_id", app.getId())
|
||||
.claim("groups", app.getGroups().stream().map(Group::getName).toList())
|
||||
.claim("token_type", "ext_app")
|
||||
.signWith(signingKey)
|
||||
.compact();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class AppsService {
|
||||
|
|
@ -39,15 +40,15 @@ public class AppsService {
|
|||
}
|
||||
|
||||
|
||||
public App validateApp(String clientId, String clientSecret) {
|
||||
public Optional<App> validateApp(String clientId, String clientSecret) {
|
||||
var app = appRepository.getByClientId(clientId);
|
||||
|
||||
if (app.isPresent() &&
|
||||
passwordEncoder.matches(clientSecret, app.get().getClientSecret())) {
|
||||
return app.get();
|
||||
return app;
|
||||
}
|
||||
|
||||
return null;
|
||||
return Optional.empty();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ public class ReportingService {
|
|||
var periodId = tuple.get().periodId();
|
||||
var setId = tuple.get().propertySetId();
|
||||
|
||||
//TODO check user node id and node id for null
|
||||
|
||||
var jobs = new ArrayList<>(nodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValid(periodId, setId, nodeId, materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
||||
jobs.addAll(userNodeIds.stream().map(nodeId -> calculationJobRepository.getCalculationJobWithJobStateValidUserNodeId(periodId, setId,nodeId ,materialId)).filter(Optional::isPresent).map(Optional::get).toList());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
spring.config.import=classpath:env.properties
|
||||
spring.profiles.active=${SPRING_PROFILES_ACTIVE}
|
||||
spring.application.name=lcc
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/${DB_DATABASE}
|
||||
spring.datasource.username=${DB_USER}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue