This commit is contained in:
41
.gitea/workflows/deploy.yaml
Normal file
41
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- staging
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: webserver
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- name: Code auschecken
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker login to Gitea registry
|
||||
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.hephex.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
- name: Build Docker image
|
||||
run: docker build -t gitea.hephex.de/daniel/getraenkekarte:latest -f Dockerfile .
|
||||
|
||||
- name: Push Docker image
|
||||
run: docker push gitea.hephex.de/daniel/getraenkekarte:latest
|
||||
|
||||
- name: Clean up runner cache
|
||||
run: |
|
||||
docker image prune -f
|
||||
docker builder prune -f
|
||||
|
||||
- name: Deploy to Proxmox LXC via SSH
|
||||
uses: appleboy/ssh-action@v0.1.5
|
||||
with:
|
||||
host: 192.168.178.132
|
||||
username: ${{ secrets.PROXMOX_USER }}
|
||||
key: ${{ secrets.PROXMOX_SSH_KEY }}
|
||||
script: |
|
||||
cd /opt/getraenkekarte
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM caddy:2-alpine
|
||||
|
||||
RUN echo -e ":80 {\n\
|
||||
root * /usr/share/caddy\n\
|
||||
encode zstd gzip\n\
|
||||
file_server\n\
|
||||
header {\n\
|
||||
Cache-Control \"public, max-age=3600\"\n\
|
||||
X-Content-Type-Options nosniff\n\
|
||||
}\n\
|
||||
}" > /etc/caddy/Caddyfile
|
||||
|
||||
COPY index.html /usr/share/caddy/
|
||||
COPY Getraenkekarte.pdf /usr/share/caddy/
|
||||
|
||||
EXPOSE 80
|
||||
86
Getraenkekarte.pdf
Normal file
86
Getraenkekarte.pdf
Normal file
@@ -0,0 +1,86 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 9 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 9 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260920223817+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260920223817+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 6 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1640
|
||||
>>
|
||||
stream
|
||||
GauHL>Bf'b&:WeD9HUj$m/bbKpsuoZ82a2Jkm"_G:(Ju^"=P*K/">ZH^V1q&Cd[QBB$!AqD)B\"^2InFJq\-3r?!ZnNX,(N)$S/j0Eiq^JADum#-I>GRS2A!E*I8kXs8a^[l=nR$+6`#:tWJH=UWiWKc438M(0@H9EfKr!m3LmQt>=Q@"P/$CU>+1KYcKN8SpHU%;psV&t^h(QUeN;*oq?g9cs9;h/0j8bqJjpgT'bC>NONGG-AM^jm0#UTN6docaN*u`+E>YZ@/TM17lD4C*^2dWD"Ik?m7HJ#9&3E'RD5PRJiPe>Eb]4/*r8/go]4EA]&<L132"/YajF)bmrCGAtpm>r(m7G[>;jPGB'O@k:kKl_PiI'E-mV_&$77L]EHtUO)Q'\+%#1(`mquK*.lJp)tW=u/@uHY]PTs6lMqO@'mA_C^1W9L]\1;OD6lOg%FsX!2f*mOrJU9n7t\QpHZ4"HE]H+j\E\!OYB(,1EJNM4W#5@<d,fW[W,(LaZQ+A'Y^>3^*%NpK=gUm0>fXrGIXp`a-l_PBIGA/pgKkTi1@%iLL5LOp^6g+\q4F6Eqk$k,Ub_ck#EE\Tg@-\c/:n@*1FGYi]i%LkU1b05dghnKBP'A09&80ff"P24e'iqK1\ae(hZmR>f5>JZ.Yb=V,qck>`etc.rUE"se*b,8]0\`]=U1bJD2BUD_G>3U<Ji#s&K57;i_[H`7\7N3#.k#QR58M4@ePeN)oRjWn"KG\Lq*qH,frVDF#n)Pi#@R>:5M&mX3hl:5K.2F-tbi1b$<O"^D\k$Xf-9iJWW8&3o_?S7tkTMa^^PlKOM`e#LNq'T[P+S#7(r[K&f\'nI)@%pH?A\WZgOXic)M5(1@7k"@h@3$^#%CX>*;2?JUrfZ5lR9HbGlhn,\Z;qZ$/0UuQ$JB%dns=h86DL)?Jc<,!5``\+etU`>+)6/Z92$IB_^\iFmJ>(6`DQ1Q6Y,Ru?8Xaul;l"qT.QoN1ZGMli-;]VqnT)K(=qUdq?<Z"A27aZt[HH.V+Orr]#-LfRHE]DH4KTW5U?OBI+rCTYT5PP&"dTBkJ1\0+=G(aufP4PLn6jU^o<9&8U,Vhp;h@pa!*,.:r(52&e-&3Y`+jaSp.!gPVQ&b%2X7^5;iV#RIfb@B/+daaT%e>"#cDtLNEhp?"5XL;qU_k,1G^1Cj@b<)u>hR@/b9@4%X^j9-cFL#1VclNIkPR.pUUGk+)7Gl+c)C-e6JuK6UDC+`L$MGT$H)Sbe`G&RS#`mMhSo!El[>a??H)'?qbdIM4+u47<I0Bocmr*4[I')IBW\V4<GJMg/ti_W!e3ngj=EcUNO#ikkt^2)+]VHSlV+4:7tftG$EkZ>D63$[@oLntm=fj9C(R1g0*J,/9Su2S4%]1c5fV*XkdN8Y<)@C#MfV?MlM1ZOlVkH\b5a'"Yq%=:YA=+LT^%$_Pf6kYGArsc,$\"l$hO6BWU&R;8l:sqLeW<,0h$)s'L6b/Np=otnU/2,_8R$R*:?m'@Od"!]%WV$L6FXGTDXjZ+cTp-AWXdIUuDLYbgj$ZSm[s'8JE[MBjP(+47h+1VuZ.;,70YV8RMb`_@]Og@rnHt@kP8tI_;?$I9OS6jO$nBh\3Z8YrS[if0ai8+calhq(F=5r<Had[]K~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000122 00000 n
|
||||
0000000229 00000 n
|
||||
0000000341 00000 n
|
||||
0000000424 00000 n
|
||||
0000000539 00000 n
|
||||
0000000743 00000 n
|
||||
0000000811 00000 n
|
||||
0000001091 00000 n
|
||||
0000001150 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<c74df38e59f13fa69b76e16abbe7b5c1><c74df38e59f13fa69b76e16abbe7b5c1>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 8 0 R
|
||||
/Root 7 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
2882
|
||||
%%EOF
|
||||
86
Getraenkekarte_50_Geburtstag.pdf
Normal file
86
Getraenkekarte_50_Geburtstag.pdf
Normal file
@@ -0,0 +1,86 @@
|
||||
%PDF-1.4
|
||||
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 9 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 9 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
8 0 obj
|
||||
<<
|
||||
/Author (\(anonymous\)) /CreationDate (D:20260920223503+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260920223503+02'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
9 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 6 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
10 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1719
|
||||
>>
|
||||
stream
|
||||
GauHL=``:f&:WeDFC%jU$ac?if=kYu+6t-j;!b26?3CZj5tK9l`"i=Km1DYt=L)(b"H8J1eo^Z\od(2(nP-=hP81li%tC/_+;F_k&8lIVYG(Eo^9pKaBb>u#S2"u!gT^/YYJXq(R)H<;m!l']$hkp#)Vf:c.O\+lX.Yj^7Lb,,DWA;m`Ds@Qg5\@Is7P&6D)h"]K+Tp8m8a1d2t6\uU=94e1iVR;bJ54r]lV8u4_RLg&*WH8K!'UCGFeW0VrBn`?EjICmloL9c[fVA$.EWPDHMq2<S>*Q@CV7t.hP&Y_,MgB$.M'#KqX%GGd:!F)(+i$e.a3@8g]`*"R?rtAHI]bhka/KOnkpRg"lRo5MXuh_n:jkB"#@W4q!1+\MOhPMGH-4g*a!QO-@?Ko<i/>N%bL!Y[*_Z()W9<GR/qC>^gKXfu5IP$gk9;%o5M/B#`B=9jIi'b1#M'$^a[4J:(Ih&B>!-;hnR[3-qaLS+rZ2LpsaD3a/QHQ"\kH)8=l9qh%)&IQG15H^E@?Mp(^MC]*tF/c9b]@o?o;4Kp@0^4LQS]a_n7F!"*q?OlFPn,#W`+"NhhM#fPT15R+3AfZauHVklk2YqSU7G@CJKPV3pi9DCraE.?Yk0X*OSHc'LrlXaU>G3t1a4"VHahno^r*$6kMP<Zr907)FUUcD3+]N)i>5LcY:J[$<cJa1#rXaa^fL3=eWYj/J(V',fQn3%&%k!9#c+8r\k&Bu=k8b*`[j@K[)F)>rOt!ONNR1L[)E-Q]AE5c;)mE!)RlQ*Z2729)S0(7AEe)kN6HiFH,Y:`<Ra]ql"8=4H3[O4(0#ql1rZc;2689"F<91WVp_-\G)c5Y+"(R/p<5q5:Ll08AP:%cX#oa8[#bi-&_4X6TO,tV\-coGX`\492S6qC"6B2n(&;S)J<MTrb'GuTM`d+$#Z,%]JCAguka_iR;+4PTd_FI!tW;k1F:W9@Wc*Sk0%s?3O#VeDL-.=Vr[9_dR9K<S7gjmlH35B2IX6.i1B`/q[<Q^*bdUAu\2%YtD]e+pNSHkS^%Xc<GYC\Xodm&CUIrmmZCJ6!"U1d2EIGbmIb>3+d)/uKh3Lm)l_TYtD?:mlZf<pc.Iie@S['@A.BB62VXG;3R&rW#%KQG'pP`nO;(G\P.h@u9L(b7W8(5*]i--%1K2IO%Z;=jrcA3*l+Y1B`6L07%*B%p"aL1$tl.`gof#-Sa"bF.q0":55EO^`Ll,lJ;6B(2&8b&5d4Q9c?<.AE4CZKs7eQ&M$Tq.Z?j/V:G3'W90PR?Y<i+n3*UdbaZIVi+@&B7p1p+&Dh6i3qfUhXteqe@s$0e`#`0lN8VLB#XOt#?22W4\#5'8N"P&k`tk"g((&0Uc)u6.SWq0p<n\9JTk7<0on&0UTFJq``.1i8C_h;3n&rQ)VjtFKi_.N[T3hZE4TdZqa*I)2$ah<&;!5<]%3R`^$>K(7cT;/%,P(u.O[3M`ULUs-*DG;ZnC@'=9[?W[9Zq=G[9pe!*+N<;g*.\5K[Q2M/Wch/G4ut;E)lA<3*MeP^#r5X^YTcJEZPDe\1Fkk8@S4/hr4Ag(kA5F"1rog"O!2%AsSE5C!LIoaD3b:,Ee)M=.4-Sk,-\=jV:1iseq*Nj1L-Z-L5C(^I3fj[5V@<U!!t3sHJI^$rghAe-GkCG9XI]0*;7P3l-P*7rp"bUX+!QJlBF(cRcDE`O5`=Ahl67<up3Cld`LT3K\=&#o'H3<~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000122 00000 n
|
||||
0000000229 00000 n
|
||||
0000000341 00000 n
|
||||
0000000424 00000 n
|
||||
0000000539 00000 n
|
||||
0000000743 00000 n
|
||||
0000000811 00000 n
|
||||
0000001091 00000 n
|
||||
0000001150 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<d217652d06a40a581f105adac8227bc7><d217652d06a40a581f105adac8227bc7>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 8 0 R
|
||||
/Root 7 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
2961
|
||||
%%EOF
|
||||
187
create_pdf.py
Normal file
187
create_pdf.py
Normal file
@@ -0,0 +1,187 @@
|
||||
import os
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
|
||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
class NumberedCanvas(canvas.Canvas):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._saved_page_states = []
|
||||
|
||||
def showPage(self):
|
||||
self._saved_page_states.append(dict(self.__dict__))
|
||||
self._startPage()
|
||||
|
||||
def save(self):
|
||||
num_pages = len(self._saved_page_states)
|
||||
for state in self._saved_page_states:
|
||||
self.__dict__.update(state)
|
||||
self.draw_page_decorations()
|
||||
super().showPage()
|
||||
super().save()
|
||||
|
||||
def draw_page_decorations(self):
|
||||
self.saveState()
|
||||
self.setStrokeColor(colors.HexColor("#D4AF37"))
|
||||
self.setLineWidth(2)
|
||||
self.rect(10*mm, 10*mm, 190*mm, 277*mm)
|
||||
self.setLineWidth(0.7)
|
||||
self.rect(11.5*mm, 11.5*mm, 187*mm, 274*mm)
|
||||
self.restoreState()
|
||||
|
||||
def generate_pdf(filename):
|
||||
doc = SimpleDocTemplate(
|
||||
filename,
|
||||
pagesize=A4,
|
||||
leftMargin=16*mm,
|
||||
rightMargin=16*mm,
|
||||
topMargin=16*mm,
|
||||
bottomMargin=16*mm
|
||||
)
|
||||
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
title_style = ParagraphStyle(
|
||||
'MainTitle',
|
||||
fontName='Helvetica-Bold',
|
||||
fontSize=22,
|
||||
leading=26,
|
||||
alignment=1,
|
||||
textColor=colors.HexColor("#1A1A1A"),
|
||||
spaceAfter=3
|
||||
)
|
||||
|
||||
subtitle_style = ParagraphStyle(
|
||||
'SubTitle',
|
||||
fontName='Helvetica-Oblique',
|
||||
fontSize=10,
|
||||
leading=13,
|
||||
alignment=1,
|
||||
textColor=colors.HexColor("#555555"),
|
||||
spaceAfter=10
|
||||
)
|
||||
|
||||
section_style = ParagraphStyle(
|
||||
'SectionHeader',
|
||||
fontName='Helvetica-Bold',
|
||||
fontSize=12,
|
||||
leading=15,
|
||||
textColor=colors.HexColor("#8A6D1C"),
|
||||
spaceBefore=6,
|
||||
spaceAfter=6,
|
||||
alignment=0
|
||||
)
|
||||
|
||||
drink_title_style = ParagraphStyle(
|
||||
'DrinkTitle',
|
||||
fontName='Helvetica-Bold',
|
||||
fontSize=10,
|
||||
leading=12,
|
||||
textColor=colors.HexColor("#111111"),
|
||||
spaceAfter=2
|
||||
)
|
||||
|
||||
drink_text_style = ParagraphStyle(
|
||||
'DrinkText',
|
||||
fontName='Helvetica',
|
||||
fontSize=8.5,
|
||||
leading=11,
|
||||
textColor=colors.HexColor("#333333")
|
||||
)
|
||||
|
||||
footer_style = ParagraphStyle(
|
||||
'Footer',
|
||||
fontName='Helvetica-Oblique',
|
||||
fontSize=9,
|
||||
leading=12,
|
||||
alignment=1,
|
||||
textColor=colors.HexColor("#777777"),
|
||||
spaceBefore=8
|
||||
)
|
||||
|
||||
story = []
|
||||
|
||||
# Header
|
||||
story.append(Paragraph("🍸 GETRÄNKEKARTE 🍸", title_style))
|
||||
story.append(Paragraph("Frisch zubereitet an der Bar auf Eis.", subtitle_style))
|
||||
|
||||
# Line
|
||||
line_table = Table([[""]], colWidths=[178*mm], rowHeights=[1])
|
||||
line_table.setStyle(TableStyle([('LINEABOVE', (0,0), (-1,-1), 1, colors.HexColor("#D4AF37"))]))
|
||||
story.append(line_table)
|
||||
story.append(Spacer(1, 4*mm))
|
||||
|
||||
# Cocktails
|
||||
story.append(Paragraph("🍹 SIGNATURE COCKTAILS", section_style))
|
||||
|
||||
cocktails = [
|
||||
("1. Moscow Mule", "<b>Zutaten:</b> Wodka, frischer Limettensaft, Ginger Beer, Minzzweig & Limettenspalte.<br/><b>Geschmack:</b> Scharf-würzig, erfrischend und spritzig."),
|
||||
("2. Gin & Tonic", "<b>Zutaten:</b> Klassischer Gin, Tonic Water, Limettenscheibe.<br/><b>Geschmack:</b> Der elegante Klassiker – herb, bitter und ohne Zuckerüberhang."),
|
||||
("3. Mojito", "<b>Zutaten:</b> Weißer Rum, frische Minzblätter, Zuckersirup, Limettensaft, Sprudelwasser.<br/><b>Geschmack:</b> Aromatisches Bar-Feeling mit frischer Minze."),
|
||||
("4. Cuba Libre", "<b>Zutaten:</b> Weißer Rum, erfrischende Cola, frischer Limettensaft, Limette.<br/><b>Geschmack:</b> Der süße, vertraute Party-Dauerbrenner."),
|
||||
("5. Vodka Cranberry", "<b>Zutaten:</b> Wodka, Cranberrysaft, Soda, frischer Limettensaft, Limettenscheibe.<br/><b>Geschmack:</b> Angenehm säuerlich-herb statt klebrig-süß."),
|
||||
("6. Skinny Bitch", "<b>Zutaten:</b> Wodka, spritziges Mineralwasser, viel frischer Limettensaft.<br/><b>Geschmack:</b> Der leichte, komplett zucker- und kalorienfreie Drink.")
|
||||
]
|
||||
|
||||
col_width = 87 * mm
|
||||
table_data = []
|
||||
for i in range(0, len(cocktails), 2):
|
||||
cell_left = [
|
||||
Paragraph(cocktails[i][0], drink_title_style),
|
||||
Paragraph(cocktails[i][1], drink_text_style)
|
||||
]
|
||||
cell_right = [
|
||||
Paragraph(cocktails[i+1][0], drink_title_style),
|
||||
Paragraph(cocktails[i+1][1], drink_text_style)
|
||||
]
|
||||
table_data.append([cell_left, cell_right])
|
||||
|
||||
cocktail_table = Table(table_data, colWidths=[col_width, col_width])
|
||||
cocktail_table.setStyle(TableStyle([
|
||||
('VALIGN', (0,0), (-1,-1), 'TOP'),
|
||||
('BOTTOMPADDING', (0,0), (-1,-1), 8),
|
||||
('RIGHTPADDING', (0,0), (0,-1), 6),
|
||||
('LEFTPADDING', (1,0), (1,-1), 6),
|
||||
]))
|
||||
story.append(cocktail_table)
|
||||
|
||||
story.append(Spacer(1, 2*mm))
|
||||
story.append(Paragraph("🍃 0.0% MOCKTAILS (ALKOHOLFREI)", section_style))
|
||||
|
||||
mocktails = [
|
||||
("Virgin Mojito", "Frische Minze, Zuckersirup, Limettensaft, aufgegossen mit Ginger Beer & Sprudelwasser."),
|
||||
("Cranberry Ginger Splash", "Feinherber Cranberrysaft, frischer Limettensaft & würziges Ginger Beer."),
|
||||
("Homemade Lime Soda", "Frischer Limettensaft, süßer Zuckersirup, Minze & eiskaltes Sprudelwasser.")
|
||||
]
|
||||
|
||||
m_data = []
|
||||
m_col_width = 58 * mm
|
||||
row_cells = []
|
||||
for m in mocktails:
|
||||
cell = [
|
||||
Paragraph(f"<b>{m[0]}</b>", drink_title_style),
|
||||
Paragraph(m[1], drink_text_style)
|
||||
]
|
||||
row_cells.append(cell)
|
||||
m_data.append(row_cells)
|
||||
|
||||
mocktail_table = Table(m_data, colWidths=[m_col_width, m_col_width, m_col_width])
|
||||
mocktail_table.setStyle(TableStyle([
|
||||
('VALIGN', (0,0), (-1,-1), 'TOP'),
|
||||
('RIGHTPADDING', (0,0), (-1,-1), 4),
|
||||
('LEFTPADDING', (0,0), (-1,-1), 4),
|
||||
]))
|
||||
story.append(mocktail_table)
|
||||
|
||||
story.append(Spacer(1, 6*mm))
|
||||
story.append(Paragraph("✨ Cheers! Cheers to great drinks and good vibes! ✨", footer_style))
|
||||
|
||||
doc.build(story, canvasmaker=NumberedCanvas)
|
||||
print("PDF updated.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
out_path = r"C:\Users\Danie\.gemini\antigravity-ide\scratch\Getraenkekarte.pdf"
|
||||
generate_pdf(out_path)
|
||||
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
getraenkekarte:
|
||||
image: gitea.hephex.de/daniel/getraenkekarte:latest
|
||||
container_name: getraenkekarte
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8085:80"
|
||||
187
getraenkekarte.html
Normal file
187
getraenkekarte.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
@page {
|
||||
size: A4 portrait;
|
||||
margin: 15mm;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
background-color: #0d1117;
|
||||
color: #e6edf3;
|
||||
padding: 24px;
|
||||
border: 3px double #d4af37;
|
||||
border-radius: 8px;
|
||||
height: calc(100vh - 30mm);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #d4af37;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 26px;
|
||||
letter-spacing: 3px;
|
||||
text-transform: uppercase;
|
||||
color: #d4af37;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.header p {
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
color: #8b949e;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
color: #d4af37;
|
||||
border-bottom: 1px dashed rgba(212, 175, 55, 0.4);
|
||||
padding-bottom: 4px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px 18px;
|
||||
}
|
||||
.drink {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(212, 175, 55, 0.2);
|
||||
border-radius: 6px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.drink-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #f0e6d2;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.drink-desc {
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: #c9d1d9;
|
||||
}
|
||||
.drink-desc strong {
|
||||
color: #d4af37;
|
||||
}
|
||||
.mocktails-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: #8b949e;
|
||||
border-top: 1px solid rgba(212, 175, 55, 0.3);
|
||||
padding-top: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
background-color: #111 !important;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<h1>🍸 GETRÄNKEKARTE 🍸</h1>
|
||||
<p>50. Geburtstag — Frisch zubereitet an der Bar auf Eis</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="section-title">🍹 Cocktail-Highlights</div>
|
||||
<div class="grid">
|
||||
<div class="drink">
|
||||
<div class="drink-title">1. Moscow Mule</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Wodka, Limettensaft, Ginger Beer, Minze & Limette.<br>
|
||||
<strong>Geschmack:</strong> Scharf-würzig, erfrischend & spritzig.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">2. Gin & Tonic</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Gin, Tonic Water, Limettenscheibe.<br>
|
||||
<strong>Geschmack:</strong> Eleganter Klassiker – herb, bitter & rein.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">3. Mojito</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Weißer Rum, frische Minze, Zuckersirup, Limettensaft, Soda.<br>
|
||||
<strong>Geschmack:</strong> Aromatisches Bar-Feeling mit frischer Minze.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">4. Cuba Libre</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Weißer Rum, Cola, frischer Limettensaft, Limettenspalte.<br>
|
||||
<strong>Geschmack:</strong> Süßer & vertrauter Party-Klassiker.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">5. Vodka Cranberry</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Wodka, Cranberrysaft, Soda, frischer Limettensaft.<br>
|
||||
<strong>Geschmack:</strong> Fruchtig-rot, angenehm herb & säuerlich.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">6. Skinny Bitch</div>
|
||||
<div class="drink-desc">
|
||||
<strong>Zutaten:</strong> Wodka, Mineralwasser, viel frischer Limettensaft.<br>
|
||||
<strong>Geschmack:</strong> Leicht, frisch, zucker- & kalorienfrei.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="section-title">🍃 Alkoholfreie Mocktails</div>
|
||||
<div class="mocktails-grid">
|
||||
<div class="drink">
|
||||
<div class="drink-title">Virgin Mojito</div>
|
||||
<div class="drink-desc">Frische Minze, Limettensaft, Zuckersirup, Ginger Beer & Sprudel.</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">Cranberry Ginger Splash</div>
|
||||
<div class="drink-desc">Cranberrysaft, frischer Limettensaft & würziges Ginger Beer.</div>
|
||||
</div>
|
||||
|
||||
<div class="drink">
|
||||
<div class="drink-title">Homemade Lime Soda</div>
|
||||
<div class="drink-desc">Frischer Limettensaft, Zuckersirup, aromatische Minze & Eiswasser.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
Cheers! Auf unvergessliche Momente und einen tollen Abend! ✨
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
348
index.html
Normal file
348
index.html
Normal file
@@ -0,0 +1,348 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BAR & COCKTAILS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700;800&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #090b10;
|
||||
--card-bg: rgba(22, 27, 34, 0.75);
|
||||
--card-border: rgba(255, 255, 255, 0.08);
|
||||
--primary: #f59e0b;
|
||||
--primary-glow: rgba(245, 158, 11, 0.25);
|
||||
--accent: #ec4899;
|
||||
--accent-glow: rgba(236, 72, 153, 0.25);
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--tag-bg: rgba(245, 158, 11, 0.12);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Plus Jakarta Sans', -apple-system, sans-serif;
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 20px 16px 40px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Ambient Glow */
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: -100px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: radial-gradient(circle, var(--accent-glow) 0%, var(--primary-glow) 50%, transparent 70%);
|
||||
filter: blur(80px);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
padding: 20px 0 10px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
color: var(--primary);
|
||||
margin-bottom: 12px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.5px;
|
||||
background: linear-gradient(135deg, #fff 30%, #cbd5e1 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, var(--card-border), transparent);
|
||||
}
|
||||
|
||||
.menu-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
backdrop-filter: blur(16px);
|
||||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card:active {
|
||||
transform: scale(0.985);
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.card-ingredients {
|
||||
font-size: 12.5px;
|
||||
color: #cbd5e1;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.taste-tag {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
background: var(--tag-bg);
|
||||
padding: 4px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.taste-tag.green {
|
||||
color: #34d399;
|
||||
background: rgba(52, 211, 153, 0.12);
|
||||
border-color: rgba(52, 211, 153, 0.2);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding-top: 16px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
}
|
||||
h1, .card-name {
|
||||
color: #000;
|
||||
-webkit-text-fill-color: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="badge">🔥 Open Bar</div>
|
||||
<h1>GETRÄNKEKARTE</h1>
|
||||
<p>Frisch im Glas zubereitet. Wähle deinen Drink!</p>
|
||||
</header>
|
||||
|
||||
<!-- Cocktails -->
|
||||
<section>
|
||||
<div class="section-title">🍹 Signature Cocktails</div>
|
||||
<div class="menu-grid">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Moscow Mule</span>
|
||||
<span class="card-icon">🥒</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Wodka, frischer Limettensaft, Ginger Beer, Minzzweig & Limettenspalte</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">Scharf-würzig & erfrischend</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Gin & Tonic</span>
|
||||
<span class="card-icon">🍋</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Klassischer Gin, Tonic Water, angedrückte Limettenscheibe</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">Herb, bitter & puristisch</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Mojito</span>
|
||||
<span class="card-icon">🌿</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Weißer Rum, frische Minzblätter, Zuckersirup, Limettensaft, Soda</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">Aromatisch & minzfrisch</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Cuba Libre</span>
|
||||
<span class="card-icon">🥃</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Weißer Rum, Cola, frischer Limettensaft, Limettenspalte</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">Süß & party-ready</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Vodka Cranberry</span>
|
||||
<span class="card-icon">🍒</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Wodka, feinherber Cranberrysaft, Soda, frischer Limettensaft</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">Fruchtig & säuerlich-herb</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Skinny Bitch</span>
|
||||
<span class="card-icon">⚡</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Wodka, spritziges Mineralwasser, viel frischer Limettensaft</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag">0% Zucker & ultraleicht</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Mocktails -->
|
||||
<section>
|
||||
<div class="section-title">🍃 0.0% Mocktails</div>
|
||||
<div class="menu-grid">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Virgin Mojito</span>
|
||||
<span class="card-icon">🌱</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Frische Minze, Limettensaft, Zuckersirup, Ginger Beer & Sprudel</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag green">Alkoholfrei & prickelnd</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Cranberry Ginger Splash</span>
|
||||
<span class="card-icon">🍓</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Cranberrysaft, frischer Limettensaft, Ginger Beer</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag green">Fruchtig & feinherb</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-top">
|
||||
<span class="card-name">Homemade Lime Soda</span>
|
||||
<span class="card-icon">🍋</span>
|
||||
</div>
|
||||
<p class="card-ingredients">Frischer Limettensaft, Zuckersirup, Minze, Eiswasser</p>
|
||||
<div class="card-footer">
|
||||
<span class="taste-tag green">Zitruskick & eiskalt</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
Cheers! Let's celebrate 🎉
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user