mirror of
https://github.com/mvvasilev/personal-finances.git
synced 2025-04-11 18:35:02 +03:00
Add Kafka, zookeeper. Add nginx.
This commit is contained in:
parent
7720263b92
commit
d7c3cbbc69
35 changed files with 536 additions and 142 deletions
59
.docker/nginx/default.conf.template
Normal file
59
.docker/nginx/default.conf.template
Normal file
|
@ -0,0 +1,59 @@
|
|||
server {
|
||||
listen 80;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For &proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto &scheme;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_redirect off;
|
||||
|
||||
location / {
|
||||
proxy_pass ${FRONTEND_URI};
|
||||
|
||||
location /login {
|
||||
proxy_pass ${LOGIN_SERVICE_URI};
|
||||
}
|
||||
|
||||
location /logout {
|
||||
proxy_pass ${LOGIN_SERVICE_URI};
|
||||
}
|
||||
|
||||
location /oauth2 {
|
||||
proxy_pass ${LOGIN_SERVICE_URI};
|
||||
}
|
||||
|
||||
location /refresh-token {
|
||||
proxy_pass ${LOGIN_SERVICE_URI};
|
||||
}
|
||||
}
|
||||
|
||||
location /api {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
|
||||
proxy_pass ${CORE_API_URI};
|
||||
|
||||
proxy_set_header Authorization "Bearer $cookie_pefi_token";
|
||||
proxy_pass_header Authorization;
|
||||
|
||||
location /api/enums/widget-types {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
proxy_pass ${WIDGETS_API_URI};
|
||||
}
|
||||
|
||||
location /api/enums/supported-conversions {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
proxy_pass ${STATEMENTS_API_URI};
|
||||
}
|
||||
|
||||
location /api/widgets {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
proxy_pass ${WIDGETS_API_URI};
|
||||
}
|
||||
|
||||
location /api/statements {
|
||||
rewrite ^/api/(.*) /$1 break;
|
||||
proxy_pass ${STATEMENTS_API_URI};
|
||||
}
|
||||
}
|
||||
}
|
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
|
@ -14,6 +14,7 @@
|
|||
<option value="$PROJECT_DIR$/pefi-common" />
|
||||
<option value="$PROJECT_DIR$/pefi-core-api" />
|
||||
<option value="$PROJECT_DIR$/pefi-frontend" />
|
||||
<option value="$PROJECT_DIR$/pefi-login-service" />
|
||||
<option value="$PROJECT_DIR$/pefi-statements-api" />
|
||||
<option value="$PROJECT_DIR$/pefi-widgets-api" />
|
||||
</set>
|
||||
|
|
|
@ -1,74 +1,87 @@
|
|||
version: '3.4'
|
||||
|
||||
services:
|
||||
api-gateway:
|
||||
build: ./pefi-api-gateway
|
||||
nginx:
|
||||
image: nginx:stable-alpine
|
||||
volumes:
|
||||
- ./.docker/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro
|
||||
ports:
|
||||
- '8080:8080'
|
||||
- '8080:80'
|
||||
environment:
|
||||
PROFILE: development
|
||||
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID}
|
||||
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_ID}
|
||||
AUTHENTIK_ISSUER_URL: https://auth.mvvasilev.dev/application/o/${AUTHENTIK_APP_NAME}/
|
||||
AUTHENTIK_BACK_CHANNEL_LOGOUT_URL: https://auth.mvvasilev.dev/application/o/${AUTHENTIK_APP_NAME}/end-session/
|
||||
GATEWAY_URI: http://localhost:8080
|
||||
CORE_API_URI: http://core-api:8081
|
||||
STATEMENTS_API_URI: http://statements-api:8081
|
||||
WIDGETS_API_URI: http://widgets-api:8081
|
||||
FRONTEND_URI: http://frontend:5173
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
SSL_ENABLED: true
|
||||
SSL_KEY_STORE_TYPE: PKCS12
|
||||
SSL_KEY_STORE: classpath:keystore/local.p12
|
||||
SSL_KEY_STORE_PASSWORD: asdf1234
|
||||
SSL_KEY_ALIAS: local
|
||||
LOGIN_SERVICE_URI: http://login-service:8081
|
||||
depends_on:
|
||||
- core-api
|
||||
- statements-api
|
||||
- widgets-api
|
||||
- frontend
|
||||
- login-service
|
||||
|
||||
frontend:
|
||||
build: ./pefi-frontend
|
||||
ports:
|
||||
- '5173:5173'
|
||||
|
||||
login-service:
|
||||
build: ./pefi-login-service
|
||||
ports:
|
||||
- '8084:8081'
|
||||
environment:
|
||||
SERVER_PORT: 8081
|
||||
FRONTEND_URI: http://localhost:8080
|
||||
AUTHENTIK_ISSUER_URI: ${AUTHENTIK_ISSUER_URL}
|
||||
AUTHENTIK_BACK_CHANNEL_LOGOUT_URL: ${AUTHENTIK_BACK_CHANNEL_LOGOUT_URL}
|
||||
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID}
|
||||
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET}
|
||||
|
||||
core-api:
|
||||
build: ./pefi-core-api
|
||||
ports:
|
||||
- '8081:8081'
|
||||
environment:
|
||||
SERVER_PORT: 8081
|
||||
PROFILE: 'development'
|
||||
AUTHENTIK_ISSUER_URL: 'https://auth.mvvasilev.dev/application/o/${AUTHENTIK_APP_NAME}/'
|
||||
AUTHENTIK_ISSUER_URI: ${AUTHENTIK_ISSUER_URL}
|
||||
DATASOURCE_URL: jdbc:postgresql://database:5432/${POSTGRES_DB}
|
||||
DATASOURCE_USER: ${POSTGRES_USER}
|
||||
DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
KAFKA_SERVERS: 'kafka-broker:9092'
|
||||
KAFKA_SERVERS: localhost:9092
|
||||
depends_on:
|
||||
- kafka1
|
||||
- database
|
||||
|
||||
statements-api:
|
||||
build: ./pefi-statements-api
|
||||
ports:
|
||||
- '8082:8081'
|
||||
environment:
|
||||
SERVER_PORT: 8081
|
||||
PROFILE: 'development'
|
||||
AUTHENTIK_ISSUER_URL: 'https://auth.mvvasilev.dev/application/o/${AUTHENTIK_APP_NAME}/'
|
||||
AUTHENTIK_ISSUER_URI: ${AUTHENTIK_ISSUER_URL}
|
||||
DATASOURCE_URL: jdbc:postgresql://database:5432/${POSTGRES_DB}
|
||||
DATASOURCE_USER: ${POSTGRES_USER}
|
||||
DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
KAFKA_SERVERS: 'kafka-broker:9092'
|
||||
KAFKA_SERVERS: localhost:9092
|
||||
depends_on:
|
||||
- kafka1
|
||||
- database
|
||||
|
||||
widgets-api:
|
||||
build: ./pefi-widgets-api
|
||||
ports:
|
||||
- '8083:8081'
|
||||
environment:
|
||||
SERVER_PORT: 8081
|
||||
PROFILE: 'development'
|
||||
AUTHENTIK_ISSUER_URL: 'https://auth.mvvasilev.dev/application/o/${AUTHENTIK_APP_NAME}/'
|
||||
AUTHENTIK_ISSUER_URI: ${AUTHENTIK_ISSUER_URL}
|
||||
DATASOURCE_URL: jdbc:postgresql://database:5432/${POSTGRES_DB}
|
||||
DATASOURCE_USER: ${POSTGRES_USER}
|
||||
DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
redis:
|
||||
image: redis/redis-stack:latest
|
||||
ports:
|
||||
- '6379:6379'
|
||||
- '6380:8001'
|
||||
depends_on:
|
||||
- database
|
||||
|
||||
database:
|
||||
image: postgres:16.1-alpine
|
||||
|
@ -79,34 +92,38 @@ services:
|
|||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
|
||||
kafka-broker:
|
||||
image: confluentinc/cp-kafka:7.5.3
|
||||
hostname: broker
|
||||
container_name: broker
|
||||
depends_on:
|
||||
- zookeeper
|
||||
ports:
|
||||
- "29092:29092"
|
||||
- "9092:9092"
|
||||
- "9101:9101"
|
||||
environment:
|
||||
KAFKA_BROKER_ID: 1
|
||||
KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
|
||||
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
|
||||
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
|
||||
KAFKA_JMX_PORT: 9101
|
||||
KAFKA_JMX_HOSTNAME: localhost
|
||||
|
||||
zookeeper:
|
||||
image: confluentinc/cp-zookeeper:7.5.3
|
||||
hostname: zookeeper
|
||||
container_name: zookeeper
|
||||
zoo1:
|
||||
image: confluentinc/cp-zookeeper:7.3.2
|
||||
hostname: zoo1
|
||||
container_name: zoo1
|
||||
ports:
|
||||
- "2181:2181"
|
||||
environment:
|
||||
ZOOKEEPER_CLIENT_PORT: 2181
|
||||
ZOOKEEPER_TICK_TIME: 2000
|
||||
ZOOKEEPER_SERVER_ID: 1
|
||||
ZOOKEEPER_SERVERS: zoo1:2888:3888
|
||||
|
||||
kafka1:
|
||||
image: confluentinc/cp-kafka:7.3.2
|
||||
hostname: kafka1
|
||||
container_name: kafka1
|
||||
ports:
|
||||
- "9092:9092"
|
||||
- "29092:29092"
|
||||
- "9999:9999"
|
||||
environment:
|
||||
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka1:19092,EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092,DOCKER://host.docker.internal:29092
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,DOCKER:PLAINTEXT
|
||||
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
|
||||
KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181"
|
||||
KAFKA_BROKER_ID: 1
|
||||
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
|
||||
KAFKA_JMX_PORT: 9999
|
||||
KAFKA_JMX_HOSTNAME: ${DOCKER_HOST_IP:-127.0.0.1}
|
||||
KAFKA_AUTHORIZER_CLASS_NAME: kafka.security.authorizer.AclAuthorizer
|
||||
KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: "true"
|
||||
depends_on:
|
||||
- zoo1
|
|
@ -10,6 +10,8 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
|
||||
|
||||
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
|
||||
implementation 'org.springframework:spring-web:6.1.3'
|
||||
implementation 'org.springframework.data:spring-data-jpa:3.2.0'
|
||||
|
|
|
@ -6,17 +6,29 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.filter.CommonsRequestLoggingFilter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class CommonControllerConfiguration {
|
||||
|
||||
@Bean
|
||||
public CommonsRequestLoggingFilter requestLoggingFilter() {
|
||||
CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
|
||||
loggingFilter.setIncludeClientInfo(true);
|
||||
loggingFilter.setIncludeQueryString(true);
|
||||
loggingFilter.setIncludePayload(true);
|
||||
loggingFilter.setMaxPayloadLength(64000);
|
||||
return loggingFilter;
|
||||
}
|
||||
|
||||
@RestControllerAdvice(basePackages = {"dev.mvvasilev"})
|
||||
public static class APIResponseAdvice {
|
||||
|
||||
|
|
|
@ -1,59 +1,43 @@
|
|||
package dev.mvvasilev.common.configuration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoders;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.filter.CommonsRequestLoggingFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
public class CommonSecurityConfiguration {
|
||||
|
||||
@Value("${jwt.issuer-url}")
|
||||
public String jwtIssuerUrl;
|
||||
|
||||
private static final String[] WHITELISTED_URLS = {
|
||||
"/v3/api-docs/**",
|
||||
"/swagger-ui/**",
|
||||
"/v2/api-docs/**",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-resources/**"
|
||||
};
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, BearerTokenResolver bearerTokenResolver) throws Exception {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
return httpSecurity
|
||||
.cors(c -> c.disable()) // won't be needing cors, as the API will be completely hidden behind an api gateway
|
||||
.csrf(c -> c.disable())
|
||||
.cors(AbstractHttpConfigurer::disable)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(c -> {
|
||||
c.requestMatchers(WHITELISTED_URLS).permitAll();
|
||||
c.requestMatchers(WHITELISTED_URLS).anonymous();
|
||||
c.anyRequest().authenticated();
|
||||
})
|
||||
.oauth2ResourceServer(c -> {
|
||||
c.jwt(Customizer.withDefaults());
|
||||
c.bearerTokenResolver(bearerTokenResolver);
|
||||
.oauth2ResourceServer(oauth2 -> {
|
||||
oauth2.jwt(Customizer.withDefaults());
|
||||
})
|
||||
.exceptionHandling(e -> e.accessDeniedHandler((req, res, ex) -> res.setStatus(HttpServletResponse.SC_UNAUTHORIZED)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtDecoder jwtDecoder() {
|
||||
return JwtDecoders.fromIssuerLocation(jwtIssuerUrl);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BearerTokenResolver bearerTokenResolver() {
|
||||
DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
|
||||
bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.AUTHORIZATION);
|
||||
return bearerTokenResolver;
|
||||
}
|
||||
}
|
|
@ -5,10 +5,15 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = { AuthorizationService.class })
|
||||
@EnableJpaRepositories("dev.mvvasilev.finances.*")
|
||||
@EnableWebMvc
|
||||
@EntityScan("dev.mvvasilev.finances.*")
|
||||
@EnableJpaRepositories("dev.mvvasilev.finances.*")
|
||||
@SpringBootApplication(
|
||||
scanBasePackageClasses = { AuthorizationService.class },
|
||||
scanBasePackages = "dev.mvvasilev.finances.*"
|
||||
)
|
||||
public class PefiCoreAPI {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package dev.mvvasilev.finances.configuration;
|
||||
|
||||
import dev.mvvasilev.common.dto.KafkaReplaceProcessedTransactionsDTO;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.clients.producer.ProducerConfig;
|
||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
import org.apache.kafka.common.serialization.StringSerializer;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.kafka.annotation.EnableKafka;
|
||||
import org.springframework.kafka.annotation.EnableKafkaRetryTopic;
|
||||
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
|
||||
import org.springframework.kafka.core.ConsumerFactory;
|
||||
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
|
||||
|
@ -15,6 +18,7 @@ import org.springframework.kafka.support.serializer.JsonSerializer;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
@EnableKafka
|
||||
@Configuration
|
||||
public class KafkaConfiguration {
|
||||
|
||||
|
@ -25,12 +29,12 @@ public class KafkaConfiguration {
|
|||
|
||||
@Bean
|
||||
public ConsumerFactory<String, KafkaReplaceProcessedTransactionsDTO> replaceTransactionsConsumerFactory() {
|
||||
// ...
|
||||
return new DefaultKafkaConsumerFactory<>(
|
||||
Map.of(
|
||||
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress,
|
||||
ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
|
||||
ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class
|
||||
ConsumerConfig.GROUP_ID_CONFIG, "core-api",
|
||||
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress,
|
||||
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringSerializer.class,
|
||||
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonSerializer.class
|
||||
),
|
||||
new StringDeserializer(),
|
||||
new JsonDeserializer<>(KafkaReplaceProcessedTransactionsDTO.class)
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.mvvasilev.common.web.APIResponseDTO;
|
|||
import dev.mvvasilev.finances.dtos.CategorizationRuleDTO;
|
||||
import dev.mvvasilev.finances.dtos.ProcessedTransactionFieldDTO;
|
||||
import dev.mvvasilev.finances.enums.CategorizationRule;
|
||||
import dev.mvvasilev.finances.enums.TimePeriod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -18,6 +19,11 @@ import java.util.Collection;
|
|||
@RequestMapping("/enums")
|
||||
public class EnumsController extends AbstractRestController {
|
||||
|
||||
@GetMapping("/statistics-time-periods")
|
||||
public ResponseEntity<APIResponseDTO<TimePeriod[]>> fetchTimePeriods() {
|
||||
return ok(TimePeriod.values());
|
||||
}
|
||||
|
||||
@GetMapping("/category-rules")
|
||||
public ResponseEntity<APIResponseDTO<Collection<CategorizationRuleDTO>>> fetchCategorizationRules() {
|
||||
return ok(
|
||||
|
|
|
@ -30,11 +30,6 @@ public class StatisticsController extends AbstractRestController {
|
|||
this.statisticsService = statisticsService;
|
||||
}
|
||||
|
||||
@GetMapping("/timePeriods")
|
||||
public ResponseEntity<APIResponseDTO<TimePeriod[]>> fetchTimePeriods() {
|
||||
return ok(TimePeriod.values());
|
||||
}
|
||||
|
||||
@GetMapping("/totalSpendingByCategory")
|
||||
@PreAuthorize("@authService.isOwner(#categoryId, T(dev.mvvasilev.finances.entity.TransactionCategory))")
|
||||
public ResponseEntity<APIResponseDTO<SpendingByCategoriesDTO>> fetchSpendingByCategory(
|
||||
|
|
|
@ -19,7 +19,7 @@ public class TransactionsKafkaListener {
|
|||
|
||||
@KafkaListener(
|
||||
topics = KafkaConfiguration.REPLACE_TRANSACTIONS_TOPIC,
|
||||
containerFactory = "replaceTransactionsKafkaListenerContainerFactory"
|
||||
groupId = "core-api"
|
||||
)
|
||||
public void replaceTransactionsListener(KafkaReplaceProcessedTransactionsDTO message) {
|
||||
service.createOrReplaceProcessedTransactions(message.statementId(), message.userId(), message.transactions());
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
server.port=8081
|
||||
debug=true
|
||||
|
||||
logging.level.org.springframework.boot.autoconfigure=ERROR
|
||||
server.port=${SERVER_PORT}
|
||||
|
||||
spring.profiles.active=${PROFILE}
|
||||
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=${AUTHENTIK_ISSUER_URI}
|
||||
|
||||
# Database
|
||||
spring.datasource.url=${DATASOURCE_URL}
|
||||
spring.datasource.username=${DATASOURCE_USER}
|
||||
|
@ -21,7 +20,4 @@ spring.jpa.show-sql=true
|
|||
spring.jpa.hibernate.ddl-auto=validate
|
||||
spring.flyway.table=core_schema_history
|
||||
spring.flyway.baseline-version=0.9
|
||||
spring.flyway.baseline-on-migrate=true
|
||||
|
||||
# Security
|
||||
jwt.issuer-url=${AUTHENTIK_ISSUER_URL}
|
||||
spring.flyway.baseline-on-migrate=true
|
|
@ -24,10 +24,10 @@ export default function CategorizationRulesEditor({selectedCategory, onRuleBehav
|
|||
|
||||
toast.promise(
|
||||
Promise.all([
|
||||
utils.performRequest("/api/categories/rules")
|
||||
utils.performRequest("/api/enums/category-rules")
|
||||
.then(resp => resp.json())
|
||||
.then(({result}) => setRuleTypes(result)),
|
||||
utils.performRequest("/api/processed-transactions/fields")
|
||||
utils.performRequest("/api/enums/processed-transaction-fields")
|
||||
.then(resp => resp.json())
|
||||
.then(({result}) => setFields(result))
|
||||
]),
|
||||
|
|
|
@ -49,7 +49,7 @@ export default function WidgetEditModal(
|
|||
}, [initialWidget]);
|
||||
|
||||
useEffect(() => {
|
||||
utils.performRequest("/api/widgets/types")
|
||||
utils.performRequest("/api/enums/widget-types")
|
||||
.then(resp => resp.json())
|
||||
.then(resp => setWidgetTypes(resp.result));
|
||||
|
||||
|
@ -57,7 +57,7 @@ export default function WidgetEditModal(
|
|||
.then(resp => resp.json())
|
||||
.then(resp => setCategories(resp.result));
|
||||
|
||||
utils.performRequest("/api/statistics/timePeriods")
|
||||
utils.performRequest("/api/enums/statistics-time-periods")
|
||||
.then(resp => resp.json())
|
||||
.then(resp => setTimePeriods(resp.result));
|
||||
}, []);
|
||||
|
|
|
@ -8,25 +8,48 @@ let LEV_FORMAT = new Intl.NumberFormat('bg-BG', {
|
|||
let utils = {
|
||||
performRequest: async (url, options) => {
|
||||
let opts = options ?? { headers: {} };
|
||||
return await fetch(url, {
|
||||
|
||||
let result = await fetch(url, {
|
||||
...opts,
|
||||
headers: {
|
||||
...opts.headers,
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
}).then(resp => {
|
||||
if (resp.status === 401) {
|
||||
window.location.replace(`${window.location.origin}/oauth2/authorization/authentik`)
|
||||
|
||||
throw "Unauthorized, please login.";
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
throw resp.status;
|
||||
}
|
||||
|
||||
return resp;
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If we are unauthorized, refresh the token, and try once more.
|
||||
if (result.status === 401) {
|
||||
let tokenResponse = await fetch("/refresh-token", {
|
||||
method: "POST",
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
|
||||
// If the token refresh failed, redirect to login
|
||||
if (!tokenResponse.ok) {
|
||||
window.location.replace(`${window.location.origin}/oauth2/authorization/authentik`);
|
||||
}
|
||||
|
||||
// Try again
|
||||
let secondAttempt = await fetch(url, {
|
||||
...opts,
|
||||
headers: {
|
||||
...opts.headers,
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
});
|
||||
|
||||
// If our second attempt failed as well after refresh, redirect to login
|
||||
if (!secondAttempt.ok && result.status === 401) {
|
||||
window.location.replace(`${window.location.origin}/oauth2/authorization/authentik`);
|
||||
}
|
||||
}
|
||||
|
||||
// If the error wasn't unauthorized, just return the response
|
||||
return result;
|
||||
},
|
||||
isSpinnerShown: () => {
|
||||
return localStorage.getItem("SpinnerShowing") === "true";
|
||||
|
|
4
pefi-login-service/.env.example
Normal file
4
pefi-login-service/.env.example
Normal file
|
@ -0,0 +1,4 @@
|
|||
AUTHENTIK_CLIENT_ID= authentik oauth2 client id
|
||||
AUTHENTIK_CLIENT_SECRET= authentik oauth2 client secret
|
||||
AUTHENTIK_ISSUER_URL= authentik issuer url ( dev: https://auth.mvvasilev.dev/application/o/personal-finances/ )
|
||||
AUTHENTIK_BACK_CHANNEL_LOGOUT_URL= authentik back channel logout url ( dev: https://auth.mvvasilev.dev/application/o/personal-finances/end-session/ )
|
7
pefi-login-service/Dockerfile
Normal file
7
pefi-login-service/Dockerfile
Normal file
|
@ -0,0 +1,7 @@
|
|||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
COPY ./build/libs/pefi-login-service-0.0.1-SNAPSHOT.jar app.jar
|
||||
|
||||
EXPOSE 8081
|
||||
|
||||
ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar $ARGS
|
24
pefi-login-service/build.gradle
Normal file
24
pefi-login-service/build.gradle
Normal file
|
@ -0,0 +1,24 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.0'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
}
|
||||
|
||||
group = 'dev.mvvasilev'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.9.1')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
1
pefi-login-service/settings.gradle
Normal file
1
pefi-login-service/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'pefi-login-service'
|
|
@ -0,0 +1,11 @@
|
|||
package dev.mvvasilev;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class PefiLoginService {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PefiLoginService.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package dev.mvvasilev.configuration;
|
||||
|
||||
import dev.mvvasilev.service.TokenRefreshService;
|
||||
import dev.mvvasilev.utils.CookieUtils;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Value("${auth.success.redirect}")
|
||||
private String redirect;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http, OAuth2AuthorizedClientRepository repository) throws Exception {
|
||||
return http
|
||||
.authorizeHttpRequests(authorize -> {
|
||||
authorize.requestMatchers(HttpMethod.POST, "/refresh-token").permitAll();
|
||||
authorize.anyRequest().authenticated();
|
||||
})
|
||||
.oauth2Login(l -> l.successHandler((req, res, auth) -> {
|
||||
OAuth2AuthenticationToken oauth = (OAuth2AuthenticationToken) auth;
|
||||
|
||||
OAuth2AuthorizedClient authorizedClient = repository.loadAuthorizedClient(
|
||||
oauth.getAuthorizedClientRegistrationId(),
|
||||
auth,
|
||||
req
|
||||
);
|
||||
|
||||
res.addCookie(
|
||||
CookieUtils.createAccessTokenCookie(authorizedClient.getAccessToken().getTokenValue())
|
||||
);
|
||||
|
||||
if (authorizedClient.getRefreshToken() != null) {
|
||||
res.addCookie(
|
||||
CookieUtils.createRefreshTokenCookie(authorizedClient.getRefreshToken().getTokenValue())
|
||||
);
|
||||
}
|
||||
|
||||
res.setStatus(HttpStatus.TEMPORARY_REDIRECT.value());
|
||||
res.addHeader("Location", redirect);
|
||||
|
||||
}))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package dev.mvvasilev.controller;
|
||||
|
||||
import dev.mvvasilev.configuration.SecurityConfiguration;
|
||||
import dev.mvvasilev.dto.TokenDTO;
|
||||
import dev.mvvasilev.exception.PefiLoginServiceException;
|
||||
import dev.mvvasilev.service.TokenRefreshService;
|
||||
import dev.mvvasilev.utils.CookieUtils;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class RefreshController {
|
||||
|
||||
private final TokenRefreshService tokenRefreshService;
|
||||
|
||||
public RefreshController(TokenRefreshService tokenRefreshService) {
|
||||
this.tokenRefreshService = tokenRefreshService;
|
||||
}
|
||||
|
||||
@PostMapping("/refresh-token")
|
||||
public ResponseEntity<Void> getOidcUserPrincipal(
|
||||
HttpServletResponse response,
|
||||
@CookieValue(CookieUtils.REFRESH_TOKEN_NAME) String refreshToken
|
||||
) {
|
||||
try {
|
||||
var token = tokenRefreshService.fetchNewTokens(refreshToken);
|
||||
|
||||
response.addCookie(CookieUtils.createAccessTokenCookie(token.accessToken()));
|
||||
response.addCookie(CookieUtils.createRefreshTokenCookie(token.refreshToken()));
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (PefiLoginServiceException e) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package dev.mvvasilev.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public record TokenDTO(
|
||||
@JsonProperty("access_token")
|
||||
String accessToken,
|
||||
@JsonProperty("refresh_token")
|
||||
String refreshToken,
|
||||
@JsonProperty("id_token")
|
||||
String idToken,
|
||||
@JsonProperty("token_type")
|
||||
String tokenType,
|
||||
@JsonProperty("expires_in")
|
||||
int expiredIn
|
||||
) {
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package dev.mvvasilev.exception;
|
||||
|
||||
public class PefiLoginServiceException extends RuntimeException {
|
||||
public PefiLoginServiceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package dev.mvvasilev.service;
|
||||
|
||||
import dev.mvvasilev.dto.TokenDTO;
|
||||
import dev.mvvasilev.exception.PefiLoginServiceException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class TokenRefreshService {
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@Autowired
|
||||
public TokenRefreshService(ClientRegistrationRepository clientRegistrationRepository) {
|
||||
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||
}
|
||||
|
||||
public TokenDTO fetchNewTokens(String refreshToken) {
|
||||
var client = clientRegistrationRepository.findByRegistrationId("authentik");
|
||||
|
||||
var template = new RestTemplate();
|
||||
|
||||
var requestBody = new LinkedMultiValueMap<String, String>();
|
||||
requestBody.put("grant_type", List.of("refresh_token"));
|
||||
requestBody.put("client_id", List.of(client.getClientId()));
|
||||
requestBody.put("client_secret", List.of(client.getClientSecret()));
|
||||
requestBody.put("refresh_token", List.of(refreshToken));
|
||||
|
||||
var requestHeaders = new LinkedMultiValueMap<String, String>();
|
||||
requestHeaders.put("Content-Type", List.of("application/x-www-form-urlencoded"));
|
||||
|
||||
var request = new HttpEntity<>(requestBody, requestHeaders);
|
||||
|
||||
var tokenResponse = template.postForEntity(client.getProviderDetails().getTokenUri(), request, TokenDTO.class);
|
||||
|
||||
if (!HttpStatus.OK.isSameCodeAs(tokenResponse.getStatusCode())) {
|
||||
throw new PefiLoginServiceException("Token refresh failure");
|
||||
}
|
||||
|
||||
return tokenResponse.getBody();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package dev.mvvasilev.utils;
|
||||
|
||||
import jakarta.servlet.http.Cookie;
|
||||
|
||||
public class CookieUtils {
|
||||
|
||||
public static final String ACCESS_TOKEN_NAME = "pefi_token";
|
||||
|
||||
public static final String REFRESH_TOKEN_NAME = "pefi_refresh_token";
|
||||
|
||||
public static Cookie createAccessTokenCookie(String value) {
|
||||
var accessTokenCookie = new Cookie(ACCESS_TOKEN_NAME, value);
|
||||
accessTokenCookie.setHttpOnly(true);
|
||||
accessTokenCookie.setPath("/api");
|
||||
|
||||
return accessTokenCookie;
|
||||
}
|
||||
|
||||
public static Cookie createRefreshTokenCookie(String value) {
|
||||
var refreshTokenCookie = new Cookie(REFRESH_TOKEN_NAME, value);
|
||||
refreshTokenCookie.setHttpOnly(true);
|
||||
refreshTokenCookie.setPath("/refresh-token");
|
||||
|
||||
return refreshTokenCookie;
|
||||
}
|
||||
|
||||
}
|
12
pefi-login-service/src/main/resources/application.properties
Normal file
12
pefi-login-service/src/main/resources/application.properties
Normal file
|
@ -0,0 +1,12 @@
|
|||
server.port=${SERVER_PORT}
|
||||
|
||||
auth.success.redirect=${FRONTEND_URI}
|
||||
|
||||
spring.security.oauth2.client.registration.authentik.client-id=${AUTHENTIK_CLIENT_ID}
|
||||
spring.security.oauth2.client.registration.authentik.client-secret=${AUTHENTIK_CLIENT_SECRET}
|
||||
spring.security.oauth2.client.registration.authentik.authorization-grant-type=authorization_code
|
||||
spring.security.oauth2.client.registration.authentik.redirect-uri={baseUrl}/login/oauth2/code/authentik
|
||||
spring.security.oauth2.client.registration.authentik.scope=openid
|
||||
|
||||
spring.security.oauth2.client.provider.authentik.issuer-uri=${AUTHENTIK_ISSUER_URI}
|
||||
spring.security.oauth2.client.provider.authentik.back-channel-logout-url=${AUTHENTIK_BACK_CHANNEL_LOGOUT_URL}
|
|
@ -5,10 +5,15 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = { AuthorizationService.class })
|
||||
@EnableJpaRepositories("dev.mvvasilev.statements.*")
|
||||
@EnableWebMvc
|
||||
@EntityScan("dev.mvvasilev.statements.*")
|
||||
@EnableJpaRepositories("dev.mvvasilev.statements.*")
|
||||
@SpringBootApplication(
|
||||
scanBasePackageClasses = { AuthorizationService.class },
|
||||
scanBasePackages = "dev.mvvasilev.statements.*"
|
||||
)
|
||||
public class PefiStatementsAPI {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PefiStatementsAPI.class, args);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
server.port=8081
|
||||
debug=true
|
||||
|
||||
logging.level.org.springframework.boot.autoconfigure=ERROR
|
||||
server.port=${SERVER_PORT}
|
||||
|
||||
spring.profiles.active=${PROFILE}
|
||||
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=${AUTHENTIK_ISSUER_URI}
|
||||
|
||||
# Database
|
||||
spring.datasource.url=${DATASOURCE_URL}
|
||||
spring.datasource.username=${DATASOURCE_USER}
|
||||
|
@ -21,7 +20,4 @@ spring.jpa.show-sql=true
|
|||
spring.jpa.hibernate.ddl-auto=validate
|
||||
spring.flyway.table=statements_schema_history
|
||||
spring.flyway.baseline-version=0.9
|
||||
spring.flyway.baseline-on-migrate=true
|
||||
|
||||
# Security
|
||||
jwt.issuer-url=${AUTHENTIK_ISSUER_URL}
|
||||
spring.flyway.baseline-on-migrate=true
|
9
pefi-widgets-api/.env.example
Normal file
9
pefi-widgets-api/.env.example
Normal file
|
@ -0,0 +1,9 @@
|
|||
PROFILE= production/development
|
||||
|
||||
AUTHENTIK_ISSUER_URL= auth server configuration url for fetching JWKs ( dev: https://auth.mvvasilev.dev/application/o/personal-finances/ )
|
||||
|
||||
KAFKA_SERVERS= comma-delimited list of kafka servers to connect to
|
||||
|
||||
DATASOURCE_URL= database jdbc url ( postgres only, example: jdbc:postgresql://localhost:5432/mydatabase )
|
||||
DATASOURCE_USER= database user
|
||||
DATASOURCE_PASSWORD= database password
|
|
@ -17,6 +17,8 @@ dependencies {
|
|||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
||||
|
||||
implementation 'org.flywaydb:flyway-core'
|
||||
implementation 'org.apache.commons:commons-lang3:3.14.0'
|
||||
|
||||
|
|
|
@ -5,10 +5,15 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = { AuthorizationService.class })
|
||||
@EnableJpaRepositories("dev.mvvasilev.widgets.*")
|
||||
@EnableWebMvc
|
||||
@EntityScan("dev.mvvasilev.widgets.*")
|
||||
@EnableJpaRepositories("dev.mvvasilev.widgets.*")
|
||||
@SpringBootApplication(
|
||||
scanBasePackageClasses = { AuthorizationService.class },
|
||||
scanBasePackages = "dev.mvvasilev.widgets.*"
|
||||
)
|
||||
public class PefiWidgetsAPI {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PefiWidgetsAPI.class, args);
|
||||
|
|
|
@ -4,9 +4,11 @@ import dev.mvvasilev.common.configuration.CommonSecurityConfiguration;
|
|||
import dev.mvvasilev.common.configuration.CommonSwaggerConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@Import(CommonSecurityConfiguration.class)
|
||||
@EnableTransactionManagement
|
||||
public class SecurityConfiguration {
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
server.port=8081
|
||||
debug=true
|
||||
|
||||
logging.level.org.springframework.boot.autoconfigure=ERROR
|
||||
server.port=${SERVER_PORT}
|
||||
|
||||
spring.profiles.active=${PROFILE}
|
||||
|
||||
spring.security.oauth2.resourceserver.jwt.issuer-uri=${AUTHENTIK_ISSUER_URI}
|
||||
|
||||
# Database
|
||||
spring.datasource.url=${DATASOURCE_URL}
|
||||
spring.datasource.username=${DATASOURCE_USER}
|
||||
|
@ -19,7 +18,4 @@ spring.jpa.show-sql=true
|
|||
spring.jpa.hibernate.ddl-auto=validate
|
||||
spring.flyway.table=widgets_schema_history
|
||||
spring.flyway.baseline-version=0.9
|
||||
spring.flyway.baseline-on-migrate=true
|
||||
|
||||
# Security
|
||||
jwt.issuer-url=${AUTHENTIK_ISSUER_URL}
|
||||
spring.flyway.baseline-on-migrate=true
|
|
@ -6,4 +6,5 @@ include 'pefi-api-gateway'
|
|||
include 'pefi-core-api'
|
||||
include 'pefi-statements-api'
|
||||
include 'pefi-widgets-api'
|
||||
include 'pefi-login-service'
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue