Merge branch 'emoji-pack-upload' into 'develop'
Add a way to upload emoji pack from zip/url easily See merge request pleroma/pleroma!4314
This commit is contained in:
commit
50a962ec6c
7 changed files with 538 additions and 3 deletions
|
|
@ -132,10 +132,25 @@ unit-testing-1.14.5-otp-25:
|
|||
- name: postgres:13-alpine
|
||||
alias: postgres
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
before_script: &testing_before_script
|
||||
- echo $MIX_ENV
|
||||
- rm -rf _build/*/lib/pleroma
|
||||
# Create a non-root user for running tests
|
||||
- useradd -m -s /bin/bash testuser
|
||||
# Install dependencies as root first
|
||||
- mix deps.get
|
||||
# Set proper ownership for everything
|
||||
- chown -R testuser:testuser .
|
||||
- chown -R testuser:testuser /root/.mix || true
|
||||
- chown -R testuser:testuser /root/.hex || true
|
||||
# Create user-specific directories
|
||||
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||
script: &testing_script
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
- mix pleroma.test_runner --cover --preload-modules
|
||||
# Run tests as non-root user
|
||||
- su testuser -c "HOME=/home/testuser mix ecto.create"
|
||||
- su testuser -c "HOME=/home/testuser mix ecto.migrate"
|
||||
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --cover --preload-modules"
|
||||
coverage: '/^Line total: ([^ ]*%)$/'
|
||||
artifacts:
|
||||
reports:
|
||||
|
|
@ -151,6 +166,7 @@ unit-testing-1.18.3-otp-27:
|
|||
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.18.3-otp-27
|
||||
cache: *testing_cache_policy
|
||||
services: *testing_services
|
||||
before_script: *testing_before_script
|
||||
script: *testing_script
|
||||
|
||||
formatting-1.15:
|
||||
|
|
|
|||
1
changelog.d/emoji-pack-upload-zip.add
Normal file
1
changelog.d/emoji-pack-upload-zip.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Added a way to upload new packs from a URL or ZIP file via Admin API
|
||||
|
|
@ -225,6 +225,97 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
end
|
||||
|
||||
def download_zip(name, opts \\ %{}) do
|
||||
with :ok <- validate_not_empty([name]),
|
||||
:ok <- validate_new_pack(name),
|
||||
{:ok, archive_data} <- fetch_archive_data(opts),
|
||||
pack_path <- path_join_name_safe(emoji_path(), name),
|
||||
:ok <- create_pack_dir(pack_path),
|
||||
:ok <- safe_unzip(archive_data, pack_path) do
|
||||
ensure_pack_json(pack_path, archive_data, opts)
|
||||
else
|
||||
{:error, :empty_values} -> {:error, "Pack name cannot be empty"}
|
||||
{:error, reason} when is_binary(reason) -> {:error, reason}
|
||||
_ -> {:error, "Could not process pack"}
|
||||
end
|
||||
end
|
||||
|
||||
defp create_pack_dir(pack_path) do
|
||||
case File.mkdir_p(pack_path) do
|
||||
:ok -> :ok
|
||||
{:error, _} -> {:error, "Could not create the pack directory"}
|
||||
end
|
||||
end
|
||||
|
||||
defp safe_unzip(archive_data, pack_path) do
|
||||
case SafeZip.unzip_data(archive_data, pack_path) do
|
||||
{:ok, _} -> :ok
|
||||
{:error, reason} when is_binary(reason) -> {:error, reason}
|
||||
_ -> {:error, "Could not unzip pack"}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_new_pack(name) do
|
||||
pack_path = path_join_name_safe(emoji_path(), name)
|
||||
|
||||
if File.exists?(pack_path) do
|
||||
{:error, "Pack already exists, refusing to import #{name}"}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_archive_data(%{url: url}) do
|
||||
case Pleroma.HTTP.get(url) do
|
||||
{:ok, %{status: 200, body: data}} -> {:ok, data}
|
||||
_ -> {:error, "Could not download pack"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_archive_data(%{file: %Plug.Upload{path: path}}) do
|
||||
case File.read(path) do
|
||||
{:ok, data} -> {:ok, data}
|
||||
_ -> {:error, "Could not read the uploaded pack file"}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_archive_data(_) do
|
||||
{:error, "Neither file nor URL was present in the request"}
|
||||
end
|
||||
|
||||
defp ensure_pack_json(pack_path, archive_data, opts) do
|
||||
pack_json_path = Path.join(pack_path, "pack.json")
|
||||
|
||||
if not File.exists?(pack_json_path) do
|
||||
create_pack_json(pack_path, pack_json_path, archive_data, opts)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp create_pack_json(pack_path, pack_json_path, archive_data, opts) do
|
||||
emoji_map =
|
||||
Pleroma.Emoji.Loader.make_shortcode_to_file_map(
|
||||
pack_path,
|
||||
Map.get(opts, :exts, [".png", ".gif", ".jpg"])
|
||||
)
|
||||
|
||||
archive_sha = :crypto.hash(:sha256, archive_data) |> Base.encode16()
|
||||
|
||||
pack_json = %{
|
||||
pack: %{
|
||||
license: Map.get(opts, :license, ""),
|
||||
homepage: Map.get(opts, :homepage, ""),
|
||||
description: Map.get(opts, :description, ""),
|
||||
src: Map.get(opts, :url),
|
||||
src_sha256: archive_sha
|
||||
},
|
||||
files: emoji_map
|
||||
}
|
||||
|
||||
File.write!(pack_json_path, Jason.encode!(pack_json, pretty: true))
|
||||
end
|
||||
|
||||
@spec download(String.t(), String.t(), String.t()) :: {:ok, t()} | {:error, atom()}
|
||||
def download(name, url, as) do
|
||||
uri = url |> String.trim() |> URI.parse()
|
||||
|
|
|
|||
|
|
@ -127,6 +127,20 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
|
|||
}
|
||||
end
|
||||
|
||||
def download_zip_operation do
|
||||
%Operation{
|
||||
tags: ["Emoji pack administration"],
|
||||
summary: "Download a pack from a URL or an uploaded file",
|
||||
operationId: "PleromaAPI.EmojiPackController.download_zip",
|
||||
security: [%{"oAuth" => ["admin:write"]}],
|
||||
requestBody: request_body("Parameters", download_zip_request(), required: true),
|
||||
responses: %{
|
||||
200 => ok_response(),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp download_request do
|
||||
%Schema{
|
||||
type: :object,
|
||||
|
|
@ -143,6 +157,25 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
|
|||
}
|
||||
end
|
||||
|
||||
defp download_zip_request do
|
||||
%Schema{
|
||||
type: :object,
|
||||
required: [:name],
|
||||
properties: %{
|
||||
url: %Schema{
|
||||
type: :string,
|
||||
format: :uri,
|
||||
description: "URL of the file"
|
||||
},
|
||||
file: %Schema{
|
||||
description: "The uploaded ZIP file",
|
||||
type: :object
|
||||
},
|
||||
name: %Schema{type: :string, format: :uri, description: "Pack Name"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["Emoji pack administration"],
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
:import_from_filesystem,
|
||||
:remote,
|
||||
:download,
|
||||
:download_zip,
|
||||
:create,
|
||||
:update,
|
||||
:delete
|
||||
|
|
@ -113,6 +114,27 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
end
|
||||
end
|
||||
|
||||
def download_zip(
|
||||
%{private: %{open_api_spex: %{body_params: params}}} = conn,
|
||||
_
|
||||
) do
|
||||
name = Map.get(params, :name)
|
||||
|
||||
with :ok <- Pack.download_zip(name, params) do
|
||||
json(conn, "ok")
|
||||
else
|
||||
{:error, error} when is_binary(error) ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: error})
|
||||
|
||||
{:error, _} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "Could not process pack"})
|
||||
end
|
||||
end
|
||||
|
||||
def download(
|
||||
%{private: %{open_api_spex: %{body_params: %{url: url, name: name} = params}}} = conn,
|
||||
_
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/import", EmojiPackController, :import_from_filesystem)
|
||||
get("/remote", EmojiPackController, :remote)
|
||||
post("/download", EmojiPackController, :download)
|
||||
post("/download_zip", EmojiPackController, :download_zip)
|
||||
|
||||
post("/files", EmojiFileController, :create)
|
||||
patch("/files", EmojiFileController, :update)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,371 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerDownloadZipTest do
|
||||
use Pleroma.Web.ConnCase, async: false
|
||||
|
||||
import Tesla.Mock
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
# Create a base temp directory for this test module
|
||||
base_temp_dir = Path.join(System.tmp_dir!(), "emoji_test_#{Ecto.UUID.generate()}")
|
||||
|
||||
# Clean up when all tests in module are done
|
||||
on_exit(fn ->
|
||||
File.rm_rf!(base_temp_dir)
|
||||
end)
|
||||
|
||||
{:ok, %{base_temp_dir: base_temp_dir}}
|
||||
end
|
||||
|
||||
setup %{base_temp_dir: base_temp_dir} do
|
||||
# Create a unique subdirectory for each test
|
||||
test_id = Ecto.UUID.generate()
|
||||
temp_dir = Path.join(base_temp_dir, test_id)
|
||||
emoji_dir = Path.join(temp_dir, "emoji")
|
||||
|
||||
# Create the directory structure
|
||||
File.mkdir_p!(emoji_dir)
|
||||
|
||||
# Configure this test to use the temp directory
|
||||
clear_config([:instance, :static_dir], temp_dir)
|
||||
|
||||
admin = insert(:user, is_admin: true)
|
||||
token = insert(:oauth_admin_token, user: admin)
|
||||
|
||||
admin_conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> assign(:token, token)
|
||||
|
||||
Pleroma.Emoji.reload()
|
||||
|
||||
{:ok, %{admin_conn: admin_conn, emoji_path: emoji_dir}}
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/emoji/packs/download_zip" do
|
||||
setup do
|
||||
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
|
||||
end
|
||||
|
||||
test "creates pack from uploaded ZIP file", %{admin_conn: admin_conn, emoji_path: emoji_path} do
|
||||
# Create a test ZIP file with emojis
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack.zip"
|
||||
}
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_zip_pack",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(200) == "ok"
|
||||
|
||||
# Verify pack was created
|
||||
assert File.exists?("#{emoji_path}/test_zip_pack/pack.json")
|
||||
assert File.exists?("#{emoji_path}/test_zip_pack/test_emoji.png")
|
||||
|
||||
# Verify pack.json contents
|
||||
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack/pack.json")
|
||||
pack_data = Jason.decode!(pack_json)
|
||||
|
||||
assert pack_data["files"]["test_emoji"] == "test_emoji.png"
|
||||
assert pack_data["pack"]["src_sha256"] != nil
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "creates pack from URL", %{admin_conn: admin_conn, emoji_path: emoji_path} do
|
||||
# Mock HTTP request to download ZIP
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
{:ok, zip_data} = File.read(zip_path)
|
||||
|
||||
mock(fn
|
||||
%{method: :get, url: "https://example.com/emoji_pack.zip"} ->
|
||||
%Tesla.Env{status: 200, body: zip_data}
|
||||
end)
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_zip_pack_url",
|
||||
url: "https://example.com/emoji_pack.zip"
|
||||
})
|
||||
|> json_response_and_validate_schema(200) == "ok"
|
||||
|
||||
# Verify pack was created
|
||||
assert File.exists?("#{emoji_path}/test_zip_pack_url/pack.json")
|
||||
assert File.exists?("#{emoji_path}/test_zip_pack_url/test_emoji.png")
|
||||
|
||||
# Verify pack.json has URL as source
|
||||
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack_url/pack.json")
|
||||
pack_data = Jason.decode!(pack_json)
|
||||
|
||||
assert pack_data["pack"]["src"] == "https://example.com/emoji_pack.zip"
|
||||
assert pack_data["pack"]["src_sha256"] != nil
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "refuses to overwrite existing pack", %{admin_conn: admin_conn, emoji_path: emoji_path} do
|
||||
# Create existing pack
|
||||
pack_path = Path.join(emoji_path, "test_zip_pack")
|
||||
File.mkdir_p!(pack_path)
|
||||
File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(%{files: %{}}))
|
||||
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack.zip"
|
||||
}
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_zip_pack",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Pack already exists, refusing to import test_zip_pack"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "handles invalid ZIP file", %{admin_conn: admin_conn} do
|
||||
# Create invalid ZIP file
|
||||
invalid_zip_path = Path.join(System.tmp_dir!(), "invalid.zip")
|
||||
File.write!(invalid_zip_path, "not a zip file")
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: invalid_zip_path,
|
||||
filename: "invalid.zip"
|
||||
}
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_invalid_pack",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Could not unzip pack"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
File.rm!(invalid_zip_path)
|
||||
end
|
||||
|
||||
test "handles URL download failure", %{admin_conn: admin_conn} do
|
||||
mock(fn
|
||||
%{method: :get, url: "https://example.com/bad_pack.zip"} ->
|
||||
%Tesla.Env{status: 404, body: "Not found"}
|
||||
end)
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_bad_url_pack",
|
||||
url: "https://example.com/bad_pack.zip"
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Could not download pack"
|
||||
}
|
||||
end
|
||||
|
||||
test "requires either file or URL parameter", %{admin_conn: admin_conn} do
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_no_source_pack"
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Neither file nor URL was present in the request"
|
||||
}
|
||||
end
|
||||
|
||||
test "returns error when pack name is empty", %{admin_conn: admin_conn} do
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack.zip"
|
||||
}
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Pack name cannot be empty"
|
||||
}
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "returns error when unable to create pack directory", %{
|
||||
admin_conn: admin_conn,
|
||||
emoji_path: emoji_path
|
||||
} do
|
||||
# Make the emoji directory read-only to trigger mkdir_p failure
|
||||
|
||||
# Save original permissions
|
||||
{:ok, %{mode: original_mode}} = File.stat(emoji_path)
|
||||
|
||||
# Make emoji directory read-only (no write permission)
|
||||
File.chmod!(emoji_path, 0o555)
|
||||
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack.zip"
|
||||
}
|
||||
|
||||
# Try to create a pack in the read-only emoji directory
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_readonly_pack",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(400) == %{
|
||||
"error" => "Could not create the pack directory"
|
||||
}
|
||||
|
||||
# Clean up - restore original permissions
|
||||
File.chmod!(emoji_path, original_mode)
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "preserves existing pack.json if present in ZIP", %{
|
||||
admin_conn: admin_conn,
|
||||
emoji_path: emoji_path
|
||||
} do
|
||||
# Create ZIP with pack.json
|
||||
{:ok, zip_path} = create_test_emoji_zip_with_pack_json()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack_with_json.zip"
|
||||
}
|
||||
|
||||
assert admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: "test_zip_pack_with_json",
|
||||
file: upload
|
||||
})
|
||||
|> json_response_and_validate_schema(200) == "ok"
|
||||
|
||||
# Verify original pack.json was preserved
|
||||
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack_with_json/pack.json")
|
||||
pack_data = Jason.decode!(pack_json)
|
||||
|
||||
assert pack_data["pack"]["description"] == "Test pack from ZIP"
|
||||
assert pack_data["pack"]["license"] == "Test License"
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
|
||||
test "rejects malicious pack names", %{admin_conn: admin_conn} do
|
||||
{:ok, zip_path} = create_test_emoji_zip()
|
||||
|
||||
upload = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
path: zip_path,
|
||||
filename: "test_pack.zip"
|
||||
}
|
||||
|
||||
# Test path traversal attempts
|
||||
malicious_names = ["../evil", "../../evil", ".", "..", "evil/../../../etc"]
|
||||
|
||||
Enum.each(malicious_names, fn name ->
|
||||
assert_raise RuntimeError, ~r/Invalid or malicious pack name/, fn ->
|
||||
admin_conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||
name: name,
|
||||
file: upload
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
# Clean up
|
||||
File.rm!(zip_path)
|
||||
end
|
||||
end
|
||||
|
||||
defp create_test_emoji_zip do
|
||||
tmp_dir = System.tmp_dir!()
|
||||
zip_path = Path.join(tmp_dir, "test_emoji_pack_#{:rand.uniform(10000)}.zip")
|
||||
|
||||
# 1x1 pixel PNG
|
||||
png_data =
|
||||
Base.decode64!(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||
)
|
||||
|
||||
files = [
|
||||
{~c"test_emoji.png", png_data},
|
||||
# Will be treated as GIF based on extension
|
||||
{~c"another_emoji.gif", png_data}
|
||||
]
|
||||
|
||||
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
|
||||
File.write!(zip_path, zip_binary)
|
||||
|
||||
{:ok, zip_path}
|
||||
end
|
||||
|
||||
defp create_test_emoji_zip_with_pack_json do
|
||||
tmp_dir = System.tmp_dir!()
|
||||
zip_path = Path.join(tmp_dir, "test_emoji_pack_json_#{:rand.uniform(10000)}.zip")
|
||||
|
||||
png_data =
|
||||
Base.decode64!(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||
)
|
||||
|
||||
pack_json =
|
||||
Jason.encode!(%{
|
||||
pack: %{
|
||||
description: "Test pack from ZIP",
|
||||
license: "Test License"
|
||||
},
|
||||
files: %{
|
||||
"test_emoji" => "test_emoji.png"
|
||||
}
|
||||
})
|
||||
|
||||
files = [
|
||||
{~c"test_emoji.png", png_data},
|
||||
{~c"pack.json", pack_json}
|
||||
]
|
||||
|
||||
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
|
||||
File.write!(zip_path, zip_binary)
|
||||
|
||||
{:ok, zip_path}
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue