Skip to content

Commit b20e97c

Browse files
committed
Improve docker alpine images
1 parent ebaf079 commit b20e97c

File tree

14 files changed

+191
-94
lines changed

14 files changed

+191
-94
lines changed

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ services:
66
context: services/app
77
dockerfile: Dockerfile.dev
88
working_dir: /app
9+
tty: true # allocates a pseudo-TTY for interactive IEx
10+
stdin_open: true # keeps STDIN open even if not attached
911
env_file: .env
1012
command: mix phx.server
1113
environment:

services/app/Dockerfile.codebattle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ COPY --from=compile-image /opt/release/lib/codebattle-0.1.0/priv/static/assets/
5252

5353
FROM elixir:1.18-alpine AS runtime-image
5454

55-
RUN apk add --no-cache ca-certificates wkhtmltopdf git make curl vim
55+
RUN apk add --no-cache ca-certificates chromium git make curl vim
5656

5757
ARG GIT_HASH
5858

services/app/Dockerfile.dev

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
FROM elixir:1.18
1+
FROM elixir:1.18-alpine
22
ENV DOCKER_CHANNEL stable
33
ENV DOCKER_VERSION 20.10.9
44

5+
# Install Elixir dependencies as root
56
RUN mix local.hex --force \
6-
&& mix local.rebar --force
7-
8-
RUN apt-get update \
9-
&& apt-get install -y inotify-tools vim wkhtmltopdf
10-
11-
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - \
12-
&& apt-get update \
13-
&& apt-get install -y nodejs \
7+
&& mix local.rebar --force \
8+
&& mix archive.install hex phx_new --force
9+
10+
# Install system dependencies
11+
RUN apk add --no-cache \
12+
inotify-tools \
13+
curl \
14+
vim \
15+
chromium \
16+
postgresql-client \
17+
build-base \
18+
git
19+
20+
# Install Node.js and Yarn
21+
RUN apk add --no-cache nodejs npm \
1422
&& npm install --global [email protected]
1523

24+
# Install Docker for local development
1625
RUN curl -fsSL "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz" \
1726
| tar -xzC /usr/local/bin --strip=1 docker/docker
1827

28+
# Add non-root user
29+
RUN adduser -D developer
1930

20-
# Get Rust
21-
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
31+
# Copy Hex and other Mix archives from root to developer
32+
RUN cp -R /root/.mix /home/developer/ && chown -R developer:developer /home/developer/.mix
2233

23-
ENV PATH="/root/.cargo/bin:${PATH}"
34+
# Switch to non-root user
35+
USER developer
2436

25-
ARG GIT_HASH
37+
# Set working directory
38+
WORKDIR /app
2639

40+
ARG GIT_HASH
2741
ENV APP_VERSION=$GIT_HASH

services/app/apps/codebattle/assets/js/widgets/middlewares/Chat.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ const establishChat = page => dispatch => {
4545
};
4646

4747
export const connectToChat = (useChat = true, chatPage = 'channel', chatId) => dispatch => {
48-
if (!isRecord && useChat) {
49-
const page = getChatTopic(chatPage, chatId);
50-
channel.setupChannel(page);
51-
const currentChannel = establishChat(page)(dispatch);
48+
if (!isRecord && useChat) {
49+
const page = getChatTopic(chatPage, chatId);
50+
channel.setupChannel(page);
51+
const currentChannel = establishChat(page)(dispatch);
5252

53-
return currentChannel;
54-
}
53+
return currentChannel;
54+
}
5555

56-
return undefined;
57-
};
56+
return undefined;
57+
};
5858

5959
export const addMessage = payload => {
6060
channel

services/app/apps/codebattle/lib/codebattle/application.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ defmodule Codebattle.Application do
3838

3939
children =
4040
[
41+
{ChromicPDF, chromic_pdf_opts()},
4142
{Codebattle.Repo, []},
4243
{Registry, keys: :unique, name: Codebattle.Registry},
4344
CodebattleWeb.Telemetry,
@@ -78,4 +79,10 @@ defmodule Codebattle.Application do
7879
CodebattleWeb.Endpoint.config_change(changed, removed)
7980
:ok
8081
end
82+
83+
@chromic_pdf_opts Application.compile_env!(:codebattle, ChromicPDF)
84+
# in milliseconds
85+
defp chromic_pdf_opts do
86+
@chromic_pdf_opts ++ [session_pool: [size: 3, timeout: 30_000, checkout_timeout: 30_000]]
87+
end
8188
end

services/app/apps/codebattle/lib/codebattle_web/controllers/game/image_controller.ex

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ defmodule CodebattleWeb.Game.ImageController do
1111
{:ok, image} =
1212
game
1313
|> prepare_image_html()
14-
|> html_to_image()
14+
|> generate_png()
1515

1616
conn
17-
|> put_resp_content_type("image/jpeg")
17+
|> put_resp_content_type("image/png")
1818
|> send_resp(200, image)
1919

2020
{:error, reason} ->
@@ -25,55 +25,59 @@ defmodule CodebattleWeb.Game.ImageController do
2525
end
2626

2727
defp prepare_image_html(game) do
28-
~s(<html style="background-color:#dee2e6;">
28+
"""
29+
<html style="background-color:#dee2e6;">
2930
<center style="padding:25px;">
30-
<img src="https://codebattle.hexlet.io/assets/images/logo.svg" alt="Logo">
31-
<span>The Codebattle</span>
32-
#{render_content(game)}
33-
<p>Made with <span style="color: #e25555;">&#9829;</span> by CodebattleCoreTeam</p>
34-
<p>Dear frontenders, pls, make it prettier, thx</p>
31+
<img src="https://codebattle.hexlet.io/assets/images/logo.svg" alt="Logo">
32+
<span>The Codebattle</span>
33+
#{render_content(game)}
34+
<p>Made with <span style="color: #e25555;">&#9829;</span> by CodebattleCoreTeam</p>
35+
<p>Dear frontenders, pls, make it prettier, thx</p>
3536
</center>
36-
</html>)
37+
</html>
38+
"""
3739
end
3840

3941
defp render_content(%{players: []}) do
40-
~s(<p>Codebattle game</p>)
42+
"""
43+
<p>Codebattle game</p>
44+
"""
4145
end
4246

4347
defp render_content(%{players: [player1]} = game) do
44-
~s(
45-
<p>Game state: #{game.state}</p>
46-
<p>Level: #{game.level}</p>
47-
#{render_player(player1)}
48-
)
48+
"""
49+
<p>Game state: #{game.state}</p>
50+
<p>Level: #{game.level}</p>
51+
#{render_player(player1)}
52+
"""
4953
end
5054

5155
defp render_content(%{players: [player1, player2]} = game) do
52-
~s(
56+
"""
5357
<p>Game state: #{game.state}</p>
5458
<p>Level: #{game.level}</p>
5559
#{render_player(player1)}
5660
<span style="font-size:77px;">VS</span>
5761
#{render_player(player2)}
58-
)
62+
"""
5963
end
6064

6165
defp render_player(player) do
62-
~s(
66+
"""
6367
<div style="display:inline-block">
6468
<center>
6569
<img src="#{player.avatar_url}" style="width:46px; height:46px">
66-
<p>@#{player.name}\(#{player.lang}\)-#{player.rating}</p>
70+
<p>@#{player.name} (#{player.lang}) - #{player.rating}</p>
6771
</center>
6872
</div>
69-
)
73+
"""
7074
end
7175

72-
defp html_to_image(html_content) do
76+
defp generate_png(html_content) do
7377
if @fake_html_to_image do
7478
{:ok, html_content}
7579
else
76-
HtmlToImage.convert(html_content, width: 777, quality: 100)
80+
ChromicPDF.capture_screenshot({:html, html_content}, capture_screenshot: %{format: "png"})
7781
end
7882
end
7983
end
Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,118 @@
11
defmodule CodebattleWeb.Tournament.ImageController do
22
use CodebattleWeb, :controller
3+
use Gettext, backend: CodebattleWeb.Gettext
34

45
alias Codebattle.Tournament
56

67
def show(conn, %{"id" => id}) do
7-
# TODO: add ets cache for image
8+
# TODO: add ETS cache for image
89
case Tournament.Context.get(id) do
910
nil ->
1011
send_resp(conn, :ok, "")
1112

1213
tournament ->
13-
{:ok, image} =
14-
tournament
15-
|> render_image()
16-
|> HtmlToImage.convert(width: 777, quality: 100)
14+
html_content = render_image(tournament)
15+
{:ok, image} = generate_png(html_content)
1716

1817
conn
19-
|> put_resp_content_type("image/jpeg")
20-
|> send_resp(200, image)
18+
|> put_resp_content_type("image/png")
19+
|> send_resp(200, Base.decode64!(image))
2120
end
2221
end
2322

2423
defp render_image(tournament) do
25-
~s(<html style="background-color:#dee2e6;">
26-
<center style="padding:25px;">
27-
<img src="https://codebattle.hexlet.io/assets/images/logo.svg" alt="Logo">
28-
<span>The Codebattle</span>
29-
<h1>Tournament</h1>
30-
#{render_content(tournament)}
31-
<p>Made with <span style="color: #e25555;">&#9829;</span> by CodebattleCoreTeam</p>
32-
<p>Dear frontenders, pls, make it prettier, thx</p>
33-
</center>
34-
</html>)
24+
"""
25+
<html>
26+
<head>
27+
<meta charset="utf-8">
28+
<style>
29+
html, body {
30+
margin: 0;
31+
padding: 0;
32+
/* Let them expand to the “browser” size (which we'll define via ChromicPDF) */
33+
width: 100%;
34+
height: 100%;
35+
background: #f5f7fa;
36+
font-family: 'Helvetica Neue', Arial, sans-serif; /* Change to desired font */
37+
}
38+
.card {
39+
/* Fill the entire viewport */
40+
width: 100%;
41+
height: 100%;
42+
background: #ffffff;
43+
border-radius: 8px;
44+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
45+
overflow: hidden;
46+
display: flex;
47+
flex-direction: column;
48+
}
49+
.header {
50+
background: linear-gradient(135deg, #667eea, #764ba2);
51+
background: #000; /* Dark background */
52+
font-family: 'Helvetica Neue', Arial, sans-serif; /* Change to desired font */
53+
color: #fff; /* White text */
54+
text-align: center;
55+
}
56+
.header img {
57+
width: 60px;
58+
height: auto;
59+
}
60+
.content {
61+
flex: 1;
62+
padding: 10px;
63+
color: #333;
64+
text-align: center;
65+
display: flex;
66+
flex-direction: column;
67+
justify-content: center;
68+
}
69+
.footer {
70+
text-align: center;
71+
font-size: 10px;
72+
color: #aaa;
73+
padding: 5px;
74+
background: #f0f0f0;
75+
}
76+
</style>
77+
</head>
78+
<body>
79+
<div class="card">
80+
<div class="header">
81+
<img src="#{logo_url()}" alt="Logo">
82+
<h1>#{tournament.name}</h1>
83+
</div>
84+
<div class="content">
85+
#{render_content(tournament)}
86+
</div>
87+
<div class="footer">
88+
Made with ♥ by Codebattle
89+
</div>
90+
</div>
91+
</body>
92+
</html>
93+
"""
94+
end
95+
96+
defp logo_url do
97+
if logo = Application.get_env(:codebattle, :collab_logo) do
98+
logo
99+
else
100+
"https://codebattle.hexlet.io/assets/images/logo.svg"
101+
end
35102
end
36103

37104
defp render_content(tournament) do
38-
~s(
39-
<h3>#{tournament.name}</h3>
40-
<h4>Type: #{tournament.type}/#{tournament.level}</h4>
41-
<h4></h4>
42-
<h4>State: #{tournament.state}</h4>
43-
<h4>StartsAt: #{tournament.starts_at} UTC</h4>
44-
<p>Creator</p>
45-
<h4>#{render_user(tournament.creator)}</h4>
46-
47-
)
105+
type = to_string(tournament.type)
106+
state = to_string(tournament.state)
107+
108+
"""
109+
<h4>#{gettext("Type: %{type}", type: type)}</h4>
110+
<h4>#{gettext("State: %{state}", state: state)}</h4>
111+
<h4>#{gettext("Starts At")}: #{tournament.starts_at} UTC</h4>
112+
"""
48113
end
49114

50-
defp render_user(u) do
51-
~s(
52-
<div style="display:inline-block">
53-
<center>
54-
<img src="https://avatars0.githubusercontent.com/u/#{u.github_id}" style="width:46px; height:46px">
55-
<p>@#{u.name}\(#{u.lang}\)-#{u.rating}</p>
56-
</center>
57-
</div>
58-
)
115+
defp generate_png(html_content) do
116+
ChromicPDF.capture_screenshot({:html, html_content}, capture_screenshot: %{format: "png"})
59117
end
60118
end

services/app/apps/codebattle/mix.exs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ defmodule Codebattle.MixProject do
5555
{:fun_with_flags, "~> 1.11"},
5656
{:fun_with_flags_ui, "~> 1.0"},
5757
{:gettext, "~> 0.18"},
58-
{:html_to_image, github: "vtm9/html_to_image"},
58+
{:chromic_pdf, "~> 1.17"},
5959
{:jason, "~> 1.2"},
6060
{:nimble_csv, "~> 1.1"},
6161
{:phoenix, "~> 1.7"},
@@ -83,7 +83,6 @@ defmodule Codebattle.MixProject do
8383
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
8484
{:styler, "~> 1.4", only: [:dev, :test], runtime: false},
8585
{:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false},
86-
8786
# dev
8887
{:phoenix_live_reload, "~> 1.3", only: :dev},
8988

services/app/apps/codebattle/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"test": "jest",
99
"test-watch": "jest --watchAll",
10-
"watch": "webpack-dev-server --config ./webpack/webpack.dev.config.js --host 0.0.0.0",
10+
"watch": "webpack-dev-server --config ./webpack/webpack.dev.config.js",
1111
"profile": "webpack --config ./webpack/webpack.build.config.js --profile --json > stats.json && webpack-bundle-analyzer stats.json ./priv/static/assets/",
1212
"profile:build": "webpack --config ./webpack/webpack.build.config.js --profile --json > stats.json",
1313
"profile:visualize": "npx webpack-bundle-analyzer stats.json",

0 commit comments

Comments
 (0)