Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b3dadc18d | |||
| a788381eaa | |||
| a031c5cc99 | |||
| db1a56965f | |||
| 9677323597 | |||
| 56c6808508 | |||
| e5d4b748dc | |||
| 98eb72bd22 | |||
| 0dbd108051 | |||
| d63629ba5c | |||
| 696c3e77be | |||
| 73f92f6770 | |||
| 4d93f51767 | |||
| 0392ac49fb | |||
| fe919eaa35 | |||
| a57cd9c294 | |||
| 0accddaad9 | |||
| 15a0c8701c | |||
| d4dbfa58e9 | |||
| b87f0fc60a | |||
| 41733ec030 |
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres
|
image: postgres
|
||||||
@@ -36,4 +35,4 @@ secrets:
|
|||||||
networks:
|
networks:
|
||||||
database:
|
database:
|
||||||
name: database
|
name: database
|
||||||
|
external: true
|
||||||
|
|||||||
+69
-25
@@ -42,7 +42,75 @@ services:
|
|||||||
image: kontor-api:0.2.0-SNAPSHOT
|
image: kontor-api:0.2.0-SNAPSHOT
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://kontor-api:8800/health"]
|
test: ["CMD", "curl", "-f", "http://kontor-api:8500/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- database
|
||||||
|
- integration
|
||||||
|
- frontend
|
||||||
|
ports:
|
||||||
|
- 8500:8500
|
||||||
|
volumes:
|
||||||
|
- images-data:/data/images
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
kontor-fiber:
|
||||||
|
build:
|
||||||
|
context: ./kontor-fiber
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
tags:
|
||||||
|
- kontor-fiber:0.2.0-SNAPSHOT
|
||||||
|
image: kontor-fiber:0.2.0-SNAPSHOT
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://kontor-fiber:8600/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- database
|
||||||
|
- integration
|
||||||
|
- frontend
|
||||||
|
ports:
|
||||||
|
- 8600:8600
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
kontor-echo:
|
||||||
|
build:
|
||||||
|
context: ./kontor-echo
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
tags:
|
||||||
|
- kontor-echo:0.2.0-SNAPSHOT
|
||||||
|
image: kontor-echo:0.2.0-SNAPSHOT
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://kontor-echo:8700/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- database
|
||||||
|
- integration
|
||||||
|
- frontend
|
||||||
|
ports:
|
||||||
|
- 8700:8700
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
kontor-quarkus:
|
||||||
|
build:
|
||||||
|
context: ./kontor-quarkus
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
tags:
|
||||||
|
- kontor-quarkus:0.2.0-SNAPSHOT
|
||||||
|
image: kontor-quarkus:0.2.0-SNAPSHOT
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://kontor-quarkus:8800/q/health"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
@@ -52,30 +120,6 @@ services:
|
|||||||
- frontend
|
- frontend
|
||||||
ports:
|
ports:
|
||||||
- 8800:8800
|
- 8800:8800
|
||||||
volumes:
|
|
||||||
- images-data:/data/images
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
kontor-api-go:
|
|
||||||
build:
|
|
||||||
context: ./kontor-api-go
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
tags:
|
|
||||||
- kontor-api-go:0.2.0-SNAPSHOT
|
|
||||||
image: kontor-api-go:0.2.0-SNAPSHOT
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://kontor-api-go:8900/health"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
networks:
|
|
||||||
- database
|
|
||||||
- integration
|
|
||||||
- frontend
|
|
||||||
ports:
|
|
||||||
- 8900:8900
|
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"kontor-api-go/pkg/schema"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/uptrace/bun"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetupComicRoutes(api fiber.Router) {
|
|
||||||
comics := api.Group("/comics")
|
|
||||||
comics.Get("/comics", GetAllComics)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllComics(c *fiber.Ctx) error {
|
|
||||||
var comics []schema.Comic
|
|
||||||
var err error
|
|
||||||
var db *bun.DB
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
db, err = schema.GetDatabase()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(comics)
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/uptrace/bun"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Publisher struct {
|
|
||||||
bun.BaseModel `bun:"table:publisher"`
|
|
||||||
|
|
||||||
ID string `bun:"id,pk"`
|
|
||||||
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
|
||||||
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
|
||||||
Version int `bun:"version,default:0"`
|
|
||||||
Name string `bun:"name,unique:name"`
|
|
||||||
WebLink string `bun:"weblink"`
|
|
||||||
|
|
||||||
ParentPublisherID *string `bun:"parent_publisher_id"`
|
|
||||||
ParentPublisher *Publisher `bun:"rel:belongs-to,join:parent_publisher_id=id"`
|
|
||||||
Imprints []Publisher `bun:"rel:has-many,join:id=parent_publisher_id"`
|
|
||||||
Comics []Comic `bun:"rel:has-many,join:id=publisher_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comic struct {
|
|
||||||
bun.BaseModel `bun:"table:comic"`
|
|
||||||
|
|
||||||
ID string `bun:"id,pk"`
|
|
||||||
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
|
||||||
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
|
||||||
Version int `bun:"version,default:0"`
|
|
||||||
Title string `bun:"title,unique:title,notnull"`
|
|
||||||
CurrentOrder bool `bun:"current_order"`
|
|
||||||
Completed bool `bun:"completed"`
|
|
||||||
WebLink string `bun:"weblink"`
|
|
||||||
|
|
||||||
PublisherID *string `bun:"publisher_id"`
|
|
||||||
Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"`
|
|
||||||
|
|
||||||
// Issues []Issue `bun:"rel:has-many,join:id=comic_id"`
|
|
||||||
// StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"`
|
|
||||||
// TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"`
|
|
||||||
// Volumes []Volume `bun:"rel:has-many,join:id=comic_id"`
|
|
||||||
// ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"`
|
|
||||||
}
|
|
||||||
@@ -51,5 +51,5 @@ ENV PATH="/app/.venv/bin:$PATH"
|
|||||||
EXPOSE $PORT
|
EXPOSE $PORT
|
||||||
|
|
||||||
# Start the application with Uvicorn in production mode, using environment variable references
|
# 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", "8800"]
|
CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8500"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, status, Depends, Response
|
from fastapi import APIRouter, Body, HTTPException, status, Depends, Response
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
|
||||||
from src.core.config import settings
|
from src.core.config import settings
|
||||||
@@ -31,7 +31,7 @@ def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depen
|
|||||||
|
|
||||||
# @router.post("/token-cookie", response_model=Token)
|
# @router.post("/token-cookie", response_model=Token)
|
||||||
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
def login_for_token_cookie(response: Response, form_data: LoginForm = Depends()):
|
||||||
user = authenticate_user(form_data.username, form_data.password)
|
user = authenticate_user(form_data.username, form_data.password) # type: ignore
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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")
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from fastapi import APIRouter, status, HTTPException
|
from fastapi import APIRouter, status, HTTPException
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.repository.media import create_new_mediaactor
|
from src.db.repository.media import create_new_mediaactor, delete_mediaactor
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
from src.schema.media.actor import Actor, MediaActorResponse, get_actor_details
|
from src.schema.media.actor import Actor, MediaActorResponse, get_actor_details
|
||||||
from src.db.models.media import MediaActor
|
from src.db.models.media import MediaActor
|
||||||
@@ -10,7 +10,7 @@ router = APIRouter()
|
|||||||
|
|
||||||
@router.get("/actors", response_model=list[MediaActorResponse])
|
@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_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]:
|
def get_all_actors(db: SessionDep, review: bool = False, download: bool = False) -> list[MediaActorResponse]: # type: ignore
|
||||||
results: list[MediaActorResponse] = []
|
results: list[MediaActorResponse] = []
|
||||||
actors = db.scalars(select(MediaActor)).all()
|
actors = db.scalars(select(MediaActor)).all()
|
||||||
for mediaactor in actors:
|
for mediaactor in actors:
|
||||||
@@ -19,15 +19,26 @@ def get_all_actors(db: SessionDep, review: bool = False, download: bool = False)
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/actors/{actor_id}", response_model=MediaActorResponse)
|
@router.get("/actors/{actor_id}", response_model=MediaActorResponse)
|
||||||
def get_actor(actor_id: str, db: SessionDep) -> MediaActorResponse:
|
def get_actor(actor_id: str, db: SessionDep) -> MediaActorResponse: # type: ignore
|
||||||
media_actor = db.get(MediaActor, actor_id)
|
media_actor = db.get(MediaActor, actor_id)
|
||||||
if not media_actor:
|
if not media_actor:
|
||||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||||
response = get_actor_details(media_actor)
|
response = get_actor_details(media_actor)
|
||||||
return response
|
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)
|
@router.post("/actors", status_code=status.HTTP_201_CREATED)
|
||||||
def add_actor(new_actor: Actor, db: SessionDep) -> MediaActorResponse:
|
def add_actor(new_actor: Actor, db: SessionDep) -> MediaActorResponse: # type: ignore
|
||||||
logger.info(f"add actor {new_actor.url}")
|
logger.info(f"add actor {new_actor.url}")
|
||||||
try:
|
try:
|
||||||
mediaActor: MediaActor = create_new_mediaactor(new_actor, db)
|
mediaActor: MediaActor = create_new_mediaactor(new_actor, db)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from src.schema.media.actorfile import MediaActorFileResponse, get_actorfile_det
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/actorfiles", response_model=list[MediaActorFileResponse])
|
@router.get("/actorfiles", response_model=list[MediaActorFileResponse])
|
||||||
def get_all_actorfiles(db: SessionDep) -> list[MediaActorFileResponse]:
|
def get_all_actorfiles(db: SessionDep) -> list[MediaActorFileResponse]: # type: ignore
|
||||||
results: list[MediaActorFileResponse] = []
|
results: list[MediaActorFileResponse] = []
|
||||||
actorfiles = db.scalars(select(MediaActorFile)).all()
|
actorfiles = db.scalars(select(MediaActorFile)).all()
|
||||||
for mediaactorfile in actorfiles:
|
for mediaactorfile in actorfiles:
|
||||||
@@ -17,7 +17,7 @@ def get_all_actorfiles(db: SessionDep) -> list[MediaActorFileResponse]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/actorfiles/{actorfile_id}", response_model=MediaActorFileResponse)
|
@router.get("/actorfiles/{actorfile_id}", response_model=MediaActorFileResponse)
|
||||||
def get_actorfile(actorfile_id: str, db: SessionDep) -> MediaActorFileResponse:
|
def get_actorfile(actorfile_id: str, db: SessionDep) -> MediaActorFileResponse: # type: ignore
|
||||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||||
if not media_actorfile:
|
if not media_actorfile:
|
||||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||||
@@ -25,7 +25,7 @@ def get_actorfile(actorfile_id: str, db: SessionDep) -> MediaActorFileResponse:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@router.delete("/actorfiles/{actorfile_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/actorfiles/{actorfile_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
def delete_actorfile(actorfile_id: str, db: SessionDep):
|
def delete_actorfile(actorfile_id: str, db: SessionDep): # type: ignore
|
||||||
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
media_actorfile = db.get(MediaActorFile, actorfile_id)
|
||||||
if not media_actorfile:
|
if not media_actorfile:
|
||||||
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
raise HTTPException(status_code=404, detail="MediaActor could not be found")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from fastapi import APIRouter, status, HTTPException, Depends
|
from fastapi import APIRouter, status, HTTPException, Depends
|
||||||
from sqlalchemy import select, Sequence
|
from sqlalchemy import select, Sequence
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.repository.media import create_new_mediaactorfile, create_new_mediafile
|
from src.db.repository.media import create_new_mediaactorfile, create_new_mediafile, delete_mediafile
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
from src.schema.media.actor import MediaActorResponse
|
from src.schema.media.actor import MediaActorResponse
|
||||||
from src.schema.media.actorfile import MediaActorFileResponse
|
from src.schema.media.actorfile import MediaActorFileResponse
|
||||||
@@ -11,7 +11,7 @@ from src.db.models.media import MediaFile
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/update-titles")
|
@router.get("/update-titles")
|
||||||
def update_titles(db: SessionDep) -> list[MediaFileResponse]:
|
def update_titles(db: SessionDep) -> list[MediaFileResponse]: # type: ignore
|
||||||
results: list[MediaFileResponse] = []
|
results: list[MediaFileResponse] = []
|
||||||
files = db.query(MediaFile).filter(MediaFile.review == True).all()
|
files = db.query(MediaFile).filter(MediaFile.review == True).all()
|
||||||
for mediafile in files:
|
for mediafile in files:
|
||||||
@@ -25,7 +25,7 @@ def update_titles(db: SessionDep) -> list[MediaFileResponse]:
|
|||||||
|
|
||||||
@router.get("/files", response_model=list[MediaFileResponse])
|
@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, 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]:
|
def get_all_files(db: SessionDep, review: bool = False, download: bool = False) -> list[MediaFileResponse]: # type: ignore
|
||||||
results: list[MediaFileResponse] = []
|
results: list[MediaFileResponse] = []
|
||||||
files: Sequence[MediaFile]
|
files: Sequence[MediaFile]
|
||||||
if review:
|
if review:
|
||||||
@@ -40,15 +40,26 @@ def get_all_files(db: SessionDep, review: bool = False, download: bool = False)
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.get("/files/{file_id}", response_model=MediaFileResponse)
|
@router.get("/files/{file_id}", response_model=MediaFileResponse)
|
||||||
def get_file(file_id: str, db: SessionDep) -> MediaFileResponse:
|
def get_file(file_id: str, db: SessionDep) -> MediaFileResponse: # type: ignore
|
||||||
mediafile = db.get(MediaFile, file_id)
|
mediafile = db.get(MediaFile, file_id)
|
||||||
if not mediafile:
|
if not mediafile:
|
||||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||||
response = get_file_details(mediafile)
|
response = get_file_details(mediafile)
|
||||||
return response
|
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])
|
@router.get("/files/{file_id}/actors", response_model=list[MediaActorResponse])
|
||||||
def get_file_actors(file_id: str, db: SessionDep) -> list[MediaActorResponse]:
|
def get_file_actors(file_id: str, db: SessionDep) -> list[MediaActorResponse]: # type: ignore
|
||||||
mediafile = db.get(MediaFile, file_id)
|
mediafile = db.get(MediaFile, file_id)
|
||||||
if not mediafile:
|
if not mediafile:
|
||||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||||
@@ -61,7 +72,7 @@ def get_file_actors(file_id: str, db: SessionDep) -> list[MediaActorResponse]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.put("/files/{file_id}/actors", response_model=list[MediaActorFileResponse])
|
@router.put("/files/{file_id}/actors", response_model=list[MediaActorFileResponse])
|
||||||
def update_file_actors(file_id: str, db: SessionDep, actors: list[MediaActorResponse]) -> list[MediaActorFileResponse]:
|
def update_file_actors(file_id: str, db: SessionDep, actors: list[MediaActorResponse]) -> list[MediaActorFileResponse]: # type: ignore
|
||||||
mediafile = db.get(MediaFile, file_id)
|
mediafile = db.get(MediaFile, file_id)
|
||||||
if not mediafile:
|
if not mediafile:
|
||||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||||
@@ -85,7 +96,7 @@ def update_file_actors(file_id: str, db: SessionDep, actors: list[MediaActorResp
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@router.put("/files/{file_id}", response_model=MediaFileResponse)
|
@router.put("/files/{file_id}", response_model=MediaFileResponse)
|
||||||
def update_file(file_id: str, db: SessionDep, info: MediaFileResponse) -> MediaFileResponse:
|
def update_file(file_id: str, db: SessionDep, info: MediaFileResponse) -> MediaFileResponse: # type: ignore
|
||||||
mediaFile = db.get(MediaFile, file_id)
|
mediaFile = db.get(MediaFile, file_id)
|
||||||
if not mediaFile:
|
if not mediaFile:
|
||||||
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
raise HTTPException(status_code=404, detail="MediaFile could not be found")
|
||||||
@@ -99,7 +110,7 @@ def update_file(file_id: str, db: SessionDep, info: MediaFileResponse) -> MediaF
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@router.post("/files", status_code=status.HTTP_201_CREATED)
|
@router.post("/files", status_code=status.HTTP_201_CREATED)
|
||||||
def add_file(new_link: Link, db: SessionDep) -> MediaFileResponse:
|
def add_file(new_link: Link, db: SessionDep) -> MediaFileResponse: # type: ignore
|
||||||
logger.info(f"add url {new_link.url}")
|
logger.info(f"add url {new_link.url}")
|
||||||
try:
|
try:
|
||||||
mediaFile: MediaFile = create_new_mediafile(new_link.url, db)
|
mediaFile: MediaFile = create_new_mediafile(new_link.url, db)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Settings:
|
|||||||
DB_USER: str = os.getenv("DB_USER", "kontor")
|
DB_USER: str = os.getenv("DB_USER", "kontor")
|
||||||
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
|
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
|
||||||
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
|
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
|
||||||
DB_PORT: str = os.getenv("DB_PORT", 5432)
|
DB_PORT: int = int(os.getenv("DB_PORT", 5432))
|
||||||
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
|
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
|
||||||
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
|
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
|
||||||
SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=")
|
SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=")
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ LOGGING_CONFIG: dict[str, Any] = {
|
|||||||
"formatters": {
|
"formatters": {
|
||||||
"default": {
|
"default": {
|
||||||
"()": "uvicorn.logging.DefaultFormatter",
|
"()": "uvicorn.logging.DefaultFormatter",
|
||||||
"fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s"
|
"fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s",
|
||||||
},
|
},
|
||||||
"access": {
|
"access": {
|
||||||
"()": "uvicorn.logging.AccessFormatter",
|
"()": "uvicorn.logging.AccessFormatter",
|
||||||
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
|
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
|
||||||
},
|
},
|
||||||
"access_file": {
|
"access_file": {
|
||||||
"()": "uvicorn.logging.AccessFormatter",
|
"()": "uvicorn.logging.AccessFormatter",
|
||||||
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
|
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
|
||||||
"use_colors": False,
|
"use_colors": False,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -34,12 +34,16 @@ LOGGING_CONFIG: dict[str, Any] = {
|
|||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
"root": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
"root": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||||
"kontor": {"handlers": ["default"], "level": "INFO", "propagate": True},
|
"kontor": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||||
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
||||||
"uvicorn.error": {"level": "INFO"},
|
"uvicorn.error": {"level": "INFO"},
|
||||||
"uvicorn.access": {"handlers": ["default"], "level": "WARNING", "propagate": False},
|
"uvicorn.access": {
|
||||||
|
"handlers": ["default"],
|
||||||
|
"level": "WARNING",
|
||||||
|
"propagate": False,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.config.dictConfig(LOGGING_CONFIG)
|
logging.config.dictConfig(LOGGING_CONFIG)
|
||||||
logger = logging.getLogger('kontor')
|
logger = logging.getLogger("kontor")
|
||||||
|
|||||||
@@ -1,29 +1,21 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from datetime import timedelta
|
from typing import Annotated, Dict, List, Optional
|
||||||
from typing import Optional, Annotated, List
|
|
||||||
from typing import Dict
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from fastapi import HTTPException, Security
|
|
||||||
from fastapi import Request
|
|
||||||
from fastapi import status
|
|
||||||
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
|
|
||||||
from fastapi.security.utils import get_authorization_scheme_param
|
|
||||||
|
|
||||||
import bcrypt
|
import bcrypt
|
||||||
from fastapi import Depends
|
from fastapi import Depends, HTTPException, Request, Security, status
|
||||||
from fastapi.security import SecurityScopes, OAuth2PasswordBearer, OAuth2
|
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 pydantic import ValidationError
|
||||||
|
|
||||||
from src.core.config import settings
|
from src.core.config import settings
|
||||||
from jose import jwt, JWTError
|
|
||||||
|
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
from src.db.models.admin import Profile
|
from src.db.models.admin import Profile
|
||||||
from src.db.repository.admin import get_profile
|
from src.db.repository.admin import get_profile
|
||||||
from src.db.session import SessionLocal
|
from src.db.session import SessionLocal
|
||||||
from src.schema.admin import TokenData, ProfileModel
|
from src.schema.admin import ProfileModel, TokenData
|
||||||
|
|
||||||
oauth2_scheme = OAuth2PasswordBearer(
|
oauth2_scheme = OAuth2PasswordBearer(
|
||||||
tokenUrl="/api/login/token",
|
tokenUrl="/api/login/token",
|
||||||
@@ -33,19 +25,19 @@ oauth2_scheme = OAuth2PasswordBearer(
|
|||||||
|
|
||||||
class OAuth2PasswordBearerWithCookie(OAuth2):
|
class OAuth2PasswordBearerWithCookie(OAuth2):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
tokenUrl: str,
|
tokenUrl: str,
|
||||||
scheme_name: Optional[str] = None,
|
scheme_name: Optional[str] = None,
|
||||||
scopes: Optional[Dict[str, str]] = None,
|
scopes: Optional[Dict[str, str]] = None,
|
||||||
auto_error: bool = True,
|
auto_error: bool = True,
|
||||||
):
|
):
|
||||||
if not scopes:
|
if not scopes:
|
||||||
scopes = {}
|
scopes = {}
|
||||||
flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes})
|
flows = OAuthFlowsModel(password={"tokenUrl": tokenUrl, "scopes": scopes}) # type: ignore
|
||||||
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
|
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
|
||||||
|
|
||||||
async def __call__(self, request: Request) -> Optional[str]:
|
async def __call__(self, request: Request) -> Optional[str]:
|
||||||
authorization: str = request.cookies.get("access_token") # changed to accept access token from httpOnly Cookie
|
authorization: str = request.cookies.get("access_token") # type: ignore # changed to accept access token from httpOnly Cookie
|
||||||
|
|
||||||
scheme, param = get_authorization_scheme_param(authorization)
|
scheme, param = get_authorization_scheme_param(authorization)
|
||||||
if not authorization or scheme.lower() != "bearer":
|
if not authorization or scheme.lower() != "bearer":
|
||||||
@@ -63,7 +55,7 @@ class OAuth2PasswordBearerWithCookie(OAuth2):
|
|||||||
def authenticate_user(username: str, password: str) -> Optional[Profile]:
|
def authenticate_user(username: str, password: str) -> Optional[Profile]:
|
||||||
with SessionLocal() as db:
|
with SessionLocal() as db:
|
||||||
user = get_profile(username=username, db=db)
|
user = get_profile(username=username, db=db)
|
||||||
logger.info(user)
|
logger.debug(user)
|
||||||
if not user:
|
if not user:
|
||||||
return None
|
return None
|
||||||
if bcrypt.checkpw(password.encode(), user.password.encode()):
|
if bcrypt.checkpw(password.encode(), user.password.encode()):
|
||||||
@@ -88,7 +80,9 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
|||||||
return encoded_jwt
|
return encoded_jwt
|
||||||
|
|
||||||
|
|
||||||
async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]):
|
async def get_current_user(
|
||||||
|
security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]
|
||||||
|
):
|
||||||
if security_scopes.scopes:
|
if security_scopes.scopes:
|
||||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||||
else:
|
else:
|
||||||
@@ -99,8 +93,10 @@ async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str
|
|||||||
headers={"WWW-Authenticate": authenticate_value},
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
payload = jwt.decode(
|
||||||
username: str = payload.get("sub")
|
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||||
|
)
|
||||||
|
username: str = payload.get("sub") # type: ignore
|
||||||
logger.info("username/email extracted is ", username)
|
logger.info("username/email extracted is ", username)
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
@@ -110,7 +106,7 @@ async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str
|
|||||||
except (JWTError, ValidationError):
|
except (JWTError, ValidationError):
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
with SessionLocal() as db:
|
with SessionLocal() as db:
|
||||||
user = get_profile(username=token_data.username, db=db)
|
user = get_profile(username=token_data.username, db=db) # type: ignore
|
||||||
if user is None:
|
if user is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
for scope in security_scopes.scopes:
|
for scope in security_scopes.scopes:
|
||||||
@@ -124,17 +120,20 @@ async def get_current_user(security_scopes: SecurityScopes, token: Annotated[str
|
|||||||
|
|
||||||
|
|
||||||
async def get_current_active_user(
|
async def get_current_active_user(
|
||||||
current_user: Annotated[Profile, Security(get_current_user, scopes=["me"])],
|
current_user: Annotated[Profile, Security(get_current_user, scopes=["me"])],
|
||||||
) -> ProfileModel:
|
) -> ProfileModel:
|
||||||
if not current_user.enabled:
|
if not current_user.enabled: # type: ignore
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
raise HTTPException(status_code=400, detail="Inactive user")
|
||||||
user_model = ProfileModel(username=current_user.user_name, email=current_user.email,
|
user_model = ProfileModel(
|
||||||
first_name=current_user.first_name, last_name=current_user.last_name,
|
username=current_user.user_name,
|
||||||
active=current_user.enabled)
|
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
|
return user_model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@@ -144,7 +143,7 @@ def get_current_user_from_token(token: str = Depends(oauth2_scheme)):
|
|||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
||||||
)
|
)
|
||||||
username: str = payload.get("sub")
|
username: str = payload.get("sub") # type: ignore
|
||||||
logger.info("username/email extracted is ", username)
|
logger.info("username/email extracted is ", username)
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ def create_new_mediafile(link: str, db: Session) -> MediaFile:
|
|||||||
logger.info(f"created {media_file}")
|
logger.info(f"created {media_file}")
|
||||||
return 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:
|
def create_new_mediaactor(new_actor: Actor, db: Session) -> MediaActor:
|
||||||
logger.info(f"create MediaActor with url {new_actor.url}")
|
logger.info(f"create MediaActor with url {new_actor.url}")
|
||||||
media_actor: MediaActor = MediaActor()
|
media_actor: MediaActor = MediaActor()
|
||||||
@@ -53,6 +59,12 @@ def create_new_mediaactor(new_actor: Actor, db: Session) -> MediaActor:
|
|||||||
logger.info(f"created {media_actor}")
|
logger.info(f"created {media_actor}")
|
||||||
return 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:
|
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}")
|
logger.info(f"create MediaActorFile with actor {actor_id} and file {file_id}")
|
||||||
media_actor_file: MediaActorFile = MediaActorFile()
|
media_actor_file: MediaActorFile = MediaActorFile()
|
||||||
|
|||||||
+18
-8
@@ -1,17 +1,18 @@
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.staticfiles import StaticFiles
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from src.apis.base import api_router
|
from src.apis.base import api_router
|
||||||
from src.apis.version1.healthcheck import health_router
|
from src.apis.version1.healthcheck import health_router
|
||||||
|
from src.apis.version1.login import login_router
|
||||||
|
from src.core.config import settings
|
||||||
from src.core.log_conf import logger
|
from src.core.log_conf import logger
|
||||||
|
from src.db.models.base import Base
|
||||||
from src.db.session import engine
|
from src.db.session import engine
|
||||||
from src.db.utils import check_db_connected, check_db_disconnected
|
from src.db.utils import check_db_connected, check_db_disconnected
|
||||||
from src.webapps.base import api_router as web_app_router
|
from src.webapps.base import api_router as web_app_router
|
||||||
from src.core.config import settings
|
|
||||||
from src.db.models.base import Base
|
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
@@ -20,33 +21,42 @@ async def lifespan(app: FastAPI):
|
|||||||
yield
|
yield
|
||||||
await check_db_disconnected()
|
await check_db_disconnected()
|
||||||
|
|
||||||
|
|
||||||
def include_router(app: FastAPI):
|
def include_router(app: FastAPI):
|
||||||
app.include_router(api_router)
|
app.include_router(api_router)
|
||||||
app.include_router(web_app_router)
|
app.include_router(web_app_router)
|
||||||
app.include_router(health_router)
|
app.include_router(health_router)
|
||||||
|
app.include_router(login_router)
|
||||||
|
|
||||||
|
|
||||||
def configure_static(app: FastAPI):
|
def configure_static(app: FastAPI):
|
||||||
app.mount("/static", StaticFiles(directory="src/static"), name="static")
|
app.mount("/static", StaticFiles(directory="src/static"), name="static")
|
||||||
|
|
||||||
|
|
||||||
def add_middle_ware(app: FastAPI):
|
def add_middle_ware(app: FastAPI):
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=['*'],
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=['*'],
|
allow_methods=["*"],
|
||||||
allow_headers=['*'],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_tables():
|
def create_tables():
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
def start_application(log):
|
def start_application(log):
|
||||||
log.info(f"using database: {settings.DATABASE_URL}")
|
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)
|
include_router(app)
|
||||||
configure_static(app)
|
configure_static(app)
|
||||||
add_middle_ware(app)
|
add_middle_ware(app)
|
||||||
create_tables()
|
create_tables()
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
kontor = start_application(logger)
|
kontor = start_application(logger)
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-echo/pkg/handler"
|
||||||
|
"kontor-api-echo/pkg/schema"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
echojwt "github.com/labstack/echo-jwt/v4"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/echo/v4/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetOutput(os.Stdout)
|
||||||
|
log.Println("Kontor started")
|
||||||
|
|
||||||
|
if _, err := schema.GetDatabase(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.GET("/health", handler.GetHealth)
|
||||||
|
e.POST("/login", handler.Login)
|
||||||
|
|
||||||
|
skipper := func(c echo.Context) bool {
|
||||||
|
// Skip health check endpoint
|
||||||
|
return c.Request().URL.Path == "/health"
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||||
|
LogStatus: true,
|
||||||
|
LogURI: true,
|
||||||
|
LogError: true,
|
||||||
|
HandleError: true,
|
||||||
|
Skipper: skipper,
|
||||||
|
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
|
||||||
|
if v.Error == nil {
|
||||||
|
log.Printf("REQUEST: uri: %v, status: %v\n", v.URI, v.Status)
|
||||||
|
} else {
|
||||||
|
log.Printf("REQUEST-ERROR: uri: %v, status: %v, err: %v\n", v.URI, v.Status, v.Error.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
group := e.Group("/api")
|
||||||
|
group.Use(echojwt.WithConfig(echojwt.Config{SigningKey: []byte("secret")}))
|
||||||
|
handler.SetupComicRoutes(group)
|
||||||
|
handler.SetupMediaRoutes(group)
|
||||||
|
|
||||||
|
e.Logger.Fatal(e.Start(":8700"))
|
||||||
|
log.Println("Kontor finished")
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
module kontor-api-echo
|
||||||
|
|
||||||
|
go 1.24.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.4.0 // indirect
|
||||||
|
github.com/labstack/echo/v4 v4.15.0 // indirect
|
||||||
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||||
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
github.com/uptrace/bun v1.2.16 // indirect
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.39.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||||
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
|
golang.org/x/net v0.48.0 // indirect
|
||||||
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
golang.org/x/text v0.32.0 // indirect
|
||||||
|
golang.org/x/time v0.14.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
mellium.im/sasl v0.3.2 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.4.0 h1:nrXaEnJupfc2R4XChcLRDyghhMZup77F8nIzHnBK19U=
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.4.0/go.mod h1:kYXWgWms9iFqI3ldR+HAEj/Zfg5rZtR7ePOgktG4Hjg=
|
||||||
|
github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24=
|
||||||
|
github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
|
||||||
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||||
|
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
|
||||||
|
github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||||
|
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||||
|
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||||
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
||||||
|
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-echo/pkg/schema"
|
||||||
|
"kontor-api-echo/pkg/utils"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jwtCustomClaims struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginRequest struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Login(c echo.Context) error {
|
||||||
|
// user := c.FormValue("email")
|
||||||
|
// pass := c.FormValue("password")
|
||||||
|
loginRequest := new(LoginRequest)
|
||||||
|
if err := c.Bind(loginRequest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
email := loginRequest.Email
|
||||||
|
password := loginRequest.Password
|
||||||
|
|
||||||
|
var profile schema.Profile
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, _ = schema.GetDatabase()
|
||||||
|
err = db.NewSelect().Model(&profile).Where("email = ?", email).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return c.String(http.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.ComparePassword(profile.Password, password) {
|
||||||
|
return echo.ErrUnauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set custom claims
|
||||||
|
claims := &jwtCustomClaims{
|
||||||
|
"Jon Snow",
|
||||||
|
true,
|
||||||
|
jwt.RegisteredClaims{
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create token with claims
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
// Generate encoded token and send it as response.
|
||||||
|
t, err := token.SignedString([]byte("secret"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, echo.Map{"access_token": t, "token_type": "bearer"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func restricted(c echo.Context) error {
|
||||||
|
user := c.Get("user").(*jwt.Token)
|
||||||
|
claims := user.Claims.(*jwtCustomClaims)
|
||||||
|
name := claims.Name
|
||||||
|
return c.String(http.StatusOK, "Welcome "+name+"!")
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"kontor-api-echo/pkg/schema"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupComicRoutes(api *echo.Group) {
|
||||||
|
comics := api.Group("/comics")
|
||||||
|
comics.GET("/comics", GetAllComics)
|
||||||
|
comics.GET("/publishers", GetAllPublishers)
|
||||||
|
comics.GET("/comicworks", GetAllComicWorks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllComics(c echo.Context) error {
|
||||||
|
var comics []schema.Comic
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
fmt.Printf("REQUEST: uri: %v, status: %v\n", c.Request().URL, c.Response().Status)
|
||||||
|
log.Printf("REQUEST: uri: %v, status: %v\n", c.Request().URL, c.Response().Status)
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return echo.ErrInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, comics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllPublishers(c echo.Context) error {
|
||||||
|
var publishers []schema.Publisher
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return echo.ErrInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, publishers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllComicWorks(c echo.Context) error {
|
||||||
|
var comic_works []schema.ComicWork
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return echo.ErrInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, comic_works)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHealth(c echo.Context) error {
|
||||||
|
status := new(Status)
|
||||||
|
status.Status = "ok"
|
||||||
|
return c.JSON(http.StatusOK, status)
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"kontor-api-echo/pkg/schema"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupMediaRoutes(api *echo.Group) {
|
||||||
|
media := api.Group("/media")
|
||||||
|
media.GET("/files", GetAllFiles)
|
||||||
|
media.POST("/files", AddFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllFiles(c echo.Context) error {
|
||||||
|
var files []schema.MediaFile
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = db.NewSelect().Model(&files).Relation("MediaActorFiles").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return echo.ErrInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddFile(c echo.Context) error {
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
link := new(Link)
|
||||||
|
if err = c.Bind(link); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("URL %s has been sent", link)
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
id := uuid.NewString()
|
||||||
|
timestamp := time.Now()
|
||||||
|
mediafile := &schema.MediaFile{ID: id, CreatedAt: timestamp, UpdatedAt: timestamp, WebLink: fmt.Sprintf("%s", link), Version: 1, ShouldDownload: true, Review: true}
|
||||||
|
_, err = db.NewInsert().Model(mediafile).Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.String(http.StatusCreated, "Link created")
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Publisher struct {
|
||||||
|
bun.BaseModel `bun:"table:publisher"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
|
||||||
|
ParentPublisherID *string `bun:"parent_publisher_id"`
|
||||||
|
ParentPublisher *Publisher `bun:"rel:belongs-to,join:parent_publisher_id=id"`
|
||||||
|
Imprints []Publisher `bun:"rel:has-many,join:id=parent_publisher_id"`
|
||||||
|
Comics []Comic `bun:"rel:has-many,join:id=publisher_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comic struct {
|
||||||
|
bun.BaseModel `bun:"table:comic"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Title string `bun:"title,unique:title,notnull"`
|
||||||
|
CurrentOrder bool `bun:"current_order"`
|
||||||
|
Completed bool `bun:"completed"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
|
||||||
|
PublisherID *string `bun:"publisher_id"`
|
||||||
|
Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"`
|
||||||
|
Issues []Issue `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
Volumes []Volume `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Artist struct {
|
||||||
|
bun.BaseModel `bun:"table:artist"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:title,notnull"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Issue struct {
|
||||||
|
bun.BaseModel `bun:"table:issue"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
InStock bool `bun:"in_stock"`
|
||||||
|
IsRead bool `bun:"is_read"`
|
||||||
|
IssueNumber string `bu:"issue_number"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
PublishedOn time.Time `bun:"published_on"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
StoryArcID *string `bun:"story_arc_id"`
|
||||||
|
StoryArc *StoryArc `bun:"rel:belongs-to,join:story_arc_id=id"`
|
||||||
|
VolumeID *string `bun:"volume_id"`
|
||||||
|
Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoryArc struct {
|
||||||
|
bun.BaseModel `bun:"table:story_arc"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
VolumeID *string `bun:"volume_id"`
|
||||||
|
Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TradePaperback struct {
|
||||||
|
bun.BaseModel `bun:"table:trade_paperback"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
IssueStart int `bun:"issue_start"`
|
||||||
|
IssueEnd int `bun:"issue_end"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Volume struct {
|
||||||
|
bun.BaseModel `bun:"table:volume"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkType struct {
|
||||||
|
bun.BaseModel `bun:"table:worktype"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComicWork struct {
|
||||||
|
bun.BaseModel `bun:"table:comic_work"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
ArtistID *string `bun:"artist_id"`
|
||||||
|
Artist *Artist `bun:"rel:belongs-to,join:artist_id=id"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
WorkTypeID *string `bun:"work_type_id"`
|
||||||
|
WorkType *WorkType `bun:"rel:belongs-to,join:work_type_id=id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
|
"github.com/uptrace/bun/driver/pgdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTestDatabase() (*bun.DB, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dsn := "postgres://kontor:kontor@localhost:5432/kontor?sslmode=disable"
|
||||||
|
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))
|
||||||
|
|
||||||
|
sqldb.SetMaxOpenConns(4)
|
||||||
|
sqldb.SetMaxIdleConns(4)
|
||||||
|
|
||||||
|
DB := bun.NewDB(sqldb, pgdialect.New())
|
||||||
|
// DB.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
||||||
|
|
||||||
|
if err = DB.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Returned Database Connection")
|
||||||
|
return DB, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
log.Println("Setup Test")
|
||||||
|
exitCode := m.Run()
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectComics(t *testing.T) {
|
||||||
|
var comics []Comic
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 168, len(comics))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectPublishers(t *testing.T) {
|
||||||
|
var publishers []Publisher
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 19, len(publishers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectWorkTypes(t *testing.T) {
|
||||||
|
var comic_works []ComicWork
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 59, len(comic_works))
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
|
"github.com/uptrace/bun/driver/pgdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *bun.DB
|
||||||
|
|
||||||
|
func GetDatabase() (*bun.DB, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dsn := "postgres://kontor:kontor@postgres:5432/kontor?sslmode=disable"
|
||||||
|
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))
|
||||||
|
|
||||||
|
sqldb.SetMaxOpenConns(4)
|
||||||
|
sqldb.SetMaxIdleConns(4)
|
||||||
|
|
||||||
|
DB := bun.NewDB(sqldb, pgdialect.New())
|
||||||
|
|
||||||
|
if err = DB.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Returned Database Connection")
|
||||||
|
return DB, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sport struct {
|
||||||
|
bun.BaseModel `bun:"table:sport"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Teams []Team `bun:"rel:has-many,join:id=sport_id"`
|
||||||
|
Positions []FieldPosition `bun:"rel:has-many,join:id=sport_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
bun.BaseModel `bun:"table:team"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
Shortname string `bun:"short_name"`
|
||||||
|
Roosters []Rooster `bun:"rel:has-many,join:id=team_id"`
|
||||||
|
SportID *string `bun:"sport_id"`
|
||||||
|
Sport *Sport `bun:"rel:belongs-to,join:sport_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldPosition struct {
|
||||||
|
bun.BaseModel `bun:"table:field_position"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
Shortname string `bun:"short_name"`
|
||||||
|
SportID *string `bun:"sport_id"`
|
||||||
|
Sport *Sport `bun:"rel:belongs-to,join:sport_id=id"`
|
||||||
|
Roosters []Rooster `bun:"rel:has-many,join:id=position_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
bun.BaseModel `bun:"table:player"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Firstname string `bun:"first_name"`
|
||||||
|
Lastname string `bun:"last_name"`
|
||||||
|
Roosters []Rooster `bun:"rel:has-many,join:id=player_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rooster struct {
|
||||||
|
bun.BaseModel `bun:"table:rooster"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Year int `bun:"year"`
|
||||||
|
TeamID *string `bun:"team_id"`
|
||||||
|
Team *Team `bun:"rel:belongs-to,join:team_id=id"`
|
||||||
|
PlayerID *string `bun:"player_id"`
|
||||||
|
Player *Player `bun:"rel:belongs-to,join:player_id=id"`
|
||||||
|
PositionID *string `bun:"position_id"`
|
||||||
|
Position *FieldPosition `bun:"rel:belongs-to,join:position_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CardSet struct {
|
||||||
|
bun.BaseModel `bun:"table:card_set"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
ParallelSet bool `bun:"parallel_set"`
|
||||||
|
InsertSet bool `bun:"insert_set"`
|
||||||
|
VendorID *string `bun:"vendor_id"`
|
||||||
|
Vendor *Vendor `bun:"rel:belongs-to,join:vendor_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Vendor struct {
|
||||||
|
bun.BaseModel `bun:"table:vendor"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
CardSets []CardSet `bun:"rel:has-many,join:id=vendor_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
bun.BaseModel `bun:"table:card"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CardNumber int `bun:"card_number"`
|
||||||
|
Year int `bun:"year"`
|
||||||
|
VendorID *string `bun:"vendor_id"`
|
||||||
|
Vendor *Vendor `bun:"rel:belongs-to,join:vendor_id=id"`
|
||||||
|
CardSetID *string `bun:"card_set_id"`
|
||||||
|
CardSet *CardSet `bun:"rel:belongs-to,join:card_set_id=id"`
|
||||||
|
RoosterID *string `bun:"rooster_id"`
|
||||||
|
Rooster *Rooster `bun:"rel:belongs-to,join:rooster_id=id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSelectSports(t *testing.T) {
|
||||||
|
var sports []Sport
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&sports).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 4, len(sports))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectTeams(t *testing.T) {
|
||||||
|
var teams []Team
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&teams).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 122, len(teams))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectFieldPositions(t *testing.T) {
|
||||||
|
var positions []FieldPosition
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&positions).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 44, len(positions))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectPlayers(t *testing.T) {
|
||||||
|
var players []Player
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&players).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 38, len(players))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectRoosters(t *testing.T) {
|
||||||
|
var roosters []Rooster
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&roosters).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 11, len(roosters))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectVendors(t *testing.T) {
|
||||||
|
var vendors []Vendor
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&vendors).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 9, len(vendors))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCardSets(t *testing.T) {
|
||||||
|
var cardSets []CardSet
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&cardSets).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 15, len(cardSets))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCards(t *testing.T) {
|
||||||
|
var cards []Card
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&cards).Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 10, len(cards))
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"kontor-api-go/pkg/schema"
|
"kontor-api-echo/pkg/schema"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# ---------- Stage 1: Build ----------
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
# Ensure a portable, static-ish binary
|
||||||
|
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
|
||||||
|
# Copy and download dependencies
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
# Copy the source code
|
||||||
|
COPY . .
|
||||||
|
# Build the Go application (strip debug info for smaller size)
|
||||||
|
RUN go build -trimpath -ldflags="-s -w" -o kontor cmd/kontor/main.go
|
||||||
|
|
||||||
|
# ---------- Stage 2: Final ----------
|
||||||
|
FROM alpine:latest
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
# Install runtime dependencies you actually need
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl
|
||||||
|
# Create non-root user for security
|
||||||
|
RUN addgroup -S kontor && adduser -S -G kontor -H -s /sbin/nologin kontor
|
||||||
|
# Copy the binary and set ownership
|
||||||
|
COPY --from=builder --chown=kontor:kontor /app/kontor /app/kontor
|
||||||
|
# Run as non-root user
|
||||||
|
USER kontor
|
||||||
|
# Set the entrypoint command
|
||||||
|
ENTRYPOINT ["/app/kontor"]
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"kontor-api-go/pkg/handler"
|
"kontor-api-fiber/pkg/handler"
|
||||||
"kontor-api-go/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
jwtware "github.com/gofiber/contrib/jwt"
|
jwtware "github.com/gofiber/contrib/jwt"
|
||||||
@@ -23,17 +23,19 @@ func main() {
|
|||||||
// SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
// SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
||||||
// }))
|
// }))
|
||||||
|
|
||||||
// app.Use(logger.New())
|
//app.Use(logger.New())
|
||||||
|
|
||||||
app.Get("/health", handler.GetHealth)
|
app.Get("/health", handler.GetHealth)
|
||||||
app.Post("/login", handler.Login)
|
app.Post("/login", handler.Login)
|
||||||
|
|
||||||
api := app.Group("/api", logger.New(), jwtware.New(jwtware.Config{
|
api := app.Group("/api", logger.New(), jwtware.New(jwtware.Config{
|
||||||
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
||||||
}))
|
}))
|
||||||
|
api.Use(logger.New())
|
||||||
handler.SetupComicRoutes(api)
|
handler.SetupComicRoutes(api)
|
||||||
handler.SetupMediaRoutes(api)
|
handler.SetupMediaRoutes(api)
|
||||||
// Listen on port 8900
|
// Listen on port 8900
|
||||||
app.Listen(":8900")
|
app.Listen(":8600")
|
||||||
|
|
||||||
log.Println("Kontor finished")
|
log.Println("Kontor finished")
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
module kontor-api-go
|
module kontor-api-fiber
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/gofiber/contrib/jwt v1.1.2 // indirect
|
github.com/gofiber/contrib/jwt v1.1.2 // indirect
|
||||||
github.com/gofiber/fiber/v2 v2.52.10 // indirect
|
github.com/gofiber/fiber/v2 v2.52.10 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||||
@@ -17,12 +19,15 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/uptrace/bun v1.2.16 // indirect
|
github.com/uptrace/bun v1.2.16 // indirect
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect
|
github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
@@ -31,6 +36,7 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.45.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
mellium.im/sasl v0.3.2 // indirect
|
mellium.im/sasl v0.3.2 // indirect
|
||||||
)
|
)
|
||||||
@@ -7,6 +7,10 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa
|
|||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs=
|
github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs=
|
||||||
github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk=
|
github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk=
|
||||||
github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o=
|
github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o=
|
||||||
@@ -38,10 +42,14 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
|
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
|
||||||
@@ -50,6 +58,8 @@ github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI
|
|||||||
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
|
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0=
|
github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
||||||
@@ -78,6 +88,11 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
||||||
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
||||||
@@ -2,8 +2,8 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"kontor-api-go/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
"kontor-api-go/pkg/utils"
|
"kontor-api-fiber/pkg/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-fiber/pkg/schema"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupComicRoutes(api fiber.Router) {
|
||||||
|
comics := api.Group("/comics")
|
||||||
|
comics.Get("/comics", GetAllComics)
|
||||||
|
comics.Get("/publishers", GetAllPublishers)
|
||||||
|
comics.Get("/comicworks", GetAllComicWorks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllComics(c *fiber.Ctx) error {
|
||||||
|
var comics []schema.Comic
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(comics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllPublishers(c *fiber.Ctx) error {
|
||||||
|
var publishers []schema.Publisher
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(publishers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllComicWorks(c *fiber.Ctx) error {
|
||||||
|
var comic_works []schema.ComicWork
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = schema.GetDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(comic_works)
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"kontor-api-go/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
bun.BaseModel `bun:"table:profile"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
FirstName string `bun:"first_name"`
|
||||||
|
LastName string `bun:"last_name"`
|
||||||
|
UserName string `bun:"user_name,unique:user_name"`
|
||||||
|
Email string `bun:"email"`
|
||||||
|
Password string `bun:"password"`
|
||||||
|
Enabled bool `bun:"enabled"`
|
||||||
|
Assignments []Assignment `bun:"rel:has-many,join:id=profile_id"`
|
||||||
|
Tokens []Token `bun:"rel:has-many,join:id=profile_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Permission struct {
|
||||||
|
bun.BaseModel `bun:"table:permission"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name"`
|
||||||
|
Assignments []Assignment `bun:"rel:has-many,join:id=permission_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
bun.BaseModel `bun:"table:token"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name"`
|
||||||
|
LastUsedAt time.Time `bun:"last_used_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Enabled bool `bun:"enabled,default:true"`
|
||||||
|
ProfileID *string `bun:"profile_id"`
|
||||||
|
Profile *Profile `bun:"rel:belongs-to,join:profile_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Assignment struct {
|
||||||
|
bun.BaseModel `bun:"table:assignment"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
ProfileID *string `bun:"profile_id"`
|
||||||
|
Profile *Profile `bun:"rel:belongs-to,join:profile_id=id"`
|
||||||
|
PermissionID *string `bun:"permission_id"`
|
||||||
|
Permission *Permission `bun:"rel:belongs-to,join:permission_id=id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Publisher struct {
|
||||||
|
bun.BaseModel `bun:"table:publisher"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
|
||||||
|
ParentPublisherID *string `bun:"parent_publisher_id"`
|
||||||
|
ParentPublisher *Publisher `bun:"rel:belongs-to,join:parent_publisher_id=id"`
|
||||||
|
Imprints []Publisher `bun:"rel:has-many,join:id=parent_publisher_id"`
|
||||||
|
Comics []Comic `bun:"rel:has-many,join:id=publisher_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comic struct {
|
||||||
|
bun.BaseModel `bun:"table:comic"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Title string `bun:"title,unique:title,notnull"`
|
||||||
|
CurrentOrder bool `bun:"current_order"`
|
||||||
|
Completed bool `bun:"completed"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
|
||||||
|
PublisherID *string `bun:"publisher_id"`
|
||||||
|
Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"`
|
||||||
|
Issues []Issue `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
Volumes []Volume `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Artist struct {
|
||||||
|
bun.BaseModel `bun:"table:artist"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:title,notnull"`
|
||||||
|
WebLink string `bun:"weblink"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Issue struct {
|
||||||
|
bun.BaseModel `bun:"table:issue"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
InStock bool `bun:"in_stock"`
|
||||||
|
IsRead bool `bun:"is_read"`
|
||||||
|
IssueNumber string `bu:"issue_number"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
PublishedOn time.Time `bun:"published_on"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
StoryArcID *string `bun:"story_arc_id"`
|
||||||
|
StoryArc *StoryArc `bun:"rel:belongs-to,join:story_arc_id=id"`
|
||||||
|
VolumeID *string `bun:"volume_id"`
|
||||||
|
Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoryArc struct {
|
||||||
|
bun.BaseModel `bun:"table:story_arc"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
VolumeID *string `bun:"volume_id"`
|
||||||
|
Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TradePaperback struct {
|
||||||
|
bun.BaseModel `bun:"table:trade_paperback"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
IssueStart int `bun:"issue_start"`
|
||||||
|
IssueEnd int `bun:"issue_end"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Volume struct {
|
||||||
|
bun.BaseModel `bun:"table:volume"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkType struct {
|
||||||
|
bun.BaseModel `bun:"table:worktype"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name,notnull"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComicWork struct {
|
||||||
|
bun.BaseModel `bun:"table:comic_work"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
ArtistID *string `bun:"artist_id"`
|
||||||
|
Artist *Artist `bun:"rel:belongs-to,join:artist_id=id"`
|
||||||
|
ComicID *string `bun:"comic_id"`
|
||||||
|
Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"`
|
||||||
|
WorkTypeID *string `bun:"work_type_id"`
|
||||||
|
WorkType *WorkType `bun:"rel:belongs-to,join:work_type_id=id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/extra/bundebug"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
|
"github.com/uptrace/bun/driver/pgdriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTestDatabase() (*bun.DB, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dsn := "postgres://kontor:kontor@localhost:5432/kontor?sslmode=disable"
|
||||||
|
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))
|
||||||
|
|
||||||
|
sqldb.SetMaxOpenConns(4)
|
||||||
|
sqldb.SetMaxIdleConns(4)
|
||||||
|
|
||||||
|
DB := bun.NewDB(sqldb, pgdialect.New())
|
||||||
|
DB.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
|
||||||
|
|
||||||
|
if err = DB.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Returned Database Connection")
|
||||||
|
return DB, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
log.Println("Setup Test")
|
||||||
|
exitCode := m.Run()
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectComics(t *testing.T) {
|
||||||
|
var comics []Comic
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 168, len(comics))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectPublishers(t *testing.T) {
|
||||||
|
var publishers []Publisher
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 19, len(publishers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectWorkTypes(t *testing.T) {
|
||||||
|
var comic_works []ComicWork
|
||||||
|
var err error
|
||||||
|
var db *bun.DB
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db, err = GetTestDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 59, len(comic_works))
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActor struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name,unique:name"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_actor_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActorFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
MediaActorID *string `bun:"media_actor_id"`
|
||||||
|
MediaActor *MediaActor `bun:"rel:belongs-to,join:media_actor_id=id"`
|
||||||
|
MediaFileID *string `bun:"media_file_id"`
|
||||||
|
MediaFile *MediaFile `bun:"rel:belongs-to,join:media_file_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaArticle struct {
|
||||||
|
bun.BaseModel `bun:"table:media_article"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaVideo struct {
|
||||||
|
bun.BaseModel `bun:"table:media_article"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
func ComparePassword(hashedPassword, password string) bool {
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-fiber/pkg/schema"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateToken(user schema.Profile) (string, error) {
|
||||||
|
// Create the Claims
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"name": user.FirstName + ", " + user.LastName,
|
||||||
|
"admin": true,
|
||||||
|
"exp": time.Now().Add(time.Hour * 72).Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create token
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
// Generate encoded token and send it as response.
|
||||||
|
t, err := token.SignedString([]byte("secret"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyToken(tokenString string) (bool, error) {
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return []byte("secret"), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token.Valid, nil
|
||||||
|
}
|
||||||
+17
-87
@@ -1,10 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
//id 'application'
|
|
||||||
id 'jacoco'
|
|
||||||
id 'test-report-aggregation'
|
|
||||||
id 'jacoco-report-aggregation'
|
|
||||||
alias(libs.plugins.lombok)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -12,89 +7,27 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation libs.javalin.bundle
|
implementation libs.javalin // Pulling in Javalin
|
||||||
compileOnly 'org.projectlombok:lombok'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
|
||||||
implementation libs.postgresql
|
|
||||||
implementation libs.hibernate
|
|
||||||
annotationProcessor libs.hibernate.modelgen
|
|
||||||
testImplementation( platform( libs.junit.bom.get().toString() ))
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter"
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
implementation libs.jackson // For JSON serialization of persons
|
implementation libs.jackson // For JSON serialization of persons
|
||||||
implementation libs.slf4j // To see some Javalin logging
|
implementation libs.slf4j // To see some Javalin logging
|
||||||
|
compileOnly libs.lombok
|
||||||
|
annotationProcessor libs.lombok
|
||||||
|
|
||||||
|
implementation libs.postgresql
|
||||||
|
implementation libs.hibernate
|
||||||
|
implementation libs.hibernate.validator
|
||||||
|
implementation libs.hypersistence
|
||||||
|
implementation libs.validation.api
|
||||||
|
annotationProcessor libs.hibernate.jpamodelgen
|
||||||
|
implementation libs.el
|
||||||
|
annotationProcessor libs.openapi.annotation
|
||||||
|
implementation libs.javalin.openapi
|
||||||
|
implementation libs.javalin.swagger
|
||||||
|
implementation libs.javalin.redoc
|
||||||
|
testImplementation(platform(libs.junit.bom))
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter"
|
||||||
}
|
}
|
||||||
|
|
||||||
testing {
|
|
||||||
suites {
|
|
||||||
configureEach {
|
|
||||||
useJUnitJupiter()
|
|
||||||
dependencies {
|
|
||||||
implementation project()
|
|
||||||
implementation libs.hsqldb
|
|
||||||
implementation libs.sqlite.jdbc
|
|
||||||
runtimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test(JvmTestSuite) {
|
|
||||||
targets {
|
|
||||||
all {
|
|
||||||
testTask.configure {
|
|
||||||
reports {
|
|
||||||
junitXml {
|
|
||||||
outputPerTestCase = true // defaults to false
|
|
||||||
mergeReruns = true // defaults to false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalizedBy(jacocoTestReport)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
integrationTest(JvmTestSuite) {
|
|
||||||
targets {
|
|
||||||
all {
|
|
||||||
testTask.configure {
|
|
||||||
shouldRunAfter(test)
|
|
||||||
finalizedBy(jacocoTestReport)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('check') {
|
|
||||||
dependsOn(testing.suites.integrationTest)
|
|
||||||
dependsOn(testing.suites.test)
|
|
||||||
dependsOn tasks.named('testAggregateTestReport', TestReport)
|
|
||||||
dependsOn tasks.named('integrationTestAggregateTestReport', TestReport)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
jacocoTestReport {
|
|
||||||
dependsOn test, integrationTest
|
|
||||||
reports {
|
|
||||||
xml.required = true
|
|
||||||
csv.required = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reporting {
|
|
||||||
reports {
|
|
||||||
testAggregateTestReport(AggregateTestReport) {
|
|
||||||
}
|
|
||||||
integrationTestAggregateTestReport(AggregateTestReport) {
|
|
||||||
}
|
|
||||||
integrationTestCodeCoverageReport(JacocoCoverageReport) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// application {
|
|
||||||
// mainClass = 'de.thpeetz.kontor.api.Main'
|
|
||||||
// }
|
|
||||||
|
|
||||||
task fatJar(type: Jar) {
|
task fatJar(type: Jar) {
|
||||||
manifest {
|
manifest {
|
||||||
attributes 'Main-Class': 'de.thpeetz.kontor.Main'
|
attributes 'Main-Class': 'de.thpeetz.kontor.Main'
|
||||||
@@ -105,9 +38,6 @@ task fatJar(type: Jar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build.dependsOn fatJar
|
build.dependsOn fatJar
|
||||||
// distZip.dependsOn fatJar
|
|
||||||
// distTar.dependsOn fatJar
|
|
||||||
// startScripts.dependsOn fatJar
|
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
gradleVersion = "9.2.1"
|
gradleVersion = "9.2.1"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
description='Kontor with Spring Boot'
|
description='Kontor with Javalin'
|
||||||
version=0.2.0-SNAPSHOT
|
version=0.2.0-SNAPSHOT
|
||||||
group=de.thpeetz
|
group=de.thpeetz
|
||||||
org.gradle.warning.mode=none
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,40 @@
|
|||||||
[versions]
|
[versions]
|
||||||
gradle = "8.6"
|
gradle = "8.6"
|
||||||
hibernate = "6.4.4.Final"
|
#junit = "5.8.2"
|
||||||
hsqldb = "2.7.1"
|
junit = "5.10.3"
|
||||||
jackson = "2.16.1"
|
#slf4j = "1.7.36"
|
||||||
javalin = "6.7.0"
|
|
||||||
junit = "5.8.2"
|
|
||||||
lombok = "8.6"
|
|
||||||
postgresql = "42.6.2"
|
|
||||||
slf4j = "2.0.16"
|
slf4j = "2.0.16"
|
||||||
sonarqube = "3.3"
|
hsqldb = "2.7.1"
|
||||||
spotbugs = "6.0.7"
|
|
||||||
sqlite = "3.25.2"
|
sqlite = "3.25.2"
|
||||||
|
jackson = "2.16.1"
|
||||||
|
#javalin = "4.6.4"
|
||||||
|
javalin = "6.7.0"
|
||||||
|
lombok = "1.18.34"
|
||||||
|
postgresql = "42.7.3"
|
||||||
|
hibernate = "7.0.5.Final"
|
||||||
|
validation = "2.0.1.Final"
|
||||||
|
hypersistence = "3.14.1"
|
||||||
|
el = "4.0.1"
|
||||||
|
openapi = "6.7.0-3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
hibernate = { module = "org.hibernate.orm:hibernate-core", version.ref = "hibernate" }
|
|
||||||
hibernate-modelgen = { module = "org.hibernate.orm:hibernate-jpamodelgen", version.ref = "hibernate" }
|
|
||||||
hsqldb = { module = "org.hsqldb:hsqldb", version.ref = "hsqldb" }
|
|
||||||
jackson = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
|
||||||
javalin = { module = "io.javalin:javalin", version.ref = "javalin" }
|
|
||||||
javalin-bundle = { module = "io.javalin:javalin-bundle", version.ref = "javalin" }
|
|
||||||
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
||||||
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
|
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
|
||||||
|
slf4j = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
hsqldb = { module = "org.hsqldb:hsqldb", version.ref = "hsqldb" }
|
||||||
|
jackson = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
||||||
|
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
|
||||||
|
javalin = { module = "io.javalin:javalin", version.ref = "javalin" }
|
||||||
|
javalin-bundle = { module = "io.javalin:javalin-bundle", version.ref = "javalin" }
|
||||||
lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" }
|
lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" }
|
||||||
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }
|
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }
|
||||||
slf4j = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
hibernate = { module = "org.hibernate.orm:hibernate-core", version.ref = "hibernate" }
|
||||||
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
|
hibernate-jpamodelgen = { module = "org.hibernate.orm:hibernate-jpamodelgen", version.ref = "hibernate" }
|
||||||
|
hibernate-validator = { module = "org.hibernate:hibernate-validator", version.ref = "hibernate" }
|
||||||
[plugins]
|
validation-api = { module = "javax.validation:validation-api", version.ref = "validation" }
|
||||||
spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugs" }
|
el = { module = "org.glassfish:jakarta.el", version.ref = "el" }
|
||||||
sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" }
|
hypersistence = { module = "io.hypersistence:hypersistence-utils-hibernate-70", version.ref = "hypersistence" }
|
||||||
lombok = { id = "io.freefair.lombok", version.ref = "lombok" }
|
javalin-openapi = { module = "io.javalin.community.openapi:javalin-openapi-plugin", version.ref = "openapi" }
|
||||||
|
javalin-swagger = { module = "io.javalin.community.openapi:javalin-swagger-plugin", version.ref = "openapi" }
|
||||||
|
javalin-redoc = { module = "io.javalin.community.openapi:javalin-redoc-plugin", version.ref = "openapi" }
|
||||||
|
openapi-annotation = { module = "io.javalin.community.openapi:openapi-annotation-processor", version.ref = "openapi" }
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package de.thpeetz.kontor;
|
|
||||||
|
|
||||||
import io.javalin.Javalin;
|
|
||||||
|
|
||||||
import static io.javalin.apibuilder.ApiBuilder.get;
|
|
||||||
import static io.javalin.apibuilder.ApiBuilder.path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import de.thpeetz.kontor.web.PersonHandler;
|
|
||||||
|
|
||||||
public class JavalinApp {
|
|
||||||
|
|
||||||
public static Javalin create() {
|
|
||||||
return Javalin.create((var config) -> config.router.apiBuilder(() -> {
|
|
||||||
|
|
||||||
path("/health", () -> get(ctx -> {
|
|
||||||
HashMap<String, String> status = new HashMap<>();
|
|
||||||
status.put("status", "ok");
|
|
||||||
ctx.json(status);
|
|
||||||
}));
|
|
||||||
path("/persons", () -> {
|
|
||||||
get(PersonHandler.listAll);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,89 @@
|
|||||||
package de.thpeetz.kontor;
|
package de.thpeetz.kontor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Main {
|
import de.thpeetz.kontor.infrastructure.AppHibernateSessionFactory;
|
||||||
|
import de.thpeetz.kontor.models.comics.Comic;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaFile;
|
||||||
|
import io.javalin.Javalin;
|
||||||
|
import io.javalin.config.Key;
|
||||||
|
//import jakarta.persistence.EntityManager;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
||||||
private static short port = 8400;
|
private static short port = 8400;
|
||||||
|
// private static Key<EntityManagerFactory> emf = new
|
||||||
|
// Key<EntityManagerFactory>("entityManagerFactory");
|
||||||
|
private static Key<SessionFactory> sf = new Key<>("SessionFactory");
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
JavalinApp.create().start(port);
|
Javalin app = Javalin.create(config -> {
|
||||||
logger.info("API's alive for real :-)))");
|
config.requestLogger.http((ctx, ms) -> {
|
||||||
|
var path = ctx.path();
|
||||||
|
if (!path.equals("/health")) {
|
||||||
|
logger.info(ctx.path());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
config.appData(sf, AppHibernateSessionFactory.getSessionFactory());
|
||||||
|
});
|
||||||
|
app.get("/", ctx -> ctx.json("Ok"));
|
||||||
|
app.get("/health", ctx -> {
|
||||||
|
HashMap<String, String> status = new HashMap<>();
|
||||||
|
status.put("status", "ok");
|
||||||
|
ctx.json(status);
|
||||||
|
});
|
||||||
|
app.get("/api/v1/comics", ctx -> {
|
||||||
|
var result = new LinkedList<Comic>();
|
||||||
|
ctx.appData(sf).inStatelessTransaction(session -> {
|
||||||
|
session.createSelectionQuery("from Comic", Comic.class).getResultList().forEach(comic -> {
|
||||||
|
result.add(comic);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// var em = ctx.appData(emf).createEntityManager();
|
||||||
|
// logger.info(em.toString());
|
||||||
|
// EntityManager entityManager = ctx.attribute("entityManager");
|
||||||
|
// entityManager.createQuery("select * from Comic",
|
||||||
|
// Comic.class).getResultList().forEach(comic -> {
|
||||||
|
// result.add(comic);
|
||||||
|
// });
|
||||||
|
ctx.json(result);
|
||||||
|
});
|
||||||
|
app.get("/api/v1/media/files", ctx -> {
|
||||||
|
var result = new LinkedList<>();
|
||||||
|
// ctx.appData(sf).inStatelessTransaction(session -> {
|
||||||
|
// var files = session.createSelectionQuery("from MediaFile",
|
||||||
|
// MediaFile.class).getResultList();
|
||||||
|
// result.addAll(files);
|
||||||
|
// });
|
||||||
|
var list = ctx.appData(sf).openStatelessSession().createSelectionQuery("from MediaFile", MediaFile.class)
|
||||||
|
.getResultList();
|
||||||
|
result.addAll(list);
|
||||||
|
ctx.json(result);
|
||||||
|
});
|
||||||
|
// app.before(ctx -> {
|
||||||
|
// ctx.attribute("configuration", AppHibernateConfig.configuration());
|
||||||
|
// ctx.attribute("sessionFactory",
|
||||||
|
// AppHibernateSessionFactory.getSessionFactory());
|
||||||
|
// });
|
||||||
|
app.after(ctx -> {
|
||||||
|
// SessionFactory sessionFactory = ctx.attribute("sessionFactory");
|
||||||
|
// if (sessionFactory != null) {
|
||||||
|
// logger.info("close SessionFactory");
|
||||||
|
// sessionFactory.close();
|
||||||
|
// }
|
||||||
|
// EntityManager entityManager = ctx.attribute("entityManager");
|
||||||
|
// if (entityManager != null) {
|
||||||
|
// logger.info("close EntityManager");
|
||||||
|
// entityManager.close();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
app.start(port);
|
||||||
|
|
||||||
|
logger.info("API's alive for real :-)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.thpeetz.kontor.infrastructure;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
|
|
||||||
|
public class AppHibernate {
|
||||||
|
|
||||||
|
public static void inTransaction(Consumer<StatelessSession> consumer) {
|
||||||
|
AppHibernateSessionFactory.getSessionFactory().inStatelessTransaction(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> R fromTransaction(Function<StatelessSession, R> function) {
|
||||||
|
return AppHibernateSessionFactory.getSessionFactory().fromStatelessTransaction(function);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package de.thpeetz.kontor.infrastructure;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.tool.schema.Action;
|
||||||
|
|
||||||
|
import de.thpeetz.kontor.models.comics.Artist;
|
||||||
|
import de.thpeetz.kontor.models.comics.Comic;
|
||||||
|
import de.thpeetz.kontor.models.comics.ComicWork;
|
||||||
|
import de.thpeetz.kontor.models.comics.Issue;
|
||||||
|
import de.thpeetz.kontor.models.comics.IssueWork;
|
||||||
|
import de.thpeetz.kontor.models.comics.Publisher;
|
||||||
|
import de.thpeetz.kontor.models.comics.StoryArc;
|
||||||
|
import de.thpeetz.kontor.models.comics.TradePaperback;
|
||||||
|
import de.thpeetz.kontor.models.comics.Volume;
|
||||||
|
import de.thpeetz.kontor.models.comics.Worktype;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaActor;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaActorFile;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaArticle;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaFile;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaVideo;
|
||||||
|
|
||||||
|
public class AppHibernateConfig {
|
||||||
|
public static Configuration configuration() {
|
||||||
|
var configuration = new Configuration();
|
||||||
|
var settings = new Properties();
|
||||||
|
settings.put(AvailableSettings.JAKARTA_JDBC_DRIVER, "org.postgresql.Driver");
|
||||||
|
settings.put(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://postgres:5432/kontor");
|
||||||
|
settings.put(AvailableSettings.JAKARTA_JDBC_USER, "kontor");
|
||||||
|
settings.put(AvailableSettings.JAKARTA_JDBC_PASSWORD, "kontor");
|
||||||
|
settings.put(AvailableSettings.HIGHLIGHT_SQL, true);
|
||||||
|
settings.put(AvailableSettings.HBM2DDL_AUTO, Action.ACTION_UPDATE);
|
||||||
|
|
||||||
|
configuration.setProperties(settings);
|
||||||
|
configuration.addAnnotatedClass(MediaFile.class);
|
||||||
|
configuration.addAnnotatedClass(MediaActorFile.class);
|
||||||
|
configuration.addAnnotatedClass(MediaActor.class);
|
||||||
|
configuration.addAnnotatedClass(MediaArticle.class);
|
||||||
|
configuration.addAnnotatedClass(MediaVideo.class);
|
||||||
|
configuration.addAnnotatedClass(Comic.class);
|
||||||
|
configuration.addAnnotatedClass(Publisher.class);
|
||||||
|
configuration.addAnnotatedClass(Artist.class);
|
||||||
|
configuration.addAnnotatedClass(ComicWork.class);
|
||||||
|
configuration.addAnnotatedClass(Issue.class);
|
||||||
|
configuration.addAnnotatedClass(IssueWork.class);
|
||||||
|
configuration.addAnnotatedClass(StoryArc.class);
|
||||||
|
configuration.addAnnotatedClass(TradePaperback.class);
|
||||||
|
configuration.addAnnotatedClass(Volume.class);
|
||||||
|
configuration.addAnnotatedClass(Worktype.class);
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package de.thpeetz.kontor.infrastructure;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class AppHibernateSessionFactory {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AppHibernateSessionFactory.class);
|
||||||
|
|
||||||
|
private static SessionFactory sessionFactory;
|
||||||
|
|
||||||
|
public static SessionFactory getSessionFactory() {
|
||||||
|
if (Objects.isNull(sessionFactory)) {
|
||||||
|
var configuration = AppHibernateConfig.configuration();
|
||||||
|
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
|
||||||
|
.applySettings(configuration.getProperties())
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
sessionFactory = configuration.buildSessionFactory(registry);
|
||||||
|
logger.info("SessionFactory created");
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
logger.error("Failed to create session factory", ex);
|
||||||
|
StandardServiceRegistryBuilder.destroy(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sessionFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package de.thpeetz.kontor.models;
|
|
||||||
|
|
||||||
public class Person {
|
|
||||||
private String name;
|
|
||||||
private int age;
|
|
||||||
|
|
||||||
public Person(String name, int age) {
|
|
||||||
this.name = name;
|
|
||||||
this.age = age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAge() {
|
|
||||||
return age;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAge(int age) {
|
|
||||||
this.age = age;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Artist {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String weblink;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "artist", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<ComicWork> comicWorks = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "artist", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<IssueWork> issueWorks = new LinkedList<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Comic {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "publisher_id", nullable = false)
|
||||||
|
@JsonIgnoreProperties({ "comics" })
|
||||||
|
private Publisher publisher;
|
||||||
|
|
||||||
|
@Column(name = "current_order")
|
||||||
|
private Boolean currentOrder = false;
|
||||||
|
|
||||||
|
private Boolean completed = false;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String weblink;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "comic", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<ComicWork> comicWorks;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "comic", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
private List<Issue> issues = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "comic", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
private List<StoryArc> storyArcs = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "comic", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
private List<TradePaperback> tradePaperbacks = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "comic", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
private List<Volume> volumes = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuffer sb = new StringBuffer("Comic{");
|
||||||
|
sb.append("title='").append(title).append('\'');
|
||||||
|
sb.append(", publisher=").append(publisher);
|
||||||
|
sb.append(", currentOrder=").append(currentOrder);
|
||||||
|
sb.append(", completed=").append(completed);
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublisherName() {
|
||||||
|
return this.publisher.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.StatelessSession;
|
||||||
|
import org.hibernate.annotations.processing.Find;
|
||||||
|
|
||||||
|
public interface ComicQueries {
|
||||||
|
|
||||||
|
@Find
|
||||||
|
List<Comic> getAllComics(StatelessSession session);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "comic_work")
|
||||||
|
public class ComicWork {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "comic_id", nullable = false)
|
||||||
|
private Comic comic;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "artist_id", nullable = false)
|
||||||
|
private Artist artist;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "work_type_id", nullable = false)
|
||||||
|
private Worktype workType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("ComicWork{");
|
||||||
|
sb.append("comic=").append(comic);
|
||||||
|
sb.append(", artist=").append(artist);
|
||||||
|
sb.append(", workType=").append(workType);
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.time.YearMonth;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import io.hypersistence.utils.hibernate.type.basic.YearMonthDateType;
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Issue {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "comic_id", nullable = false)
|
||||||
|
@JsonIgnoreProperties({ "issues" })
|
||||||
|
private Comic comic;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "volume_id", nullable = true)
|
||||||
|
@JsonIgnoreProperties({ "issues" })
|
||||||
|
private Volume volume;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "story_arc_id", nullable = true)
|
||||||
|
@JsonIgnoreProperties({ "issues" })
|
||||||
|
private StoryArc storyArc;
|
||||||
|
|
||||||
|
@Column(name = "issue_number", nullable = false)
|
||||||
|
private String issueNumber;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Type(YearMonthDateType.class)
|
||||||
|
@Column(name = "published_on", columnDefinition = "date", nullable = true)
|
||||||
|
private YearMonth publishedOn;
|
||||||
|
|
||||||
|
@Column(name = "is_read")
|
||||||
|
private Boolean isRead;
|
||||||
|
|
||||||
|
@Column(name = "in_stock")
|
||||||
|
private Boolean inStock;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "issue", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<IssueWork> issueWorks;
|
||||||
|
|
||||||
|
public String getComicTitle() {
|
||||||
|
return comic.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVolumeName() {
|
||||||
|
if (volume != null) {
|
||||||
|
return volume.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullTitle() {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append(this.getComic().getTitle());
|
||||||
|
stringBuilder.append(" #");
|
||||||
|
stringBuilder.append(this.getIssueNumber());
|
||||||
|
if (this.title != null && !this.title.isEmpty()) {
|
||||||
|
stringBuilder.append(": ");
|
||||||
|
stringBuilder.append(this.title);
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append("Issue{");
|
||||||
|
result.append("comic=");
|
||||||
|
result.append(comic);
|
||||||
|
result.append(", volume=");
|
||||||
|
result.append(volume);
|
||||||
|
result.append(", storyArc=");
|
||||||
|
result.append(storyArc);
|
||||||
|
result.append(", issueNumber='");
|
||||||
|
result.append(issueNumber);
|
||||||
|
result.append(", title='");
|
||||||
|
result.append(title);
|
||||||
|
result.append(", publishedOn=");
|
||||||
|
result.append(publishedOn);
|
||||||
|
result.append(", isRead=");
|
||||||
|
result.append(isRead);
|
||||||
|
result.append(", inStock=");
|
||||||
|
result.append(inStock);
|
||||||
|
result.append("}");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "issue_work")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class IssueWork {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "issue_id", nullable = false)
|
||||||
|
private Issue issue;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "artist_id", nullable = false)
|
||||||
|
private Artist artist;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "work_type_id", nullable = false)
|
||||||
|
private Worktype workType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("IssueWork{");
|
||||||
|
sb.append("issue=").append(issue);
|
||||||
|
sb.append(", artist=").append(artist);
|
||||||
|
sb.append(", workType=").append(workType);
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Publisher {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String weblink;
|
||||||
|
|
||||||
|
@JsonBackReference
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "parent_publisher_id", nullable = true)
|
||||||
|
@JsonIgnoreProperties({ "comics" })
|
||||||
|
private Publisher parentCompany;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parentCompany", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
private List<Publisher> imprints = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "publisher", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
private List<Comic> comics = new LinkedList<>();
|
||||||
|
|
||||||
|
public String getParentCompanyName() {
|
||||||
|
if (parentCompany != null) {
|
||||||
|
return parentCompany.name;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuffer sb = new StringBuffer("Publisher{");
|
||||||
|
sb.append("name='").append(name).append('\'');
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "story_arc")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class StoryArc {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "comic_id", nullable = false)
|
||||||
|
@JsonIgnoreProperties({ "storyArcs" })
|
||||||
|
private Comic comic;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "volume_id", nullable = true)
|
||||||
|
@JsonIgnoreProperties({ "storyArcs" })
|
||||||
|
private Volume volume;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "storyArc", cascade = CascadeType.REMOVE, orphanRemoval = true)
|
||||||
|
private List<Issue> issues = new LinkedList<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "trade_paperback")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class TradePaperback {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "comic_id", nullable = false)
|
||||||
|
private Comic comic;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private Integer issueStart;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private Integer issueEnd;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Volume {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "comic_id", nullable = false)
|
||||||
|
@JsonIgnoreProperties({ "volumes" })
|
||||||
|
private Comic comic;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "volume", cascade = CascadeType.REMOVE, orphanRemoval = true)
|
||||||
|
private List<StoryArc> storyArcs = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "volume", cascade = CascadeType.REMOVE, orphanRemoval = true)
|
||||||
|
private List<Issue> issues = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder("Volume{");
|
||||||
|
result.append("name=");
|
||||||
|
result.append(name);
|
||||||
|
result.append("comic=Comic{title=");
|
||||||
|
result.append(comic.getTitle());
|
||||||
|
result.append("}}");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package de.thpeetz.kontor.models.comics;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table
|
||||||
|
public class Worktype {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(unique = true, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "workType", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<ComicWork> comicWorks = new LinkedList<>();
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "workType", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<IssueWork> issueWorks = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuffer sb = new StringBuffer("Worktype{");
|
||||||
|
sb.append("name='").append(name).append('\'');
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Entity
|
||||||
|
@Table(name = "media_actor")
|
||||||
|
public class MediaActor {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
@Column(unique = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "media_actor", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<MediaActorFile> mediaActorFiles = new LinkedList<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Index;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.UniqueConstraint;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Entity
|
||||||
|
@Table(name = "media_actor_file", indexes = {
|
||||||
|
@Index(columnList = "media_file_id, media_actor_id") }, uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||||
|
"media_file_id", "media_actor_id" }))
|
||||||
|
public class MediaActorFile {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "media_file_id")
|
||||||
|
@NotNull
|
||||||
|
private MediaFile media_file;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "media_actor_id")
|
||||||
|
@NotNull
|
||||||
|
private MediaActor media_actor;
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return media_file.getTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Index;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.UniqueConstraint;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "media_article", indexes = @Index(columnList = "url"), uniqueConstraints = @UniqueConstraint(columnNames = {
|
||||||
|
"url" }))
|
||||||
|
public class MediaArticle {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date", nullable = true)
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private boolean review;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String title;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Entity
|
||||||
|
@Table(name = "media_file")
|
||||||
|
public class MediaFile {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date")
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private boolean review;
|
||||||
|
|
||||||
|
@Column(name = "should_download")
|
||||||
|
private boolean shouldDownload;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column(name = "cloud_link", nullable = true)
|
||||||
|
private String cloudLink;
|
||||||
|
|
||||||
|
@Column(name = "file_name", nullable = true)
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
@OneToMany(fetch = FetchType.EAGER, mappedBy = "media_file", cascade = CascadeType.REFRESH, orphanRemoval = true)
|
||||||
|
List<MediaActorFile> mediaActorFiles = new LinkedList<>();
|
||||||
|
|
||||||
|
public static MediaFile newMediaFile(String url) {
|
||||||
|
var mediaFile = new MediaFile();
|
||||||
|
mediaFile.setUrl(url);
|
||||||
|
return mediaFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.processing.Find;
|
||||||
|
|
||||||
|
public interface MediaFileQueries {
|
||||||
|
|
||||||
|
@Find
|
||||||
|
List<MediaFile> getAllMediaFiles();
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package de.thpeetz.kontor.models.media;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.UniqueConstraint;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Entity
|
||||||
|
@Table(name = "media_video", uniqueConstraints = { @UniqueConstraint(columnNames = { "url" }) })
|
||||||
|
public class MediaVideo {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private int version;
|
||||||
|
|
||||||
|
@Column(name = "created_date")
|
||||||
|
private Date createdDate;
|
||||||
|
|
||||||
|
@Column(name = "last_modified_date", nullable = true)
|
||||||
|
private Date lastModifiedDate;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private boolean review;
|
||||||
|
|
||||||
|
@Column(name = "should_download")
|
||||||
|
private boolean shouldDownload;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column(name = "cloud_link", nullable = true)
|
||||||
|
private String cloudLink;
|
||||||
|
|
||||||
|
@Column(name = "file_name", nullable = true)
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Column(nullable = true)
|
||||||
|
private String path;
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package de.thpeetz.kontor.services.api;
|
|
||||||
|
|
||||||
import de.thpeetz.kontor.models.Person;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface PersonReader {
|
|
||||||
List<Person> getAll();
|
|
||||||
}
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
package de.thpeetz.kontor.services.inmemory;
|
|
||||||
|
|
||||||
import de.thpeetz.kontor.models.Person;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.thpeetz.kontor.services.api.PersonReader;
|
|
||||||
|
|
||||||
public class InMemoryPersonReader implements PersonReader {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Person> getAll() {
|
|
||||||
return List.of(
|
|
||||||
new Person("Vincent Vega", 73),
|
|
||||||
new Person("Jules Winnfield", 12));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.thpeetz.kontor.web;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.thpeetz.kontor.infrastructure.AppHibernate;
|
||||||
|
import de.thpeetz.kontor.models.comics.Comic;
|
||||||
|
import de.thpeetz.kontor.models.comics.ComicQueries_;
|
||||||
|
import de.thpeetz.kontor.web.model.ResultComic;
|
||||||
|
import io.javalin.http.Handler;
|
||||||
|
|
||||||
|
public class ComicHandler {
|
||||||
|
public static Handler listAll = (context) -> {
|
||||||
|
List<Comic> result = AppHibernate.fromTransaction(ComicQueries_::getAllComics);
|
||||||
|
context.json(new ResultComic(result));
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package de.thpeetz.kontor.web;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.thpeetz.kontor.infrastructure.AppHibernate;
|
||||||
|
import io.javalin.http.Handler;
|
||||||
|
import io.javalin.http.HttpStatus;
|
||||||
|
import de.thpeetz.kontor.models.media.MediaFile;
|
||||||
|
import de.thpeetz.kontor.web.model.NewMediaFile;
|
||||||
|
import de.thpeetz.kontor.web.model.ResultMediaFile;
|
||||||
|
|
||||||
|
public class MediaFileHandler {
|
||||||
|
|
||||||
|
public static Handler listAll = (context) -> {
|
||||||
|
List<MediaFile> result = new LinkedList<>();
|
||||||
|
context.json(new ResultMediaFile(result));
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Handler save = (context) -> {
|
||||||
|
var newMediaFile = context.bodyAsClass(NewMediaFile.class);
|
||||||
|
var result = AppHibernate.fromTransaction(session -> {
|
||||||
|
var insertedId = session.insert(MediaFile.newMediaFile(newMediaFile.url()));
|
||||||
|
return session.get(MediaFile.class, insertedId);
|
||||||
|
});
|
||||||
|
context.json(result).status(HttpStatus.CREATED);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package de.thpeetz.kontor.web;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
import de.thpeetz.kontor.services.inmemory.InMemoryPersonReader;
|
|
||||||
import io.javalin.http.Handler;
|
|
||||||
|
|
||||||
public class PersonHandler {
|
|
||||||
|
|
||||||
public static Handler listAll = (context) -> {
|
|
||||||
var personReader = new InMemoryPersonReader();
|
|
||||||
var objMapper = new ObjectMapper();
|
|
||||||
var result = objMapper.valueToTree(personReader.getAll());
|
|
||||||
context.json(result);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package de.thpeetz.kontor.web.model;
|
||||||
|
|
||||||
|
public record NewMediaFile(String url) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.thpeetz.kontor.web.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.thpeetz.kontor.models.comics.Comic;
|
||||||
|
|
||||||
|
public record ResultComic(List<Comic> comics) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.thpeetz.kontor.web.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.thpeetz.kontor.models.media.MediaFile;
|
||||||
|
|
||||||
|
public record ResultMediaFile(List<MediaFile> mediaFiles) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<configuration>
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<root level="info">
|
|
||||||
<appender-ref ref="STDOUT"/>
|
|
||||||
</root>
|
|
||||||
|
|
||||||
<logger name="org.hibernate.SQL" level="debug" additivity="false">
|
|
||||||
<appender-ref ref="STDOUT"/>
|
|
||||||
</logger>
|
|
||||||
|
|
||||||
<!-- <logger name="org.hibernate.orm.jdbc.bind" level="trace" additivity="false">-->
|
|
||||||
<!-- <appender-ref ref="STDOUT" />-->
|
|
||||||
<!-- </logger>-->
|
|
||||||
|
|
||||||
<!-- <logger name="org.hibernate.orm.jdbc.extract" level="trace" additivity="false">-->
|
|
||||||
<!-- <appender-ref ref="STDOUT" />-->
|
|
||||||
<!-- </logger>-->
|
|
||||||
|
|
||||||
</configuration>
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
*
|
||||||
|
!build.gradle.kts
|
||||||
|
!gradle.properties
|
||||||
|
!settings.gradle.kts
|
||||||
|
!src/*
|
||||||
|
!build/*-runner
|
||||||
|
!build/*-runner.jar
|
||||||
|
!build/lib/*
|
||||||
|
!build/quarkus-app/*
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Gradle
|
||||||
|
.gradle/
|
||||||
|
.kotlin/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
*.ipr
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# NetBeans
|
||||||
|
nb-configuration.xml
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# patch
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
|
||||||
|
# Local environment
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Plugin directory
|
||||||
|
/.quarkus/cli/plugins/
|
||||||
|
# TLS Certificates
|
||||||
|
.certs/
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# ----------------------------------------------------------------------- #
|
||||||
|
FROM gradle:9.2-jdk AS builder
|
||||||
|
WORKDIR /
|
||||||
|
COPY ./src/ ./src/
|
||||||
|
COPY ./build.gradle.kts ./
|
||||||
|
COPY ./gradle.properties ./
|
||||||
|
COPY ./settings.gradle.kts ./
|
||||||
|
RUN gradle build --no-daemon
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------- #
|
||||||
|
#FROM alpine/java:21-jdk AS run
|
||||||
|
|
||||||
|
#RUN adduser --system appuser
|
||||||
|
|
||||||
|
#COPY --from=builder --chown=appuser:appuser /build/libs/kontor-quarkus-0.2.0-SNAPSHOT.jar app.jar
|
||||||
|
|
||||||
|
#EXPOSE 8800
|
||||||
|
#USER appuser
|
||||||
|
#CMD ["java", "-jar", "-Dquarkus.http.host=0.0.0.0", "app.jar"]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------- #
|
||||||
|
FROM registry.access.redhat.com/ubi9/openjdk-21:1.23
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||||
|
COPY --from=builder --chown=185 build/quarkus-app/lib/ /deployments/lib/
|
||||||
|
COPY --from=builder --chown=185 build/quarkus-app/*.jar /deployments/
|
||||||
|
COPY --from=builder --chown=185 build/quarkus-app/app/ /deployments/app/
|
||||||
|
COPY --from=builder --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
|
||||||
|
|
||||||
|
EXPOSE 8800
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# kontor-quarkus
|
||||||
|
|
||||||
|
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.jar.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.native.enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
./gradlew build -Dquarkus.native.enabled=true -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
|
||||||
|
|
||||||
|
- REST Jackson ([guide](https://quarkus.io/guides/rest#json-serialisation)): Jackson serialization support for Quarkus REST. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it
|
||||||
|
- Kotlin ([guide](https://quarkus.io/guides/kotlin)): Write your services in Kotlin
|
||||||
|
|
||||||
|
## Provided Code
|
||||||
|
|
||||||
|
### REST
|
||||||
|
|
||||||
|
Easily start your REST Web Services
|
||||||
|
|
||||||
|
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "2.2.21"
|
||||||
|
kotlin("plugin.allopen") version "2.2.21"
|
||||||
|
id("io.quarkus")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
val quarkusPlatformGroupId: String by project
|
||||||
|
val quarkusPlatformArtifactId: String by project
|
||||||
|
val quarkusPlatformVersion: String by project
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
||||||
|
implementation("io.quarkus:quarkus-container-image-docker")
|
||||||
|
implementation("io.quarkus:quarkus-rest-jackson")
|
||||||
|
implementation("io.quarkus:quarkus-kotlin")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
implementation("io.quarkus:quarkus-arc")
|
||||||
|
implementation("io.quarkus:quarkus-rest")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-openapi")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-health")
|
||||||
|
implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin")
|
||||||
|
implementation("io.quarkus:quarkus-jdbc-h2")
|
||||||
|
implementation("io.quarkus:quarkus-jdbc-postgresql")
|
||||||
|
testImplementation("io.quarkus:quarkus-junit5")
|
||||||
|
testImplementation("io.rest-assured:rest-assured")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "de.thpeetz"
|
||||||
|
version = "0.2.0-SNAPSHOT"
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test> {
|
||||||
|
systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
|
||||||
|
jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
|
||||||
|
}
|
||||||
|
allOpen {
|
||||||
|
annotation("jakarta.ws.rs.Path")
|
||||||
|
annotation("jakarta.enterprise.context.ApplicationScoped")
|
||||||
|
annotation("jakarta.persistence.Entity")
|
||||||
|
annotation("io.quarkus.test.junit.QuarkusTest")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21
|
||||||
|
javaParameters = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Gradle properties
|
||||||
|
|
||||||
|
quarkusPluginId=io.quarkus
|
||||||
|
quarkusPluginVersion=3.30.6
|
||||||
|
quarkusPlatformGroupId=io.quarkus.platform
|
||||||
|
quarkusPlatformArtifactId=quarkus-bom
|
||||||
|
quarkusPlatformVersion=3.30.6
|
||||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
# https://gradle.org/release-checksums/
|
||||||
|
distributionSha256Sum=f86344275d1b194688dd330abf9f6f2344cd02872ffee035f2d1ea2fd60cf7f3
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-all.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
+245
@@ -0,0 +1,245 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# 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 ;; #(
|
||||||
|
MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# 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"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
Vendored
+92
@@ -0,0 +1,92 @@
|
|||||||
|
@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=.
|
||||||
|
@rem This is normally unused
|
||||||
|
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% equ 0 goto execute
|
||||||
|
|
||||||
|
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 execute
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
: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 %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 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!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
pluginManagement {
|
||||||
|
val quarkusPluginVersion: String by settings
|
||||||
|
val quarkusPluginId: String by settings
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
plugins {
|
||||||
|
id(quarkusPluginId) version quarkusPluginVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name="kontor-quarkus"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
####
|
||||||
|
# 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 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# 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") - Be aware that this will override
|
||||||
|
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
|
||||||
|
# - 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/ubi9/openjdk-21:1.23
|
||||||
|
|
||||||
|
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 JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
####
|
||||||
|
# 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.jar.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 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# 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") - Be aware that this will override
|
||||||
|
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
|
||||||
|
# - 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/ubi9/openjdk-21:1.23
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
COPY build/lib/* /deployments/lib/
|
||||||
|
COPY build/*-runner.jar /deployments/quarkus-run.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
####
|
||||||
|
# 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.native.enabled=true
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9.
|
||||||
|
# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`.
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
####
|
||||||
|
# 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.native.enabled=true
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9.
|
||||||
|
# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`.
|
||||||
|
###
|
||||||
|
FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package de.thpeetz
|
||||||
|
|
||||||
|
import jakarta.ws.rs.GET
|
||||||
|
import jakarta.ws.rs.Path
|
||||||
|
import jakarta.ws.rs.Produces
|
||||||
|
import jakarta.ws.rs.core.MediaType
|
||||||
|
|
||||||
|
@Path("/hello")
|
||||||
|
class GreetingResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
fun hello() = "Hello from Quarkus REST"
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package de.thpeetz.kontor
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.Application
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.info.Info
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag
|
||||||
|
|
||||||
|
@OpenAPIDefinition(
|
||||||
|
info = Info(
|
||||||
|
title = "Kontor",
|
||||||
|
version = "0.2.0"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
class KontorApplication: Application()
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package de.thpeetz.kontor.comics.domain
|
||||||
|
|
||||||
|
import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntityBase
|
||||||
|
import jakarta.persistence.Entity
|
||||||
|
import jakarta.persistence.Id
|
||||||
|
import jakarta.persistence.Table
|
||||||
|
import org.eclipse.microprofile.openapi.annotations.media.Schema
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "comic")
|
||||||
|
class Comic: PanacheEntityBase {
|
||||||
|
@Id
|
||||||
|
lateinit var id: String
|
||||||
|
lateinit var createdDate: LocalDateTime
|
||||||
|
lateinit var lastModifiedDate: LocalDateTime
|
||||||
|
lateinit var version: Integer
|
||||||
|
lateinit var title: String
|
||||||
|
lateinit var webLink: String
|
||||||
|
var completed: Boolean = false
|
||||||
|
var currentOrder: Boolean = false
|
||||||
|
lateinit var publisherId: String
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user