Compare commits
154 Commits
v0.2.0
..
14a4461f6c
| Author | SHA1 | Date | |
|---|---|---|---|
| 14a4461f6c | |||
| 81ad30bb45 | |||
| 3893a31e42 | |||
| b04f1aa451 | |||
| 26f8b992af | |||
| 23a48d2472 | |||
| 56de399c07 | |||
| 7bc211c9bd | |||
| 4a6afcfb76 | |||
| 26a91274c2 | |||
| 518a72c6e8 | |||
| 02d177fedb | |||
| 16ee2541dc | |||
| 3c5ba33922 | |||
| 1a227d41d7 | |||
| 2d07199bce | |||
| b457847178 | |||
| 89caedc0d2 | |||
| e0396eb5a9 | |||
| c4705a2d5e | |||
| 142c515780 | |||
| fab7bd145e | |||
| 230e94aa70 | |||
| fc2a78cd2c | |||
| e1fca67157 | |||
| 57ae257e0c | |||
| 18ef6efc4e | |||
| 17f507479a | |||
| 6a89ab8d0f | |||
| 30f9e89c60 | |||
| aa375573de | |||
| cf49408d0c | |||
| 1786fecfed | |||
| e5fa32d653 | |||
| 595786d126 | |||
| 19a5623075 | |||
| c30bdd1cd5 | |||
| 2d706cc3d8 | |||
| 9cb71f18c2 | |||
| 061be5a3b4 | |||
| 330d414e72 | |||
| f9d09d5357 | |||
| d862476097 | |||
| 87c6fb7071 | |||
| 3ba1fa40fd | |||
| faa1ec44c9 | |||
| 7cdbadbf84 | |||
| 6310377d84 | |||
| 7b374b1eeb | |||
| fb289ab298 | |||
| b2cf1c4698 | |||
| f1abf03b35 | |||
| eeb2f23e71 | |||
| ab997d7bbd | |||
| fc89ee2b86 | |||
| 5ad5a84b09 | |||
| 72cafe08ac | |||
| 8cbd04c97a | |||
| 188f405b23 | |||
| 746fd48de7 | |||
| fa5f4cfa36 | |||
| b5726af898 | |||
| b59ecc6ac9 | |||
| 9dce493c2a | |||
| 72f6591819 | |||
| 2b7b483019 | |||
| c8fdb032ea | |||
| 32971afd94 | |||
| 123c7ef6ef | |||
| 313b07e875 | |||
| bba417922c | |||
| ea39bbbe1d | |||
| 0715e7fdea | |||
| 17de883bce | |||
| c9a350bc58 | |||
| 9ffbe2cc2f | |||
| 9e7d31d903 | |||
| c77079c9d9 | |||
| d6e2b7daa7 | |||
| 662bc75b0d | |||
| 6944b3a209 | |||
| 99d3756da7 | |||
| 182d77354e | |||
| dc577a9b55 | |||
| 13693aad8b | |||
| 840ca57428 | |||
| e0b585f72b | |||
| 47df61af32 | |||
| b2ce2172a2 | |||
| 65c4dea710 | |||
| 5659d116c5 | |||
| a196f25526 | |||
| 44fac3f471 | |||
| 729842a71c | |||
| 3d1eeeab53 | |||
| 4871f56320 | |||
| ec404c9956 | |||
| fc9db8200f | |||
| 69caa825de | |||
| 123be2e9c0 | |||
| 928536b414 | |||
| 8ed8599f17 | |||
| 435de2ec92 | |||
| 529a199262 | |||
| 510e2f8130 | |||
| da4f286180 | |||
| d9136e45f6 | |||
| b610947403 | |||
| 6e520a46f0 | |||
| b4a0c2d7a5 | |||
| ea9f596abe | |||
| 4bb7d61f80 | |||
| cc8a166f5c | |||
| a70bf8ae96 | |||
| 902ee03e3f | |||
| f1f49ab014 | |||
| 7a16225c3b | |||
| 8e13af5b8c | |||
| 767c069404 | |||
| dd5281e2a1 | |||
| aefd56d1ff | |||
| 293a0b3478 | |||
| 076466b895 | |||
| 370738ff14 | |||
| 1151b0e45e | |||
| 7c5c571716 | |||
| 1b636efdb7 | |||
| 374c242890 | |||
| 87b1c24783 | |||
| 456162da44 | |||
| 8cfb60f9a1 | |||
| 4c70046a32 | |||
| 546f8ebbdf | |||
| bd86379d07 | |||
| edcaed1b1a | |||
| 20ed0b2f40 | |||
| 3197288eee | |||
| 84d64f04d2 | |||
| 1ecf64a228 | |||
| 41d513e402 | |||
| aa4b47e032 | |||
| 3537642df9 | |||
| 06a48a03ac | |||
| d60e606663 | |||
| 13dad3961c | |||
| 4cf1941f44 | |||
| ace568a800 | |||
| c77adb0e04 | |||
| 7ff2bf912d | |||
| 72c1a7d265 | |||
| 2efacf6d67 | |||
| d6549132ea | |||
| bbf422cc5d | |||
| 4c96de27db |
@@ -0,0 +1,19 @@
|
||||
name: Gitea Actions Demo
|
||||
run-name: ${{ gitea.actor }} ist testing out Gitea Actions
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Explore-Gitea-Actions:
|
||||
runs-on: inky
|
||||
steps:
|
||||
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- run: echo "The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
- run: echo "The workflow is now ready to test your code on the runner."
|
||||
- name: List files in the directory
|
||||
run: |
|
||||
ls ${{ gitea.workspace }}
|
||||
- run: echo "The job's status is ${{ job.status }}."
|
||||
+15
-3
@@ -2,7 +2,19 @@
|
||||
.idea/
|
||||
.angular/
|
||||
__pycache__/
|
||||
node_modules/
|
||||
.editorconfig
|
||||
bonus/
|
||||
icons/
|
||||
icons-shadowless/
|
||||
springboot/.factorypath
|
||||
java-ee/.settings
|
||||
java-ee/.project
|
||||
kontor-schema/env
|
||||
kontor-schema/kontor_schema.egg-info
|
||||
kontor-gui/.pdm-python
|
||||
kontor-gui/dist
|
||||
fastapi/.coverage
|
||||
kontor-api/.coverage
|
||||
db-password.txt
|
||||
couchdb-password.txt
|
||||
kontor-api/tests/test_main.py
|
||||
kontor-api/tests/test_db.db
|
||||
kontor-api/test_db.db
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
kontor_api := kontor-api
|
||||
kontor_spring := kontor-spring
|
||||
kontor_angular := kontor-angular
|
||||
TARGET=docker
|
||||
kontor_servicemix := kontor-servicemix
|
||||
|
||||
.PHONY: all $(kontor_spring) $(kontor_api) $(kontor_angular)
|
||||
.PHONY: all $(kontor_spring) $(kontor_api)
|
||||
all: $(kontor_spring) $(kontor_api) $(kontor_servicemix)
|
||||
|
||||
all: $(kontor_spring) $(kontor_api) $(kontor_angular)
|
||||
|
||||
$(kontor_spring) $(kontor_api) $(kontor_angular):
|
||||
$(kontor_spring) $(kontor_api) $(kontor_servicemix):
|
||||
$(MAKE) --directory=$@ $(TARGET)
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=kontor
|
||||
- POSTGRES_USER=kontor
|
||||
#- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||
- POSTGRES_PASSWORD=kontor
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U kontor"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
ports:
|
||||
- 5432:5432
|
||||
networks:
|
||||
- database
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data:rw
|
||||
secrets:
|
||||
- db-password
|
||||
adminer:
|
||||
image: adminer
|
||||
ports:
|
||||
- 8090:8080
|
||||
networks:
|
||||
- database
|
||||
- frontend
|
||||
volumes:
|
||||
postgres-data:
|
||||
secrets:
|
||||
db-password:
|
||||
file: db-password.txt
|
||||
networks:
|
||||
database:
|
||||
name: database
|
||||
external: true
|
||||
+31
-77
@@ -1,25 +1,35 @@
|
||||
include:
|
||||
- ./compose-postgres.yaml
|
||||
|
||||
services:
|
||||
activemq:
|
||||
image: apache/activemq-artemis:latest-alpine
|
||||
postgres:
|
||||
image: postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=kontor
|
||||
- POSTGRES_USER=kontor
|
||||
#- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||
- POSTGRES_PASSWORD=kontor
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U kontor"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
ports:
|
||||
- 61616:61616
|
||||
- 8161:8161
|
||||
- 5672:5672
|
||||
- 5432:5432
|
||||
networks:
|
||||
- integration
|
||||
- frontend
|
||||
volumes:
|
||||
- activemq-data:/var/lib/artemis-instance
|
||||
- postgres-data:/var/lib/postgresql/data:rw
|
||||
secrets:
|
||||
- db-password
|
||||
adminer:
|
||||
image: adminer
|
||||
ports:
|
||||
- 8090:8080
|
||||
networks:
|
||||
- database
|
||||
- frontend
|
||||
kontor:
|
||||
build:
|
||||
context: ./kontor-spring
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- kontor:0.2.0-SNAPSHOT
|
||||
image: kontor:0.2.0-SNAPSHOT
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
@@ -27,18 +37,13 @@ services:
|
||||
- integration
|
||||
- frontend
|
||||
ports:
|
||||
- 8100:8100
|
||||
- 8000:8000
|
||||
volumes:
|
||||
- images-data:/data/images
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
kontor-api:
|
||||
build:
|
||||
context: ./kontor-api
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- kontor-api:0.2.0-SNAPSHOT
|
||||
image: kontor-api:0.2.0-SNAPSHOT
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
@@ -120,70 +125,19 @@ services:
|
||||
- frontend
|
||||
ports:
|
||||
- 8800:8800
|
||||
volumes:
|
||||
- images-data:/data/images
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
kontor-angular:
|
||||
build:
|
||||
context: ./kontor-angular
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- kontor-angular:0.2.0-SNAPSHOT
|
||||
image: kontor-angular:0.2.0-SNAPSHOT
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- database
|
||||
- integration
|
||||
- frontend
|
||||
ports:
|
||||
- 8200:80
|
||||
depends_on:
|
||||
kontor-api:
|
||||
condition: service_healthy
|
||||
kontor-vue:
|
||||
build:
|
||||
context: ./kontor-vue
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- kontor-vue:0.2.0-SNAPSHOT
|
||||
image: kontor-vue:0.2.0-SNAPSHOT
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- database
|
||||
- integration
|
||||
- frontend
|
||||
ports:
|
||||
- 8300:80
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
kontor-javalin:
|
||||
build:
|
||||
context: ./kontor-javalin
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- kontor-javalin:0.2.0-SNAPSHOT
|
||||
image: kontor-javalin:0.2.0-SNAPSHOT
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://kontor-javalin:8400/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- database
|
||||
- integration
|
||||
- frontend
|
||||
ports:
|
||||
- 8400:8400
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
networks:
|
||||
integration:
|
||||
name: integration
|
||||
frontend:
|
||||
volumes:
|
||||
activemq-data:
|
||||
postgres-data:
|
||||
images-data:
|
||||
secrets:
|
||||
db-password:
|
||||
file: db-password.txt
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
build/
|
||||
.settings/
|
||||
.gradle/
|
||||
.classpath
|
||||
.project
|
||||
.vscode/
|
||||
bin/
|
||||
.idea/
|
||||
/.eclipse-pmd
|
||||
@@ -1,38 +0,0 @@
|
||||
variables:
|
||||
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||
|
||||
before_script:
|
||||
- source "/home/gitlab-runner/.sdkman/bin/sdkman-init.sh"
|
||||
- sdk u java 11.0.12-open
|
||||
- chmod +x ./gradlew
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- analysis
|
||||
- publish
|
||||
|
||||
Build Application:
|
||||
stage: build
|
||||
script: ./gradlew --build-cache compileJava
|
||||
|
||||
Create Documentation:
|
||||
stage: build
|
||||
script: ./gradlew asciidoctorPdf
|
||||
|
||||
Test Application:
|
||||
stage: test
|
||||
script: ./gradlew check
|
||||
|
||||
sonarqube-check:
|
||||
stage: analysis
|
||||
variables:
|
||||
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
|
||||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
|
||||
script: ./gradlew sonarqube
|
||||
allow_failure: true
|
||||
|
||||
Publish Artifacts:
|
||||
stage: publish
|
||||
script: ./gradlew publish
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# kontor-quarkus Project
|
||||
|
||||
This project uses Quarkus, the Supersonic Subatomic Java Framework.
|
||||
|
||||
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
|
||||
|
||||
## Running the application in dev mode
|
||||
|
||||
You can run your application in dev mode that enables live coding using:
|
||||
```shell script
|
||||
./gradlew quarkusDev
|
||||
```
|
||||
|
||||
> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/.
|
||||
|
||||
## Packaging and running the application
|
||||
|
||||
The application can be packaged using:
|
||||
```shell script
|
||||
./gradlew build
|
||||
```
|
||||
It produces the `quarkus-run.jar` file in the `build/quarkus-app/` directory.
|
||||
Be aware that it’s not an _über-jar_ as the dependencies are copied into the `build/quarkus-app/lib/` directory.
|
||||
|
||||
The application is now runnable using `java -jar build/quarkus-app/quarkus-run.jar`.
|
||||
|
||||
If you want to build an _über-jar_, execute the following command:
|
||||
```shell script
|
||||
./gradlew build -Dquarkus.package.type=uber-jar
|
||||
```
|
||||
|
||||
The application, packaged as an _über-jar_, is now runnable using `java -jar build/*-runner.jar`.
|
||||
|
||||
## Creating a native executable
|
||||
|
||||
You can create a native executable using:
|
||||
```shell script
|
||||
./gradlew build -Dquarkus.package.type=native
|
||||
```
|
||||
|
||||
Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
|
||||
```shell script
|
||||
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true
|
||||
```
|
||||
|
||||
You can then execute your native executable with: `./build/kontor-quarkus-1.0.0-SNAPSHOT-runner`
|
||||
|
||||
If you want to learn more about building native executables, please consult https://quarkus.io/guides/gradle-tooling.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A JAX-RS implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it.
|
||||
|
||||
## Provided Code
|
||||
|
||||
### RESTEasy Reactive
|
||||
|
||||
Easily start your Reactive RESTful Web Services
|
||||
|
||||
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
|
||||
@@ -1,158 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id 'jacoco'
|
||||
id 'jacoco-report-aggregation'
|
||||
id 'pmd'
|
||||
id 'checkstyle'
|
||||
id 'jvm-test-suite'
|
||||
id 'io.quarkus'
|
||||
id "org.sonarqube" version "3.3"
|
||||
alias(versionsLibs.plugins.asciidoctorConvert)
|
||||
alias(versionsLibs.plugins.asciidoctorPdf)
|
||||
alias(versionsLibs.plugins.asciidoctorGems)
|
||||
}
|
||||
|
||||
repositories {
|
||||
ruby.gems()
|
||||
maven {
|
||||
url "https://nexus.thpeetz.de/nexus/content/groups/public/"
|
||||
}
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
|
||||
implementation 'io.quarkus:quarkus-resteasy-reactive'
|
||||
implementation 'io.quarkus:quarkus-resteasy-reactive-jackson'
|
||||
implementation 'io.quarkus:quarkus-arc'
|
||||
testImplementation 'io.quarkus:quarkus-junit5'
|
||||
testImplementation 'io.rest-assured:rest-assured'
|
||||
testImplementation("io.quarkus:quarkus-jacoco")
|
||||
}
|
||||
|
||||
final BUILD_DATE = new Date().format('dd.MM.yyyy').toString()
|
||||
def pdfFile = layout.buildDirectory.file("docs/asciidocPdf/" + project.name + ".pdf")
|
||||
def pdfArtifact = artifacts.add('archives', pdfFile.get().asFile) {
|
||||
type 'pdf'
|
||||
builtBy asciidoctorPdf
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata).configureEach {
|
||||
suppressedValidationErrors.add('enforced-platform')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
quarkusApp(MavenPublication){
|
||||
from components.java
|
||||
}
|
||||
docs(MavenPublication) {
|
||||
groupId = group + '.docs'
|
||||
artifactId = project.name
|
||||
artifact pdfArtifact
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
name = 'nexusPeetz'
|
||||
def releasesRepoUrl = "https://nexus.thpeetz.de/nexus/content/repositories/releases/"
|
||||
def snapshotsRepoUrl = "https://nexus.thpeetz.de/nexus/content/repositories/snapshots/"
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
credentials(PasswordCredentials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata).configureEach {
|
||||
suppressedValidationErrors.add('enforced-platform')
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs << '-parameters'
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.required = true
|
||||
}
|
||||
}
|
||||
|
||||
test.finalizedBy jacocoTestReport
|
||||
|
||||
|
||||
pmd {
|
||||
//ruleSetFiles = files("custom-pmd-ruleset.xml")
|
||||
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
|
||||
ignoreFailures = true
|
||||
}
|
||||
|
||||
tasks.withType(Checkstyle) {
|
||||
reports {
|
||||
xml.required = true
|
||||
html.required = true
|
||||
}
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
test {
|
||||
useJUnitJupiter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pmdTest.dependsOn("compileQuarkusTestGeneratedSourcesJava")
|
||||
checkstyleTest.dependsOn("compileQuarkusTestGeneratedSourcesJava")
|
||||
jacocoTestReport.dependsOn("compileQuarkusGeneratedSourcesJava")
|
||||
pmdMain.dependsOn("compileQuarkusGeneratedSourcesJava")
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.host.url", "https://sonar.thpeetz.de"
|
||||
property "sonar.qualitygate.wait", true
|
||||
property "sonar.sourceEncoding", "UTF-8"
|
||||
property "sonar.projectKey", "kontor_kontor-quarkus_AYQek8Mxz0hBjLSV8I8O"
|
||||
property "sonar.login", "5ecd90dee57806857e07443a9b0efd3cd7774a81"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('sonarqube').configure {
|
||||
dependsOn 'test', 'pmdMain', 'checkstyleMain', 'jacocoTestReport'
|
||||
}
|
||||
|
||||
asciidoctorPdf {
|
||||
baseDirFollowsSourceFile()
|
||||
|
||||
asciidoctorj {
|
||||
attributes 'build-gradle': file('build.gradle'),
|
||||
'endpoint-url': 'https://www.thpeetz.de',
|
||||
'source-highlighter': 'rouge',
|
||||
'imagesdir': './images',
|
||||
'toc': 'left',
|
||||
'toc-title': 'Inhaltsverzeichnis',
|
||||
'revdate': BUILD_DATE,
|
||||
'revnumber': { project.version.toString() },
|
||||
'revremark': 'Entwurf',
|
||||
'chapter-label': '',
|
||||
'icons': 'font',
|
||||
'idprefix': 'id_',
|
||||
'idseparator': '-',
|
||||
'docinfo1': ''
|
||||
}
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = "7.5"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<module name="TreeWalker">
|
||||
<module name="UnusedImports" />
|
||||
<module name="AvoidStarImport" />
|
||||
<module name="ConstantName" />
|
||||
<module name="NeedBraces" />
|
||||
<module name="RightCurly" />
|
||||
<module name="ModifierOrder">
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
@@ -1,9 +0,0 @@
|
||||
#Gradle properties
|
||||
group=de.thpeetz.kontor
|
||||
version=1.0.0-SNAPSHOT
|
||||
|
||||
quarkusPluginId=io.quarkus
|
||||
quarkusPluginVersion=2.13.2.Final
|
||||
quarkusPlatformGroupId=io.quarkus.platform
|
||||
quarkusPlatformArtifactId=quarkus-bom
|
||||
quarkusPlatformVersion=2.13.2.Final
|
||||
BIN
Binary file not shown.
Vendored
-185
@@ -1,185 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
-104
@@ -1,104 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1,84 +0,0 @@
|
||||
= Projektbeschreibung kontor-quarkus: Pflichtenheft und Projekthandbuch
|
||||
:author: Thomas Peetz
|
||||
:email: <thomas.peetz@thpeetz.de>
|
||||
:doctype: article
|
||||
:sectnums:
|
||||
:sectnumlevels: 4
|
||||
:toc:
|
||||
:toclevels: 4
|
||||
:table-caption!:
|
||||
:counter: table-number: 0
|
||||
|
||||
//[title="Dokumenthistorie", caption="Tabelle {counter:table-number} ", id="Tabelle-{counter:table-number}", options="header"]
|
||||
//[title="Dokumenthistorie", id="Table-{counter:table-number}", options="header", cols="4"]
|
||||
[title="Dokumenthistorie", id="Table-{counter:table-number}", options="header"]
|
||||
|===
|
||||
| Version | Datum | Autor | Änderungsgrund / Bemerkungen
|
||||
| 0.0.1 | 16.05.2022 | Thomas Peetz | Ersterstellung
|
||||
|===
|
||||
|
||||
== Einführung
|
||||
|
||||
=== Zweck
|
||||
|
||||
=== Stakeholder des Systems
|
||||
|
||||
=== Systemumfang
|
||||
|
||||
==== Zielsetzung des Systems
|
||||
|
||||
=== Systemübersicht
|
||||
|
||||
==== Systemkontext
|
||||
|
||||
==== Systemarchitektur
|
||||
|
||||
==== Systemschnittstellen
|
||||
|
||||
===== Realisierte Schnittstellen
|
||||
|
||||
===== Verwendete Schnittstellen
|
||||
|
||||
==== Logisches Datenmodell
|
||||
|
||||
==== Einschränkungen
|
||||
|
||||
== Anforderungen der Domäne
|
||||
|
||||
=== Systemfunktionen
|
||||
|
||||
==== Anwendungsfälle
|
||||
|
||||
==== Akteure
|
||||
|
||||
==== Zielgruppen
|
||||
|
||||
=== Anforderungen
|
||||
|
||||
==== Anforderungen an externe Schnittstellen
|
||||
|
||||
==== Funktionale Anforderungen
|
||||
|
||||
==== Qualitätsanforderungen
|
||||
|
||||
==== Randbedingungen
|
||||
|
||||
==== Weitere Anforderungen
|
||||
|
||||
==== Wartungs- und Supportinformationen
|
||||
|
||||
=== Verifikation
|
||||
|
||||
[bibliography]
|
||||
== Referenzen
|
||||
|
||||
[glossary]
|
||||
== Glossar
|
||||
|
||||
== Verzeichnisse
|
||||
|
||||
=== Abbildungsverzeichnis
|
||||
|
||||
=== Tabellenverzeichnis
|
||||
|
||||
<<Table-1, Tabelle 1>> <<Table-1>>
|
||||
@@ -1,94 +0,0 @@
|
||||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./gradlew build
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/kontor-quarkus-jvm .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-jvm
|
||||
#
|
||||
# If you want to include the debug port into your docker image
|
||||
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005
|
||||
#
|
||||
# Then run the container using :
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-jvm
|
||||
#
|
||||
# This image uses the `run-java.sh` script to run the application.
|
||||
# This scripts computes the command line to execute your Java application, and
|
||||
# includes memory/GC tuning.
|
||||
# You can configure the behavior using the following environment properties:
|
||||
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||
# of the container available memory as set here. The default is `50` which means 50%
|
||||
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||
# a container without any memory constraints for the container then this option has
|
||||
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||
# when things are happening. This option, if set to true, will set
|
||||
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||
# true").
|
||||
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||
# (example: "20")
|
||||
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||
# (example: "40")
|
||||
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||
# (example: "4")
|
||||
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||
# previous GC times. (example: "90")
|
||||
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||
# contain the necessary JRE command-line options to specify the required GC, which
|
||||
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-11:1.14
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||
COPY --chown=185 build/quarkus-app/lib/ /deployments/lib/
|
||||
COPY --chown=185 build/quarkus-app/*.jar /deployments/
|
||||
COPY --chown=185 build/quarkus-app/app/ /deployments/app/
|
||||
COPY --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
|
||||
|
||||
EXPOSE 8080
|
||||
USER 185
|
||||
ENV AB_JOLOKIA_OFF=""
|
||||
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./gradlew build -Dquarkus.package.type=legacy-jar
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/kontor-quarkus-legacy-jar .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-legacy-jar
|
||||
#
|
||||
# If you want to include the debug port into your docker image
|
||||
# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005
|
||||
#
|
||||
# Then run the container using :
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-legacy-jar
|
||||
#
|
||||
# This image uses the `run-java.sh` script to run the application.
|
||||
# This scripts computes the command line to execute your Java application, and
|
||||
# includes memory/GC tuning.
|
||||
# You can configure the behavior using the following environment properties:
|
||||
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||
# of the container available memory as set here. The default is `50` which means 50%
|
||||
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||
# a container without any memory constraints for the container then this option has
|
||||
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||
# when things are happening. This option, if set to true, will set
|
||||
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||
# true").
|
||||
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||
# (example: "20")
|
||||
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||
# (example: "40")
|
||||
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||
# (example: "4")
|
||||
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||
# previous GC times. (example: "90")
|
||||
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||
# contain the necessary JRE command-line options to specify the required GC, which
|
||||
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-11:1.14
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
COPY build/lib/* /deployments/lib/
|
||||
COPY build/*-runner.jar /deployments/quarkus-run.jar
|
||||
|
||||
EXPOSE 8080
|
||||
USER 185
|
||||
ENV AB_JOLOKIA_OFF=""
|
||||
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||
@@ -1,27 +0,0 @@
|
||||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./gradlew build -Dquarkus.package.type=native
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.native -t quarkus/kontor-quarkus .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root build/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
@@ -1,30 +0,0 @@
|
||||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||
# It uses a micro base image, tuned for Quarkus native executables.
|
||||
# It reduces the size of the resulting container image.
|
||||
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./gradlew build -Dquarkus.package.type=native
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/kontor-quarkus .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus
|
||||
#
|
||||
###
|
||||
FROM quay.io/quarkus/quarkus-micro-image:1.0
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root build/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
@@ -1,95 +0,0 @@
|
||||
package de.thpeetz.kontor.comics;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import de.thpeetz.kontor.comics.service.ArtistService;
|
||||
import de.thpeetz.kontor.comics.service.ComicsService;
|
||||
import de.thpeetz.kontor.comics.service.PublisherService;
|
||||
|
||||
@Path("/kontor/comics")
|
||||
public class ComicsResource {
|
||||
|
||||
@Inject
|
||||
transient ComicsService comicsService;
|
||||
|
||||
@Inject
|
||||
transient ArtistService artistService;
|
||||
|
||||
@Inject
|
||||
transient PublisherService publisherService;
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/")
|
||||
public String showComicList() {
|
||||
return comicsService.showComicList();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/artist")
|
||||
public String showArtistList() {
|
||||
return artistService.showArtistList();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/artist")
|
||||
public String showArtistListJson() {
|
||||
return artistService.showArtistList();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/artist/view/{artist_id}")
|
||||
public String showArtist(String artist_id) {
|
||||
return artistService.showArtist(artist_id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/artist/create")
|
||||
public String showArtistCreation() {
|
||||
return artistService.showArtistCreation();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/artist/create")
|
||||
public String validateArtistDetails() {
|
||||
return artistService.validateArtistDetails();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/publisher")
|
||||
public String showPublisherList() {
|
||||
return publisherService.showPublisherList();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/publisher/view/{publisher_id}")
|
||||
public String showPublisher(String publisher_id) {
|
||||
return publisherService.showPublisher(publisher_id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/publisher/create")
|
||||
public String showPublisherCreation() {
|
||||
return publisherService.showPublisherCreation();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Path("/publisher/create")
|
||||
public String validatePublisherDetails() {
|
||||
return publisherService.validatePublisherDetails();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.service;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class ArtistService {
|
||||
|
||||
public String showArtistList() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String showArtist(String artist_id) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String showArtistCreation() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String validateArtistDetails() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.service;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class ComicsService {
|
||||
|
||||
public String showComicList() {
|
||||
return "comics";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package de.thpeetz.kontor.comics.service;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PublisherService {
|
||||
|
||||
public String showPublisherList() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String showPublisher(String publisher_id) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String showPublisherCreation() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public String validatePublisherDetails() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +0,0 @@
|
||||
# Your configuration properties
|
||||
quarkus.http.test-port=8083
|
||||
quarkus.http.test-ssl-port=8446
|
||||
@@ -1,8 +0,0 @@
|
||||
package de.thpeetz.kontor.comics;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusIntegrationTest;
|
||||
|
||||
@QuarkusIntegrationTest
|
||||
public class ComicsResourceIT extends ComicsResourceTest {
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package de.thpeetz.kontor.comics;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
|
||||
@QuarkusTest
|
||||
class ComicsResourceTest {
|
||||
|
||||
@Test
|
||||
void testComicsEndpoint() {
|
||||
given().when().get("/kontor/comics")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body(is("comics"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
### STAGE 1: Build ###
|
||||
FROM node:22.15-alpine AS build
|
||||
FROM docker.io/node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
@@ -7,8 +7,9 @@ COPY . .
|
||||
RUN npm run build
|
||||
|
||||
### STAGE 2: Run ###
|
||||
FROM nginx:1.17.1-alpine
|
||||
FROM docker.io/library/nginx:stable-alpine
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist/kontor-angular/browser /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
server {
|
||||
listen 8800;
|
||||
# Root-Verzeichnis für den Server setzen (wir kopieren unsere Anwendung hierher)
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
## ------------------------------- Builder Stage ------------------------------ ##
|
||||
FROM python:3.13-bookworm AS builder
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y build-essential && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Download the latest installer, install it and then remove it
|
||||
ADD https://astral.sh/uv/install.sh /install.sh
|
||||
RUN chmod -R 655 /install.sh && /install.sh && rm /install.sh
|
||||
|
||||
# Set up the UV environment path correctly
|
||||
ENV PATH="/root/.local/bin:${PATH}"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./pyproject.toml .
|
||||
|
||||
RUN uv sync
|
||||
|
||||
# ------------------------------- Production Stage ------------------------------ ##
|
||||
FROM python:3.13-slim-bookworm AS production
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y curl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY /src src
|
||||
COPY --from=builder /app/.venv .venv
|
||||
|
||||
# Set up environment variables for production
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
|
||||
# Expose the specified port for FastAPI
|
||||
EXPOSE 8200
|
||||
|
||||
# Start the application with Uvicorn in production mode, using environment variable references
|
||||
CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8200"]
|
||||
|
||||
@@ -34,8 +34,6 @@ FROM python:3.13-slim-bookworm AS production
|
||||
|
||||
#RUN --mount=type=secret,id=secret-key,target=secrets.json
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y curl
|
||||
|
||||
RUN useradd --create-home appuser
|
||||
USER appuser
|
||||
|
||||
@@ -51,5 +49,5 @@ ENV PATH="/app/.venv/bin:$PATH"
|
||||
EXPOSE $PORT
|
||||
|
||||
# Start the application with Uvicorn in production mode, using environment variable references
|
||||
CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8500"]
|
||||
CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8800"]
|
||||
|
||||
|
||||
@@ -28,5 +28,4 @@ dependencies = [
|
||||
"jinja2>=3.1.6",
|
||||
"asyncpg>=0.30.0",
|
||||
"bcrypt>=4.3.0",
|
||||
"fastapi-jwt-auth>=0.5.0",
|
||||
]
|
||||
|
||||
+234
-8
@@ -1,11 +1,237 @@
|
||||
from fastapi import APIRouter
|
||||
"""
|
||||
add router for different parts (like comics, tysc, media)
|
||||
"""
|
||||
|
||||
from src.apis.version1 import comic, mediaactor, mediafile, mediaactorfile, tysc, admin
|
||||
from fastapi import APIRouter, Depends
|
||||
from src.apis.version1.admin import mailaccount
|
||||
from src.apis.version1.comics import (
|
||||
artist,
|
||||
publisher,
|
||||
comic,
|
||||
issue,
|
||||
worktype,
|
||||
volume,
|
||||
storyarc,
|
||||
comicwork,
|
||||
issuework,
|
||||
)
|
||||
from src.apis.version1.media import (
|
||||
actor,
|
||||
file,
|
||||
mediaactorfile,
|
||||
mediavideo,
|
||||
mediaarticle,
|
||||
)
|
||||
from src.apis.version1.tysc import (
|
||||
card,
|
||||
cardset,
|
||||
fieldposition,
|
||||
player,
|
||||
rooster,
|
||||
sport,
|
||||
team,
|
||||
vendor,
|
||||
)
|
||||
from src.core.security import get_current_user_from_token
|
||||
|
||||
from src.apis.version1 import comic, media, tysc, admin
|
||||
|
||||
api_router = APIRouter(prefix="/api")
|
||||
api_router.include_router(comic.router, prefix="/comics", tags=["comics"])
|
||||
api_router.include_router(mediafile.router, prefix="/media", tags=["media"])
|
||||
api_router.include_router(mediaactor.router, prefix="/media", tags=["media"])
|
||||
api_router.include_router(mediaactorfile.router, prefix="/media", tags=["media"])
|
||||
api_router.include_router(tysc.router, prefix="/tysc", tags=["tysc"])
|
||||
api_router.include_router(admin.router, prefix="/login", tags=["login"])
|
||||
api_router.include_router(
|
||||
comic.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
publisher.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
artist.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
issue.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
worktype.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
volume.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
storyarc.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
comicwork.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
issuework.router,
|
||||
prefix="/comics",
|
||||
tags=["comics"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
file.router,
|
||||
prefix="/media",
|
||||
tags=["media"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
mediavideo.router,
|
||||
prefix="/media",
|
||||
tags=["media"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
mediaarticle.router,
|
||||
prefix="/media",
|
||||
tags=["media"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
actor.router,
|
||||
prefix="/media",
|
||||
tags=["media"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
mediaactorfile.router,
|
||||
prefix="/media",
|
||||
tags=["media"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
sport.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
player.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
team.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
fieldposition.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
rooster.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
vendor.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
cardset.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
card.router,
|
||||
prefix="/tysc",
|
||||
tags=["tysc"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
article.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
bookshelf_publisher.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
book.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
author.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
articleauthor.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
bookauthor.router,
|
||||
prefix="/bookshelf",
|
||||
tags=["bookshelf"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
profile.router,
|
||||
prefix="/user",
|
||||
tags=["user"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
token.router,
|
||||
prefix="/user",
|
||||
tags=["user"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
permission.router,
|
||||
prefix="/user",
|
||||
tags=["user"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
assignment.router,
|
||||
prefix="/user",
|
||||
tags=["user"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
api_router.include_router(
|
||||
mailaccount.router,
|
||||
prefix="/admin",
|
||||
tags=["admin"],
|
||||
dependencies=[Depends(get_current_user_from_token)],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
from typing import Annotated
|
||||
from typing import Dict
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException
|
||||
from fastapi import Request
|
||||
from fastapi import status
|
||||
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
||||
from fastapi.security import OAuth2
|
||||
from fastapi.security.utils import get_authorization_scheme_param
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.db.session import get_db
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_db)]
|
||||
|
||||
|
||||
class OAuth2PasswordBearerWithCookie(OAuth2):
|
||||
def __init__(
|
||||
self,
|
||||
tokenUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
auto_error: bool = True,
|
||||
):
|
||||
if not scopes:
|
||||
scopes = {}
|
||||
flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes})
|
||||
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.cookies.get(
|
||||
"access_token"
|
||||
) # changed to accept access token from httpOnly Cookie
|
||||
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Not authenticated",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
else:
|
||||
return None
|
||||
return param
|
||||
@@ -1,37 +1,35 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Body, HTTPException, status, Depends, Response
|
||||
import bcrypt
|
||||
from fastapi import APIRouter, HTTPException, status, Response, Depends
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
from jose import jwt, JWTError
|
||||
from src.apis.utils import SessionDep, OAuth2PasswordBearerWithCookie
|
||||
from src.core.config import settings
|
||||
from src.core.security import create_access_token, authenticate_user, get_current_active_user
|
||||
from src.core.security import create_access_token
|
||||
from src.db.models.admin import Profile
|
||||
from src.schema.admin import Token, ProfileModel
|
||||
from src.webapps.auth.forms import LoginForm
|
||||
from src.db.repository.admin import get_profile
|
||||
from src.schema.admin import Token
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/token")
|
||||
def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token:
|
||||
user = authenticate_user(form_data.username, form_data.password)
|
||||
def authenticate_user(username: str, password: str, db: SessionDep) -> Profile | None:
|
||||
user = get_profile(username=username, db=db)
|
||||
print(user)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.email, "scope": " ".join(form_data.scopes)}, expires_delta=access_token_expires
|
||||
)
|
||||
return Token(access_token=access_token, token_type="bearer")
|
||||
return None
|
||||
if bcrypt.checkpw(password.encode(), user.password.encode()):
|
||||
print("User successful authenticated")
|
||||
else:
|
||||
logging.info("Authentication failed!")
|
||||
return user
|
||||
|
||||
|
||||
# @router.post("/token-cookie", response_model=Token)
|
||||
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
||||
user = authenticate_user(form_data.username, form_data.password) # type: ignore
|
||||
@router.post("/token", response_model=Token)
|
||||
def login_for_access_token(response: Response, db: SessionDep, form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(form_data.username, form_data.password, db)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -47,6 +45,25 @@ def login_for_token_cookie(response: Response, form_data: LoginForm = Depends())
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@router.get("/profile", response_model=ProfileModel)
|
||||
async def read_profile(current_user: Annotated[Profile, Depends(get_current_active_user)]):
|
||||
return current_user
|
||||
oauth2_scheme = OAuth2PasswordBearerWithCookie(tokenUrl="/api/login/token")
|
||||
|
||||
|
||||
def get_current_user_from_token(db: SessionDep, token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||
)
|
||||
username: str = payload.get("sub")
|
||||
print("username/email extracted is ", username)
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
user = get_profile(username=username, db=db)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from src.core.config import settings
|
||||
from src.core.log_conf import logger
|
||||
from src.core.security import (
|
||||
authenticate_user_by_email,
|
||||
authenticate_user_by_username,
|
||||
create_access_token,
|
||||
)
|
||||
from src.schema.admin.login import LoginRequest
|
||||
from src.schema.admin.token import Token
|
||||
from src.webapps.auth.forms import LoginForm
|
||||
|
||||
login_router = APIRouter()
|
||||
|
||||
|
||||
@login_router.post(
|
||||
"/login",
|
||||
tags=["login"],
|
||||
summary="Login and get token",
|
||||
response_description="Return HTTP status code 200 (OK)",
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
def login(request: LoginRequest) -> Token:
|
||||
logger.info("login with %s", request.email)
|
||||
user = authenticate_user_by_email(str(request.email), str(request.password))
|
||||
scopes = ["admin", "read"]
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.email, "scope": " ".join(scopes)},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
return Token(access_token=access_token, token_type="bearer")
|
||||
|
||||
|
||||
@login_router.post("/token", tags=["login"], summary="Login for access token")
|
||||
# async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token:
|
||||
async def login_for_access_token(
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
) -> Token:
|
||||
user = authenticate_user_by_username(form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect username or password")
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.user_name, "scope": " ".join(form_data.scopes)},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
return Token(access_token=access_token, token_type="bearer")
|
||||
|
||||
|
||||
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
||||
user = authenticate_user_by_email(str(form_data.username), str(form_data.password))
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
)
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.email}, expires_delta=access_token_expires
|
||||
)
|
||||
response.set_cookie(
|
||||
key="access_token", value=f"Bearer {access_token}", httponly=True
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
@@ -0,0 +1,32 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.admin import MailAccount
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.admin.mailaccount import MailAccountResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/mailaccounts", response_model=List[MailAccountResponse])
|
||||
def get_all_mailaccounts(db: SessionDep) -> List[MailAccountResponse]:
|
||||
"""
|
||||
return all MailAccounts as JSON.
|
||||
"""
|
||||
results: List[MailAccountResponse] = []
|
||||
mailaccounts = db.query(MailAccount).all()
|
||||
for mailaccount in mailaccounts:
|
||||
response = to_response(mailaccount)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/mailaccounts/{mailaccount_id}", response_model=MailAccountResponse)
|
||||
def get_mailaccount(mailaccount_id: str, db: SessionDep) -> MailAccountResponse:
|
||||
"""
|
||||
return MailAccounts by id.
|
||||
"""
|
||||
mailaccount = db.get(MailAccount, mailaccount_id)
|
||||
if mailaccount is None:
|
||||
raise HTTPException(status_code=409, detail="Mailaccount could not be found")
|
||||
response = to_response(mailaccount)
|
||||
return response
|
||||
@@ -0,0 +1,20 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.bookshelf import Article
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.article import ArticleResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/articles", response_model=List[ArticleResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[ArticleResponse]:
|
||||
results: List[ArticleResponse] = []
|
||||
articles = db.query(Article).all()
|
||||
for article in articles:
|
||||
response = to_response(article)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.bookshelf import ArticleAuthor
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.articleauthor import ArticleAuthorResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/articleauthors", response_model=List[ArticleAuthorResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[ArticleAuthorResponse]:
|
||||
results: List[ArticleAuthorResponse] = []
|
||||
articleauthors = db.query(ArticleAuthor).all()
|
||||
for articleauthor in articleauthors:
|
||||
response = to_response(articleauthor)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.bookshelf import Author
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.author import AuthorResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/authors", response_model=List[AuthorResponse])
|
||||
def get_all_authors(db: SessionDep) -> List[AuthorResponse]:
|
||||
results: List[AuthorResponse] = []
|
||||
authors = db.query(Author).all()
|
||||
for author in authors:
|
||||
response = to_response(author)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/authors/{author_id}", response_model=AuthorResponse)
|
||||
def get_author(author_id: str, db: SessionDep) -> AuthorResponse:
|
||||
author = db.get(Author, author_id)
|
||||
if author is None:
|
||||
raise HTTPException(status_code=404, detail="Author could not be found")
|
||||
response = to_response(author)
|
||||
return response
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.bookshelf import Book
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.book import BookResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/books", response_model=List[BookResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[BookResponse]:
|
||||
results: List[BookResponse] = []
|
||||
books = db.query(Book).all()
|
||||
for book in books:
|
||||
response = to_response(book)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.bookshelf import BookAuthor
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.bookauthor import BookAuthorResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/bookauthors", response_model=List[BookAuthorResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[BookAuthorResponse]:
|
||||
results: List[BookAuthorResponse] = []
|
||||
bookauthors = db.query(BookAuthor).all()
|
||||
for bookauthor in bookauthors:
|
||||
response = to_response(bookauthor)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.bookshelf import BookshelfPublisher
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.bookshelf.publisher import PublisherResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/publishers", response_model=List[PublisherResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[PublisherResponse]:
|
||||
results: List[PublisherResponse] = []
|
||||
publishers = db.query(BookshelfPublisher).all()
|
||||
for publisher in publishers:
|
||||
response = to_response(publisher)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -1,25 +1,13 @@
|
||||
from typing import List
|
||||
|
||||
from typing import List, AnyStr
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.comic import Artist, Comic, Issue, Publisher
|
||||
from src.apis.utils import SessionDep
|
||||
from src.db.repository.comics.artist import get_artist_details
|
||||
from src.db.repository.comics.comic import (
|
||||
get_comic_details,
|
||||
get_issue_details,
|
||||
get_short_info,
|
||||
list_comics,
|
||||
)
|
||||
from src.db.repository.comics.publisher import get_publisher_details
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.artist import ArtistCreation, ArtistResponse
|
||||
from src.schema.comics.artist_details import ArtistDetailResponse
|
||||
from src.schema.comics.comic import ComicResponse
|
||||
from src.schema.comics.comic_details import ComicDetailsResponse
|
||||
from src.schema.comics.issue_details import IssueDetailsResponse
|
||||
from src.schema.comics.publisher import PublisherResponse
|
||||
from src.schema.comics.publisher_details import PublisherDetailsResponse
|
||||
from src.db.repository.comics.comic import list_comics, get_issue_details
|
||||
from src.schema.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details, get_short_info
|
||||
from src.schema.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse
|
||||
from src.db.models.comic import Comic, Artist, Issue
|
||||
from src.schema.comics.issue import IssueDetailsResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -33,36 +21,30 @@ def get_all_comics(db: SessionDep) -> List[ComicResponse]:
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/comics/{comic_id}", response_model=ComicDetailsResponse)
|
||||
def get_comic(comic_id: str, db: SessionDep) -> ComicDetailsResponse:
|
||||
def get_comic(comic_id: AnyStr, db: SessionDep) -> ComicDetailsResponse:
|
||||
comic = db.get(Comic, comic_id)
|
||||
if comic is None:
|
||||
raise HTTPException(status_code=404, detail="Comic could not be found")
|
||||
logger.info(f"create ComicDetailsResponse for {comic}")
|
||||
response: ComicDetailsResponse = get_comic_details(comic)
|
||||
logger.info(f"ComicDetailsResponse: {response}")
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/artists", response_model=List[ArtistResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[ArtistResponse]:
|
||||
results: List[ArtistResponse] = []
|
||||
artists = db.query(Artist).all()
|
||||
for artist in artists:
|
||||
results.append(ArtistResponse(id=artist.id, name=str(artist.name))) # type: ignore
|
||||
results.append(ArtistResponse(id=artist.id, name=artist.name))
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/artists/{artist_id}", response_model=ArtistDetailResponse)
|
||||
def get_artist(artist_id: str, db: SessionDep) -> ArtistDetailResponse:
|
||||
def get_artist(artist_id: AnyStr, db: SessionDep) -> ArtistDetailResponse:
|
||||
artist = db.get(Artist, artist_id)
|
||||
if artist is None:
|
||||
raise HTTPException(status_code=404, detail="Artist could not be found")
|
||||
response: ArtistDetailResponse = get_artist_details(artist)
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/artists", status_code=status.HTTP_201_CREATED)
|
||||
def add_artist(db: SessionDep, artist_creation: ArtistCreation) -> ArtistResponse:
|
||||
artist: Artist = Artist()
|
||||
@@ -72,28 +54,9 @@ def add_artist(db: SessionDep, artist_creation: ArtistCreation) -> ArtistRespons
|
||||
db.commit()
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Artist already added")
|
||||
response = ArtistResponse(id=artist.id, name=str(artist.name))
|
||||
response = ArtistResponse(id=artist.id, name=artist.name)
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/publishers", response_model=List[PublisherResponse])
|
||||
def get_all_publishers(db: SessionDep) -> List[PublisherResponse]:
|
||||
results: List[PublisherResponse] = []
|
||||
publishers = db.query(Publisher).all()
|
||||
for publisher in publishers:
|
||||
results.append(PublisherResponse(id=publisher.id, name=str(publisher.name)))
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/publishers/{publisher_id}", response_model=PublisherDetailsResponse)
|
||||
def get_publisher(publisher_id: str, db: SessionDep) -> PublisherDetailsResponse:
|
||||
publisher = db.get(Publisher, publisher_id)
|
||||
if publisher is None:
|
||||
raise HTTPException(status_code=404, detail="Publisher could not be found")
|
||||
response: PublisherDetailsResponse = get_publisher_details(publisher)
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/issues", response_model=List[IssueDetailsResponse])
|
||||
def get_issues(db: SessionDep) -> List[IssueDetailsResponse]:
|
||||
results: List[IssueDetailsResponse] = []
|
||||
@@ -101,3 +64,4 @@ def get_issues(db: SessionDep) -> List[IssueDetailsResponse]:
|
||||
for issue in issues:
|
||||
results.append(get_issue_details(issue))
|
||||
return results
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from src.db.models.comic import Artist
|
||||
from src.db.repository.comics.artist import get_artist_details
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.artist import ArtistCreation, ArtistResponse, artist_to_response
|
||||
from src.schema.comics.artist_details import ArtistDetailResponse
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/artists", response_model=List[ArtistResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[ArtistResponse]:
|
||||
results: List[ArtistResponse] = []
|
||||
artists = db.query(Artist).all()
|
||||
for artist in artists:
|
||||
response = artist_to_response(artist)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/artists/{artist_id}", response_model=ArtistResponse)
|
||||
def get_artist(artist_id: str, db: SessionDep) -> ArtistResponse:
|
||||
artist = db.get(Artist, artist_id)
|
||||
if artist is None:
|
||||
raise HTTPException(status_code=404, detail="Artist could not be found")
|
||||
response: ArtistResponse = artist_to_response(artist)
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/artists/details/{artist_id}", response_model=ArtistDetailResponse)
|
||||
def get_artist_details_by_id(artist_id: str, db: SessionDep) -> ArtistDetailResponse:
|
||||
artist = db.get(Artist, artist_id)
|
||||
if artist is None:
|
||||
raise HTTPException(status_code=404, detail="Artist could not be found")
|
||||
response: ArtistDetailResponse = get_artist_details(artist)
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/artists", status_code=status.HTTP_201_CREATED)
|
||||
def add_artist(db: SessionDep, artist_creation: ArtistCreation) -> ArtistResponse:
|
||||
artist: Artist = Artist()
|
||||
setattr(artist, "name", artist_creation.name)
|
||||
try:
|
||||
db.add(artist)
|
||||
db.commit()
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Artist already added")
|
||||
response = artist_to_response(artist)
|
||||
return response
|
||||
@@ -0,0 +1,73 @@
|
||||
from typing import List, AnyStr
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from src.apis.utils import SessionDep
|
||||
from src.db.repository.comics.artist import get_artist_details
|
||||
from src.db.repository.comics.comic import list_comics, get_issue_details
|
||||
from src.schema.comics.comic import ComicResponse, ComicDetailsResponse, get_comic_details, get_short_info
|
||||
from src.schema.comics.artist import ArtistCreation, ArtistDetailResponse, ArtistResponse
|
||||
from src.db.models.comic import Comic, Artist, Issue
|
||||
from src.schema.comics.issue import IssueDetailsResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/comics")
|
||||
def get_all_comics(db: SessionDep) -> List[ComicResponse]:
|
||||
results: List[ComicResponse] = []
|
||||
comics = db.query(Comic).all()
|
||||
for comic in comics:
|
||||
response = comic_to_response(comic)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/comics/{comic_id}", response_model=ComicDetailsResponse)
|
||||
def get_comic(comic_id: AnyStr, db: SessionDep) -> ComicDetailsResponse:
|
||||
comic = db.get(Comic, comic_id)
|
||||
if comic is None:
|
||||
raise HTTPException(status_code=404, detail="Comic could not be found")
|
||||
logger.info(f"create ComicDetailsResponse for {comic}")
|
||||
response: ComicDetailsResponse = get_comic_details(comic)
|
||||
logger.info(f"ComicDetailsResponse: {response}")
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/artists", response_model=List[ArtistResponse])
|
||||
def get_all_artists(db: SessionDep) -> List[ArtistResponse]: # type: ignore
|
||||
results: List[ArtistResponse] = []
|
||||
artists = db.query(Artist).all()
|
||||
for artist in artists:
|
||||
results.append(ArtistResponse(id=artist.id, name=str(artist.name))) # type: ignore
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/artists/{artist_id}", response_model=ArtistDetailResponse)
|
||||
def get_artist(artist_id: AnyStr, db: SessionDep) -> ArtistDetailResponse:
|
||||
artist = db.get(Artist, artist_id)
|
||||
if artist is None:
|
||||
raise HTTPException(status_code=404, detail="Artist could not be found")
|
||||
response: ArtistDetailResponse = get_artist_details(artist)
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/artists", status_code=status.HTTP_201_CREATED)
|
||||
def add_artist(db: SessionDep, artist_creation: ArtistCreation) -> ArtistResponse: # type: ignore
|
||||
artist: Artist = Artist()
|
||||
setattr(artist, "name", artist_creation.name)
|
||||
try:
|
||||
db.add(artist)
|
||||
db.commit()
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Artist already added")
|
||||
response = ArtistResponse(id=artist.id, name=str(artist.name))
|
||||
return response
|
||||
|
||||
@router.get("/issues", response_model=List[IssueDetailsResponse])
|
||||
def get_issues(db: SessionDep) -> List[IssueDetailsResponse]: # type: ignore
|
||||
results: List[IssueDetailsResponse] = []
|
||||
issues = db.query(Issue).all()
|
||||
for issue in issues:
|
||||
results.append(get_issue_details(issue))
|
||||
return results
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import ComicWork
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.comicwork import ComicWorkResponse, comicwork_to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/comicworks", response_model=List[ComicWorkResponse])
|
||||
def get_comicworks(db: SessionDep) -> List[ComicWorkResponse]:
|
||||
results: List[ComicWorkResponse] = []
|
||||
worktypes = db.query(ComicWork).all()
|
||||
for worktype in worktypes:
|
||||
response = comicwork_to_response(worktype)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/comicworks/{comicwork_id}", response_model=ComicWorkResponse)
|
||||
def get_comicwork(comicwork_id: str, db: SessionDep) -> ComicWorkResponse:
|
||||
worktype = db.get(ComicWork, comicwork_id)
|
||||
if worktype is None:
|
||||
raise HTTPException(status_code=404, detail="Comicwork could not be found")
|
||||
response = comicwork_to_response(worktype)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import Issue
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.issue import IssueResponse, issue_to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/issues", response_model=List[IssueResponse])
|
||||
def get_issues(db: SessionDep) -> List[IssueResponse]:
|
||||
results: List[IssueResponse] = []
|
||||
issues = db.query(Issue).all()
|
||||
for issue in issues:
|
||||
response = issue_to_response(issue)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/issues/{issue_id}", response_model=IssueResponse)
|
||||
def get_issue(issue_id: str, db: SessionDep) -> IssueResponse:
|
||||
issue = db.get(Issue, issue_id)
|
||||
if issue is None:
|
||||
raise HTTPException(status_code=404, detail="Issue could not be found")
|
||||
response = issue_to_response(issue)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import IssueWork
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.issuework import IssueWorkResponse, issuework_to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/issueworks", response_model=List[IssueWorkResponse])
|
||||
def get_issueworks(db: SessionDep) -> List[IssueWorkResponse]:
|
||||
results: List[IssueWorkResponse] = []
|
||||
worktypes = db.query(IssueWork).all()
|
||||
for worktype in worktypes:
|
||||
response = issuework_to_response(worktype)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/issueworks/{issuework_id}", response_model=IssueWorkResponse)
|
||||
def get_issuework(issuework_id: str, db: SessionDep) -> IssueWorkResponse:
|
||||
worktype = db.get(IssueWork, issuework_id)
|
||||
if worktype is None:
|
||||
raise HTTPException(status_code=404, detail="Issuework could not be found")
|
||||
response = issuework_to_response(worktype)
|
||||
return response
|
||||
@@ -0,0 +1,37 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import Publisher
|
||||
from src.db.repository.comics.publisher import get_publisher_details
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.publisher import PublisherResponse, publisher_to_response
|
||||
from src.schema.comics.publisher_details import PublisherDetailsResponse
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/publishers", response_model=List[PublisherResponse])
|
||||
def get_all_publishers(db: SessionDep) -> List[PublisherResponse]:
|
||||
results: List[PublisherResponse] = []
|
||||
publishers = db.query(Publisher).all()
|
||||
for publisher in publishers:
|
||||
response: PublisherResponse = publisher_to_response(publisher)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/publishers/{publisher_id}", response_model=PublisherResponse)
|
||||
def get_publisher(publisher_id: str, db: SessionDep) -> PublisherResponse:
|
||||
publisher = db.get(Publisher, publisher_id)
|
||||
if publisher is None:
|
||||
raise HTTPException(status_code=404, detail="Publisher could not be found")
|
||||
response: PublisherResponse = publisher_to_response(publisher)
|
||||
return response
|
||||
|
||||
@router.get("/publishers/details/{publisher_id}", response_model=PublisherDetailsResponse)
|
||||
def get_publisher_details_by_id(publisher_id: str, db: SessionDep) -> PublisherDetailsResponse:
|
||||
publisher = db.get(Publisher, publisher_id)
|
||||
if publisher is None:
|
||||
raise HTTPException(status_code=404, detail="Publisher could not be found")
|
||||
response: PublisherDetailsResponse = get_publisher_details(publisher)
|
||||
return response
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import StoryArc
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.storyarc import StoryArcResponse, storyarc_to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/storyarcs", response_model=List[StoryArcResponse])
|
||||
def get_storyarcs(db: SessionDep) -> List[StoryArcResponse]:
|
||||
results: List[StoryArcResponse] = []
|
||||
storyarcs = db.query(StoryArc).all()
|
||||
for storyarc in storyarcs:
|
||||
response = storyarc_to_response(storyarc)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/storyarcs/{storyarc_id}", response_model=StoryArcResponse)
|
||||
def get_storyarc(story_arc_id: str, db: SessionDep) -> StoryArcResponse:
|
||||
storyarc = db.get(StoryArc, story_arc_id)
|
||||
if storyarc is None:
|
||||
raise HTTPException(status_code=404, detail="Storyarc could not be found")
|
||||
response = storyarc_to_response(storyarc)
|
||||
return response
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import Volume
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.volume import VolumeResponse, volume_to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/volumes", response_model=List[VolumeResponse])
|
||||
def volumes(db: SessionDep) -> List[VolumeResponse]:
|
||||
results: List[VolumeResponse] = []
|
||||
worktypes = db.query(Volume).all()
|
||||
for worktype in worktypes:
|
||||
response = volume_to_response(worktype)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/volumes/{volume_id}", response_model=VolumeResponse)
|
||||
def get_volume(volume_id: str, db: SessionDep) -> VolumeResponse:
|
||||
worktype = db.get(Volume, volume_id)
|
||||
if worktype is None:
|
||||
raise HTTPException(status_code=404, detail="Volume could not be found")
|
||||
response = volume_to_response(worktype)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.comic import WorkType
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.comics.worktype import WorktypeResponse, worktype_to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/worktypes", response_model=List[WorktypeResponse])
|
||||
def get_issues(db: SessionDep) -> List[WorktypeResponse]:
|
||||
results: List[WorktypeResponse] = []
|
||||
worktypes = db.query(WorkType).all()
|
||||
for worktype in worktypes:
|
||||
response = worktype_to_response(worktype)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/worktypes/{worktype_id}", response_model=WorktypeResponse)
|
||||
def get_issue(worktype_id: str, db: SessionDep) -> WorktypeResponse:
|
||||
worktype = db.get(WorkType, worktype_id)
|
||||
if worktype is None:
|
||||
raise HTTPException(status_code=404, detail="Worktype could not be found")
|
||||
response = worktype_to_response(worktype)
|
||||
return response
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, status
|
||||
|
||||
from src.schema.admin import HealthCheck
|
||||
from src.schema.admin.healthcheck import HealthCheck
|
||||
|
||||
health_router = APIRouter()
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.core.config import settings
|
||||
from src.core.log_conf import logger
|
||||
from src.core.security import authenticate_user, create_access_token
|
||||
from src.schema.admin import Token
|
||||
|
||||
login_router = APIRouter()
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
email: str | None = None
|
||||
password: str | None = None
|
||||
|
||||
|
||||
@login_router.post(
|
||||
"/login",
|
||||
tags=["login"],
|
||||
summary="Login and get token",
|
||||
response_description="Return HTTP status code 200 (OK)",
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
def login(request: LoginRequest) -> Token:
|
||||
logger.info(f"login with {request.email}")
|
||||
user = authenticate_user(request.email, request.password) # type: ignore
|
||||
scopes = ["admin", "read"]
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.email, "scope": " ".join(scopes)},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
return Token(access_token=access_token, token_type="bearer")
|
||||
@@ -0,0 +1,69 @@
|
||||
from typing import List, AnyStr
|
||||
|
||||
from fastapi import APIRouter, status, HTTPException, Depends
|
||||
from sqlalchemy import select, Sequence
|
||||
from src.core.log_conf import logger
|
||||
from src.apis.utils import SessionDep
|
||||
from src.db.repository.media import create_new_mediafile
|
||||
from src.schema.media.file import MediaFileResponse, Link, get_file_details, set_file
|
||||
from src.db.models.media import MediaFile
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/update-titles")
|
||||
def update_titles(db: SessionDep) -> list[MediaFileResponse]:
|
||||
results: list[MediaFileResponse] = []
|
||||
files = db.query(MediaFile).filter(MediaFile.review == True).all()
|
||||
for mediafile in files:
|
||||
mediafile.update_title()
|
||||
db.add(mediafile)
|
||||
response = get_file_details(mediafile)
|
||||
results.append(response)
|
||||
db.commit()
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/files", response_model=List[MediaFileResponse])
|
||||
#def get_all_files(db: SessionDep, review: bool = False, download: bool = False, current_user: Profile = Depends(get_current_user_from_token)) -> List[MediaFileResponse]:
|
||||
def get_all_files(db: SessionDep, review: bool = False, download: bool = False) -> List[MediaFileResponse]:
|
||||
results: list[MediaFileResponse] = []
|
||||
files: Sequence[MediaFile]
|
||||
if review:
|
||||
files = db.query(MediaFile).filter(MediaFile.review == True).all()
|
||||
elif download:
|
||||
files = db.query(MediaFile).filter(MediaFile.should_download == True).all()
|
||||
else:
|
||||
files = db.scalars(select(MediaFile)).all()
|
||||
for mediafile in files:
|
||||
response = get_file_details(mediafile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def get_file(file_id: AnyStr, db: SessionDep) -> MediaFileResponse:
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
response = get_file_details(mediafile)
|
||||
return response
|
||||
|
||||
@router.put("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def update_file(file_id: AnyStr, db: SessionDep, info: MediaFileResponse) -> MediaFileResponse:
|
||||
mediaFile = db.get(MediaFile, file_id)
|
||||
if not mediaFile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
set_file(info, mediaFile)
|
||||
db.add(mediaFile)
|
||||
db.commit()
|
||||
return info
|
||||
|
||||
|
||||
@router.post("/files", status_code=status.HTTP_201_CREATED)
|
||||
def add_file(new_link: Link, db: SessionDep) -> MediaFileResponse:
|
||||
logger.info(f"add url {new_link.url}")
|
||||
try:
|
||||
mediaFile: MediaFile = create_new_mediafile(new_link.url, db)
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Link duplicate")
|
||||
response = get_file_details(mediaFile)
|
||||
return response
|
||||
@@ -0,0 +1,45 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, status, HTTPException
|
||||
from src.core.log_conf import logger
|
||||
from src.db.repository.media.actor import delete_mediaactor, import_mediaactor
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.actor import MediaActorModel, MediaActorResponse, actor_to_response
|
||||
from src.db.models.media import MediaActor
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/actors", response_model=List[MediaActorResponse])
|
||||
def get_all_actors(db: SessionDep) -> List[MediaActorResponse]:
|
||||
results: list[MediaActorResponse] = []
|
||||
actors = db.query(MediaActor).all()
|
||||
for mediaactor in actors:
|
||||
response = actor_to_response(mediaactor)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/actors/{actor_id}", response_model=MediaActorResponse)
|
||||
def get_actor(actor_id: str, db: SessionDep) -> MediaActorResponse:
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
if not media_actor:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
response = actor_to_response(media_actor)
|
||||
return response
|
||||
|
||||
@router.delete("/actors/{actor_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_actor(actor_id: str, db: SessionDep):
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
if not media_actor:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
logger.info(f"delete MediaActor: {actor_id}")
|
||||
delete_mediaactor(db, media_actor.id)
|
||||
|
||||
@router.post("/actors", status_code=status.HTTP_201_CREATED)
|
||||
def add_actor(new_actor: MediaActorModel, db: SessionDep) -> MediaActorResponse:
|
||||
logger.info(f"add actor {new_actor.url}")
|
||||
try:
|
||||
mediaActor: MediaActor = import_mediaactor(db, new_actor)
|
||||
except Exception as exception:
|
||||
raise HTTPException(status_code=409, detail=f"Link duplicate: {exception}")
|
||||
response = actor_to_response(mediaActor)
|
||||
return response
|
||||
@@ -0,0 +1,163 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, status, HTTPException
|
||||
from src.core.log_conf import logger
|
||||
from src.db.repository.media.actorfile import (
|
||||
create_new_mediaactorfile,
|
||||
delete_mediaactorfile,
|
||||
)
|
||||
from src.db.repository.media.file import delete_mediafile, import_mediafile
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.actor import MediaActorResponse, actor_to_response
|
||||
from src.schema.media.actorfile import MediaActorFileResponse, actorfile_to_response
|
||||
from src.schema.media.file import (
|
||||
MediaFileModel,
|
||||
MediaFileResponse,
|
||||
file_to_response,
|
||||
file_to_model,
|
||||
)
|
||||
from src.db.models.media import MediaFile
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/update-titles")
|
||||
def update_titles(db: SessionDep) -> List[MediaFileResponse]:
|
||||
"""
|
||||
Update title for given MediaFile.
|
||||
"""
|
||||
results: list[MediaFileResponse] = []
|
||||
files = db.query(MediaFile).filter(MediaFile.review.is_(True)).all()
|
||||
for mediafile in files:
|
||||
mediafile.update_title()
|
||||
db.add(mediafile)
|
||||
response = file_to_response(mediafile)
|
||||
results.append(response)
|
||||
db.commit()
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/files", response_model=List[MediaFileResponse])
|
||||
def get_all_files(
|
||||
db: SessionDep, review: bool = False, download: bool = False
|
||||
) -> List[MediaFileResponse]:
|
||||
"""
|
||||
Get all MediaFiles.
|
||||
"""
|
||||
results: List[MediaFileResponse] = []
|
||||
files: List[MediaFile]
|
||||
if review:
|
||||
files = db.query(MediaFile).filter(MediaFile.review.is_(True)).all()
|
||||
elif download:
|
||||
files = db.query(MediaFile).filter(MediaFile.should_download.is_(True)).all()
|
||||
else:
|
||||
files = db.query(MediaFile).all()
|
||||
for mediafile in files:
|
||||
response = file_to_response(mediafile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def get_file(file_id: str, db: SessionDep) -> MediaFileResponse:
|
||||
"""
|
||||
Get MediaFile with given id or return HTTPException.
|
||||
"""
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
response = file_to_response(mediafile)
|
||||
return response
|
||||
|
||||
|
||||
@router.delete("/files/{file_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_file(file_id: str, db: SessionDep):
|
||||
"""
|
||||
Delete MediaFile by given id.
|
||||
"""
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
logger.info("delete MediaFile: %s", file_id)
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info("MediaActorFiles links %s", len(actor_files))
|
||||
if len(actor_files) > 0:
|
||||
logger.info("delete MediaActor relations first")
|
||||
for actor_file in actor_files:
|
||||
delete_mediaactorfile(db, actor_file.id)
|
||||
delete_mediafile(db, mediafile.id)
|
||||
|
||||
|
||||
@router.get("/files/{file_id}/actors", response_model=list[MediaActorResponse])
|
||||
def get_file_actors(file_id: str, db: SessionDep) -> list[MediaActorResponse]:
|
||||
"""
|
||||
Get list of Actors for given MediaFile.
|
||||
"""
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info("already known actors: %s", actor_files)
|
||||
results: list[MediaActorResponse] = []
|
||||
for actor_file in actor_files:
|
||||
response = actor_to_response(actor_file.media_actor)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.put("/files/{file_id}/actors", response_model=List[MediaActorFileResponse])
|
||||
def update_file_actors(
|
||||
file_id: str, db: SessionDep, actors: List[MediaActorResponse]
|
||||
) -> List[MediaActorFileResponse]:
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info("already known actors: %s", actor_files)
|
||||
for actor in actors:
|
||||
already_associated = False
|
||||
for actor_file in actor_files:
|
||||
if actor.id == actor_file.media_actor_id:
|
||||
logger.info("alreay associated - do nothing")
|
||||
already_associated = True
|
||||
break
|
||||
if not already_associated:
|
||||
create_new_mediaactorfile(db, actor.id, mediafile.id)
|
||||
db.refresh(mediafile)
|
||||
actor_files = mediafile.media_actor_files
|
||||
results: List[MediaActorFileResponse] = []
|
||||
for actor_file in actor_files:
|
||||
response = actorfile_to_response(actor_file)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
|
||||
@router.put("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def update_file(
|
||||
file_id: str, db: SessionDep, info: MediaFileResponse
|
||||
) -> MediaFileResponse:
|
||||
"""
|
||||
Update MediaFile with given id and data.
|
||||
"""
|
||||
media_file = db.get(MediaFile, file_id)
|
||||
if not media_file:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
file_to_model(info, media_file)
|
||||
db.add(media_file)
|
||||
db.commit()
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be updated")
|
||||
response = file_to_response(mediafile)
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/files", status_code=status.HTTP_201_CREATED)
|
||||
def add_file(new_file: MediaFileModel, db: SessionDep) -> MediaFileResponse:
|
||||
logger.info("add mediafile %s", new_file)
|
||||
try:
|
||||
mediaFile: MediaFile = import_mediafile(db, new_file)
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="MediaFile duplicate")
|
||||
response = file_to_response(mediaFile)
|
||||
return response
|
||||
+12
-13
@@ -1,33 +1,32 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, status, HTTPException
|
||||
from sqlalchemy import select
|
||||
from src.db.models.media import MediaActorFile
|
||||
from src.db.repository.media import delete_mediaactorfile
|
||||
from src.db.repository.media.actorfile import delete_mediaactorfile
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.actorfile import MediaActorFileResponse, get_actorfile_details
|
||||
from src.schema.media.actorfile import MediaActorFileResponse, actorfile_to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/actorfiles", response_model=list[MediaActorFileResponse])
|
||||
def get_all_actorfiles(db: SessionDep) -> list[MediaActorFileResponse]: # type: ignore
|
||||
results: list[MediaActorFileResponse] = []
|
||||
actorfiles = db.scalars(select(MediaActorFile)).all()
|
||||
for mediaactorfile in actorfiles:
|
||||
response = MediaActorFileResponse(id=mediaactorfile.id, actor_id=str(mediaactorfile.media_actor_id), file_id=str(mediaactorfile.media_file_id))
|
||||
@router.get("/actorfiles", response_model=List[MediaActorFileResponse])
|
||||
def get_all_actorfiles(db: SessionDep) -> List[MediaActorFileResponse]:
|
||||
results: List[MediaActorFileResponse] = []
|
||||
actorfiles = db.query(MediaActorFile).all()
|
||||
for media_actorfile in actorfiles:
|
||||
response = actorfile_to_response(media_actorfile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/actorfiles/{actorfile_id}", response_model=MediaActorFileResponse)
|
||||
def get_actorfile(actorfile_id: str, db: SessionDep) -> MediaActorFileResponse: # type: ignore
|
||||
def get_actorfile(actorfile_id: str, db: SessionDep) -> MediaActorFileResponse:
|
||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||
if not media_actorfile:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
response = get_actorfile_details(media_actorfile)
|
||||
response = actorfile_to_response(media_actorfile)
|
||||
return response
|
||||
|
||||
@router.delete("/actorfiles/{actorfile_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_actorfile(actorfile_id: str, db: SessionDep): # type: ignore
|
||||
def delete_actorfile(actorfile_id: str, db: SessionDep):
|
||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||
if not media_actorfile:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
delete_mediaactorfile(db, media_actorfile.id)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.media import MediaArticle
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.article import MediaArticleResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/articles", response_model=List[MediaArticleResponse])
|
||||
def get_all_files(
|
||||
db: SessionDep, review: bool = False, download: bool = False
|
||||
) -> List[MediaArticleResponse]:
|
||||
"""
|
||||
Get all MediaVideos.
|
||||
"""
|
||||
results: List[MediaArticleResponse] = []
|
||||
articles: List[MediaArticle]
|
||||
articles = db.query(MediaArticle).all()
|
||||
for article in articles:
|
||||
response = to_response(article)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.media import MediaVideo
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.video import MediaVideoResponse, video_to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/videos", response_model=List[MediaVideoResponse])
|
||||
def get_all_videos(
|
||||
db: SessionDep, review: bool = False, download: bool = False
|
||||
) -> List[MediaVideoResponse]:
|
||||
"""
|
||||
Get all MediaVideos.
|
||||
"""
|
||||
results: List[MediaVideoResponse] = []
|
||||
files: List[MediaVideo]
|
||||
if review:
|
||||
files = db.query(MediaVideo).filter(MediaVideo.review.is_(True)).all()
|
||||
elif download:
|
||||
files = db.query(MediaVideo).filter(MediaVideo.should_download.is_(True)).all()
|
||||
else:
|
||||
files = db.query(MediaVideo).all()
|
||||
for mediafile in files:
|
||||
response = video_to_response(mediafile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/videos/{video_id}", response_model=MediaVideoResponse)
|
||||
def get_video(video_id: str, db: SessionDep) -> MediaVideoResponse:
|
||||
"""
|
||||
Get MediaVideo by id.
|
||||
"""
|
||||
video = db.get(MediaVideo, video_id)
|
||||
if video is None:
|
||||
raise HTTPException(status_code=404, detail="MediaVideo could not be found")
|
||||
response = video_to_response(video)
|
||||
return response
|
||||
@@ -1,48 +0,0 @@
|
||||
from fastapi import APIRouter, status, HTTPException
|
||||
from sqlalchemy import select
|
||||
from src.core.log_conf import logger
|
||||
from src.db.repository.media import create_new_mediaactor, delete_mediaactor
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.actor import Actor, MediaActorResponse, get_actor_details
|
||||
from src.db.models.media import MediaActor
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/actors", response_model=list[MediaActorResponse])
|
||||
# def get_all_files(db: SessionDep, review: bool = False, download: bool = False, current_user: Profile = Depends(get_current_user_from_token)) -> List[MediaFileResponse]:
|
||||
def get_all_actors(db: SessionDep, review: bool = False, download: bool = False) -> list[MediaActorResponse]: # type: ignore
|
||||
results: list[MediaActorResponse] = []
|
||||
actors = db.scalars(select(MediaActor)).all()
|
||||
for mediaactor in actors:
|
||||
response = MediaActorResponse(id=mediaactor.id, name=str(mediaactor.name), url=str(mediaactor.url))
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/actors/{actor_id}", response_model=MediaActorResponse)
|
||||
def get_actor(actor_id: str, db: SessionDep) -> MediaActorResponse: # type: ignore
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
if not media_actor:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
response = get_actor_details(media_actor)
|
||||
return response
|
||||
|
||||
@router.delete("/actors/{actor_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_actor(actor_id: str, db: SessionDep): # type: ignore
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
if not media_actor:
|
||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||
logger.info(f"delete MediaActor: {actor_id}")
|
||||
actor_files = media_actor.media_actor_files
|
||||
logger.info(f"MediaActorFiles links {len(actor_files)}")
|
||||
if len(actor_files) == 0:
|
||||
delete_mediaactor(db, media_actor.id)
|
||||
|
||||
@router.post("/actors", status_code=status.HTTP_201_CREATED)
|
||||
def add_actor(new_actor: Actor, db: SessionDep) -> MediaActorResponse: # type: ignore
|
||||
logger.info(f"add actor {new_actor.url}")
|
||||
try:
|
||||
mediaActor: MediaActor = create_new_mediaactor(new_actor, db)
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Link duplicate")
|
||||
response = get_actor_details(mediaActor)
|
||||
return response
|
||||
@@ -1,120 +0,0 @@
|
||||
from fastapi import APIRouter, status, HTTPException, Depends
|
||||
from sqlalchemy import select, Sequence
|
||||
from src.core.log_conf import logger
|
||||
from src.db.repository.media import create_new_mediaactorfile, create_new_mediafile, delete_mediafile
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.media.actor import MediaActorResponse
|
||||
from src.schema.media.actorfile import MediaActorFileResponse
|
||||
from src.schema.media.file import MediaFileResponse, Link, get_file_details, set_file
|
||||
from src.db.models.media import MediaFile
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/update-titles")
|
||||
def update_titles(db: SessionDep) -> list[MediaFileResponse]: # type: ignore
|
||||
results: list[MediaFileResponse] = []
|
||||
files = db.query(MediaFile).filter(MediaFile.review == True).all()
|
||||
for mediafile in files:
|
||||
mediafile.update_title()
|
||||
db.add(mediafile)
|
||||
response = get_file_details(mediafile)
|
||||
results.append(response)
|
||||
db.commit()
|
||||
return results
|
||||
|
||||
|
||||
@router.get("/files", response_model=list[MediaFileResponse])
|
||||
# def get_all_files(db: SessionDep, review: bool = False, download: bool = False, current_user: Profile = Depends(get_current_user_from_token)) -> List[MediaFileResponse]:
|
||||
def get_all_files(db: SessionDep, review: bool = False, download: bool = False) -> list[MediaFileResponse]: # type: ignore
|
||||
results: list[MediaFileResponse] = []
|
||||
files: Sequence[MediaFile]
|
||||
if review:
|
||||
files = db.query(MediaFile).filter(MediaFile.review == True).all() # type: ignore
|
||||
elif download:
|
||||
files = db.query(MediaFile).filter(MediaFile.should_download == True).all() # type: ignore
|
||||
else:
|
||||
files = db.scalars(select(MediaFile)).all() # type: ignore
|
||||
for mediafile in files: # type: ignore
|
||||
response = get_file_details(mediafile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def get_file(file_id: str, db: SessionDep) -> MediaFileResponse: # type: ignore
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
response = get_file_details(mediafile)
|
||||
return response
|
||||
|
||||
@router.delete("/files/{file_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_file(file_id: str, db: SessionDep): # type: ignore
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
logger.info(f"delete MediaFile: {file_id}")
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info(f"MediaActorFiles links {len(actor_files)}")
|
||||
if len(actor_files) == 0:
|
||||
delete_mediafile(db, mediafile.id)
|
||||
|
||||
@router.get("/files/{file_id}/actors", response_model=list[MediaActorResponse])
|
||||
def get_file_actors(file_id: str, db: SessionDep) -> list[MediaActorResponse]: # type: ignore
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info(f"already known actors: {actor_files}")
|
||||
results: list[MediaActorResponse] = []
|
||||
for actor_file in actor_files:
|
||||
response = MediaActorResponse(id=actor_file.media_actor.id, name=actor_file.media_actor.name, url=actor_file.media_actor.url)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.put("/files/{file_id}/actors", response_model=list[MediaActorFileResponse])
|
||||
def update_file_actors(file_id: str, db: SessionDep, actors: list[MediaActorResponse]) -> list[MediaActorFileResponse]: # type: ignore
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
actor_files = mediafile.media_actor_files
|
||||
logger.info(f"already known actors: {actor_files}")
|
||||
for actor in actors:
|
||||
already_associated = False
|
||||
for actor_file in actor_files:
|
||||
if actor.id == actor_file.media_actor_id:
|
||||
logger.info("alreay associated - do nothing")
|
||||
already_associated = True
|
||||
break
|
||||
if not already_associated:
|
||||
create_new_mediaactorfile(db, actor.id, mediafile.id)
|
||||
db.refresh(mediafile)
|
||||
actor_files = mediafile.media_actor_files
|
||||
results: list[MediaActorFileResponse] = []
|
||||
for actor_file in actor_files:
|
||||
response = MediaActorFileResponse(id=actor_file.id, actor_id=actor_file.media_actor_id, file_id=actor_file.media_file_id)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.put("/files/{file_id}", response_model=MediaFileResponse)
|
||||
def update_file(file_id: str, db: SessionDep, info: MediaFileResponse) -> MediaFileResponse: # type: ignore
|
||||
mediaFile = db.get(MediaFile, file_id)
|
||||
if not mediaFile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||
set_file(info, mediaFile)
|
||||
db.add(mediaFile)
|
||||
db.commit()
|
||||
mediafile = db.get(MediaFile, file_id)
|
||||
if not mediafile:
|
||||
raise HTTPException(status_code=404, detail="MediaFile could not be updated")
|
||||
response = get_file_details(mediafile)
|
||||
return response
|
||||
|
||||
@router.post("/files", status_code=status.HTTP_201_CREATED)
|
||||
def add_file(new_link: Link, db: SessionDep) -> MediaFileResponse: # type: ignore
|
||||
logger.info(f"add url {new_link.url}")
|
||||
try:
|
||||
mediaFile: MediaFile = create_new_mediafile(new_link.url, db)
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Link duplicate")
|
||||
response = get_file_details(mediaFile)
|
||||
return response
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.session import SessionDep
|
||||
from src.apis.utils import SessionDep
|
||||
from src.schema.tysc.sport import SportResponse
|
||||
from src.db.models.tysc import Sport
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import Card
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.card import CardResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/cards")
|
||||
def get_all_cards(db: SessionDep) -> List[CardResponse]:
|
||||
results: List[CardResponse] = []
|
||||
cards = db.query(Card).all()
|
||||
for card in cards:
|
||||
response = to_response(card)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/cards/{card_id}", response_model=CardResponse)
|
||||
def get_card(card_id: str, db: SessionDep) -> CardResponse:
|
||||
card = db.get(Card, card_id)
|
||||
if card is None:
|
||||
raise HTTPException(status_code=404, detail="Card could not be found")
|
||||
response = to_response(card)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import CardSet
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.cardset import CardSetResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/cardsets")
|
||||
def get_all_cardsets(db: SessionDep) -> List[CardSetResponse]:
|
||||
results: List[CardSetResponse] = []
|
||||
cardsets = db.query(CardSet).all()
|
||||
for cardset in cardsets:
|
||||
response = to_response(cardset)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/cardsets/{cardset_id}", response_model=CardSetResponse)
|
||||
def get_cardset(cardset_id: str, db: SessionDep) -> CardSetResponse:
|
||||
cardset = db.get(CardSet, cardset_id)
|
||||
if cardset is None:
|
||||
raise HTTPException(status_code=404, detail="Cardset could not be found")
|
||||
response = to_response(cardset)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import FieldPosition
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.fieldposition import FieldPositionResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/positions")
|
||||
def get_all_positions(db: SessionDep) -> List[FieldPositionResponse]:
|
||||
results: list[FieldPositionResponse] = []
|
||||
positions = db.query(FieldPosition).all()
|
||||
for position in positions:
|
||||
response = to_response(position)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/positions/{position_id}", response_model=FieldPositionResponse)
|
||||
def get_position(position_id: str, db: SessionDep) -> FieldPositionResponse:
|
||||
position = db.get(FieldPosition, position_id)
|
||||
if position is None:
|
||||
raise HTTPException(status_code=404, detail="Fieldposition could not be found")
|
||||
response = to_response(position)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import Player
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.player import PlayerResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/players")
|
||||
def get_all_players(db: SessionDep) -> List[PlayerResponse]:
|
||||
results: List[PlayerResponse] = []
|
||||
players = db.query(Player).all()
|
||||
for player in players:
|
||||
response = to_response(player)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/players/{player_id}", response_model=PlayerResponse)
|
||||
def get_player(player_id: str, db: SessionDep) -> PlayerResponse:
|
||||
player = db.get(Player, player_id)
|
||||
if player is None:
|
||||
raise HTTPException(status_code=404, detail="Player could not be found")
|
||||
response = to_response(player)
|
||||
return response
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import Rooster
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.rooster import RoosterResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/roosters")
|
||||
def get_all_roosters(db: SessionDep) -> List[RoosterResponse]:
|
||||
results: list[RoosterResponse] = []
|
||||
roosters = db.query(Rooster).all()
|
||||
for rooster in roosters:
|
||||
response = to_response(rooster)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/roosters/{rooster_id}", response_model=RoosterResponse)
|
||||
def get_rooster(rooster_id: str, db: SessionDep) -> RoosterResponse:
|
||||
rooster = db.get(Rooster, rooster_id)
|
||||
if rooster is None:
|
||||
raise HTTPException(status_code=404, detail="Rooster could not be found")
|
||||
response = to_response(rooster)
|
||||
return response
|
||||
@@ -0,0 +1,28 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.sport import SportResponse, to_response
|
||||
from src.db.models.tysc import Sport
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/sports")
|
||||
def get_all_sports(db: SessionDep) -> List[SportResponse]:
|
||||
results: list[SportResponse] = []
|
||||
sports = db.query(Sport).all()
|
||||
for sport in sports:
|
||||
response = to_response(sport)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/sports/{sport_id}", response_model=SportResponse)
|
||||
def get_sport(sport_id: str, db: SessionDep) -> SportResponse:
|
||||
sport = db.get(Sport, sport_id)
|
||||
if sport is None:
|
||||
raise HTTPException(status_code=404, detail="Sport could not be found")
|
||||
logger.debug(f"create SportResponse for {sport}")
|
||||
response: SportResponse = to_response(sport)
|
||||
return response
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import Team
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.team import TeamResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/teams")
|
||||
def get_all_teams(db: SessionDep) -> List[TeamResponse]:
|
||||
results: list[TeamResponse] = []
|
||||
teams = db.query(Team).all()
|
||||
for team in teams:
|
||||
response = to_response(team)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/teams/{team_id}", response_model=TeamResponse)
|
||||
def get_team(team_id: str, db: SessionDep) -> TeamResponse:
|
||||
team = db.get(Team, team_id)
|
||||
if team is None:
|
||||
raise HTTPException(status_code=404, detail="Team could not be found")
|
||||
response = to_response(team)
|
||||
return response
|
||||
@@ -0,0 +1,33 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.tysc import Vendor
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.tysc.vendor import VendorResponse, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/vendors")
|
||||
def get_all_vendors(db: SessionDep) -> List[VendorResponse]:
|
||||
"""
|
||||
retrieve all vendors as json response.
|
||||
"""
|
||||
results: list[VendorResponse] = []
|
||||
vendors = db.query(Vendor).all()
|
||||
for vendor in vendors:
|
||||
response = to_response(vendor)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/vendors/{vendor_id}", response_model=VendorResponse)
|
||||
def get_vendor(vendor_id: str, db: SessionDep) -> VendorResponse:
|
||||
"""
|
||||
retrieve vendor by id as json response.
|
||||
"""
|
||||
vendor = db.get(Vendor, vendor_id)
|
||||
if vendor is None:
|
||||
raise HTTPException(status_code=404, detail="Vendor could not be found")
|
||||
response = to_response(vendor)
|
||||
return response
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.admin import Assignment
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.user.assignment import AssignmentResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/assignments", response_model=List[AssignmentResponse])
|
||||
def get_all_assignments(db: SessionDep) -> List[AssignmentResponse]:
|
||||
results: List[AssignmentResponse] = []
|
||||
assignments = db.query(Assignment).all()
|
||||
for assignment in assignments:
|
||||
response = to_response(assignment)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/assignments/{assignment_id}", response_model=AssignmentResponse)
|
||||
def get_assignment(assignment_id: str, db: SessionDep) -> AssignmentResponse:
|
||||
assignment = db.get(Assignment, assignment_id)
|
||||
if assignment is None:
|
||||
raise HTTPException(status_code=404, detail="Assignment could not be found")
|
||||
response = to_response(assignment)
|
||||
return response
|
||||
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.db.models.admin import Permission
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.user.permission import PermissionResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/permissions", response_model=List[PermissionResponse])
|
||||
def get_all_permissions(db: SessionDep) -> List[PermissionResponse]:
|
||||
results: List[PermissionResponse] = []
|
||||
permissions = db.query(Permission).all()
|
||||
for permission in permissions:
|
||||
response = to_response(permission)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/permissions/{permission_id}", response_model=PermissionResponse)
|
||||
def get_permission(permission_id: str, db: SessionDep) -> PermissionResponse:
|
||||
permission = db.get(Permission, permission_id)
|
||||
if permission is None:
|
||||
raise HTTPException(status_code=404, detail="Permission could not be found")
|
||||
response = to_response(permission)
|
||||
return response
|
||||
@@ -0,0 +1,53 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from src.core.log_conf import logger
|
||||
|
||||
from src.core.security import CurrentUser
|
||||
from src.db.models.admin import Profile
|
||||
from src.db.repository.user import create_new_profile
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.user.profile import ProfileResponse, ProfileModel, to_response
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/profile", response_model=ProfileModel)
|
||||
async def read_profile(current_user: CurrentUser):
|
||||
return current_user
|
||||
|
||||
@router.get("/profiles", response_model=List[ProfileResponse])
|
||||
def get_all_profiles(db: SessionDep) -> List[ProfileResponse]:
|
||||
results: List[ProfileResponse] = []
|
||||
profiles = db.scalars(select(Profile)).all()
|
||||
for profile in profiles:
|
||||
response = to_response(profile)
|
||||
results.append(response)
|
||||
return results
|
||||
|
||||
@router.get("/profiles/{profile_id}", response_model=ProfileResponse)
|
||||
def get_profile(profile_id: str, db: SessionDep) -> ProfileResponse:
|
||||
profile = db.get(Profile, profile_id)
|
||||
if not profile:
|
||||
raise HTTPException(status_code=404, detail="Profile could not be found")
|
||||
response = to_response(profile)
|
||||
return response
|
||||
|
||||
@router.delete("/profiles/{profile_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_profile(profile_id: str, db: SessionDep): # type: ignore
|
||||
profile = db.get(Profile, profile_id)
|
||||
if not profile:
|
||||
raise HTTPException(status_code=404, detail="Profile could not be found")
|
||||
logger.info(f"delete Profile: {profile_id}")
|
||||
delete_profile(profile_id=profile_id, db=db)
|
||||
|
||||
@router.post("/profiles", status_code=status.HTTP_201_CREATED)
|
||||
def add_profile(new_profile: ProfileModel, db: SessionDep) -> ProfileResponse:
|
||||
logger.info(f"add profile {new_profile.username}")
|
||||
try:
|
||||
profile: Profile = create_new_profile(new_profile, db)
|
||||
except:
|
||||
raise HTTPException(status_code=409, detail="Profile duplicate")
|
||||
response = to_response(profile)
|
||||
return response
|
||||
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from src.db.models.admin import Token
|
||||
from src.db.session import SessionDep
|
||||
from src.schema.user.token import TokenResponse, to_response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/tokens", response_model=List[TokenResponse])
|
||||
def get_all_profiles(db: SessionDep) -> List[TokenResponse]:
|
||||
results: List[TokenResponse] = []
|
||||
tokens = db.query(Token).all()
|
||||
for token in tokens:
|
||||
response = to_response(token)
|
||||
results.append(response)
|
||||
return results
|
||||
@@ -9,17 +9,17 @@ load_dotenv(dotenv_path=env_path)
|
||||
|
||||
class Settings:
|
||||
PROJECT_NAME: str = "Kontor"
|
||||
PROJECT_VERSION: str = "0.2.0"
|
||||
PROJECT_VERSION: str = "0.3.0"
|
||||
|
||||
DB_USER: str = os.getenv("DB_USER", "kontor")
|
||||
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
|
||||
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
|
||||
DB_PORT: int = int(os.getenv("DB_PORT", 5432))
|
||||
DB_PORT: str = os.getenv("DB_PORT", 5432)
|
||||
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
|
||||
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
|
||||
SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=")
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60*24*7 # one week in mins
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 600 # in mins
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ LOGGING_CONFIG: dict[str, Any] = {
|
||||
"formatters": {
|
||||
"default": {
|
||||
"()": "uvicorn.logging.DefaultFormatter",
|
||||
"fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s",
|
||||
"fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s"
|
||||
},
|
||||
"access": {
|
||||
"()": "uvicorn.logging.AccessFormatter",
|
||||
@@ -34,16 +34,11 @@ LOGGING_CONFIG: dict[str, Any] = {
|
||||
},
|
||||
"loggers": {
|
||||
"root": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||
"kontor": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||
"uvicorn.error": {"level": "INFO"},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["default"],
|
||||
"level": "WARNING",
|
||||
"propagate": False,
|
||||
},
|
||||
"uvicorn.access": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||
},
|
||||
}
|
||||
|
||||
logging.config.dictConfig(LOGGING_CONFIG)
|
||||
logger = logging.getLogger("kontor")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
import time
|
||||
|
||||
from fastapi import Request, Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from src.core.log_conf import logger
|
||||
|
||||
class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request: Request, call_next) -> Response:
|
||||
start_time = time.time()
|
||||
path = request.url.path
|
||||
|
||||
if path != "/health":
|
||||
# Log request info
|
||||
request_info ={
|
||||
"method": request.method,
|
||||
"path": path,
|
||||
"client_ip": request.client.host if request.client else "unknown"
|
||||
}
|
||||
logger.info("Incoming: %s", json.dumps(request_info))
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
if path != "/health":
|
||||
# Log response info
|
||||
duration_ms = (time.time()- start_time)*1000
|
||||
response_info = {
|
||||
"status_code": response.status_code,
|
||||
"duration_ms": round(duration_ms, 2)
|
||||
}
|
||||
logger.info("completed: %s", json.dumps(response_info))
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,76 +1,17 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Annotated, Dict, List, Optional
|
||||
|
||||
import bcrypt
|
||||
from fastapi import Depends, HTTPException, Request, Security, status
|
||||
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
||||
from fastapi.security import OAuth2, OAuth2PasswordBearer, SecurityScopes
|
||||
from fastapi.security.utils import get_authorization_scheme_param
|
||||
from jose import JWTError, jwt
|
||||
from pydantic import ValidationError
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from typing import Optional
|
||||
|
||||
from src.core.config import settings
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.admin import Profile
|
||||
from src.db.repository.admin import get_profile
|
||||
from src.db.session import SessionLocal
|
||||
from src.schema.admin import ProfileModel, TokenData
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(
|
||||
tokenUrl="/api/login/token",
|
||||
scopes={"me": "read", "admin": "read"},
|
||||
)
|
||||
|
||||
|
||||
class OAuth2PasswordBearerWithCookie(OAuth2):
|
||||
def __init__(
|
||||
self,
|
||||
tokenUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
auto_error: bool = True,
|
||||
):
|
||||
if not scopes:
|
||||
scopes = {}
|
||||
flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) # type: ignore
|
||||
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization: str = request.cookies.get("access_token") # type: ignore # changed to accept access token from httpOnly Cookie
|
||||
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
if self.auto_error:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Not authenticated",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
else:
|
||||
return None
|
||||
return param
|
||||
|
||||
|
||||
def authenticate_user(username: str, password: str) -> Optional[Profile]:
|
||||
with SessionLocal() as db:
|
||||
user = get_profile(username=username, db=db)
|
||||
logger.debug(user)
|
||||
if not user:
|
||||
return None
|
||||
if bcrypt.checkpw(password.encode(), user.password.encode()):
|
||||
print("User successful authenticated")
|
||||
else:
|
||||
logger.info("Authentication failed!")
|
||||
return user
|
||||
from jose import jwt
|
||||
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.now(timezone.utc) + timedelta(
|
||||
expire = datetime.utcnow() + timedelta(
|
||||
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
to_encode.update({"exp": expire})
|
||||
@@ -78,79 +19,3 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||
to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM
|
||||
)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]
|
||||
):
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = "Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||
)
|
||||
username: str = payload.get("sub") # type: ignore
|
||||
logger.info("username/email extracted is ", username)
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
scope: str = payload.get("scope", "")
|
||||
token_scopes: List[str] = scope.split(" ")
|
||||
token_data = TokenData(scopes=token_scopes, username=username)
|
||||
except (JWTError, ValidationError):
|
||||
raise credentials_exception
|
||||
with SessionLocal() as db:
|
||||
user = get_profile(username=token_data.username, db=db) # type: ignore
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in token_scopes:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="not enough permissions",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_active_user(
|
||||
current_user: Annotated[Profile, Security(get_current_user, scopes=["me"])],
|
||||
) -> ProfileModel:
|
||||
if not current_user.enabled: # type: ignore
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
user_model = ProfileModel(
|
||||
username=current_user.user_name,
|
||||
email=current_user.email, # type: ignore
|
||||
first_name=current_user.first_name,
|
||||
last_name=current_user.last_name, # type: ignore
|
||||
active=current_user.enabled,
|
||||
) # type: ignore
|
||||
return user_model
|
||||
|
||||
|
||||
def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||
)
|
||||
username: str = payload.get("sub") # type: ignore
|
||||
logger.info("username/email extracted is ", username)
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
with SessionLocal() as db:
|
||||
user = get_profile(username=username, db=db)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import relationship, mapped_column, Mapped
|
||||
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
@@ -8,23 +9,23 @@ from src.db.models.base import Base, BaseMixin
|
||||
|
||||
class Profile(Base, BaseMixin):
|
||||
__tablename__ = 'profile'
|
||||
first_name = Column(String)
|
||||
last_name = Column(String)
|
||||
user_name = Column(String, nullable=False)
|
||||
email = Column(String)
|
||||
password = Column(String)
|
||||
enabled = Column(Boolean)
|
||||
assignments = relationship("Assignment")
|
||||
tokens = relationship("Token")
|
||||
first_name: Mapped[str]
|
||||
last_name: Mapped[str]
|
||||
user_name: Mapped[str] = mapped_column(nullable=False)
|
||||
email: Mapped[str]
|
||||
password: Mapped[str]
|
||||
enabled: Mapped[bool]
|
||||
assignments: Mapped[List["Assignment"]] = relationship(back_populates="profile")
|
||||
tokens: Mapped[List["Token"]] = relationship(back_populates="profile")
|
||||
|
||||
def get_full_name(self) -> str:
|
||||
full_name = ""
|
||||
full_name: str = ""
|
||||
if self.first_name is not None:
|
||||
full_name += self.first_name
|
||||
full_name += str(self.first_name)
|
||||
if self.last_name is not None:
|
||||
if len(full_name) > 0:
|
||||
full_name += " "
|
||||
full_name += self.last_name
|
||||
full_name += str(self.last_name)
|
||||
return full_name
|
||||
|
||||
def __str__(self):
|
||||
@@ -33,42 +34,42 @@ class Profile(Base, BaseMixin):
|
||||
|
||||
class Token(Base, BaseMixin):
|
||||
__tablename__ = "token"
|
||||
token = Column(String, nullable=False, unique=True)
|
||||
name = Column(String)
|
||||
last_used_date: Mapped[datetime] = mapped_column()
|
||||
enabled = Column(Boolean)
|
||||
profile_id = Column(String, ForeignKey("profile.id"), nullable=False)
|
||||
profile = relationship("Profile", back_populates="tokens")
|
||||
token: Mapped[str] = mapped_column(nullable=False, unique=True)
|
||||
name: Mapped[str]
|
||||
last_used_date: Mapped[datetime]
|
||||
enabled: Mapped[bool]
|
||||
profile_id = mapped_column(ForeignKey("profile.id"), nullable=False)
|
||||
profile: Mapped[Profile] = relationship(back_populates="tokens")
|
||||
|
||||
|
||||
class Permission(Base, BaseMixin):
|
||||
__tablename__ = "permission"
|
||||
name = Column(String, nullable=False)
|
||||
assignments = relationship("Assignment")
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
assignments: Mapped[List["Assignment"]] = relationship(back_populates="permission")
|
||||
|
||||
|
||||
class Assignment(Base, BaseMixin):
|
||||
__tablename__ = "assignment"
|
||||
profile_id = Column(String, ForeignKey("profile.id"), nullable=False)
|
||||
profile = relationship("Profile", back_populates="assignments")
|
||||
permission_id = Column(String, ForeignKey("permission.id"), nullable=False)
|
||||
permission = relationship("Permission", back_populates="assignments")
|
||||
profile_id = mapped_column(ForeignKey("profile.id"), nullable=False)
|
||||
profile: Mapped[Profile] = relationship(back_populates="assignments")
|
||||
permission_id = mapped_column(ForeignKey("permission.id"), nullable=False)
|
||||
permission: Mapped[Permission] = relationship(back_populates="assignments")
|
||||
|
||||
|
||||
class MailAccount(Base, BaseMixin):
|
||||
__tablename__ = "mail_account"
|
||||
host = Column(String)
|
||||
port = Column(Integer)
|
||||
protocol = Column(String)
|
||||
user_name = Column(String)
|
||||
password = Column(String)
|
||||
start_tls = Column(Boolean)
|
||||
host: Mapped[str]
|
||||
port: Mapped[int]
|
||||
protocol: Mapped[str]
|
||||
user_name: Mapped[str]
|
||||
password: Mapped[str]
|
||||
start_tls: Mapped[bool]
|
||||
|
||||
|
||||
class Mail(Base, BaseMixin):
|
||||
__tablename__ = "mail"
|
||||
folder: Mapped[str] = mapped_column()
|
||||
subject: Mapped[str] = mapped_column()
|
||||
body: Mapped[str] = mapped_column()
|
||||
sent_date: Mapped[datetime] = mapped_column()
|
||||
received_date: Mapped[datetime] = mapped_column()
|
||||
folder: Mapped[str]
|
||||
subject: Mapped[str]
|
||||
body: Mapped[str]
|
||||
sent_date: Mapped[datetime]
|
||||
received_date: Mapped[datetime]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import func, Column, String, Boolean
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
|
||||
@@ -10,21 +11,17 @@ class Base(DeclarativeBase):
|
||||
|
||||
|
||||
class BaseMixin:
|
||||
#id = Column(String, primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[str] = mapped_column(primary_key=True, default=str(uuid.uuid4()))
|
||||
# created_date = Column(DateTime)
|
||||
created_date: Mapped[datetime] = mapped_column(default=func.now())
|
||||
# last_modified_date = Column(DateTime)
|
||||
last_modified_date: Mapped[datetime] = mapped_column(default=func.now())
|
||||
# version = Column(Integer)
|
||||
version: Mapped[int] = mapped_column(default=0)
|
||||
|
||||
|
||||
class BaseVideoMixin:
|
||||
cloud_link = Column(String, nullable=True)
|
||||
file_name = Column(String, nullable=True)
|
||||
cloud_link = Column(String)
|
||||
file_name = Column(String)
|
||||
path = Column(String)
|
||||
review = Column(Boolean)
|
||||
title = Column(String)
|
||||
url = Column(String, nullable=True)
|
||||
url = Column(String, unique=True)
|
||||
should_download = Column(Boolean)
|
||||
|
||||
@@ -1,50 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
|
||||
class Article(Base, BaseMixin):
|
||||
__tablename__ = 'article'
|
||||
title = Column(String, unique=True)
|
||||
title: Mapped[str] = mapped_column(unique=True)
|
||||
article_authors = relationship("ArticleAuthor")
|
||||
|
||||
|
||||
class Author(Base, BaseMixin):
|
||||
__tablename__ = 'author'
|
||||
first_name = Column(String)
|
||||
last_name = Column(String)
|
||||
article_authors = relationship("ArticleAuthor")
|
||||
book_authors = relationship("BookAuthor")
|
||||
first_name: Mapped[str]
|
||||
last_name: Mapped[str]
|
||||
article_authors: Mapped[List["ArticleAuthor"]] = relationship(back_populates="author")
|
||||
book_authors: Mapped[List["BookAuthor"]] = relationship(back_populates="author")
|
||||
|
||||
|
||||
class BookshelfPublisher(Base, BaseMixin):
|
||||
__tablename__ = 'bookshelf_publisher'
|
||||
name = Column(String, unique=True)
|
||||
books = relationship("Book")
|
||||
name: Mapped[str] = mapped_column(unique=True)
|
||||
books: Mapped[List["Book"]] = relationship(back_populates="publisher")
|
||||
|
||||
|
||||
class Book(Base, BaseMixin):
|
||||
__tablename__ = 'book'
|
||||
isbn = Column(String, unique=True)
|
||||
title = Column(String)
|
||||
year = Column(Integer, nullable=False)
|
||||
publisher_id = Column(String, ForeignKey('bookshelf_publisher.id'), nullable=False)
|
||||
publisher = relationship('BookshelfPublisher', back_populates="books")
|
||||
book_authors = relationship("BookAuthor")
|
||||
isbn: Mapped[str] = mapped_column(unique=True)
|
||||
title: Mapped[str]
|
||||
year: Mapped[int] = mapped_column(nullable=False)
|
||||
publisher_id: Mapped[str] = mapped_column(ForeignKey("bookshelf_publisher.id"), nullable=False)
|
||||
publisher: Mapped[BookshelfPublisher] = relationship(back_populates="books")
|
||||
book_authors: Mapped[List["BookAuthor"]] = relationship(back_populates="book")
|
||||
|
||||
|
||||
class ArticleAuthor(Base, BaseMixin):
|
||||
__tablename__ = 'article_author'
|
||||
article_id = Column(String, ForeignKey('article.id'), nullable=False)
|
||||
article = relationship('Article', back_populates="article_authors")
|
||||
author_id = Column(String, ForeignKey('author.id'), nullable=False)
|
||||
author = relationship('Author', back_populates="article_authors")
|
||||
article_id: Mapped[str] = mapped_column(ForeignKey("article.id"), nullable=False)
|
||||
article: Mapped[Article] = relationship(back_populates="article_authors")
|
||||
author_id: Mapped[str] = mapped_column(ForeignKey("author.id"), nullable=False)
|
||||
author: Mapped[Author] = relationship(back_populates="article_authors")
|
||||
|
||||
|
||||
class BookAuthor(Base, BaseMixin):
|
||||
__tablename__ = 'book_author'
|
||||
author_id = Column(String, ForeignKey('author.id'), nullable=False)
|
||||
author = relationship('Author', back_populates="book_authors")
|
||||
book_id = Column(String, ForeignKey('book.id'), nullable=False)
|
||||
book = relationship('Book', back_populates="book_authors")
|
||||
author_id: Mapped[str] = mapped_column(ForeignKey("author.id"), nullable=False)
|
||||
author: Mapped[Author] = relationship(back_populates="book_authors")
|
||||
book_id: Mapped[str] = mapped_column(ForeignKey("book.id"), nullable=False)
|
||||
book: Mapped[Book] = relationship(back_populates="book_authors")
|
||||
|
||||
@@ -2,7 +2,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
from typing import AnyStr, Dict, List, Optional, Any
|
||||
from natsort import natsorted
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean, func
|
||||
from sqlalchemy import ForeignKey, func
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
@@ -14,12 +14,12 @@ class Publisher(Base):
|
||||
created_date: Mapped[datetime] = mapped_column(default=func.now())
|
||||
last_modified_date: Mapped[datetime] = mapped_column(default=func.now())
|
||||
version: Mapped[int] = mapped_column(default=0)
|
||||
name = Column(String, unique=True)
|
||||
weblink = Column(String, nullable=True)
|
||||
name: Mapped[str] = mapped_column(unique=True)
|
||||
weblink: Mapped[Optional[str]]
|
||||
parent_publisher_id: Mapped[Optional[str]] = mapped_column(ForeignKey('publisher.id'))
|
||||
parent_publisher: Mapped[Optional['Publisher']] = relationship("Publisher", back_populates="imprints", remote_side=[id])
|
||||
imprints: Mapped[List['Publisher']] = relationship('Publisher', back_populates="parent_publisher")
|
||||
comics = relationship("Comic")
|
||||
parent_publisher: Mapped[Optional['Publisher']] = relationship(back_populates="imprints", remote_side=[id])
|
||||
imprints: Mapped[List['Publisher']] = relationship(back_populates="parent_publisher")
|
||||
comics: Mapped[List["Comic"]] = relationship(back_populates="publisher")
|
||||
|
||||
def __repr__(self):
|
||||
return f'Publisher({self.id} {self.name})'
|
||||
@@ -30,17 +30,17 @@ class Publisher(Base):
|
||||
|
||||
class Comic(Base, BaseMixin):
|
||||
__tablename__ = 'comic'
|
||||
title = Column(String, unique=True)
|
||||
publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False)
|
||||
publisher = relationship("Publisher", back_populates="comics")
|
||||
current_order = Column(Boolean)
|
||||
completed = Column(Boolean)
|
||||
weblink = Column(String, nullable=True)
|
||||
issues = relationship("Issue", order_by="Issue.issue_number")
|
||||
story_arcs = relationship("StoryArc")
|
||||
trade_paperbacks = relationship("TradePaperback")
|
||||
volumes = relationship("Volume")
|
||||
comic_works = relationship("ComicWork")
|
||||
title: Mapped[str] = mapped_column(unique=True)
|
||||
publisher_id: Mapped[str] = mapped_column(ForeignKey('publisher.id'), nullable=False)
|
||||
publisher: Mapped[Publisher] = relationship(back_populates="comics")
|
||||
current_order: Mapped[bool]
|
||||
completed: Mapped[bool]
|
||||
weblink: Mapped[Optional[str]]
|
||||
issues: Mapped[List["Issue"]] = relationship(back_populates="comic", order_by="Issue.issue_number")
|
||||
story_arcs: Mapped[List["StoryArc"]] = relationship(back_populates="comic")
|
||||
trade_paperbacks: Mapped[List["TradePaperback"]] = relationship(back_populates="comic")
|
||||
volumes: Mapped[List["Volume"]] = relationship(back_populates="comic")
|
||||
comic_works: Mapped[List["ComicWork"]] = relationship(back_populates="comic")
|
||||
|
||||
def __repr__(self):
|
||||
return f'Comic({self.id} {self.version} {self.title} {self.publisher.name})'
|
||||
@@ -66,51 +66,69 @@ class Comic(Base, BaseMixin):
|
||||
|
||||
class Volume(Base, BaseMixin):
|
||||
__tablename__ = "volume"
|
||||
name = Column(String, nullable=False)
|
||||
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
|
||||
comic = relationship("Comic", back_populates="volumes")
|
||||
story_arcs = relationship("StoryArc")
|
||||
issues = relationship("Issue")
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
comic_id: Mapped[str] = mapped_column(ForeignKey("comic.id"), nullable=False)
|
||||
comic: Mapped[Comic] = relationship(back_populates="volumes")
|
||||
story_arcs: Mapped[List["StoryArc"]] = relationship(back_populates="volume")
|
||||
issues: Mapped[List["Issue"]] = relationship(back_populates="volume")
|
||||
|
||||
|
||||
class TradePaperback(Base, BaseMixin):
|
||||
__tablename__ = "trade_paperback"
|
||||
name = Column(String, nullable=False)
|
||||
issue_start = Column(Integer)
|
||||
issue_end = Column(Integer)
|
||||
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
|
||||
comic = relationship("Comic", back_populates="trade_paperbacks")
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
issue_start: Mapped[int]
|
||||
issue_end: Mapped[int]
|
||||
comic_id: Mapped[str] = mapped_column(ForeignKey("comic.id"), nullable=False)
|
||||
comic: Mapped[Comic] = relationship(back_populates="trade_paperbacks")
|
||||
|
||||
|
||||
class StoryArc(Base, BaseMixin):
|
||||
__tablename__ = "story_arc"
|
||||
name = Column(String, nullable=False)
|
||||
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
|
||||
comic = relationship("Comic", back_populates="story_arcs")
|
||||
volume_id = Column(String, ForeignKey("volume.id"), nullable=True)
|
||||
volume = relationship("Volume", back_populates="story_arcs")
|
||||
issues = relationship("Issue")
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
comic_id: Mapped[str] = mapped_column(ForeignKey("comic.id"), nullable=False)
|
||||
comic: Mapped[Comic] = relationship(back_populates="story_arcs")
|
||||
volume_id: Mapped[str] = mapped_column(ForeignKey("volume.id"), nullable=True)
|
||||
volume: Mapped[Volume] = relationship(back_populates="story_arcs")
|
||||
issues: Mapped[List["Issue"]] = relationship(back_populates="story_arc")
|
||||
|
||||
|
||||
class Issue(Base, BaseMixin):
|
||||
__tablename__ = "issue"
|
||||
issue_number = Column(String)
|
||||
title = Column(String, nullable=True)
|
||||
published_on: Mapped[datetime] = mapped_column(nullable=True)
|
||||
in_stock = Column(Boolean)
|
||||
is_read = Column(Boolean)
|
||||
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
|
||||
comic = relationship("Comic", back_populates="issues")
|
||||
volume_id = Column(String, ForeignKey("volume.id"), nullable=True)
|
||||
volume = relationship("Volume", back_populates="issues")
|
||||
story_arc_id = Column(String, ForeignKey("story_arc.id"), nullable=True)
|
||||
story_arc = relationship("StoryArc", back_populates="issues")
|
||||
issue_works = relationship("IssueWork")
|
||||
issue_number: Mapped[str]
|
||||
title: Mapped[Optional[str]]
|
||||
published_on: Mapped[Optional[datetime]]
|
||||
in_stock: Mapped[bool]
|
||||
is_read: Mapped[bool]
|
||||
comic_id: Mapped[str] = mapped_column(ForeignKey("comic.id"), nullable=False)
|
||||
comic: Mapped[Comic] = relationship(back_populates="issues")
|
||||
volume_id: Mapped[str] = mapped_column(ForeignKey("volume.id"), nullable=True)
|
||||
volume: Mapped[Volume] = relationship(back_populates="issues")
|
||||
story_arc_id: Mapped[str] = mapped_column(ForeignKey("story_arc.id"), nullable=True)
|
||||
story_arc: Mapped[StoryArc] = relationship(back_populates="issues")
|
||||
issue_works: Mapped[List["IssueWork"]] = relationship(back_populates="issue")
|
||||
|
||||
def get_full_title(self) -> str:
|
||||
full_title: str = str(self.issue_number)
|
||||
def get_full_title(self) -> AnyStr:
|
||||
full_title: AnyStr = self.issue_number
|
||||
if self.title:
|
||||
full_title += str(": " + self.title)
|
||||
full_title += ": " + self.title
|
||||
return full_title
|
||||
|
||||
def get_artists(self) -> Dict[Any, List[Any]]:
|
||||
works: Dict[Any, List[Any]] = {}
|
||||
for work in self.issue_works:
|
||||
work_type = work.work_type
|
||||
artist = work.artist
|
||||
if work_type in works:
|
||||
works[work_type].append(artist)
|
||||
else:
|
||||
works[work_type] = [artist]
|
||||
return works
|
||||
|
||||
|
||||
def get_full_title(self) -> AnyStr:
|
||||
full_title: AnyStr = self.issue_number
|
||||
if self.title:
|
||||
full_title += ": " + self.title
|
||||
return full_title
|
||||
|
||||
def get_artists(self) -> Dict[Any, List[Any]]:
|
||||
@@ -127,10 +145,10 @@ class Issue(Base, BaseMixin):
|
||||
|
||||
class Artist(Base, BaseMixin):
|
||||
__tablename__ = "artist"
|
||||
name = Column(String, nullable=False)
|
||||
weblink = Column(String, nullable=True)
|
||||
comic_works = relationship("ComicWork")
|
||||
issue_works = relationship("IssueWork")
|
||||
name: Mapped[str] = mapped_column(nullable=False)
|
||||
weblink: Mapped[str] = mapped_column(nullable=True)
|
||||
comic_works: Mapped[List["ComicWork"]] = relationship(back_populates="artist")
|
||||
issue_works: Mapped[List["IssueWork"]] = relationship(back_populates="artist")
|
||||
|
||||
def get_comics(self) -> Dict[Any, List[Comic]]:
|
||||
works: Dict[Any, List[Comic]] = {}
|
||||
@@ -146,9 +164,20 @@ class Artist(Base, BaseMixin):
|
||||
|
||||
class WorkType(Base, BaseMixin):
|
||||
__tablename__ = "worktype"
|
||||
name = Column(String, nullable=False, unique=True)
|
||||
comic_works = relationship("ComicWork")
|
||||
issue_works = relationship("IssueWork")
|
||||
name: Mapped[str] = mapped_column(nullable=False, unique=True)
|
||||
comic_works: Mapped[List["ComicWork"]] = relationship(back_populates="work_type")
|
||||
issue_works: Mapped[List["IssueWork"]] = relationship(back_populates="work_type")
|
||||
|
||||
def get_artists(self) -> Dict[str, List[Artist]]:
|
||||
works: Dict[str, List[Artist]] = {}
|
||||
for work in self.comic_works:
|
||||
comic = work.comic.title
|
||||
artist = work.artist
|
||||
if comic in works:
|
||||
works[comic].append(artist)
|
||||
else:
|
||||
works[comic] = [artist]
|
||||
return works
|
||||
|
||||
def get_artists(self) -> Dict[str, List[str]]:
|
||||
works: Dict[str, List[str]] = {}
|
||||
@@ -170,12 +199,12 @@ class WorkType(Base, BaseMixin):
|
||||
|
||||
class ComicWork(Base, BaseMixin):
|
||||
__tablename__ = "comic_work"
|
||||
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
|
||||
comic = relationship("Comic", back_populates="comic_works")
|
||||
artist_id = Column(String, ForeignKey("artist.id"), nullable=False)
|
||||
artist = relationship("Artist", back_populates="comic_works")
|
||||
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
|
||||
work_type = relationship("WorkType", back_populates="comic_works")
|
||||
comic_id: Mapped[str] = mapped_column(ForeignKey("comic.id"), nullable=False)
|
||||
comic: Mapped[Comic] = relationship(back_populates="comic_works")
|
||||
artist_id: Mapped[str] = mapped_column(ForeignKey("artist.id"), nullable=False)
|
||||
artist: Mapped[Artist] = relationship(back_populates="comic_works")
|
||||
work_type_id: Mapped[str] = mapped_column(ForeignKey("worktype.id"), nullable=False)
|
||||
work_type: Mapped[WorkType] = relationship(back_populates="comic_works")
|
||||
|
||||
|
||||
class IssueWork(Base, BaseMixin):
|
||||
@@ -186,3 +215,4 @@ class IssueWork(Base, BaseMixin):
|
||||
artist = relationship("Artist", back_populates="issue_works")
|
||||
work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False)
|
||||
work_type = relationship("WorkType", back_populates="issue_works")
|
||||
|
||||
|
||||
@@ -3,119 +3,96 @@ import re
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from sqlalchemy import Column, String, ForeignKey, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import Mapped, relationship, mapped_column
|
||||
|
||||
from src.db.models.base import Base, BaseMixin, BaseVideoMixin
|
||||
|
||||
|
||||
class MediaFile(Base, BaseMixin, BaseVideoMixin):
|
||||
__tablename__ = 'media_file'
|
||||
media_actor_files = relationship("MediaActorFile")
|
||||
"""
|
||||
MediaFile represents video link.
|
||||
"""
|
||||
|
||||
__tablename__ = "media_file"
|
||||
media_actor_files: Mapped[List["MediaActorFile"]] = relationship(
|
||||
back_populates="media_file"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f'MediaFile({self.id} {self.title} {self.title})'
|
||||
return f"MediaFile({self.id} {self.title} {self.title})"
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.title}({self.id})'
|
||||
return f"{self.title}({self.id})"
|
||||
|
||||
def update_title(self) -> None:
|
||||
logging.info(f"update title for {self.url}")
|
||||
try:
|
||||
r = requests.get(str(self.url))
|
||||
soup = BeautifulSoup(r.content, "html.parser")
|
||||
title = soup.title.get_text() # type: ignore
|
||||
self.title = title
|
||||
self.review = False
|
||||
except:
|
||||
self.title = None
|
||||
self.review = True
|
||||
self.last_modified_date = datetime.now()
|
||||
|
||||
def download_file(self, download_dir: str, dl_tool: str):
|
||||
logging.info(f"download file for {self.url} to {download_dir}")
|
||||
result = subprocess.run([dl_tool, self.url], cwd=download_dir, capture_output=True, text=True) # type: ignore
|
||||
if result.returncode == 0:
|
||||
output = result.stdout
|
||||
output = re.sub(' +', ' ', output)
|
||||
lines_list = output.splitlines()
|
||||
file_name = self.__parse_output__(lines_list)
|
||||
if file_name is None:
|
||||
self.review = True
|
||||
self.should_download = True
|
||||
self.file_name = None
|
||||
else:
|
||||
download_file = Path(file_name)
|
||||
self.should_download = False
|
||||
self.file_name = download_file.name
|
||||
self.cloud_link = str(download_file.absolute())
|
||||
self.last_modified_date = datetime.now()
|
||||
|
||||
def __parse_output__(self, lines_list):
|
||||
self.file_name = None
|
||||
for line in lines_list:
|
||||
if 'has already been downloaded' in line:
|
||||
end_len = len(' has already been downloaded')
|
||||
self.file_name = line[11:-end_len]
|
||||
if 'Destination' in line:
|
||||
line_len = len(line)
|
||||
start_len = len('[download] Destination: ')
|
||||
file_len = line_len - start_len
|
||||
self.file_name = line[-file_len:]
|
||||
return self.file_name
|
||||
def update_title(self):
|
||||
"""
|
||||
Update title from url.
|
||||
"""
|
||||
|
||||
|
||||
class MediaActor(Base, BaseMixin):
|
||||
__tablename__ = 'media_actor'
|
||||
name = Column(String)
|
||||
url = Column(String, unique=True, nullable=True)
|
||||
media_actor_files = relationship("MediaActorFile")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'MediaActor({self.id} {self.name} {self.url})'
|
||||
return f"MediaActor({self.id} {self.name} {self.url})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.url}({self.id})'
|
||||
return f"{self.url}({self.id})"
|
||||
|
||||
|
||||
class MediaActorFile(Base, BaseMixin):
|
||||
__tablename__ = 'media_actor_file'
|
||||
media_actor_id = Column(String, ForeignKey("media_actor.id"), nullable=False)
|
||||
media_actor = relationship("MediaActor", back_populates="media_actor_files")
|
||||
media_file_id = Column(String, ForeignKey("media_file.id"), nullable=True)
|
||||
media_file = relationship("MediaFile", back_populates="media_actor_files")
|
||||
__tablename__ = "media_actor_file"
|
||||
media_actor_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("media_actor.id"), nullable=False
|
||||
)
|
||||
media_actor: Mapped[MediaActor] = relationship(back_populates="media_actor_files")
|
||||
media_file_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("media_file.id"), nullable=True
|
||||
)
|
||||
media_file: Mapped[MediaFile] = relationship(back_populates="media_actor_files")
|
||||
|
||||
def __repr__(self):
|
||||
return f'MediaActorFile({self.id} {self.media_actor_id} {self.media_file_id})'
|
||||
return f"MediaActorFile({self.id} {self.media_actor_id} {self.media_file_id})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.id} {self.media_actor_id} {self.media_file_id}'
|
||||
return f"{self.id} {self.media_actor_id} {self.media_file_id}"
|
||||
|
||||
|
||||
class MediaArticle(Base, BaseMixin):
|
||||
__tablename__ = 'media_article'
|
||||
review = Column(Boolean)
|
||||
title = Column(String)
|
||||
url = Column(String, unique=True)
|
||||
__tablename__ = "media_article"
|
||||
review: Mapped[bool]
|
||||
title: Mapped[str]
|
||||
url: Mapped[str] = mapped_column(unique=True)
|
||||
|
||||
|
||||
class MediaVideo(Base, BaseMixin):
|
||||
__tablename__ = 'media_video'
|
||||
cloud_link = Column(String)
|
||||
file_name = Column(String)
|
||||
path = Column(String)
|
||||
review = Column(Boolean)
|
||||
title = Column(String)
|
||||
url = Column(String, unique=True)
|
||||
should_download = Column(Boolean)
|
||||
"""
|
||||
MediaFile represents video link.
|
||||
"""
|
||||
|
||||
__tablename__ = "media_video"
|
||||
cloud_link: Mapped[str]
|
||||
file_name: Mapped[str]
|
||||
path: Mapped[str]
|
||||
review: Mapped[bool]
|
||||
title: Mapped[str]
|
||||
url: Mapped[str] = mapped_column(unique=True)
|
||||
should_download: Mapped[bool]
|
||||
|
||||
def __repr__(self):
|
||||
return f'MediaFile({self.id} {self.title} {self.url})'
|
||||
return f"MediaFile({self.id} {self.title} {self.url})"
|
||||
|
||||
def __str__(self):
|
||||
if self.title is None:
|
||||
return f'{self.url}({self.id})'
|
||||
return f"{self.url}({self.id})"
|
||||
else:
|
||||
return f'{self.title}({self.id})'
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
from typing import List
|
||||
from sqlalchemy import ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import relationship, mapped_column, Mapped
|
||||
|
||||
from src.db.models.base import Base, BaseMixin
|
||||
|
||||
@@ -9,18 +10,18 @@ class Sport(Base, BaseMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint("name"),
|
||||
)
|
||||
name = Column(String, nullable=False, index=True, unique=True)
|
||||
teams = relationship("Team")
|
||||
positions = relationship("FieldPosition")
|
||||
name: Mapped[str] = mapped_column(nullable=False, index=True, unique=True)
|
||||
teams: Mapped[List["Team"]] = relationship(back_populates="sport")
|
||||
positions: Mapped[List["FieldPosition"]] = relationship(back_populates="sport")
|
||||
|
||||
|
||||
class Team(Base, BaseMixin):
|
||||
__tablename__ = "team"
|
||||
name = Column(String, nullable=False, index=True, unique=True)
|
||||
short_name = Column(String, nullable=False, )
|
||||
sport_id = Column(String, ForeignKey("sport.id"), nullable=False)
|
||||
sport = relationship("Sport", back_populates="teams")
|
||||
roosters = relationship("Rooster")
|
||||
name: Mapped[str] = mapped_column(nullable=False, index=True, unique=True)
|
||||
short_name: Mapped[str] = mapped_column(nullable=False)
|
||||
sport_id: Mapped[str] = mapped_column(ForeignKey("sport.id"), nullable=False)
|
||||
sport: Mapped[Sport] = relationship(back_populates="teams")
|
||||
roosters: Mapped[List["Rooster"]] = relationship(back_populates="team")
|
||||
|
||||
|
||||
class FieldPosition(Base, BaseMixin):
|
||||
@@ -29,11 +30,11 @@ class FieldPosition(Base, BaseMixin):
|
||||
UniqueConstraint("name", "sport_id"),
|
||||
UniqueConstraint("short_name", "sport_id"),
|
||||
)
|
||||
name = Column(String, nullable=False, index=True)
|
||||
short_name = Column(String, nullable=False)
|
||||
sport_id = Column(String, ForeignKey("sport.id"), nullable=False, index=True)
|
||||
sport = relationship("Sport", back_populates="positions")
|
||||
roosters = relationship("Rooster")
|
||||
name: Mapped[str] = mapped_column(nullable=False, index=True)
|
||||
short_name: Mapped[str] = mapped_column(nullable=False)
|
||||
sport_id: Mapped[str] = mapped_column(ForeignKey("sport.id"), nullable=False, index=True)
|
||||
sport: Mapped[Sport] = relationship(back_populates="positions")
|
||||
roosters: Mapped[List["Rooster"]] = relationship(back_populates="position")
|
||||
|
||||
|
||||
class Player(Base, BaseMixin):
|
||||
@@ -41,9 +42,9 @@ class Player(Base, BaseMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint("first_name", "last_name"),
|
||||
)
|
||||
first_name = Column(String, nullable=False, index=True)
|
||||
last_name = Column(String, nullable=False, index=True)
|
||||
roosters = relationship("Rooster")
|
||||
first_name: Mapped[str] = mapped_column(nullable=False, index=True)
|
||||
last_name: Mapped[str] = mapped_column(nullable=False, index=True)
|
||||
roosters: Mapped[List["Rooster"]] = relationship(back_populates="player")
|
||||
|
||||
def get_full_name(self) -> str:
|
||||
return f"{self.last_name}, {self.first_name}"
|
||||
@@ -54,21 +55,21 @@ class Rooster(Base, BaseMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint("year", "team_id", "player_id", "position_id"),
|
||||
)
|
||||
year = Column(Integer)
|
||||
team_id = Column(String, ForeignKey("team.id"), nullable=False, index=True)
|
||||
team = relationship("Team", back_populates="roosters")
|
||||
player_id = Column(String, ForeignKey("player.id"), nullable=False, index=True)
|
||||
player = relationship("Player", back_populates="roosters")
|
||||
position_id = Column(String, ForeignKey("field_position.id"), nullable=False, index=True)
|
||||
position = relationship("FieldPosition", back_populates="roosters")
|
||||
cards = relationship("Card")
|
||||
year: Mapped[int]
|
||||
team_id: Mapped[str] = mapped_column(ForeignKey("team.id"), nullable=False, index=True)
|
||||
team: Mapped[Team] = relationship(back_populates="roosters")
|
||||
player_id: Mapped[str] = mapped_column(ForeignKey("player.id"), nullable=False, index=True)
|
||||
player: Mapped[Player] = relationship(back_populates="roosters")
|
||||
position_id: Mapped[str] = mapped_column(ForeignKey("field_position.id"), nullable=False, index=True)
|
||||
position: Mapped[FieldPosition] = relationship(back_populates="roosters")
|
||||
cards: Mapped[List["Card"]] = relationship(back_populates="rooster")
|
||||
|
||||
|
||||
class Vendor(Base, BaseMixin):
|
||||
__tablename__ = "vendor"
|
||||
name = Column(String, nullable=False, unique=True, index=True)
|
||||
card_sets = relationship("CardSet")
|
||||
cards = relationship("Card")
|
||||
name: Mapped[str] = mapped_column(nullable=False, unique=True, index=True)
|
||||
card_sets: Mapped[List["CardSet"]] = relationship(back_populates="vendor")
|
||||
cards: Mapped[List["Card"]] = relationship(back_populates="vendor")
|
||||
|
||||
|
||||
class CardSet(Base, BaseMixin):
|
||||
@@ -76,12 +77,12 @@ class CardSet(Base, BaseMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint("name", "vendor_id"),
|
||||
)
|
||||
name = Column(String, index=True)
|
||||
parallel_set = Column(Boolean)
|
||||
insert_set = Column(Boolean)
|
||||
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False, index=True)
|
||||
vendor = relationship("Vendor", back_populates="card_sets")
|
||||
cards = relationship("Card")
|
||||
name: Mapped[str] = mapped_column(index=True)
|
||||
parallel_set: Mapped[bool]
|
||||
insert_set: Mapped[bool]
|
||||
vendor_id: Mapped[str] = mapped_column(ForeignKey("vendor.id"), nullable=False, index=True)
|
||||
vendor: Mapped[Vendor] = relationship(back_populates="card_sets")
|
||||
cards: Mapped[List["Card"]] = relationship(back_populates="card_set")
|
||||
|
||||
|
||||
class Card(Base, BaseMixin):
|
||||
@@ -89,11 +90,11 @@ class Card(Base, BaseMixin):
|
||||
__table_args__ = (
|
||||
UniqueConstraint("card_number", "year", "vendor_id", "card_set_id"),
|
||||
)
|
||||
card_number = Column(Integer, index=True)
|
||||
year = Column(Integer, index=True)
|
||||
card_set_id = Column(String, ForeignKey("card_set.id"), nullable=False)
|
||||
card_set = relationship("CardSet", back_populates="cards")
|
||||
rooster_id = Column(String, ForeignKey("rooster.id"), nullable=False)
|
||||
rooster = relationship("Rooster", back_populates="cards")
|
||||
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False)
|
||||
vendor = relationship("Vendor", back_populates="cards")
|
||||
card_number: Mapped[int] = mapped_column(index=True)
|
||||
year: Mapped[int] = mapped_column(index=True)
|
||||
card_set_id: Mapped[str] = mapped_column(ForeignKey("card_set.id"), nullable=False)
|
||||
card_set: Mapped[CardSet] = relationship(back_populates="cards")
|
||||
rooster_id: Mapped[str] = mapped_column(ForeignKey("rooster.id"), nullable=False)
|
||||
rooster: Mapped[Rooster] = relationship(back_populates="cards")
|
||||
vendor_id: Mapped[str] = mapped_column(ForeignKey("vendor.id"), nullable=False)
|
||||
vendor: Mapped[Vendor] = relationship(back_populates="cards")
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from typing import AnyStr, Optional
|
||||
from typing import AnyStr
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.db.models.admin import Profile
|
||||
|
||||
|
||||
def get_profile(username: AnyStr, db: Session) -> Optional[Profile]:
|
||||
def get_profile(username: AnyStr, db: Session):
|
||||
profile = db.query(Profile).filter(Profile.email == username).first()
|
||||
return profile
|
||||
|
||||
def is_database_empty(db: Session) -> bool:
|
||||
profiles = db.query(Profile).all()
|
||||
return len(profiles) == 0
|
||||
|
||||
@@ -1,46 +1,19 @@
|
||||
import uuid
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.comic import Artist
|
||||
from src.schema.comics.artist import AddArtist
|
||||
from src.schema.comics.artist_details import ArtistDetailResponse, ArtistWorktypeComicResponse, ArtistWorktypeIssueResponse
|
||||
from src.schema.comics.comic import ComicResponse
|
||||
from src.schema.comics.worktype import WorktypeResponse
|
||||
from src.schema.comics.artist import ArtistDetailResponse
|
||||
|
||||
|
||||
def get_artist_details(artist: Artist) -> ArtistDetailResponse:
|
||||
comic_works: List[ArtistWorktypeComicResponse] = []
|
||||
comic_works_map = {}
|
||||
works = {}
|
||||
for work in artist.comic_works:
|
||||
worktype_id = work.work_type.id
|
||||
if worktype_id in comic_works_map:
|
||||
comic = ComicResponse(id=work.comic.id, title=work.comic.title, completed=work.comic.completed)
|
||||
comic_works_map[worktype_id].comics.append(comic)
|
||||
work_type = work.work_type.name
|
||||
comic_title = work.comic.title
|
||||
if work_type in works:
|
||||
works[work_type].append(comic_title)
|
||||
else:
|
||||
comic_works_map[worktype_id] = ArtistWorktypeComicResponse(
|
||||
worktype=WorktypeResponse(id=worktype_id, name=work.work_type.name),
|
||||
comics=[ComicResponse(id=work.comic.id, title=work.comic.title, completed=work.comic.completed)]
|
||||
)
|
||||
for value in comic_works_map.values():
|
||||
comic_works.append(value)
|
||||
issue_works: List[ArtistWorktypeIssueResponse] = []
|
||||
works[work_type] = [comic_title]
|
||||
response = ArtistDetailResponse(
|
||||
id=artist.id,
|
||||
name=str(artist.name),
|
||||
weblink=str(artist.weblink),
|
||||
comic_works=comic_works,
|
||||
issue_works=issue_works,
|
||||
name=artist.name,
|
||||
works=works
|
||||
)
|
||||
return response
|
||||
|
||||
def update_artist(add_artist: AddArtist, artist_id: str, db: Session) -> Artist:
|
||||
logger.info("update artist")
|
||||
artist: Optional[Artist] = db.get(Artist, artist_id)
|
||||
artist.name = add_artist.name
|
||||
db.add(artist)
|
||||
db.commit()
|
||||
db.refresh(artist)
|
||||
return artist
|
||||
|
||||
@@ -1,87 +1,34 @@
|
||||
from typing import Dict, List
|
||||
from typing import List, Type, AnyStr
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.comic import Comic, Issue
|
||||
from src.schema.comics.artist import ArtistResponse
|
||||
from src.schema.comics.comic import ComicResponse, ComicSchema
|
||||
from src.schema.comics.comic_details import ComicDetailsResponse, ComicWorktypeArtistResponse
|
||||
from src.schema.comics.issue import IssueResponse
|
||||
from src.schema.comics.issue_details import IssueDetailsResponse
|
||||
from src.schema.comics.publisher import PublisherResponse
|
||||
from src.schema.comics.volume import VolumeResponse
|
||||
from src.schema.comics.worktype import WorktypeResponse
|
||||
from src.schema.comics.comic import ComicSchema
|
||||
from src.schema.comics.issue import IssueDetailsResponse
|
||||
|
||||
|
||||
def list_comics(db: Session) -> List[Comic]:
|
||||
def list_comics(db: Session) -> List[Type[Comic]]:
|
||||
comics = db.query(Comic).all()
|
||||
return comics
|
||||
|
||||
|
||||
def get_issue_details(issue: Issue) -> IssueDetailsResponse:
|
||||
volume = None
|
||||
if issue.volume:
|
||||
volume = volume_to_response(issue.volume)
|
||||
response = IssueDetailsResponse(
|
||||
id=issue.id,
|
||||
issue_number=str(issue.issue_number),
|
||||
in_stock=bool(issue.in_stock),
|
||||
is_read=bool(issue.is_read),
|
||||
comic=ComicResponse(id=issue.comic.id, title=issue.comic.title, completed=issue.comic.completed),
|
||||
volume=VolumeResponse(id=issue.volume.id, name=issue.volume.name)
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def update_comic(comic: ComicSchema, comic_id: str, db: Session) -> type[Comic] | None:
|
||||
logger.info(f"update_comic: {comic} with {comic_id}")
|
||||
comic = db.get(Comic, comic_id) # type: ignore
|
||||
return comic # type: ignore
|
||||
|
||||
def get_short_info(comic: Comic) -> ComicResponse:
|
||||
response = ComicResponse(
|
||||
id=comic.id,
|
||||
title=str(comic.title),
|
||||
completed=bool(comic.completed == 1)
|
||||
)
|
||||
return response
|
||||
|
||||
def get_comic_details(comic: Comic) -> ComicDetailsResponse:
|
||||
volumes: List[VolumeResponse] = []
|
||||
for volume in comic.volumes:
|
||||
volumes.append(VolumeResponse(id=volume.id, name=volume.name))
|
||||
issues: List[IssueResponse] = []
|
||||
for issue in comic.issues:
|
||||
issues.append(IssueResponse(
|
||||
id=issue.id,
|
||||
issue_number=issue.issue_number,
|
||||
in_stock=issue.in_stock,
|
||||
is_read=issue.is_read
|
||||
))
|
||||
works: List[ComicWorktypeArtistResponse] = []
|
||||
works_map: Dict[str, ComicWorktypeArtistResponse] = {}
|
||||
for work in comic.comic_works:
|
||||
worktype_id = work.work_type.id
|
||||
if worktype_id in works_map:
|
||||
artist = ArtistResponse(id=work.artist.id, name=work.artist.name)
|
||||
works_map[worktype_id].artists.append(artist)
|
||||
logger.info(f"add artist to response map: {artist} -> {works_map}")
|
||||
print(f"add artist to response map: {artist} -> {works_map}")
|
||||
else:
|
||||
works_map[worktype_id] = ComicWorktypeArtistResponse(
|
||||
worktype=WorktypeResponse(id=worktype_id, name=work.work_type.name),
|
||||
artists=[ArtistResponse(id=work.artist.id, name=work.artist.name)]
|
||||
)
|
||||
for value in works_map.values():
|
||||
works.append(value)
|
||||
response = ComicDetailsResponse(
|
||||
id=str(comic.id),
|
||||
created=str(comic.created_date),
|
||||
title=str(comic.title),
|
||||
completed=bool(comic.completed),
|
||||
current_order=bool(comic.current_order),
|
||||
weblink=str(comic.weblink),
|
||||
publisher=PublisherResponse(id=comic.publisher.id, name=comic.publisher.name),
|
||||
issues=issues,
|
||||
volumes=volumes,
|
||||
works=works
|
||||
is_read=issue.is_read,
|
||||
comic_id=issue.comic_id,
|
||||
volume_id=issue.volume_id
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def update_comic(comic: ComicSchema, comic_id: AnyStr, db: Session) -> type[Comic] | None:
|
||||
logger.info(f"update_comic: {comic} with {comic_id}")
|
||||
comic = db.get(Comic, comic_id)
|
||||
return comic
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import AnyStr
|
||||
from typing import AnyStr, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -22,9 +22,10 @@ def create_new_worktype(work: AddWorkType, db: Session) -> WorkType:
|
||||
return worktype
|
||||
|
||||
|
||||
def update_worktype(work: AddWorkType, worktype_id: AnyStr, db: Session) -> WorkType:
|
||||
def update_worktype(work: AddWorkType, worktype_id: AnyStr, db: Session) -> Optional[WorkType]:
|
||||
logger.info("update worktype")
|
||||
worktype = db.get(WorkType, worktype_id)
|
||||
worktype: Optional[WorkType] = db.get(WorkType, worktype_id)
|
||||
if worktype is not None:
|
||||
worktype.name = work.worktype
|
||||
db.add(worktype)
|
||||
db.commit()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import AnyStr
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.media import MediaActor, MediaActorFile, MediaFile, MediaVideo
|
||||
from src.schema.media.actor import Actor
|
||||
from src.db.models.media import MediaFile, MediaVideo
|
||||
from src.webapps.media.forms import AddLinkForm
|
||||
|
||||
|
||||
@@ -11,22 +11,22 @@ def create_new_video(video: AddLinkForm, db: Session) -> MediaVideo:
|
||||
print(video.url)
|
||||
media_video = MediaVideo()
|
||||
media_video.id = str(uuid.uuid4())
|
||||
media_video.url = video.url # type: ignore
|
||||
media_video.url = video.url
|
||||
media_video.created_date = datetime.now()
|
||||
media_video.last_modified_date = datetime.now()
|
||||
media_video.review = True # type: ignore
|
||||
media_video.should_download = True # type: ignore
|
||||
media_video.review = True
|
||||
media_video.should_download = True
|
||||
db.add(media_video)
|
||||
db.commit()
|
||||
db.refresh(media_video)
|
||||
print(media_video)
|
||||
return media_video
|
||||
|
||||
def create_new_mediafile(link: str, db: Session) -> MediaFile:
|
||||
def create_new_mediafile(link: AnyStr, db: Session) -> MediaFile:
|
||||
logger.info("create MediaFile with url {link}")
|
||||
media_file: MediaFile = MediaFile()
|
||||
media_file.id = str(uuid.uuid4())
|
||||
media_file.url = link # type: ignore
|
||||
media_file.url = link
|
||||
media_file.created_date = datetime.now()
|
||||
media_file.last_modified_date = datetime.now()
|
||||
media_file.version = 0
|
||||
@@ -38,49 +38,3 @@ def create_new_mediafile(link: str, db: Session) -> MediaFile:
|
||||
logger.info(f"created {media_file}")
|
||||
return media_file
|
||||
|
||||
def delete_mediafile(db: Session, media_file_id: str):
|
||||
logger.info(f"delete MediaFile with id {media_file_id}")
|
||||
media_file = db.get(MediaFile, media_file_id)
|
||||
db.delete(media_file)
|
||||
db.commit()
|
||||
|
||||
def create_new_mediaactor(new_actor: Actor, db: Session) -> MediaActor:
|
||||
logger.info(f"create MediaActor with url {new_actor.url}")
|
||||
media_actor: MediaActor = MediaActor()
|
||||
media_actor.id = str(uuid.uuid4())
|
||||
media_actor.name = str(new_actor.name) # type: ignore
|
||||
media_actor.url = str(new_actor.url) # type: ignore
|
||||
media_actor.created_date = datetime.now()
|
||||
media_actor.last_modified_date = datetime.now()
|
||||
media_actor.version = 0
|
||||
db.add(media_actor)
|
||||
db.commit()
|
||||
db.refresh(media_actor)
|
||||
logger.info(f"created {media_actor}")
|
||||
return media_actor
|
||||
|
||||
def delete_mediaactor(db: Session, actor_id: str):
|
||||
logger.info(f"delete MediaActor with id {actor_id}")
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
db.delete(media_actor)
|
||||
db.commit()
|
||||
|
||||
def create_new_mediaactorfile(db: Session, actor_id: str, file_id: str) -> MediaActorFile:
|
||||
logger.info(f"create MediaActorFile with actor {actor_id} and file {file_id}")
|
||||
media_actor_file: MediaActorFile = MediaActorFile()
|
||||
media_actor_file.id = str(uuid.uuid4())
|
||||
media_actor_file.created_date = datetime.now()
|
||||
media_actor_file.last_modified_date = datetime.now()
|
||||
media_actor_file.version = 0
|
||||
media_actor_file.media_actor_id = actor_id # type: ignore
|
||||
media_actor_file.media_file_id = file_id # type: ignore
|
||||
db.add(media_actor_file)
|
||||
db.commit()
|
||||
db.refresh(media_actor_file)
|
||||
return media_actor_file
|
||||
|
||||
def delete_mediaactorfile(db: Session, actorfile_id: str):
|
||||
logger.info(f"delete MediaActorFile with id {actorfile_id}")
|
||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||
db.delete(media_actorfile)
|
||||
db.commit()
|
||||
@@ -0,0 +1,66 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.media import MediaActor
|
||||
from src.db.repository.media.actorfile import delete_mediaactorfile
|
||||
from src.schema.media.actor import MediaActorModel
|
||||
|
||||
|
||||
def create_new_mediaactor(new_actor: MediaActorModel, db: Session) -> MediaActor:
|
||||
logger.info(f"create MediaActor with url {new_actor.url}")
|
||||
media_actor: MediaActor = MediaActor()
|
||||
media_actor.id = str(uuid.uuid4())
|
||||
if new_actor.name is not None:
|
||||
media_actor.name = new_actor.name
|
||||
media_actor.url = new_actor.url
|
||||
media_actor.created_date = datetime.now()
|
||||
media_actor.last_modified_date = datetime.now()
|
||||
media_actor.version = 0
|
||||
db.add(media_actor)
|
||||
db.commit()
|
||||
db.refresh(media_actor)
|
||||
logger.info(f"created {media_actor}")
|
||||
return media_actor
|
||||
|
||||
def delete_mediaactor(db: Session, actor_id: str):
|
||||
logger.info(f"delete MediaActor with id {actor_id}")
|
||||
media_actor = db.get(MediaActor, actor_id)
|
||||
if media_actor is not None:
|
||||
actor_files = media_actor.media_actor_files
|
||||
for actor_file in actor_files:
|
||||
delete_mediaactorfile(db, actorfile_id=actor_file.id)
|
||||
db.refresh(media_actor)
|
||||
db.delete(media_actor)
|
||||
db.commit()
|
||||
|
||||
def import_mediaactor(db: Session, new_actor: MediaActorModel) -> MediaActor:
|
||||
"""
|
||||
import MediaFile and set missing values with default ones.
|
||||
"""
|
||||
logger.info("import MediaActor with %s", new_actor)
|
||||
media_actor: MediaActor = MediaActor()
|
||||
media_actor.id = new_actor.id
|
||||
if new_actor.created_date:
|
||||
media_actor.created_date = new_actor.created_date
|
||||
else:
|
||||
media_actor.created_date = datetime.now()
|
||||
if new_actor.last_modified_date:
|
||||
media_actor.last_modified_date = new_actor.last_modified_date
|
||||
else:
|
||||
media_actor.last_modified_date = datetime.now()
|
||||
media_actor.version = new_actor.version
|
||||
if new_actor.name:
|
||||
media_actor.name = new_actor.name
|
||||
else:
|
||||
media_actor.name = ""
|
||||
if new_actor.url:
|
||||
media_actor.url = new_actor.url
|
||||
else:
|
||||
media_actor.url = ""
|
||||
db.add(media_actor)
|
||||
db.commit()
|
||||
db.refresh(media_actor)
|
||||
return media_actor
|
||||
@@ -0,0 +1,49 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.media import MediaActorFile
|
||||
from src.schema.media.actorfile import MediaActorFileModel
|
||||
|
||||
|
||||
def create_new_mediaactorfile(
|
||||
db: Session, actor_id: str, file_id: str
|
||||
) -> MediaActorFile:
|
||||
"""
|
||||
Create relation for MediaFile and MediaActor
|
||||
"""
|
||||
logger.info("create MediaActorFile with actor %s and file %s", actor_id, file_id)
|
||||
media_actor_file: MediaActorFile = MediaActorFile()
|
||||
media_actor_file.id = str(uuid.uuid4())
|
||||
media_actor_file.created_date = datetime.now()
|
||||
media_actor_file.last_modified_date = datetime.now()
|
||||
media_actor_file.version = 0
|
||||
media_actor_file.media_actor_id = actor_id
|
||||
media_actor_file.media_file_id = file_id
|
||||
db.add(media_actor_file)
|
||||
db.commit()
|
||||
db.refresh(media_actor_file)
|
||||
return media_actor_file
|
||||
|
||||
|
||||
def delete_mediaactorfile(db: Session, actorfile_id: str):
|
||||
"""
|
||||
Delete relation between MediaFile and MediaActor.
|
||||
"""
|
||||
logger.info("delete MediaActorFile with id %s", actorfile_id)
|
||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||
db.delete(media_actorfile)
|
||||
db.commit()
|
||||
|
||||
|
||||
def import_mediaactorfile(
|
||||
db: Session, new_actorfile: MediaActorFileModel
|
||||
) -> MediaActorFile:
|
||||
"""
|
||||
Import MediaFile and set missing values with default ones.
|
||||
"""
|
||||
logger.info("import MediaActorFile with %s", new_actorfile)
|
||||
media_actor_file: MediaActorFile = MediaActorFile()
|
||||
return media_actor_file
|
||||
@@ -0,0 +1,78 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.media import MediaFile
|
||||
from src.schema.media.file import MediaFileModel
|
||||
|
||||
|
||||
def create_new_mediafile(link: str, db: Session) -> MediaFile:
|
||||
"""
|
||||
Create MediaFile with gievne URL.
|
||||
"""
|
||||
logger.info("create MediaFile with url {link}")
|
||||
media_file: MediaFile = MediaFile()
|
||||
media_file.id = str(uuid.uuid4())
|
||||
media_file.url = link
|
||||
media_file.created_date = datetime.now()
|
||||
media_file.last_modified_date = datetime.now()
|
||||
media_file.version = 0
|
||||
media_file.review = True
|
||||
media_file.should_download = True
|
||||
db.add(media_file)
|
||||
db.commit()
|
||||
db.refresh(media_file)
|
||||
logger.info("created %s", media_file)
|
||||
return media_file
|
||||
|
||||
|
||||
def delete_mediafile(db: Session, media_file_id: str):
|
||||
"""
|
||||
Delete MediaFile with given ID from db.
|
||||
"""
|
||||
logger.info("delete MediaFile with id %s", media_file_id)
|
||||
media_file = db.get(MediaFile, media_file_id)
|
||||
db.delete(media_file)
|
||||
db.commit()
|
||||
|
||||
|
||||
def import_mediafile(db: Session, new_file: MediaFileModel) -> MediaFile:
|
||||
"""
|
||||
import MediaActor and set missing values with defautl ones.
|
||||
"""
|
||||
logger.info("import MediaFile with %s", new_file)
|
||||
media_file: MediaFile = MediaFile()
|
||||
media_file.id = new_file.id
|
||||
if new_file.created_date:
|
||||
media_file.created_date = new_file.created_date
|
||||
else:
|
||||
media_file.created_date = datetime.now()
|
||||
if new_file.last_modified_date:
|
||||
media_file.last_modified_date = new_file.last_modified_date
|
||||
else:
|
||||
media_file.last_modified_date = datetime.now()
|
||||
media_file.version = new_file.version
|
||||
if new_file.title:
|
||||
media_file.title = new_file.title
|
||||
else:
|
||||
media_file.title = ""
|
||||
if new_file.file_name:
|
||||
media_file.file_name = new_file.file_name
|
||||
else:
|
||||
media_file.file_name = ""
|
||||
if new_file.cloud_link:
|
||||
media_file.cloud_link = new_file.cloud_link
|
||||
else:
|
||||
media_file.cloud_link = ""
|
||||
if new_file.url:
|
||||
media_file.url = new_file.url
|
||||
else:
|
||||
media_file.url = ""
|
||||
media_file.review = new_file.review
|
||||
media_file.should_download = new_file.should_download
|
||||
db.add(media_file)
|
||||
db.commit()
|
||||
db.refresh(media_file)
|
||||
return media_file
|
||||
@@ -0,0 +1,23 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.db.models.media import MediaVideo
|
||||
from src.webapps.media.forms import AddLinkForm
|
||||
|
||||
|
||||
def create_new_video(video: AddLinkForm, db: Session) -> MediaVideo:
|
||||
print(video.url)
|
||||
media_video = MediaVideo()
|
||||
media_video.id = str(uuid.uuid4())
|
||||
media_video.url = str(video.url)
|
||||
media_video.created_date = datetime.now()
|
||||
media_video.last_modified_date = datetime.now()
|
||||
media_video.review = True
|
||||
media_video.should_download = True
|
||||
db.add(media_video)
|
||||
db.commit()
|
||||
db.refresh(media_video)
|
||||
print(media_video)
|
||||
return media_video
|
||||
@@ -0,0 +1,42 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from src.core.log_conf import logger
|
||||
from src.db.models.admin import Assignment, Profile
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.schema.user.profile import ProfileModel
|
||||
|
||||
|
||||
def create_new_profile(new_profile: ProfileModel, db: Session) -> Profile:
|
||||
logger.info(f"create MediaActor with url {new_profile.username}")
|
||||
profile: Profile = Profile()
|
||||
profile.id = str(uuid.uuid4())
|
||||
profile.user_name = new_profile.username
|
||||
profile.first_name = new_profile.first_name
|
||||
profile.last_name = new_profile.last_name
|
||||
profile.created_date = datetime.now()
|
||||
profile.last_modified_date = datetime.now()
|
||||
profile.version = 0
|
||||
db.add(profile)
|
||||
db.commit()
|
||||
db.refresh(profile)
|
||||
logger.info(f"created {profile}")
|
||||
return profile
|
||||
|
||||
def delete_profile(db: Session, profile_id: str):
|
||||
logger.info(f"delete Profile with id {profile_id}")
|
||||
profile: Optional[Profile] = db.get(Profile, profile_id)
|
||||
if profile is not None:
|
||||
assignments = profile.assignments
|
||||
for assignment in assignments:
|
||||
delete_assignment(db, assignment.id)
|
||||
db.delete(profile)
|
||||
db.commit()
|
||||
|
||||
def delete_assignment(db: Session, assignment_id: str) -> None:
|
||||
logger.info(f"delete Assignment with id {assignment_id}")
|
||||
assignment: Optional[Assignment] = db.get(Assignment, assignment_id)
|
||||
if assignment is not None:
|
||||
db.delete(assignment)
|
||||
db.commit()
|
||||
@@ -1,8 +1,8 @@
|
||||
from typing import Generator, Annotated
|
||||
from typing import Generator
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from src.core.config import settings
|
||||
|
||||
@@ -12,8 +12,10 @@ engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||
SessionLocal = sessionmaker(bind=engine)
|
||||
|
||||
|
||||
def get_db() -> Generator:
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
""" """
|
||||
with SessionLocal() as db:
|
||||
yield db
|
||||
|
||||
SessionDep: type[Session] = Annotated[Session, Depends(get_db)]
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_db)]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import databases
|
||||
|
||||
from src.core.log_conf import logger
|
||||
from src.db.session import SQLALCHEMY_DATABASE_URL
|
||||
|
||||
|
||||
@@ -11,7 +9,7 @@ async def check_db_connected():
|
||||
if not database.is_connected:
|
||||
await database.connect()
|
||||
await database.execute("SELECT 1")
|
||||
logger.info("Database is connected (^_^)")
|
||||
print("Database is connected (^_^)")
|
||||
except Exception as e:
|
||||
print(
|
||||
"Looks like db is missing or is there is some problem in connection,see below traceback"
|
||||
@@ -25,6 +23,6 @@ async def check_db_disconnected():
|
||||
database = databases.Database(SQLALCHEMY_DATABASE_URL)
|
||||
if database.is_connected:
|
||||
await database.disconnect()
|
||||
logger.info("Database is Disconnected (-_-) zZZ")
|
||||
print("Database is Disconnected (-_-) zZZ")
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
+26
-11
@@ -1,3 +1,5 @@
|
||||
import logging
|
||||
import logging.config
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
@@ -5,10 +7,13 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from src.apis.base import api_router
|
||||
from src.apis.version1.healthcheck import health_router
|
||||
from src.apis.version1.login import login_router
|
||||
from src.core.log_conf import LOGGING_CONFIG, logger
|
||||
from src.db.session import engine
|
||||
from src.db.utils import check_db_connected, check_db_disconnected
|
||||
from src.webapps.base import api_router as web_app_router
|
||||
from src.core.config import settings
|
||||
from src.core.log_conf import logger
|
||||
from src.core.middleware import RequestLoggingMiddleware
|
||||
from src.db.models.base import Base
|
||||
from src.db.session import engine
|
||||
from src.db.utils import check_db_connected, check_db_disconnected
|
||||
@@ -22,11 +27,24 @@ async def lifespan(app: FastAPI):
|
||||
await check_db_disconnected()
|
||||
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
await check_db_connected()
|
||||
yield
|
||||
await check_db_disconnected()
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
await check_db_connected()
|
||||
yield
|
||||
await check_db_disconnected()
|
||||
|
||||
def include_router(app: FastAPI):
|
||||
app.include_router(api_router)
|
||||
app.include_router(web_app_router)
|
||||
app.include_router(health_router)
|
||||
app.include_router(login_router)
|
||||
app.include_router(api_router, tags=["api"])
|
||||
app.include_router(web_app_router, tags=["webapp"])
|
||||
app.include_router(health_router, tags=["admin"])
|
||||
app.include_router(login_router, tags=["webapp"])
|
||||
|
||||
|
||||
def configure_static(app: FastAPI):
|
||||
@@ -41,22 +59,19 @@ def add_middle_ware(app: FastAPI):
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
app.add_middleware(RequestLoggingMiddleware)
|
||||
|
||||
|
||||
def create_tables():
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
|
||||
def start_application(log):
|
||||
log.info(f"using database: {settings.DATABASE_URL}")
|
||||
app = FastAPI(
|
||||
title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, lifespan=lifespan
|
||||
)
|
||||
app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, lifespan=lifespan)
|
||||
include_router(app)
|
||||
configure_static(app)
|
||||
add_middle_ware(app)
|
||||
create_tables()
|
||||
return app
|
||||
|
||||
|
||||
kontor = start_application(logger)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional, List
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -6,22 +6,3 @@ from pydantic import BaseModel
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
username: Optional[str] = None
|
||||
scopes: List[str] = []
|
||||
|
||||
|
||||
class ProfileModel(BaseModel):
|
||||
username: str
|
||||
email: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
active: bool
|
||||
|
||||
class HealthCheck(BaseModel):
|
||||
"""
|
||||
Health check model
|
||||
"""
|
||||
status: str = "ok"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user