Skip to content

Commit

Permalink
checkpoint: starting to build out Work pages, and made some changes t…
Browse files Browse the repository at this point in the history
…o schema stuff
  • Loading branch information
zkat committed Feb 25, 2024
1 parent 6938292 commit 2244fdd
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 5 deletions.
6 changes: 5 additions & 1 deletion lib/banchan/works/work.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ defmodule Banchan.Works.Work do

import Banchan.Validators

@primary_key {:id, :binary_id, autogenerate: true}
schema "works" do
field :public_id, :string, autogenerate: {__MODULE__, :rand_id, []}
field :title, :string
field :description, :string
field :tags, {:array, :string}
Expand Down Expand Up @@ -45,4 +45,8 @@ defmodule Banchan.Works.Work do
|> validate_tags()
|> validate_length(:tags, max: 5)
end

def rand_id() do
:crypto.strong_rand_bytes(10) |> Base.url_encode64(padding: false)
end
end
2 changes: 1 addition & 1 deletion lib/banchan/works/work_upload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Banchan.Works.WorkUpload do
schema "work_uploads" do
field :index, :integer
field :comment, :string
belongs_to :work, Banchan.Works.Work, type: :binary_id
belongs_to :work, Banchan.Works.Work
belongs_to :upload, Banchan.Uploads.Upload, type: :binary_id
belongs_to :preview, Banchan.Uploads.Upload, on_replace: :nilify, type: :binary_id

Expand Down
49 changes: 49 additions & 0 deletions lib/banchan/works/works.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule Banchan.Works do
alias Banchan.Workers.Thumbnailer
alias Banchan.Works.{Work, WorkUpload}

@public_id_size Work.rand_id() |> byte_size()

## Creation

@doc """
Expand Down Expand Up @@ -300,6 +302,53 @@ defmodule Banchan.Works do
"""
def get_work!(id), do: Repo.get!(Work, id) |> Repo.preload(:uploads)

@doc """
Gets a single work by its `public_id`. You may pass in a padded `public_id`
with a `title` slug appended to it. It will simply be stripped.
"""
def get_work_by_public_id!(%Studio{} = studio, <<public_id::binary-size(@public_id_size), _>>) do
get_work_by_public_id!(studio, public_id)
end

def get_work_by_public_id!(%Studio{id: studio_id}, <<public_id::binary-size(@public_id_size)>>) do
from(
work in Work,
where: work.public_id == ^public_id and work.studio_id == ^studio_id,
select: work
)
|> Repo.one!()
|> Repo.preload(:uploads)
end

@doc """
Fetches a `WorkUpload`, but only if the given `User` is allowed access.
"""
def get_work_upload_if_allowed!(%Work{} = work, upload_id, user) do
user_id = user && user.id

from(
work_upload in WorkUpload,
join: work in assoc(work_upload, :work),
join: studio in assoc(work, :studio),
join: artist in assoc(studio, :artists),
left_join: current_user in User,
on: current_user.id == ^user_id,
where:
work_upload.work_id == ^work.id and
work_upload.upload_id == ^upload_id and
(work.private == false or
(not is_nil(work.client_id) and work.client_id == ^user_id) or
artist.id == ^user_id or
:admin in current_user.roles or
:mod in current_user.roles),
select: work_upload
)
|> Repo.one!()
|> Repo.preload([:upload, :preview])
end

## Updating

@doc """
Updates a work.
Expand Down
53 changes: 53 additions & 0 deletions lib/banchan_web/controllers/work_uploads_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule BanchanWeb.WorkUploadsController do
@moduledoc """
Serves work uploads and previews, taking into account permissions.
"""
use BanchanWeb, :controller

alias Banchan.Studios
alias Banchan.Uploads
alias Banchan.Works

def show(conn, %{"handle" => studio_handle, "work_id" => public_id, "upload_id" => upload_id}) do
studio = Studios.get_studio_by_handle!(studio_handle)
work = Works.get_work_by_public_id!(studio, public_id)
work_upload = Works.get_work_upload_if_allowed!(work, upload_id, conn.assigns.current_user)

conn
|> put_resp_header("content-length", "#{work_upload.upload.size}")
|> put_resp_header(
"content-disposition",
"attachment; filename=\"#{work_upload.upload.name || work_upload.upload.key}\""
)
|> put_resp_content_type(work_upload.upload.type)
|> send_chunked(200)
|> then(
&Enum.reduce_while(Uploads.stream_data!(work_upload.upload), &1, fn chunk, conn ->
case chunk(conn, chunk) do
{:ok, conn} ->
{:cont, conn}

{:error, :closed} ->
{:halt, conn}
end
end)
)
end

def preview(conn, %{"handle" => studio_handle, "work_id" => public_id, "upload_id" => upload_id}) do
studio = Studios.get_studio_by_handle!(studio_handle)
work = Works.get_work_by_public_id!(studio, public_id)
work_upload = Works.get_work_upload_if_allowed!(work, upload_id, conn.assigns.current_user)

if work_upload.preview && !work_upload.preview.pending do
conn
|> put_resp_header("content-length", "#{work_upload.preview.size}")
|> put_resp_content_type(work_upload.preview.type)
|> send_resp(200, Uploads.get_data!(work_upload.preview))
else
conn
|> resp(404, "Not Found")
|> send_resp()
end
end
end
93 changes: 93 additions & 0 deletions lib/banchan_web/live/work_live/show.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
defmodule BanchanWeb.WorksLive.Show do
@moduledoc """
Displays a work.
"""
use BanchanWeb, :live_view

import BanchanWeb.StudioLive.Helpers

alias Banchan.Repo
alias Banchan.Works

alias BanchanWeb.Components.{
Carousel,
Layout,
Lightbox
}

@impl true
def handle_params(%{"work_id" => work_id} = params, _url, socket) do
socket = assign_studio_defaults(params, socket, false, true)

work = Works.get_work!(work_id) |> Repo.preload(:uploads)

{:noreply, socket |> assign(work: work)}
end

@impl true
def render(assigns) do
~F"""
<style>
.container {
@apply w-full p-4 mx-auto max-w-7xl;
}
.work-grid {
@apply grid grid-cols-1 gap-6 md:grid-cols-3;
}
.preview-column {
@apply flex flex-col gap-4 md:order-1;
}
.carousel-image {
@apply w-full h-full object-contain aspect-video;
}
.work-info {
}
.work-title {
@apply text-3xl font-bold;
}
.work-description {
@apply text-base;
}
</style>
<Layout flashes={@flash}>
<div class="container">
<div class="work-grid">
<div class="preview-column">
<Carousel id="preview-carousel">
<Lightbox id="preview-lightbox">
{#for wupload <- @work.uploads}
{#if !is_nil(wupload.preview_id)}
<Carousel.Slide>
<Lightbox.Item
src={~p"/studios/#{@studio.handle}/works/#{@work.public_id}/upload/#{wupload.id}/preview"}
download={~p"/studios/#{@studio.handle}/works/#{@work.public_id}/upload/#{wupload.id}"}
>
<img
class="carousel-image"
src={~p"/studios/#{@studio.handle}/works/#{@work.public_id}/upload/#{wupload.id}/preview"}
/>
</Lightbox.Item>
</Carousel.Slide>
{/if}
{/for}
</Lightbox>
</Carousel>
</div>
<div class="work-info">
<h1 class="work-title">{@work.title}</h1>
<p class="work-description">{@work.description}</p>
</div>
</div>
</div>
</Layout>
"""
end
end
14 changes: 14 additions & 0 deletions lib/banchan_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,20 @@ defmodule BanchanWeb.Router do
live("/studios/:handle/portfolio", StudioLive.Portfolio, :show)
live("/studios/:handle/disabled", StudioLive.Disabled, :show)

live("/studios/:handle/works/:work_id", WorksLive.Show, :show)

get(
"/studios/:handle/works/:work_id/upload/:upload_id",
WorkUploadsController,
:show
)

get(
"/studios/:handle/works/:work_id/upload/:upload_id/preview",
WorkUploadsController,
:preview
)

live("/confirm", ConfirmationLive, :show)

live("/reset_password", ForgotPasswordLive, :edit)
Expand Down
6 changes: 3 additions & 3 deletions priv/repo/migrations/20240218024033_create_works.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ defmodule Banchan.Repo.Migrations.CreateWorks do
use Ecto.Migration

def change do
create table(:works, primary_key: false) do
add :id, :binary_id, primary_key: true
create table(:works) do
add :public_id, :string, null: false
add :title, :string, null: false
add :description, :text, null: false
add :tags, {:array, :string}, null: false
Expand Down Expand Up @@ -85,7 +85,7 @@ defmodule Banchan.Repo.Migrations.CreateWorks do

create table(:work_uploads) do
add :comment, :text
add :work_id, references(:works, on_delete: :delete_all, type: :uuid), null: false
add :work_id, references(:works, on_delete: :delete_all), null: false
add :upload_id, references(:uploads, on_delete: :delete_all, type: :uuid), null: false
add :preview_id, references(:uploads, on_delete: :nilify_all, type: :uuid)
add :index, :integer, null: false
Expand Down

0 comments on commit 2244fdd

Please sign in to comment.