diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Dockerfile | 6 | ||||
-rw-r--r-- | Readme.md | 1 | ||||
-rw-r--r-- | docker-compose.yml | 49 | ||||
-rw-r--r-- | index.php | 105 | ||||
-rw-r--r-- | mtg-pdf.php | 128 | ||||
-rw-r--r-- | web.conf.template | 17 |
7 files changed, 207 insertions, 100 deletions
@@ -2,3 +2,4 @@ /output/ AllPrintings.sqlite +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bbfce84 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM php:fpm-alpine + +COPY index.php . +COPY mtg-pdf.php . +COPY vendor vendor +COPY AllPrintings.sqlite . diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..6508228 --- /dev/null +++ b/Readme.md @@ -0,0 +1 @@ +TODO: split into generic package and server service implementation diff --git a/docker-compose.yml b/docker-compose.yml index f2b177c..6c6add5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,14 +3,55 @@ version: "3" services: gotenberg: image: "gotenberg/gotenberg:7" + networks: + - "internal" - php: - image: "php" - links: + cli: + image: "php:cli-alpine" + depends_on: - "gotenberg" environment: - "DECKLIST=${DECKLIST}" volumes: - "./:/usr/src/app" + networks: + - "internal" working_dir: "/usr/src/app" - command: ["php", "./index.php"] + command: ["php", "./mtg-pdf.php"] + + php: + # image: php:fpm-alpine + build: . + depends_on: + - "gotenberg" + # volumes: + # - "./:/var/www/html" + networks: + - "internal" + + web: + image: nginx:alpine + depends_on: + - "php" + environment: + - "DOMAIN=${DOMAIN}" + volumes: + - "./:/var/www/html" + - "./web.conf.template:/etc/nginx/templates/web.conf.template" + networks: + - "internal" + - "traefik" + labels: + - "traefik.enable=true" + - "traefik.docker.network=${TRAEFIK_NETWORK}" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${DOMAIN}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls=true" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt" + + +networks: + internal: + traefik: + name: "${TRAEFIK_NETWORK}" + external: true @@ -1,101 +1,14 @@ <?php -use Gotenberg\Gotenberg; -use Gotenberg\Stream; - -require_once __DIR__ . '/vendor/autoload.php'; - -$decklist = $_ENV['DECKLIST']; - -/* - * (\d+) ([\w\s]+) \(([\w\d]+)\) (\d+) - * 2 Hall of Storm Giants (AFR) 257 - */ - -preg_match_all("/(\d+) ([\w\s]+) \(([\w\d]+)\) (\d+)/", $decklist, $matches); -$cards = []; -for ($idx = 0; $idx < count($matches[0]); $idx++) { - $cards[] = [ - 'quantity' => $matches[1][$idx], - 'name' => $matches[2][$idx], - 'set' => $matches[3][$idx], - 'number' => $matches[4][$idx], - ]; -} - -/* - * SELECT scryfallId from cardIdentifiers - * JOIN cards ON cardIdentifiers.uuid=cards.uuid - * WHERE cards.setCode='$SETCODE' AND cards.number='$NUMBER'; - */ - -$db = new \PDO('sqlite:AllPrintings.sqlite'); - -$images = []; -foreach ($cards as $card) { - $query = 'SELECT scryfallId from cardIdentifiers JOIN cards on cardIdentifiers.uuid=cards.uuid WHERE cards.setCode=:setCode AND cards.number=:number'; - $statement = $db->prepare($query); - $statement->execute(['setCode' => $card['set'], 'number' => $card['number']]); - - $id = $statement->fetchColumn()?: ''; - - if (empty($id)) { - continue; - } - - // TODO: check double sided - - $images[] = [ - 'quantity' => $card['quantity'], - 'src' => "https://cards.scryfall.io/png/front/" . substr($id, 0, 1) . "/" . substr($id, 1, 1) . "/$id.png", - ]; +if (isset($_POST['decklist'])) { + $_ENV['DECKLIST'] = $_POST['decklist']; + include __DIR__ . '/mtg-pdf.php'; + exit; } -// https://cards.scryfall.io/png/front/substr($scryfallId, 0, 1)/substr($scryfallId, 1, 1)/$scryfallId.png - -/* - curl \ ---request POST 'http://localhost:3000/forms/chromium/convert/html' \ ---form 'files=@"./index.html"' \ --o my.pdf -F marginTop=0.0 -F marginBottom=0.0 -F marginRight=0.0 -F marginLeft=0.0 - */ - -$template = <<<HTML -<html> - <head> - <style>* { margin: 0; padding: 0; } html { } #bg { background-color: #fff; display: flex; flex-wrap: wrap; } img {display:block;}</style> - <style> - @media print { - @page { - } - } - html, body { - padding: 0; - margin: 0; - } - img { - width: 2.49in; - height: 3.48in; - background-color: #16130e; - background-color: #000; - } - </style> - </head> - <body> - <div id="bg">{{imgs}}</div> - </body> -</html> -HTML; - -$imgs = array_map(function ($image) { - return str_repeat("<img src=\"$image[src]\">", $image['quantity']); -}, $images); - -$html = str_replace('{{imgs}}', implode('', $imgs), $template); -$request = Gotenberg::chromium('gotenberg:3000') - ->paperSize(8.27, 11.7) # A4 - ->margins(0, 0, 0, 0) - ->outputFilename(date("Ymd_His")) - ->html(Stream::string('index.html', $html)); +?> -Gotenberg::save($request, __DIR__ . '/output'); +<form action="" method="POST"> + <textarea name="decklist"></textarea> + <button>Submit</button> +</form> diff --git a/mtg-pdf.php b/mtg-pdf.php new file mode 100644 index 0000000..9b705b0 --- /dev/null +++ b/mtg-pdf.php @@ -0,0 +1,128 @@ +<?php + +use Gotenberg\Gotenberg; +use Gotenberg\Stream; + +$decklist = $_ENV['DECKLIST']; +if (empty($decklist)) { + die('No decklist provided.'); +} + +require_once __DIR__ . '/vendor/autoload.php'; + +/* + * Match cards from input + */ + +preg_match_all("/(\d+) (.+) \(([\w\d]+)\) (\d+\w*) ?([\*\w]*)/", $decklist, $matches); +$cards = []; +for ($idx = 0; $idx < count($matches[0]); $idx++) { + $cards[] = [ + 'quantity' => $matches[1][$idx], + 'name' => $matches[2][$idx], + 'set' => $matches[3][$idx], + 'number' => $matches[4][$idx], + ]; +} + +/* + * Select data from DB + */ + +$db = new \PDO('sqlite:AllPrintings.sqlite'); + +$images = []; +foreach ($cards as $card) { + $query = 'SELECT scryfallId,layout from cardIdentifiers JOIN cards on cardIdentifiers.uuid=cards.uuid WHERE cards.setCode=:setCode AND cards.number=:number'; + $statement = $db->prepare($query); + $statement->execute(['setCode' => $card['set'], 'number' => $card['number']]); + $result = $statement->fetch(); + $id = $result['scryfallId'] ?? ''; + + if (empty($id)) { + $query = 'SELECT scryfallId,layout from tokenIdentifiers JOIN tokens on tokenIdentifiers.uuid=tokens.uuid WHERE tokens.setCode=:setCode AND tokens.number=:number'; + $statement = $db->prepare($query); + $statement->execute(['setCode' => $card['set'], 'number' => $card['number']]); + $result = $statement->fetch(); + $id = $result['scryfallId'] ?? ''; + + if (empty($id)) { + echo "$card[name] ($card[set]) $card[number] not found."; + continue; + } + } + + $images[] = [ + 'quantity' => $card['quantity'], + 'src' => "https://cards.scryfall.io/png/front/" . substr($id, 0, 1) . "/" . substr($id, 1, 1) . "/$id.png", + 'alt' => $card['name'], + ]; + + if (in_array($result['layout'] ?? '', ['transform', 'double_faced_token'])) { + $images[] = [ + 'quantity' => $card['quantity'], + 'src' => "https://cards.scryfall.io/png/back/" . substr($id, 0, 1) . "/" . substr($id, 1, 1) . "/$id.png", + 'alt' => $card['name'], + ]; + } +} + +/* + * Build HTML + */ + +$template = <<<HTML +<html> + <head> + <style> + html, body { + padding: 0; + margin: 0; + } + + img { + width: 2.49in; + height: 3.48in; + display: block; + background-color: #16130e; + background-color: #000; + } + + #bg { + display: flex; + flex-wrap: wrap; + } + </style> + </head> + <body> + <div id="bg">{{imgs}}</div> + </body> +</html> +HTML; + +$imgs = array_map(function ($image) { + return str_repeat("<img src=\"$image[src]\" alt=\"$image[alt]\">", $image['quantity']); +}, $images); + +$html = str_replace('{{imgs}}', implode('', $imgs), $template); + +/* + * Build PDF + */ + +$request = Gotenberg::chromium('gotenberg:3000') + ->paperSize(8.27, 11.7) # A4 + ->margins(0, 0, 0, 0) + ->outputFilename(date("Ymd_His")) + ->html(Stream::string('index.html', $html)); + +if (php_sapi_name() === 'cli') { + @mkdir(__DIR__ . '/output'); + Gotenberg::save($request, __DIR__ . '/output'); +} else { + $response = Gotenberg::send($request); + + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; filename="' . date("Ymd_His") . '"'); + echo $response->getBody(); +} diff --git a/web.conf.template b/web.conf.template new file mode 100644 index 0000000..36016fa --- /dev/null +++ b/web.conf.template @@ -0,0 +1,17 @@ +server { + server_name ${DOMAIN}; + + root /var/www/html; + index index.php; + + location / { + try_files $uri $uri/ /index.php$query_string; + } + + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} |