feat(#27): Set up consola logger, make use of log levels in backend and frontend
This commit is contained in:
@@ -639,6 +639,7 @@ Set in `nuxt.config.ts` → `runtimeConfig.public`:
|
|||||||
- `serverApiBaseUrl` - Direct backend URL (server-side)
|
- `serverApiBaseUrl` - Direct backend URL (server-side)
|
||||||
- `serverApiBasePath` - Backend API base path
|
- `serverApiBasePath` - Backend API base path
|
||||||
- `keycloakTokenUrl` - Keycloak token endpoint
|
- `keycloakTokenUrl` - Keycloak token endpoint
|
||||||
|
- `logLevel` - Frontend log level (Consola). Controlled via `NUXT_PUBLIC_LOG_LEVEL` (defaults: `debug` in dev, `warn` in prod)
|
||||||
|
|
||||||
OAuth configuration (`runtimeConfig.oauth.keycloak`):
|
OAuth configuration (`runtimeConfig.oauth.keycloak`):
|
||||||
- `clientId`, `clientSecret`, `realm`, `serverUrl`, `redirectURL`
|
- `clientId`, `clientSecret`, `realm`, `serverUrl`, `redirectURL`
|
||||||
@@ -652,6 +653,9 @@ Main config in `src/main/resources/application.yaml`:
|
|||||||
- Database connection (PostgreSQL)
|
- Database connection (PostgreSQL)
|
||||||
- Keycloak JWT validation
|
- Keycloak JWT validation
|
||||||
- Server port (default: 8080)
|
- Server port (default: 8080)
|
||||||
|
- Logging levels via env vars:
|
||||||
|
- `LOGGING_LEVEL_ROOT`
|
||||||
|
- `LOGGING_LEVEL_APP` for `com.betriebsratkanzlei.legalconsenthub`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -74,53 +74,55 @@ services:
|
|||||||
- legalconsenthub-net
|
- legalconsenthub-net
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
|
#
|
||||||
backend:
|
# backend:
|
||||||
image: gitea.lugnas.de/denis/legalconsenthub-backend:latest
|
# image: gitea.lugnas.de/denis/legalconsenthub-backend:latest
|
||||||
container_name: legalconsenthub-backend-local
|
# container_name: legalconsenthub-backend-local
|
||||||
restart: on-failure:2
|
# restart: on-failure:2
|
||||||
environment:
|
# environment:
|
||||||
LEGALCONSENTHUB_DB_URL: jdbc:postgresql://legalconsenthub-db:5432/${LEGALCONSENTHUB_POSTGRES_DB}
|
# LOGGING_LEVEL_ROOT: ${LOGGING_LEVEL_ROOT:-DEBUG}
|
||||||
LEGALCONSENTHUB_DB_APP_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
# LOGGING_LEVEL_APP: ${LOGGING_LEVEL_APP:-DEBUG}
|
||||||
LEGALCONSENTHUB_DB_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
# LEGALCONSENTHUB_DB_URL: jdbc:postgresql://legalconsenthub-db:5432/${LEGALCONSENTHUB_POSTGRES_DB}
|
||||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: ${KEYCLOAK_ISSUER_URL}/realms/${KEYCLOAK_REALM}
|
# LEGALCONSENTHUB_DB_APP_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
||||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/${KEYCLOAK_REALM}/protocol/openid-connect/certs
|
# LEGALCONSENTHUB_DB_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
||||||
SERVER_PORT: 8080
|
# SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: ${KEYCLOAK_ISSUER_URL}/realms/${KEYCLOAK_REALM}
|
||||||
LEGALCONSENTHUB_PDF_STORAGE_FILESYSTEM_BASE_DIR: /var/lib/legalconsenthub/pdfs
|
# SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/${KEYCLOAK_REALM}/protocol/openid-connect/certs
|
||||||
ports:
|
# SERVER_PORT: 8080
|
||||||
- "8081:8080"
|
# LEGALCONSENTHUB_PDF_STORAGE_FILESYSTEM_BASE_DIR: /var/lib/legalconsenthub/pdfs
|
||||||
volumes:
|
# ports:
|
||||||
- legalconsenthub_pdf_cache:/var/lib/legalconsenthub/pdfs
|
# - "8081:8080"
|
||||||
depends_on:
|
# volumes:
|
||||||
legalconsenthub-db:
|
# - legalconsenthub_pdf_cache:/var/lib/legalconsenthub/pdfs
|
||||||
condition: service_started
|
# depends_on:
|
||||||
networks:
|
# legalconsenthub-db:
|
||||||
- legalconsenthub-net
|
# condition: service_started
|
||||||
env_file:
|
# networks:
|
||||||
- .env.dev
|
# - legalconsenthub-net
|
||||||
|
# env_file:
|
||||||
frontend:
|
# - .env.dev
|
||||||
image: gitea.lugnas.de/denis/legalconsenthub:latest
|
#
|
||||||
container_name: legalconsenthub-frontend-local
|
# frontend:
|
||||||
ports:
|
# image: gitea.lugnas.de/denis/legalconsenthub:latest
|
||||||
- "3211:3000"
|
# container_name: legalconsenthub-frontend-local
|
||||||
networks:
|
# ports:
|
||||||
- legalconsenthub-net
|
# - "3211:3000"
|
||||||
env_file:
|
# networks:
|
||||||
- .env.dev
|
# - legalconsenthub-net
|
||||||
|
# env_file:
|
||||||
legalconsenthub-db:
|
# - .env.dev
|
||||||
image: postgres:latest
|
#
|
||||||
container_name: legalconsenthub-postgres-local
|
# legalconsenthub-db:
|
||||||
environment:
|
# image: postgres:latest
|
||||||
POSTGRES_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
# container_name: legalconsenthub-postgres-local
|
||||||
POSTGRES_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
# environment:
|
||||||
POSTGRES_DB: ${LEGALCONSENTHUB_POSTGRES_DB}
|
# POSTGRES_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
||||||
ports:
|
# POSTGRES_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
||||||
- "5446:5432"
|
# POSTGRES_DB: ${LEGALCONSENTHUB_POSTGRES_DB}
|
||||||
networks:
|
# ports:
|
||||||
- legalconsenthub-net
|
# - "5446:5432"
|
||||||
volumes:
|
# networks:
|
||||||
- legalconsenthub_postgres_data:/var/lib/postgresql
|
# - legalconsenthub-net
|
||||||
env_file:
|
# volumes:
|
||||||
- .env.dev
|
# - legalconsenthub_postgres_data:/var/lib/postgresql
|
||||||
|
# env_file:
|
||||||
|
# - .env.dev
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ services:
|
|||||||
container_name: legalconsenthub-backend
|
container_name: legalconsenthub-backend
|
||||||
restart: on-failure:2
|
restart: on-failure:2
|
||||||
environment:
|
environment:
|
||||||
|
LOGGING_LEVEL_ROOT: ${LOGGING_LEVEL_ROOT:-WARN}
|
||||||
|
LOGGING_LEVEL_APP: ${LOGGING_LEVEL_APP:-WARN}
|
||||||
LEGALCONSENTHUB_DB_URL: jdbc:postgresql://legalconsenthub-db:5432/${LEGALCONSENTHUB_POSTGRES_DB}
|
LEGALCONSENTHUB_DB_URL: jdbc:postgresql://legalconsenthub-db:5432/${LEGALCONSENTHUB_POSTGRES_DB}
|
||||||
LEGALCONSENTHUB_DB_APP_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
LEGALCONSENTHUB_DB_APP_USER: ${LEGALCONSENTHUB_POSTGRES_USER}
|
||||||
LEGALCONSENTHUB_DB_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
LEGALCONSENTHUB_DB_PASSWORD: ${LEGALCONSENTHUB_POSTGRES_PASSWORD}
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class ApplicationFormService(
|
|||||||
id: UUID,
|
id: UUID,
|
||||||
applicationFormDto: ApplicationFormDto,
|
applicationFormDto: ApplicationFormDto,
|
||||||
): ApplicationForm {
|
): ApplicationForm {
|
||||||
println("Updating ApplicationForm: $applicationFormDto")
|
|
||||||
val existingApplicationForm = getApplicationFormById(id)
|
val existingApplicationForm = getApplicationFormById(id)
|
||||||
val existingSnapshot = versionService.createSnapshot(existingApplicationForm)
|
val existingSnapshot = versionService.createSnapshot(existingApplicationForm)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ class JwtUserSyncFilter(
|
|||||||
val logger = LoggerFactory.getLogger(JwtUserSyncFilter::class.java)
|
val logger = LoggerFactory.getLogger(JwtUserSyncFilter::class.java)
|
||||||
val auth: Authentication? = SecurityContextHolder.getContext().authentication
|
val auth: Authentication? = SecurityContextHolder.getContext().authentication
|
||||||
|
|
||||||
println("JwtUserSyncFilter invoked for request: ${request.requestURI}")
|
|
||||||
try {
|
try {
|
||||||
if (auth is JwtAuthenticationToken && auth.isAuthenticated) {
|
if (auth is JwtAuthenticationToken && auth.isAuthenticated) {
|
||||||
val jwt: Jwt = auth.token
|
val jwt: Jwt = auth.token
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ spring:
|
|||||||
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: create
|
ddl-auto: create
|
||||||
show-sql: true
|
show-sql: false
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
format_sql: true
|
format_sql: true
|
||||||
@@ -39,16 +39,6 @@ spring:
|
|||||||
issuer-uri: http://localhost:7080/realms/legalconsenthub
|
issuer-uri: http://localhost:7080/realms/legalconsenthub
|
||||||
jwk-set-uri: http://localhost:7080/realms/legalconsenthub/protocol/openid-connect/certs
|
jwk-set-uri: http://localhost:7080/realms/legalconsenthub/protocol/openid-connect/certs
|
||||||
|
|
||||||
logging:
|
|
||||||
level:
|
|
||||||
org:
|
|
||||||
springframework:
|
|
||||||
security: TRACE
|
|
||||||
oauth2: TRACE
|
|
||||||
web: TRACE
|
|
||||||
org.testcontainers: INFO
|
|
||||||
com.github.dockerjava: WARN
|
|
||||||
|
|
||||||
management:
|
management:
|
||||||
health:
|
health:
|
||||||
mail:
|
mail:
|
||||||
|
|||||||
@@ -61,3 +61,14 @@ management:
|
|||||||
health:
|
health:
|
||||||
mail:
|
mail:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
# Log levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.betriebsratkanzlei.legalconsenthub: ${LOGGING_LEVEL_APP:INFO}
|
||||||
|
org:
|
||||||
|
springframework:
|
||||||
|
security: ${LOGGING_LEVEL_ROOT:INFO}
|
||||||
|
oauth2: ${LOGGING_LEVEL_ROOT:INFO}
|
||||||
|
web: ${LOGGING_LEVEL_ROOT:INFO}
|
||||||
|
testcontainers: ${LOGGING_LEVEL_ROOT:INFO}
|
||||||
|
|||||||
@@ -91,9 +91,10 @@ const emit = defineEmits<{
|
|||||||
const commentStore = useCommentStore()
|
const commentStore = useCommentStore()
|
||||||
const { load: loadComments } = commentStore
|
const { load: loadComments } = commentStore
|
||||||
const { comments } = storeToRefs(commentStore)
|
const { comments } = storeToRefs(commentStore)
|
||||||
|
const logger = useLogger().withTag('FormEngine')
|
||||||
|
|
||||||
if (props.applicationFormId) {
|
if (props.applicationFormId) {
|
||||||
console.log('Loading comments for application form:', props.applicationFormId)
|
logger.debug('Loading comments for application form:', props.applicationFormId)
|
||||||
await loadComments(props.applicationFormId)
|
await loadComments(props.applicationFormId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import type { ApplicationFormDto, PagedApplicationFormDto, FormElementDto } from '~~/.api-client'
|
import type { ApplicationFormDto, PagedApplicationFormDto, FormElementDto } from '~~/.api-client'
|
||||||
import { useApplicationFormApi } from './useApplicationFormApi'
|
import { useApplicationFormApi } from './useApplicationFormApi'
|
||||||
|
import { useLogger } from '../useLogger'
|
||||||
|
|
||||||
export function useApplicationForm() {
|
export function useApplicationForm() {
|
||||||
const applicationFormApi = useApplicationFormApi()
|
const applicationFormApi = useApplicationFormApi()
|
||||||
|
const logger = useLogger().withTag('applicationForm')
|
||||||
|
|
||||||
async function createApplicationForm(applicationFormDto: ApplicationFormDto): Promise<ApplicationFormDto> {
|
async function createApplicationForm(applicationFormDto: ApplicationFormDto): Promise<ApplicationFormDto> {
|
||||||
try {
|
try {
|
||||||
return await applicationFormApi.createApplicationForm(applicationFormDto)
|
return await applicationFormApi.createApplicationForm(applicationFormDto)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error('Failed creating application form:', e)
|
logger.error('Failed creating application form:', e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +19,7 @@ export function useApplicationForm() {
|
|||||||
try {
|
try {
|
||||||
return await applicationFormApi.getAllApplicationForms(organizationId)
|
return await applicationFormApi.getAllApplicationForms(organizationId)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error('Failed retrieving application forms:', e, JSON.stringify(e))
|
logger.error('Failed retrieving application forms:', e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +28,7 @@ export function useApplicationForm() {
|
|||||||
try {
|
try {
|
||||||
return await applicationFormApi.getApplicationFormById(id)
|
return await applicationFormApi.getApplicationFormById(id)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed retrieving application form with ID ${id}:`, e)
|
logger.error(`Failed retrieving application form with ID ${id}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,11 +41,11 @@ export function useApplicationForm() {
|
|||||||
return Promise.reject(new Error('ID or application form DTO missing'))
|
return Promise.reject(new Error('ID or application form DTO missing'))
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Updating application form with ID:', id, applicationFormDto)
|
logger.debug('Updating application form with ID:', id, applicationFormDto)
|
||||||
try {
|
try {
|
||||||
return await applicationFormApi.updateApplicationForm(id, applicationFormDto)
|
return await applicationFormApi.updateApplicationForm(id, applicationFormDto)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed updating application form with ID ${id}:`, e)
|
logger.error(`Failed updating application form with ID ${id}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,7 @@ export function useApplicationForm() {
|
|||||||
try {
|
try {
|
||||||
return await applicationFormApi.deleteApplicationFormById(id)
|
return await applicationFormApi.deleteApplicationFormById(id)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed deleting application form with ID ${id}:`, e)
|
logger.error(`Failed deleting application form with ID ${id}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +67,7 @@ export function useApplicationForm() {
|
|||||||
try {
|
try {
|
||||||
return await applicationFormApi.submitApplicationForm(id)
|
return await applicationFormApi.submitApplicationForm(id)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed submitting application form with ID ${id}:`, e)
|
logger.error(`Failed submitting application form with ID ${id}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,7 +90,7 @@ export function useApplicationForm() {
|
|||||||
position
|
position
|
||||||
)
|
)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed adding form element to subsection ${subsectionId}:`, e)
|
logger.error(`Failed adding form element to subsection ${subsectionId}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { type ApplicationFormDto, type PagedApplicationFormDto, ResponseError } from '~~/.api-client'
|
import { type ApplicationFormDto, type PagedApplicationFormDto, ResponseError } from '~~/.api-client'
|
||||||
import { useApplicationFormTemplateApi } from './useApplicationFormTemplateApi'
|
import { useApplicationFormTemplateApi } from './useApplicationFormTemplateApi'
|
||||||
|
import { useLogger } from '../useLogger'
|
||||||
|
|
||||||
const currentApplicationForm: Ref<ApplicationFormDto | undefined> = ref()
|
const currentApplicationForm: Ref<ApplicationFormDto | undefined> = ref()
|
||||||
|
|
||||||
export async function useApplicationFormTemplate() {
|
export async function useApplicationFormTemplate() {
|
||||||
const applicationFormApi = await useApplicationFormTemplateApi()
|
const applicationFormApi = await useApplicationFormTemplateApi()
|
||||||
|
const logger = useLogger().withTag('applicationFormTemplate')
|
||||||
|
|
||||||
async function createApplicationFormTemplate(applicationFormDto: ApplicationFormDto): Promise<ApplicationFormDto> {
|
async function createApplicationFormTemplate(applicationFormDto: ApplicationFormDto): Promise<ApplicationFormDto> {
|
||||||
try {
|
try {
|
||||||
@@ -12,9 +14,9 @@ export async function useApplicationFormTemplate() {
|
|||||||
return currentApplicationForm.value
|
return currentApplicationForm.value
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error('Failed creating application form:', e.response)
|
logger.error('Failed creating application form:', e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed creating application form:', e)
|
logger.error('Failed creating application form:', e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -25,9 +27,9 @@ export async function useApplicationFormTemplate() {
|
|||||||
return await applicationFormApi.getAllApplicationFormTemplates()
|
return await applicationFormApi.getAllApplicationFormTemplates()
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error('Failed retrieving application forms:', e.response)
|
logger.error('Failed retrieving application forms:', e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed retrieving application forms:', e)
|
logger.error('Failed retrieving application forms:', e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -38,9 +40,9 @@ export async function useApplicationFormTemplate() {
|
|||||||
return await applicationFormApi.getApplicationFormTemplateById(id)
|
return await applicationFormApi.getApplicationFormTemplateById(id)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed retrieving application form with ID ${id}:`, e.response)
|
logger.error(`Failed retrieving application form with ID ${id}:`, e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed retrieving application form with ID ${id}:`, e)
|
logger.error(`Failed retrieving application form with ID ${id}:`, e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -59,9 +61,9 @@ export async function useApplicationFormTemplate() {
|
|||||||
return currentApplicationForm.value
|
return currentApplicationForm.value
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed updating application form with ID ${id}:`, e.response)
|
logger.error(`Failed updating application form with ID ${id}:`, e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed updating application form with ID ${id}:`, e)
|
logger.error(`Failed updating application form with ID ${id}:`, e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -72,9 +74,9 @@ export async function useApplicationFormTemplate() {
|
|||||||
return await applicationFormApi.deleteApplicationFormTemplateById(id)
|
return await applicationFormApi.deleteApplicationFormTemplateById(id)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed deleting application form with ID ${id}:`, e.response)
|
logger.error(`Failed deleting application form with ID ${id}:`, e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed deleting application form with ID ${id}:`, e)
|
logger.error(`Failed deleting application form with ID ${id}:`, e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import type { ApplicationFormVersionDto, ApplicationFormVersionListItemDto, ApplicationFormDto } from '~~/.api-client'
|
import type { ApplicationFormVersionDto, ApplicationFormVersionListItemDto, ApplicationFormDto } from '~~/.api-client'
|
||||||
import { useApplicationFormVersionApi } from '~/composables'
|
import { useApplicationFormVersionApi } from '~/composables'
|
||||||
|
import { useLogger } from '../useLogger'
|
||||||
|
|
||||||
export function useApplicationFormVersion() {
|
export function useApplicationFormVersion() {
|
||||||
const versionApi = useApplicationFormVersionApi()
|
const versionApi = useApplicationFormVersionApi()
|
||||||
|
const logger = useLogger().withTag('applicationFormVersion')
|
||||||
|
|
||||||
async function getVersions(applicationFormId: string): Promise<ApplicationFormVersionListItemDto[]> {
|
async function getVersions(applicationFormId: string): Promise<ApplicationFormVersionListItemDto[]> {
|
||||||
try {
|
try {
|
||||||
return await versionApi.getVersions(applicationFormId)
|
return await versionApi.getVersions(applicationFormId)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed retrieving versions for application form ${applicationFormId}:`, e)
|
logger.error(`Failed retrieving versions for application form ${applicationFormId}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +19,7 @@ export function useApplicationFormVersion() {
|
|||||||
try {
|
try {
|
||||||
return await versionApi.getVersion(applicationFormId, versionNumber)
|
return await versionApi.getVersion(applicationFormId, versionNumber)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed retrieving version ${versionNumber} for application form ${applicationFormId}:`, e)
|
logger.error(`Failed retrieving version ${versionNumber} for application form ${applicationFormId}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +32,7 @@ export function useApplicationFormVersion() {
|
|||||||
try {
|
try {
|
||||||
return await versionApi.restoreVersion(applicationFormId, versionNumber)
|
return await versionApi.restoreVersion(applicationFormId, versionNumber)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error(`Failed restoring version ${versionNumber} for application form ${applicationFormId}:`, e)
|
logger.error(`Failed restoring version ${versionNumber} for application form ${applicationFormId}:`, e)
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { CreateCommentDto, CommentDto } from '~~/.api-client'
|
import type { CreateCommentDto, CommentDto } from '~~/.api-client'
|
||||||
import { useCommentStore } from '~~/stores/useCommentStore'
|
import { useCommentStore } from '~~/stores/useCommentStore'
|
||||||
import { useUserStore } from '~~/stores/useUserStore'
|
import { useUserStore } from '~~/stores/useUserStore'
|
||||||
|
import { useLogger } from '../useLogger'
|
||||||
|
|
||||||
export function useCommentTextarea(applicationFormId: string) {
|
export function useCommentTextarea(applicationFormId: string) {
|
||||||
const commentStore = useCommentStore()
|
const commentStore = useCommentStore()
|
||||||
@@ -11,6 +12,8 @@ export function useCommentTextarea(applicationFormId: string) {
|
|||||||
const currentEditedComment = ref<CommentDto | null>(null)
|
const currentEditedComment = ref<CommentDto | null>(null)
|
||||||
const commentTextAreaValue = ref('')
|
const commentTextAreaValue = ref('')
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const { t: $t } = useI18n()
|
||||||
|
const logger = useLogger().withTag('commentTextarea')
|
||||||
|
|
||||||
async function submitComment(formElementId: string) {
|
async function submitComment(formElementId: string) {
|
||||||
const newCommentDto: CreateCommentDto = {
|
const newCommentDto: CreateCommentDto = {
|
||||||
@@ -19,10 +22,10 @@ export function useCommentTextarea(applicationFormId: string) {
|
|||||||
try {
|
try {
|
||||||
await createComment(applicationFormId, formElementId, newCommentDto)
|
await createComment(applicationFormId, formElementId, newCommentDto)
|
||||||
commentTextAreaValue.value = ''
|
commentTextAreaValue.value = ''
|
||||||
toast.add({ title: 'Comment created successfully', color: 'success' })
|
toast.add({ title: $t('comments.created'), color: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({ title: 'Error creating comment', color: 'error' })
|
toast.add({ title: $t('comments.createError'), color: 'error' })
|
||||||
console.error('Error creating comment:', e)
|
logger.error('Error creating comment:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,10 +37,10 @@ export function useCommentTextarea(applicationFormId: string) {
|
|||||||
commentTextAreaValue.value = ''
|
commentTextAreaValue.value = ''
|
||||||
currentEditedComment.value = null
|
currentEditedComment.value = null
|
||||||
isEditingComment.value = false
|
isEditingComment.value = false
|
||||||
toast.add({ title: 'Comment updated successfully', color: 'success' })
|
toast.add({ title: $t('comments.updated'), color: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.add({ title: 'Error updating comment', color: 'error' })
|
toast.add({ title: $t('comments.updateError'), color: 'error' })
|
||||||
console.error('Error updating comment:', e)
|
logger.error('Error updating comment:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import type { NotificationDto } from '~~/.api-client'
|
|||||||
import { useUserStore } from '~~/stores/useUserStore'
|
import { useUserStore } from '~~/stores/useUserStore'
|
||||||
|
|
||||||
export const useNotification = () => {
|
export const useNotification = () => {
|
||||||
|
const logger = useLogger().withTag('notification')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getNotifications,
|
getNotifications,
|
||||||
getUnreadNotifications,
|
getUnreadNotifications,
|
||||||
@@ -22,7 +24,7 @@ export const useNotification = () => {
|
|||||||
|
|
||||||
const fetchNotifications = async (page: number = 0, size: number = 20) => {
|
const fetchNotifications = async (page: number = 0, size: number = 20) => {
|
||||||
if (!organizationId.value) {
|
if (!organizationId.value) {
|
||||||
console.warn('No organization selected')
|
logger.warn('No organization selected')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
@@ -31,7 +33,7 @@ export const useNotification = () => {
|
|||||||
notifications.value = response.content || []
|
notifications.value = response.content || []
|
||||||
return response
|
return response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch notifications:', error)
|
logger.error('Failed to fetch notifications:', error)
|
||||||
throw error
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
@@ -40,7 +42,7 @@ export const useNotification = () => {
|
|||||||
|
|
||||||
const fetchUnreadNotifications = async () => {
|
const fetchUnreadNotifications = async () => {
|
||||||
if (!organizationId.value) {
|
if (!organizationId.value) {
|
||||||
console.warn('No organization selected')
|
logger.warn('No organization selected')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -48,14 +50,14 @@ export const useNotification = () => {
|
|||||||
unreadNotifications.value = response || []
|
unreadNotifications.value = response || []
|
||||||
return response
|
return response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch unread notifications:', error)
|
logger.error('Failed to fetch unread notifications:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchUnreadCount = async () => {
|
const fetchUnreadCount = async () => {
|
||||||
if (!userId.value || !organizationId.value) {
|
if (!userId.value || !organizationId.value) {
|
||||||
console.warn('No user or organization selected')
|
logger.warn('No user or organization selected')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -63,14 +65,14 @@ export const useNotification = () => {
|
|||||||
unreadCount.value = count || 0
|
unreadCount.value = count || 0
|
||||||
return count
|
return count
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch unread count:', error)
|
logger.error('Failed to fetch unread count:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAllAsRead = async () => {
|
const markAllAsRead = async () => {
|
||||||
if (!organizationId.value) {
|
if (!organizationId.value) {
|
||||||
console.warn('No organization selected')
|
logger.warn('No organization selected')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -79,14 +81,14 @@ export const useNotification = () => {
|
|||||||
unreadNotifications.value = []
|
unreadNotifications.value = []
|
||||||
notifications.value = notifications.value.map((n) => ({ ...n, isRead: true }))
|
notifications.value = notifications.value.map((n) => ({ ...n, isRead: true }))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to mark all as read:', error)
|
logger.error('Failed to mark all as read:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAsRead = async (notificationId: string) => {
|
const markAsRead = async (notificationId: string) => {
|
||||||
if (!organizationId.value) {
|
if (!organizationId.value) {
|
||||||
console.warn('No organization selected')
|
logger.warn('No organization selected')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -102,7 +104,7 @@ export const useNotification = () => {
|
|||||||
unreadCount.value--
|
unreadCount.value--
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to mark notification as read:', error)
|
logger.error('Failed to mark notification as read:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { ComplianceStatus, type FormElementDto } from '~~/.api-client'
|
import { ComplianceStatus, type FormElementDto } from '~~/.api-client'
|
||||||
import { complianceCheckableElementTypes, complianceMap } from './complianceMap'
|
import { complianceCheckableElementTypes, complianceMap } from './complianceMap'
|
||||||
import type { FormElementId } from '~~/types/formElement'
|
import type { FormElementId } from '~~/types/formElement'
|
||||||
|
import { useLogger } from './useLogger'
|
||||||
|
|
||||||
const formElementComplianceMap = ref(new Map<FormElementId, ComplianceStatus>())
|
const formElementComplianceMap = ref(new Map<FormElementId, ComplianceStatus>())
|
||||||
|
|
||||||
export function useApplicationFormValidator() {
|
export function useApplicationFormValidator() {
|
||||||
|
const logger = useLogger().withTag('validator')
|
||||||
|
|
||||||
function getHighestComplianceStatus(): ComplianceStatus {
|
function getHighestComplianceStatus(): ComplianceStatus {
|
||||||
const complianceStatusValues = Array.from(formElementComplianceMap.value.values())
|
const complianceStatusValues = Array.from(formElementComplianceMap.value.values())
|
||||||
const highestComplianceNumber = Math.max(
|
const highestComplianceNumber = Math.max(
|
||||||
@@ -37,7 +40,7 @@ export function useApplicationFormValidator() {
|
|||||||
|
|
||||||
formElement.options.forEach((option) => {
|
formElement.options.forEach((option) => {
|
||||||
if (!option.value) {
|
if (!option.value) {
|
||||||
console.log(`Value missing for ${formElement.type}`)
|
logger.debug(`Value missing for ${formElement.type}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
legalconsenthub/app/composables/useLogger.ts
Normal file
5
legalconsenthub/app/composables/useLogger.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { ConsolaInstance } from 'consola'
|
||||||
|
|
||||||
|
export function useLogger(): ConsolaInstance {
|
||||||
|
return useNuxtApp().$logger
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { useLogger } from './useLogger'
|
||||||
|
|
||||||
export const isServerAvailable = ref(true)
|
export const isServerAvailable = ref(true)
|
||||||
export const isChecking = ref(false)
|
export const isChecking = ref(false)
|
||||||
export const lastCheckTime = ref<Date | null>(null)
|
export const lastCheckTime = ref<Date | null>(null)
|
||||||
|
|
||||||
export function useServerHealth() {
|
export function useServerHealth() {
|
||||||
|
const logger = useLogger().withTag('serverHealth')
|
||||||
const checkInterval = ref<ReturnType<typeof setInterval> | null>(null)
|
const checkInterval = ref<ReturnType<typeof setInterval> | null>(null)
|
||||||
const healthCheckUrl = '/api/actuator/health'
|
const healthCheckUrl = '/api/actuator/health'
|
||||||
|
|
||||||
@@ -30,11 +33,11 @@ export function useServerHealth() {
|
|||||||
isServerAvailable.value = response.ok
|
isServerAvailable.value = response.ok
|
||||||
|
|
||||||
if (!wasAvailable && isServerAvailable.value) {
|
if (!wasAvailable && isServerAvailable.value) {
|
||||||
console.info('Server is back online')
|
logger.info('Server is back online')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasAvailable && !isServerAvailable.value) {
|
if (wasAvailable && !isServerAvailable.value) {
|
||||||
console.warn('Server is no longer available')
|
logger.warn('Server is no longer available')
|
||||||
}
|
}
|
||||||
|
|
||||||
return isServerAvailable.value
|
return isServerAvailable.value
|
||||||
@@ -43,7 +46,7 @@ export function useServerHealth() {
|
|||||||
isServerAvailable.value = false
|
isServerAvailable.value = false
|
||||||
|
|
||||||
if (wasAvailable) {
|
if (wasAvailable) {
|
||||||
console.warn('Server health check failed:', error)
|
logger.warn('Server health check failed:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ const links = [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
|
const logger = useLogger().withTag('layout')
|
||||||
|
|
||||||
const isNotificationsSlideoverOpen = ref(false)
|
const isNotificationsSlideoverOpen = ref(false)
|
||||||
const { unreadCount, fetchUnreadCount, startPeriodicRefresh } = useNotification()
|
const { unreadCount, fetchUnreadCount, startPeriodicRefresh } = useNotification()
|
||||||
@@ -73,13 +74,13 @@ provide('notificationState', {
|
|||||||
|
|
||||||
async function copyAccessTokenToClipboard() {
|
async function copyAccessTokenToClipboard() {
|
||||||
const { session } = useUserSession()
|
const { session } = useUserSession()
|
||||||
console.log('Access Token :', session.value?.jwt?.accessToken)
|
logger.debug('Access Token :', session.value?.jwt?.accessToken)
|
||||||
const accessToken = session.value?.jwt?.accessToken
|
const accessToken = session.value?.jwt?.accessToken
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
navigator.clipboard.writeText(accessToken)
|
navigator.clipboard.writeText(accessToken)
|
||||||
console.log('Access token copied to clipboard')
|
logger.info('Access token copied to clipboard')
|
||||||
} else {
|
} else {
|
||||||
console.warn('No access token found in session')
|
logger.warn('No access token found in session')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,14 +3,16 @@
|
|||||||
import { appendResponseHeader } from 'h3'
|
import { appendResponseHeader } from 'h3'
|
||||||
import { parse, parseSetCookie, serialize } from 'cookie-es'
|
import { parse, parseSetCookie, serialize } from 'cookie-es'
|
||||||
import { jwtDecode, type JwtPayload } from 'jwt-decode'
|
import { jwtDecode, type JwtPayload } from 'jwt-decode'
|
||||||
|
import { useLogger } from '../composables/useLogger'
|
||||||
|
|
||||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
const nuxtApp = useNuxtApp()
|
const nuxtApp = useNuxtApp()
|
||||||
// Don't run on client hydration when server rendered
|
// Don't run on client hydration when server rendered
|
||||||
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
|
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
|
||||||
|
|
||||||
console.log('🔍 Middleware: refreshToken.global.ts')
|
const logger = useLogger().withTag('refreshToken')
|
||||||
console.log(` from: ${from.fullPath} to: ${to.fullPath}`)
|
logger.debug('🔍 Middleware: refreshToken.global.ts')
|
||||||
|
logger.debug(`from: ${from.fullPath} to: ${to.fullPath}`)
|
||||||
|
|
||||||
const { session, clear: clearSession, fetch: fetchSession } = useUserSession()
|
const { session, clear: clearSession, fetch: fetchSession } = useUserSession()
|
||||||
// Ignore if no tokens
|
// Ignore if no tokens
|
||||||
@@ -25,11 +27,11 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
|||||||
|
|
||||||
// Both tokens expired, clearing session
|
// Both tokens expired, clearing session
|
||||||
if (isExpired(accessPayload) && isExpired(refreshPayload)) {
|
if (isExpired(accessPayload) && isExpired(refreshPayload)) {
|
||||||
console.info('both tokens expired, clearing session')
|
logger.info('both tokens expired, clearing session')
|
||||||
await clearSession()
|
await clearSession()
|
||||||
return navigateTo('/login')
|
return navigateTo('/login')
|
||||||
} else if (isExpired(accessPayload)) {
|
} else if (isExpired(accessPayload)) {
|
||||||
console.info('access token expired, refreshing')
|
logger.info('access token expired, refreshing')
|
||||||
await useRequestFetch()('/api/jwt/refresh', {
|
await useRequestFetch()('/api/jwt/refresh', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
onResponse({ response: { headers } }: { response: { headers: Headers } }) {
|
onResponse({ response: { headers } }: { response: { headers: Headers } }) {
|
||||||
@@ -41,7 +43,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
|||||||
const { name, value } = parseSetCookie(setCookie)
|
const { name, value } = parseSetCookie(setCookie)
|
||||||
|
|
||||||
if (name === runtimeConfig.session.name) {
|
if (name === runtimeConfig.session.name) {
|
||||||
console.log('updating headers.cookie to', value)
|
logger.debug('updating headers.cookie to', value)
|
||||||
const cookies = parse(serverEvent.headers.get('cookie') || '')
|
const cookies = parse(serverEvent.headers.get('cookie') || '')
|
||||||
|
|
||||||
// set or overwrite existing cookie
|
// set or overwrite existing cookie
|
||||||
@@ -64,10 +66,10 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError() {
|
onError() {
|
||||||
console.error('🔍 Middleware: Token refresh failed')
|
logger.error('🔍 Middleware: Token refresh failed')
|
||||||
const { loggedIn } = useUserSession()
|
const { loggedIn } = useUserSession()
|
||||||
if (!loggedIn.value) {
|
if (!loggedIn.value) {
|
||||||
console.log('🔍 Middleware: User not logged in, redirecting to /login')
|
logger.debug('🔍 Middleware: User not logged in, redirecting to /login')
|
||||||
return navigateTo('/login')
|
return navigateTo('/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ const { hasRole } = usePermissions()
|
|||||||
const { getAllApplicationFormTemplates, updateApplicationFormTemplate, createApplicationFormTemplate } =
|
const { getAllApplicationFormTemplates, updateApplicationFormTemplate, createApplicationFormTemplate } =
|
||||||
await useApplicationFormTemplate()
|
await useApplicationFormTemplate()
|
||||||
const { t: $t } = useI18n()
|
const { t: $t } = useI18n()
|
||||||
|
const logger = useLogger().withTag('administration')
|
||||||
|
|
||||||
if (!hasRole('CHIEF_EXECUTIVE_OFFICER') && !hasRole('IT_DEPARTMENT')) {
|
if (!hasRole('CHIEF_EXECUTIVE_OFFICER') && !hasRole('IT_DEPARTMENT')) {
|
||||||
await navigateTo('/')
|
await navigateTo('/')
|
||||||
@@ -239,7 +240,7 @@ async function saveTemplate() {
|
|||||||
description: $t('templates.saveError'),
|
description: $t('templates.saveError'),
|
||||||
color: 'error'
|
color: 'error'
|
||||||
})
|
})
|
||||||
console.error('Error saving template:', error)
|
logger.error('Error saving template:', error)
|
||||||
} finally {
|
} finally {
|
||||||
isSaving.value = false
|
isSaving.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
const logger = useLogger().withTag('auth callback')
|
||||||
try {
|
try {
|
||||||
await navigateTo('/')
|
await navigateTo('/')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error during login', e)
|
logger.error('Error during login', e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const userStore = useUserStore()
|
|||||||
const { selectedOrganization } = storeToRefs(userStore)
|
const { selectedOrganization } = storeToRefs(userStore)
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const { t: $t } = useI18n()
|
const { t: $t } = useI18n()
|
||||||
|
const logger = useLogger().withTag('create')
|
||||||
|
|
||||||
const { data, error } = await useAsyncData<PagedApplicationFormDto>(
|
const { data, error } = await useAsyncData<PagedApplicationFormDto>(
|
||||||
'create-application-form',
|
'create-application-form',
|
||||||
@@ -132,11 +133,11 @@ function handleFormElementSectionsUpdate(sections: FormElementSectionDto[]) {
|
|||||||
|
|
||||||
async function prepareAndCreateApplicationForm() {
|
async function prepareAndCreateApplicationForm() {
|
||||||
if (!applicationFormTemplate.value) {
|
if (!applicationFormTemplate.value) {
|
||||||
console.error('Application form data is undefined')
|
logger.error('Application form data is undefined')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('selectedOrganization', selectedOrganization.value)
|
logger.debug('selectedOrganization', selectedOrganization.value)
|
||||||
applicationFormTemplate.value.organizationId = selectedOrganization.value?.id ?? ''
|
applicationFormTemplate.value.organizationId = selectedOrganization.value?.id ?? ''
|
||||||
|
|
||||||
return await createApplicationForm(applicationFormTemplate.value)
|
return await createApplicationForm(applicationFormTemplate.value)
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ const appConfig = useAppConfig()
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { getUserById, updateEmailPreferences } = useUser()
|
const { getUserById, updateEmailPreferences } = useUser()
|
||||||
|
const logger = useLogger().withTag('settings')
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
'red',
|
'red',
|
||||||
@@ -152,7 +153,7 @@ onMounted(async () => {
|
|||||||
emailOnFormCreated.value = userData.emailOnFormCreated ?? true
|
emailOnFormCreated.value = userData.emailOnFormCreated ?? true
|
||||||
emailOnFormSubmitted.value = userData.emailOnFormSubmitted ?? true
|
emailOnFormSubmitted.value = userData.emailOnFormSubmitted ?? true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load user email preferences:', error)
|
logger.error('Failed to load user email preferences:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
|
import { createLogger } from '~~/shared/utils/logger'
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const logger = createLogger({
|
||||||
|
level: config.public.logLevel,
|
||||||
|
tag: 'app error-handler',
|
||||||
|
fancy: import.meta.env.MODE !== 'production'
|
||||||
|
})
|
||||||
|
|
||||||
nuxtApp.hook('vue:error', (error, instance, info) => {
|
nuxtApp.hook('vue:error', (error, instance, info) => {
|
||||||
console.error('Vue error:', error, 'Instance:', instance, 'Info:', info)
|
logger.error('Vue error:', error, 'Instance:', instance, 'Info:', info)
|
||||||
})
|
})
|
||||||
|
|
||||||
nuxtApp.hook('app:error', (error) => {
|
nuxtApp.hook('app:error', (error) => {
|
||||||
console.error('App error:', error)
|
logger.error('App error:', error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
16
legalconsenthub/app/plugins/logger.ts
Normal file
16
legalconsenthub/app/plugins/logger.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { createLogger } from '~~/shared/utils/logger'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(() => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const logger = createLogger({
|
||||||
|
level: config.public.logLevel,
|
||||||
|
tag: 'legalconsenthub',
|
||||||
|
fancy: import.meta.env.MODE !== 'production'
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
provide: {
|
||||||
|
logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { HTTPMethod } from 'h3'
|
import type { HTTPMethod } from 'h3'
|
||||||
|
import { useLogger } from '../composables/useLogger'
|
||||||
|
|
||||||
// Custom OpenAPI fetch client that wraps useRequestFetch. This ensures that authentication headers
|
// Custom OpenAPI fetch client that wraps useRequestFetch. This ensures that authentication headers
|
||||||
// are forwarded correctly during SSR. Unlike fetch, useRequestFetch returns data directly,
|
// are forwarded correctly during SSR. Unlike fetch, useRequestFetch returns data directly,
|
||||||
@@ -28,7 +29,8 @@ export const wrappedFetchWrap = (requestFetch: ReturnType<typeof useRequestFetch
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Fetch error:', error)
|
const logger = useLogger().withTag('wrappedFetch')
|
||||||
|
logger.error('Fetch error:', error)
|
||||||
|
|
||||||
// Check if it's a FetchError from ofetch
|
// Check if it's a FetchError from ofetch
|
||||||
if (error && typeof error === 'object' && 'status' in error) {
|
if (error && typeof error === 'object' && 'status' in error) {
|
||||||
|
|||||||
@@ -93,7 +93,11 @@
|
|||||||
"empty": "Keine Kommentare vorhanden",
|
"empty": "Keine Kommentare vorhanden",
|
||||||
"submit": "Absenden",
|
"submit": "Absenden",
|
||||||
"edit": "Kommentar bearbeiten",
|
"edit": "Kommentar bearbeiten",
|
||||||
"editAction": "Bearbeiten"
|
"editAction": "Bearbeiten",
|
||||||
|
"created": "Kommentar erfolgreich erstellt",
|
||||||
|
"createError": "Fehler beim Erstellen des Kommentars",
|
||||||
|
"updated": "Kommentar erfolgreich aktualisiert",
|
||||||
|
"updateError": "Fehler beim Aktualisieren des Kommentars"
|
||||||
},
|
},
|
||||||
"compliance": {
|
"compliance": {
|
||||||
"title": "Compliance-Status",
|
"title": "Compliance-Status",
|
||||||
|
|||||||
@@ -93,7 +93,11 @@
|
|||||||
"empty": "No comments available",
|
"empty": "No comments available",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"edit": "Edit Comment",
|
"edit": "Edit Comment",
|
||||||
"editAction": "Edit"
|
"editAction": "Edit",
|
||||||
|
"created": "Comment created successfully",
|
||||||
|
"createError": "Error creating comment",
|
||||||
|
"updated": "Comment updated successfully",
|
||||||
|
"updateError": "Error updating comment"
|
||||||
},
|
},
|
||||||
"compliance": {
|
"compliance": {
|
||||||
"title": "Compliance Status",
|
"title": "Compliance Status",
|
||||||
|
|||||||
14
legalconsenthub/index.d.ts
vendored
14
legalconsenthub/index.d.ts
vendored
@@ -3,6 +3,20 @@ declare module 'nuxt/schema' {
|
|||||||
clientProxyBasePath: string
|
clientProxyBasePath: string
|
||||||
serverApiBaseUrl: string
|
serverApiBaseUrl: string
|
||||||
serverApiBasePath: string
|
serverApiBasePath: string
|
||||||
|
keycloakTokenUrl: string
|
||||||
|
logLevel: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '#app' {
|
||||||
|
interface NuxtApp {
|
||||||
|
$logger: import('consola').ConsolaInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
$logger: import('consola').ConsolaInstance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ export default defineNuxtConfig({
|
|||||||
clientProxyBasePath: 'NOT_SET',
|
clientProxyBasePath: 'NOT_SET',
|
||||||
serverApiBaseUrl: 'NOT_SET',
|
serverApiBaseUrl: 'NOT_SET',
|
||||||
serverApiBasePath: 'NOT_SET',
|
serverApiBasePath: 'NOT_SET',
|
||||||
keycloakTokenUrl: 'NOT_SET'
|
keycloakTokenUrl: 'NOT_SET',
|
||||||
|
logLevel: 'NOT_SET'
|
||||||
},
|
},
|
||||||
oauth: {
|
oauth: {
|
||||||
keycloak: {
|
keycloak: {
|
||||||
|
|||||||
@@ -16,11 +16,12 @@
|
|||||||
"api:generate": "openapi-generator-cli generate -i ../api/legalconsenthub.yml -g typescript-fetch -o .api-client"
|
"api:generate": "openapi-generator-cli generate -i ../api/legalconsenthub.yml -g typescript-fetch -o .api-client"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@guolao/vue-monaco-editor": "^1.6.0",
|
"@guolao/vue-monaco-editor": "1.6.0",
|
||||||
"@nuxt/ui": "4.3.0",
|
"@nuxt/ui": "4.3.0",
|
||||||
"@nuxtjs/i18n": "10.0.3",
|
"@nuxtjs/i18n": "10.0.3",
|
||||||
"@pinia/nuxt": "0.11.2",
|
"@pinia/nuxt": "0.11.2",
|
||||||
"@vueuse/core": "^13.6.0",
|
"@vueuse/core": "^13.6.0",
|
||||||
|
"consola": "3.4.2",
|
||||||
"h3": "1.15.4",
|
"h3": "1.15.4",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"nuxt": "4.2.0",
|
"nuxt": "4.2.0",
|
||||||
|
|||||||
5
legalconsenthub/pnpm-lock.yaml
generated
5
legalconsenthub/pnpm-lock.yaml
generated
@@ -9,7 +9,7 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@guolao/vue-monaco-editor':
|
'@guolao/vue-monaco-editor':
|
||||||
specifier: ^1.6.0
|
specifier: 1.6.0
|
||||||
version: 1.6.0(monaco-editor@0.55.1)(vue@3.5.26(typescript@5.7.3))
|
version: 1.6.0(monaco-editor@0.55.1)(vue@3.5.26(typescript@5.7.3))
|
||||||
'@nuxt/ui':
|
'@nuxt/ui':
|
||||||
specifier: 4.3.0
|
specifier: 4.3.0
|
||||||
@@ -23,6 +23,9 @@ importers:
|
|||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^13.6.0
|
specifier: ^13.6.0
|
||||||
version: 13.9.0(vue@3.5.26(typescript@5.7.3))
|
version: 13.9.0(vue@3.5.26(typescript@5.7.3))
|
||||||
|
consola:
|
||||||
|
specifier: 3.4.2
|
||||||
|
version: 3.4.2
|
||||||
h3:
|
h3:
|
||||||
specifier: 1.15.4
|
specifier: 1.15.4
|
||||||
version: 1.15.4
|
version: 1.15.4
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import type { H3Event } from 'h3'
|
import type { H3Event } from 'h3'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from 'jwt-decode'
|
||||||
|
import { createLogger } from '~~/shared/utils/logger'
|
||||||
|
|
||||||
export default defineEventHandler(async (event: H3Event) => {
|
export default defineEventHandler(async (event: H3Event) => {
|
||||||
const { serverApiBaseUrl, clientProxyBasePath } = useRuntimeConfig().public
|
const { serverApiBaseUrl, clientProxyBasePath } = useRuntimeConfig().public
|
||||||
|
const logger = createLogger({
|
||||||
|
level: useRuntimeConfig().public.logLevel,
|
||||||
|
tag: '🔀 proxy'
|
||||||
|
})
|
||||||
const escapedClientProxyBasePath = clientProxyBasePath.replace(/^\//, '\\/')
|
const escapedClientProxyBasePath = clientProxyBasePath.replace(/^\//, '\\/')
|
||||||
// Use the escaped value in the regex
|
// Use the escaped value in the regex
|
||||||
const path = event.path.replace(new RegExp(`^${escapedClientProxyBasePath}`), '')
|
const path = event.path.replace(new RegExp(`^${escapedClientProxyBasePath}`), '')
|
||||||
@@ -19,8 +24,10 @@ export default defineEventHandler(async (event: H3Event) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessToken) console.log('🔀 [PROXY] Expiration:', new Date(jwtDecode(accessToken).exp! * 1000).toISOString())
|
if (accessToken) {
|
||||||
console.log('🔀 [PROXY] Proxying request to:', target)
|
logger.debug('Expiration:', new Date(jwtDecode(accessToken).exp! * 1000).toISOString())
|
||||||
|
}
|
||||||
|
logger.debug('Proxying request to:', target)
|
||||||
|
|
||||||
return proxyRequest(event, target, {
|
return proxyRequest(event, target, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from 'jwt-decode'
|
||||||
import type { KeycloakTokenPayload, Organization } from '~~/types/keycloak'
|
import type { KeycloakTokenPayload, Organization } from '~~/types/keycloak'
|
||||||
|
import { createLogger } from '~~/shared/utils/logger'
|
||||||
|
|
||||||
export default defineOAuthKeycloakEventHandler({
|
export default defineOAuthKeycloakEventHandler({
|
||||||
async onSuccess(event, { user, tokens }) {
|
async onSuccess(event, { user, tokens }) {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const logger = createLogger({
|
||||||
|
level: config.public.logLevel,
|
||||||
|
tag: 'auth',
|
||||||
|
fancy: import.meta.env.MODE !== 'production'
|
||||||
|
})
|
||||||
|
|
||||||
const rawAccessToken = tokens?.access_token
|
const rawAccessToken = tokens?.access_token
|
||||||
let decodedJwt: KeycloakTokenPayload | null = null
|
let decodedJwt: KeycloakTokenPayload | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
decodedJwt = jwtDecode<KeycloakTokenPayload>(rawAccessToken!)
|
decodedJwt = jwtDecode<KeycloakTokenPayload>(rawAccessToken!)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[auth] Failed to decode access token:', err)
|
logger.warn('Failed to decode access token:', err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const organizations = decodedJwt ? extractOrganizations(decodedJwt) : []
|
const organizations = decodedJwt ? extractOrganizations(decodedJwt) : []
|
||||||
@@ -34,7 +42,13 @@ export default defineOAuthKeycloakEventHandler({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onError(event) {
|
onError(event) {
|
||||||
console.log('error during keycloak authentication', event)
|
const config = useRuntimeConfig()
|
||||||
|
const logger = createLogger({
|
||||||
|
level: config.public.logLevel,
|
||||||
|
tag: 'auth'
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.error('Error during keycloak authentication')
|
||||||
return sendRedirect(event, '/login')
|
return sendRedirect(event, '/login')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
|
import { createLogger } from '~~/shared/utils/logger'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const logger = createLogger({
|
||||||
|
level: config.public.logLevel,
|
||||||
|
tag: 'auth',
|
||||||
|
fancy: import.meta.env.MODE !== 'production'
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cleared = await clearUserSession(event)
|
const cleared = await clearUserSession(event)
|
||||||
if (!cleared) {
|
if (!cleared) {
|
||||||
console.warn('Failed to clear user session')
|
logger.warn('Failed to clear user session')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error clearing user session:', error)
|
logger.error('Error clearing user session:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendRedirect(event, '/login', 200)
|
return sendRedirect(event, '/login', 200)
|
||||||
|
|||||||
48
legalconsenthub/shared/utils/logger.ts
Normal file
48
legalconsenthub/shared/utils/logger.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { createConsola } from 'consola'
|
||||||
|
import type { ConsolaInstance } from 'consola'
|
||||||
|
|
||||||
|
export type AppLogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'verbose' | 'log'
|
||||||
|
|
||||||
|
export function createLogger(options: { level?: string; tag?: string; fancy?: boolean } = {}): ConsolaInstance {
|
||||||
|
const level = resolveConsolaLevel(options.level)
|
||||||
|
|
||||||
|
const logger = createConsola({
|
||||||
|
level
|
||||||
|
})
|
||||||
|
|
||||||
|
return options.tag ? logger.withTag(options.tag) : logger
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveConsolaLevel(level?: string): number {
|
||||||
|
const normalized = (level ?? '').toString().trim().toLowerCase()
|
||||||
|
|
||||||
|
// Consola levels (from docs):
|
||||||
|
// 0: Fatal and Error
|
||||||
|
// 1: Warnings
|
||||||
|
// 2: Normal logs
|
||||||
|
// 3: Informational logs
|
||||||
|
// 4: Debug logs
|
||||||
|
// 5: Trace logs
|
||||||
|
// -999: Silent
|
||||||
|
// +999: Verbose logs
|
||||||
|
switch (normalized as AppLogLevel) {
|
||||||
|
case 'silent':
|
||||||
|
return -999
|
||||||
|
case 'error':
|
||||||
|
return 0
|
||||||
|
case 'warn':
|
||||||
|
return 1
|
||||||
|
case 'log':
|
||||||
|
return 2
|
||||||
|
case 'info':
|
||||||
|
return 3
|
||||||
|
case 'debug':
|
||||||
|
return 4
|
||||||
|
case 'trace':
|
||||||
|
return 5
|
||||||
|
case 'verbose':
|
||||||
|
return 999
|
||||||
|
default:
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { type CreateCommentDto, type CommentDto, ResponseError } from '~~/.api-client'
|
import { type CreateCommentDto, type CommentDto, ResponseError } from '~~/.api-client'
|
||||||
import { useCommentApi } from '~/composables/comment/useCommentApi'
|
import { useCommentApi } from '~/composables/comment/useCommentApi'
|
||||||
|
import { useLogger } from '../app/composables/useLogger'
|
||||||
|
|
||||||
export const useCommentStore = defineStore('Comment', () => {
|
export const useCommentStore = defineStore('Comment', () => {
|
||||||
type FormElementId = string
|
type FormElementId = string
|
||||||
const commentApi = useCommentApi()
|
const commentApi = useCommentApi()
|
||||||
const comments = ref<Record<FormElementId, CommentDto[]>>({})
|
const comments = ref<Record<FormElementId, CommentDto[]>>({})
|
||||||
const loadedForms = ref(new Set<string>())
|
const loadedForms = ref(new Set<string>())
|
||||||
|
const logger = useLogger().withTag('commentStore')
|
||||||
|
|
||||||
async function load(applicationFormId: string) {
|
async function load(applicationFormId: string) {
|
||||||
if (loadedForms.value.has(applicationFormId)) return
|
if (loadedForms.value.has(applicationFormId)) return
|
||||||
@@ -14,7 +16,7 @@ export const useCommentStore = defineStore('Comment', () => {
|
|||||||
commentApi.getCommentsByApplicationFormId(applicationFormId)
|
commentApi.getCommentsByApplicationFormId(applicationFormId)
|
||||||
)
|
)
|
||||||
if (error.value) {
|
if (error.value) {
|
||||||
console.error('Failed loading comments:', error.value)
|
logger.error('Failed loading comments:', error.value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
comments.value =
|
comments.value =
|
||||||
@@ -43,9 +45,9 @@ export const useCommentStore = defineStore('Comment', () => {
|
|||||||
return newComment
|
return newComment
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error('Failed creating comment:', e.response)
|
logger.error('Failed creating comment:', e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed creating comment:', e)
|
logger.error('Failed creating comment:', e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -69,9 +71,9 @@ export const useCommentStore = defineStore('Comment', () => {
|
|||||||
return updatedComment
|
return updatedComment
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed updating comment with ID ${id}:`, e.response)
|
logger.error(`Failed updating comment with ID ${id}:`, e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed updating comment with ID ${id}:`, e)
|
logger.error(`Failed updating comment with ID ${id}:`, e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
@@ -92,9 +94,9 @@ export const useCommentStore = defineStore('Comment', () => {
|
|||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof ResponseError) {
|
if (e instanceof ResponseError) {
|
||||||
console.error(`Failed deleting comment with ID ${id}:`, e.response)
|
logger.error(`Failed deleting comment with ID ${id}:`, e.response)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed deleting comment with ID ${id}:`, e)
|
logger.error(`Failed deleting comment with ID ${id}:`, e)
|
||||||
}
|
}
|
||||||
return Promise.reject(e)
|
return Promise.reject(e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user