2025-03-10 17:51:35 -08:00

130 lines
3.3 KiB
Elixir

defmodule Bright.B2 do
@moduledoc """
The B2 context.
Note: b2 buckets may need CORS configuration to allow uploads from a domain. This is done using b2's CLI tool.
```
b2 bucket update --cors-rules "$(<~/Documents/futureporn-meta/cors-rules.json)" futureporn
```
Where cors-rules.json is as follows
```json
[
{
"allowedHeaders": [
"*"
],
"allowedOperations": [
"s3_head",
"s3_put",
"s3_get"
],
"allowedOrigins": [
"https://futureporn.net"
],
"corsRuleName": "downloadFromAnyOriginWithUpload",
"exposeHeaders": [
"etag"
],
"maxAgeSeconds": 3600
}
]
```
"""
import Ecto.Query, warn: false
require Logger
alias ExAws.S3
alias Bright.Cache
alias Bright.Streams.Vod
@doc """
Put a file from local disk to Backblaze. This function uses the filename as the S3 key. Use put/2 if you want to specify the key
"""
def put(local_file) do
object_key = Path.basename(local_file)
put(local_file, object_key)
end
def put(local_file, object_key) do
put(local_file, object_key, "application/octet-stream")
end
@doc """
Put a file from local disk to Backblaze.
"""
def put(local_file, object_key, mime_type) do
Logger.debug("put/2 called with local_file=#{local_file}, object_key=#{object_key}")
bucket = Application.get_env(:bright, :aws_bucket)
if bucket === nil do
raise("bucket specification is missing")
end
public_s3_endpoint = Application.get_env(:bright, :public_s3_endpoint)
# access_key_id = Application.get_env(:ex_aws, :access_key_id)
# secret_access_key = Application.get_env(:ex_aws, :secret_access_key)
if public_s3_endpoint === nil do
raise("public_s3_endpoint specification is missing")
end
cdn_url = "#{public_s3_endpoint}/#{object_key}"
Logger.debug(
"putting local_file=#{local_file} to bucket=#{bucket} public_s3_endpoint=#{public_s3_endpoint} key=#{object_key}"
)
opts = [content_type: mime_type]
local_file
|> S3.Upload.stream_file()
|> S3.upload(bucket, object_key, opts)
|> ExAws.request()
|> case do
{:ok, %{status_code: 200}} -> {:ok, %{key: object_key, cdn_url: cdn_url}}
{:error, reason} -> {:error, reason}
end
end
def get(%Vod{} = vod) do
object_key =
vod.s3_cdn_url
|> URI.parse()
|> Map.get(:path)
|> String.trim_leading("/")
local_file = Cache.generate_filename(object_key)
Logger.debug("get/1 object_key=#{object_key} local_file=#{local_file}")
get(object_key, local_file)
end
@doc """
Download a file from Backblaze to local disk
"""
def get(object_key, local_file) do
bucket = Application.get_env(:bright, :aws_bucket)
S3.download_file(bucket, object_key, local_file)
|> ExAws.request()
|> case do
{:ok, :done} -> {:ok, local_file}
{:error, reason} -> {:error, reason}
end
end
@doc """
Given a S3 object_key, generate an appropriate CDN url.
"""
def generate_cdn_url(object_key) do
cdn_url =
Application.get_env(:bright, :public_s3_endpoint) ||
raise(":public_s3_endpoint missing from App env")
"#{cdn_url}/#{object_key}"
end
end