Merge pull request 'devel' (#57) from devel into main

Reviewed-on: #57
This commit is contained in:
inhji 2023-04-10 19:19:06 +02:00
commit 6b0c64eae4
34 changed files with 524 additions and 67 deletions

View file

@ -84,7 +84,7 @@
/* Set width and color for identity icons */
a[rel] svg {
width: 1em;
fill: rgb(var(--color-text-primary));
fill: rgb(var(--color-primary));
}
/* Set width and color for identity icons */

View file

@ -5,9 +5,9 @@ defmodule Chiya.Notes do
import Ecto.Query, warn: false
alias Chiya.Repo
alias Chiya.Notes.{Note, NoteImage, NoteNote, References}
alias Chiya.Notes.{Note, NoteImage, NoteNote, NoteTag}
@preloads [:channels, :images, :links_from, :links_to]
@preloads [:channels, :images, :links_from, :links_to, :tags]
@doc """
Returns the list of notes.
@ -158,6 +158,7 @@ defmodule Chiya.Notes do
note
|> Note.changeset(attrs)
|> Repo.update()
|> Chiya.Tags.TagUpdater.update_tags(attrs)
|> Chiya.Notes.References.update_references(attrs)
end
@ -248,4 +249,18 @@ defmodule Chiya.Notes do
def change_note_image(%NoteImage{} = note_image, attrs \\ %{}) do
NoteImage.update_changeset(note_image, attrs)
end
def get_note_tag!(attrs \\ %{}) do
Repo.get_by!(NoteTag, attrs)
end
def create_note_tag(attrs \\ %{}) do
%NoteTag{}
|> NoteTag.changeset(attrs)
|> Repo.insert()
end
def delete_note_tag(%NoteTag{} = note_tag) do
Repo.delete(note_tag)
end
end

View file

@ -8,7 +8,11 @@ defmodule Chiya.Notes.Note do
@derive {Jason.Encoder, only: [:id, :name, :content, :slug, :channels]}
schema "notes" do
field :content, :string
field :kind, Ecto.Enum, values: [:post, :bookmark], default: :post
field :kind, Ecto.Enum,
values: [:post, :bookmark, :recipe],
default: :post
field :name, :string
field :published_at, :naive_datetime
field :slug, NoteSlug.Type
@ -27,8 +31,16 @@ defmodule Chiya.Notes.Note do
join_through: Chiya.Notes.NoteNote,
join_keys: [source_id: :id, target_id: :id]
many_to_many :tags, Chiya.Tags.Tag,
join_through: Chiya.Notes.NoteTag,
join_keys: [note_id: :id, tag_id: :id]
has_many :images, Chiya.Notes.NoteImage
field :tags_string, :string,
virtual: true,
default: ""
timestamps()
end

View file

@ -0,0 +1,19 @@
defmodule Chiya.Notes.NoteTag do
@moduledoc """
The NoteTag module
"""
use Ecto.Schema
import Ecto.Changeset
schema "notes_tags" do
belongs_to :note, Chiya.Notes.Note
belongs_to :tag, Chiya.Tags.Tag
end
@doc false
def changeset(note_tag, attrs) do
note_tag
|> cast(attrs, [:note_id, :tag_id])
|> validate_required([:note_id, :tag_id])
end
end

View file

@ -75,7 +75,6 @@ defmodule Chiya.Notes.References do
end)
end
def update_references({:ok, note}, attrs) do
new_reference_slugs = get_reference_ids(attrs["content"])
old_reference_slugs = Enum.map(note.links_from, fn n -> n.slug end)
@ -118,6 +117,7 @@ defmodule Chiya.Notes.References do
attrs = get_attrs(origin_note.id, linked_note.id)
note_note = Chiya.Notes.get_note_note(attrs)
if note_note do
case Chiya.Notes.delete_note_note(note_note) do
{:ok, _note_note} ->
Logger.info("Reference to '#{slug}' deleted")
@ -125,6 +125,10 @@ defmodule Chiya.Notes.References do
error ->
Logger.warn(error)
end
else
Logger.debug("Note '#{slug}' does not exist anymore.")
end
end)
end

136
lib/chiya/tags.ex Normal file
View file

@ -0,0 +1,136 @@
defmodule Chiya.Tags do
@moduledoc """
The Tags context.
"""
import Ecto.Query, warn: false
alias Chiya.Repo
alias Chiya.Tags.Tag
@preloads [:notes]
defp with_preloads(query), do: preload(query, ^@preloads)
@doc """
Preloads a tag with all defined preloads.
## Examples
iex> preload_tag(tag)
%Tag{}
"""
def preload_tag(tag), do: Repo.preload(tag, @preloads)
@doc """
Returns the list of tags.
## Examples
iex> list_tags()
[%Tag{}, ...]
"""
def list_tags do
Tag
|> with_preloads()
|> order_by(:title)
|> Repo.all()
end
@doc """
Gets a single tag.
Raises `Ecto.NoResultsError` if the Tag does not exist.
## Examples
iex> get_tag!(123)
%Tag{}
iex> get_tag!(456)
** (Ecto.NoResultsError)
"""
def get_tag_by_slug!(slug), do: Repo.get_by!(Tag, slug: slug) |> preload_tag()
@doc """
Gets a single tag.
Returns nil if the Tag does not exist.
## Examples
iex> get_tag(123)
%Tag{}
iex> get_tag(456)
nil
"""
def get_tag_by_slug(slug), do: Repo.get_by(Tag, slug: slug) |> preload_tag()
@doc """
Creates a tag.
## Examples
iex> create_tag(%{field: value})
{:ok, %Tag{}}
iex> create_tag(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_tag(attrs \\ %{}) do
%Tag{}
|> Tag.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a tag.
## Examples
iex> update_tag(tag, %{field: new_value})
{:ok, %Tag{}}
iex> update_tag(tag, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_tag(%Tag{} = tag, attrs) do
tag
|> Tag.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a tag.
## Examples
iex> delete_tag(tag)
{:ok, %Tag{}}
iex> delete_tag(tag)
{:error, %Ecto.Changeset{}}
"""
def delete_tag(%Tag{} = tag) do
Repo.delete(tag)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking tag changes.
## Examples
iex> change_tag(tag)
%Ecto.Changeset{data: %Tag{}}
"""
def change_tag(%Tag{} = tag, attrs \\ %{}) do
Tag.changeset(tag, attrs)
end
end

32
lib/chiya/tags/tag.ex Normal file
View file

@ -0,0 +1,32 @@
defmodule Chiya.Tags.Tag do
@moduledoc """
The Tag Schema
"""
use Ecto.Schema
import Ecto.Changeset
alias Chiya.Tags.TagSlug
schema "tags" do
field :name, :string
field :slug, TagSlug.Type
field :content, :string
field :icon, :string
field :regex, :string
many_to_many :notes, Chiya.Notes.Note, join_through: "notes_tags"
timestamps()
end
@doc false
def changeset(tag, attrs) do
tag
|> cast(attrs, [:name, :content, :icon, :regex])
|> validate_required([:name])
|> unique_constraint(:name)
|> TagSlug.maybe_generate_slug()
|> TagSlug.unique_constraint()
end
end

View file

@ -0,0 +1,3 @@
defmodule Chiya.Tags.TagSlug do
use EctoAutoslugField.Slug, from: :name, to: :slug
end

View file

@ -0,0 +1,112 @@
defmodule Chiya.Tags.TagUpdater do
@moduledoc """
Updates tags for a schema like notes by adding new ones and removing old ones.
"""
require Logger
alias Chiya.{Notes, Tags}
alias Chiya.Notes.Note
def update_tags({:ok, %Note{} = note}, attrs) do
update_tags(note, attrs)
{:ok, note}
end
@doc """
Updates the tags for the given schema
## Examples
iex> update_tags(note, "foo,bar")
"""
def update_tags(schema, %{tags_string: new_tags} = attrs) when is_map(attrs) do
update_tags(schema, new_tags)
end
def update_tags(schema, %{"tags_string" => new_tags} = attrs) when is_map(attrs) do
update_tags(schema, new_tags)
end
def update_tags(schema, attrs) when is_map(attrs) do
schema
end
def update_tags(schema, new_tags) when is_binary(new_tags) do
update_tags(schema, split_tags(new_tags))
end
def update_tags(%{tags: tags} = schema, new_tags) when is_list(new_tags) do
old_tags = Enum.map(tags, fn tag -> tag.name end)
Logger.info("Adding tags #{inspect(new_tags -- old_tags)}")
Logger.info("Removing tags #{inspect(old_tags -- new_tags)}")
schema
|> add_tags(new_tags -- old_tags)
|> remove_tags(old_tags -- new_tags)
end
defp split_tags(tags_string) when is_binary(tags_string) do
tags_string
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.filter(&(String.length(&1) > 0))
end
defp add_tags(schema, tags) do
Enum.each(tags, &add_tag(schema, &1))
schema
end
defp add_tag(%{id: schema_id} = schema, tag) when is_binary(tag) do
slug = Slugger.slugify_downcase(tag)
Logger.debug("Looking up tag [#{tag}] with slug [#{slug}]")
{:ok, tag} =
case Tags.get_tag(slug) do
nil ->
Logger.debug("Tag [#{tag}] does not exist. Creating.")
Tags.create_tag(%{name: tag})
tag ->
Logger.debug("Tag already exists. Returning.")
{:ok, tag}
end
case schema do
%Note{} ->
attrs = %{
note_id: schema_id,
tag_id: tag.id
}
{:ok, _note_tag} = Notes.create_note_tag(attrs)
end
end
defp remove_tags(schema, tags) do
Enum.each(tags, &remove_tag(schema, &1))
schema
end
defp remove_tag(schema, tag) do
slug = Slugger.slugify_downcase(tag)
if tag = Tags.get_tag(slug) do
case schema do
%Note{} ->
attrs = %{tag_id: tag.id, note_id: schema.id}
note_tag = Notes.get_note_tag!(attrs)
{:ok, _} = Notes.delete_note_tag(note_tag)
end
else
Logger.warn("Tag with slug #{slug} was not removed.")
nil
end
end
end

View file

@ -321,8 +321,11 @@ defmodule ChiyaWeb.CoreComponents do
attr :prompt, :string, default: nil, doc: "the prompt for select inputs"
attr :options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2"
attr :multiple, :boolean, default: false, doc: "the multiple flag for select inputs"
attr :rest, :global, include: ~w(autocomplete accept cols disabled form max maxlength min minlength
attr :rest, :global,
include: ~w(autocomplete accept cols disabled form max maxlength min minlength
pattern placeholder readonly required rows size step)
slot :inner_block
def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do

View file

@ -32,8 +32,10 @@
</main>
<footer class="px-4 py-1 sm:px-6 lg:px-8 bg-white/30 dark:bg-black/30 text-gray-900 dark:text-gray-100 text-xs leading-6 font-semibold">
<span class="text-theme-primary">⌘</span> <span>Chiya</span> <span class="rounded-full bg-theme-primary/10 px-2 text-xs font-medium leading-6 text-theme-primary">
v<%= Application.spec(:chiya, :vsn) %></span>
<span class="text-theme-primary">⌘</span> <span>Chiya</span>
<span class="rounded-full bg-theme-primary/10 px-2 text-xs font-medium leading-6 text-theme-primary">
v<%= Application.spec(:chiya, :vsn) %>
</span>
</footer>
<%= raw(@settings.custom_html) %>

View file

@ -13,7 +13,10 @@
</section>
<section class="mt-4 flex gap-4">
<a href="/admin/notes/new" class="p-4 rounded text-gray-700 bg-gray-100 shadow border border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 hover:text-gray-700/80 dark:hover:text-gray-200/80 transition hover:shadow-none">
<a
href="/admin/notes/new"
class="p-4 rounded text-gray-700 bg-gray-100 shadow border border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 hover:text-gray-700/80 dark:hover:text-gray-200/80 transition hover:shadow-none"
>
<.icon name="hero-document-plus" /> New Note
</a>
</section>

View file

@ -10,7 +10,9 @@
<.table id="channels" rows={@channels} row_click={&JS.navigate(~p"/admin/channels/#{&1}")}>
<:col :let={channel} label="Name"><%= channel.name %></:col>
<:col :let={channel} label="Visibility"><%= Chiya.Channels.Channel.icon(channel) %> <%= channel.visibility %></:col>
<:col :let={channel} label="Visibility">
<%= Chiya.Channels.Channel.icon(channel) %> <%= channel.visibility %>
</:col>
<:col :let={channel} label="Notes"><%= Enum.count(channel.notes) %></:col>
<:col :let={channel} label="Slug"><%= channel.slug %></:col>
<:action :let={channel}>

View file

@ -8,7 +8,9 @@
</.header>
<.table id="identities" rows={@identities} row_click={&JS.navigate(~p"/admin/identities/#{&1}")}>
<:col :let={identity} label="Icon"><%= raw(identity.icon) %></:col>
<:col :let={identity} label="Icon">
<span class="fill-gray-900 dark:fill-gray-200"><%= raw(identity.icon) %></span>
</:col>
<:col :let={identity} label="Name"><%= identity.name %></:col>
<:col :let={identity} label="Rel"><%= identity.rel %></:col>
<:col :let={identity} label="Url"><%= identity.url %></:col>

View file

@ -24,7 +24,12 @@ defmodule ChiyaWeb.NoteController do
def new(conn, _params) do
changeset = Notes.change_note(%Note{})
render(conn, :new, changeset: changeset, channels: to_channel_options())
render(conn, :new,
changeset: changeset,
channels: to_channel_options(),
tags: []
)
end
def create(conn, %{"note" => note_params}) do
@ -50,7 +55,13 @@ defmodule ChiyaWeb.NoteController do
def edit(conn, %{"id" => id}) do
note = Notes.get_note_preloaded!(id)
changeset = Notes.change_note(note)
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
render(conn, :edit,
note: note,
changeset: changeset,
channels: to_channel_options(),
tags: note.tags
)
end
def update(conn, %{"id" => id, "note" => note_params}) do
@ -64,7 +75,11 @@ defmodule ChiyaWeb.NoteController do
|> redirect(to: ~p"/admin/notes/#{note}")
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
render(conn, :edit,
note: note,
changeset: changeset,
channels: to_channel_options(),
tags: note.tags)
end
end

View file

@ -9,8 +9,13 @@ defmodule ChiyaWeb.NoteHTML do
attr :changeset, Ecto.Changeset, required: true
attr :action, :string, required: true
attr :channels, :list, required: true
attr :tags, :list, required: true
def note_form(assigns)
def selected_channels(changeset), do: Enum.map(changeset.data.channels, fn c -> c.id end)
def selected_channels(changeset), do:
Enum.map(changeset.data.channels, fn c -> c.id end)
def tags_to_string(tags), do:
Enum.map_join(tags, ", ", fn t -> t.name end)
end

View file

@ -3,6 +3,6 @@
<:subtitle>Use this form to manage note records in your database.</:subtitle>
</.header>
<.note_form changeset={@changeset} action={~p"/admin/notes/#{@note}"} channels={@channels} />
<.note_form changeset={@changeset} action={~p"/admin/notes/#{@note}"} channels={@channels} tags={@tags} />
<.back navigate={~p"/admin/notes"}>Back to notes</.back>

View file

@ -3,7 +3,11 @@
<:subtitle>Use this form to manage note image records in your database.</:subtitle>
</.header>
<.simple_form :let={f} for={@changeset} action={~p"/admin/notes/#{@image.note.id}/image/#{@image.id}"}>
<.simple_form
:let={f}
for={@changeset}
action={~p"/admin/notes/#{@image.note.id}/image/#{@image.id}"}
>
<.error :if={@changeset.action}>
Oops, something went wrong! Please check the errors below.
</.error>
@ -17,5 +21,4 @@
</:actions>
</.simple_form>
<.back navigate={~p"/admin/notes/#{@image.note_id}"}>Back to notes</.back>

View file

@ -12,9 +12,20 @@
</.header>
<section class="flex flex-row flex-wrap mt-4 gap-3">
<a href={~p"/admin/notes"} class="text-sm dark:text-gray-300 rounded-full bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-2 py-1">All</a>
<a
href={~p"/admin/notes"}
class="text-sm dark:text-gray-300 rounded-full bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-2 py-1"
>
All
</a>
<%= for channel <- @channels do %>
<a href={~p"/admin/notes?channel=#{channel.slug}"} class="text-sm dark:text-gray-300 rounded-full bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-2 py-1"><%= channel.name %> <span class="text-gray-600 dark:text-gray-500">(<%= Enum.count(channel.notes) %>)</span></a>
<a
href={~p"/admin/notes?channel=#{channel.slug}"}
class="text-sm dark:text-gray-300 rounded-full bg-gray-300 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 px-2 py-1"
>
<%= channel.name %>
<span class="text-gray-600 dark:text-gray-500">(<%= Enum.count(channel.notes) %>)</span>
</a>
<% end %>
</section>

View file

@ -3,6 +3,6 @@
<:subtitle>Use this form to manage note records in your database.</:subtitle>
</.header>
<.note_form changeset={@changeset} action={~p"/admin/notes"} channels={@channels} />
<.note_form changeset={@changeset} action={~p"/admin/notes"} channels={@channels} tags={@tags} />
<.back navigate={~p"/admin/notes"}>Back to notes</.back>

View file

@ -14,6 +14,7 @@
options={Ecto.Enum.values(Chiya.Notes.Note, :kind)}
/>
<.input field={f[:url]} type="text" label="Url" />
<.input field={f[:tags_string]} type="text" label="Tags" value={tags_to_string(@tags)} />
<.input
field={f[:channels]}
type="select"
@ -22,7 +23,9 @@
options={@channels}
value={selected_channels(@changeset)}
/>
<:actions>
<.button>Save Note</.button>
</:actions>
</.simple_form>

View file

@ -29,6 +29,16 @@ defmodule ChiyaWeb.PageController do
)
end
def tag(conn, %{"slug" => tag_slug}) do
tag = Chiya.Tags.get_tag_by_slug!(tag_slug)
render(conn, :tag,
layout: {ChiyaWeb.Layouts, "public.html"},
tag: tag,
page_title: tag.name
)
end
def note(conn, %{"slug" => note_slug}) do
note = Chiya.Notes.get_note_by_slug_preloaded!(note_slug)

View file

@ -2,4 +2,7 @@ defmodule ChiyaWeb.PageHTML do
use ChiyaWeb, :html_public
embed_templates "page_html/*"
def tag_list([]), do: "No Tags"
def tag_list(tags), do: Enum.map_join(tags, ", ", fn t -> t.name end)
end

View file

@ -4,9 +4,18 @@
<%= @note.name %>
</h1>
<p class="mt-2 text-sm leading-6 text-theme-base">
<span>Published</span> <time class="text-theme-primary font-semibold"><%= pretty_date(@note.published_at) %></time>
<span>Published</span>
<time class="text-theme-primary font-semibold"><%= pretty_date(@note.published_at) %></time>
<span>·</span>
<span>Last Updated</span> <time class="text-theme-primary font-semibold"><%= pretty_date(@note.updated_at) %></time>
<span>Last Updated</span>
<time class="text-theme-primary font-semibold"><%= pretty_date(@note.updated_at) %></time>
<span>·</span>
<span>Tags</span>
<span class="text-theme-primary font-semibold">
<%= for tag <- @note.tags do %>
<a href={~p"/t/#{tag.slug}"}><%= tag.name %></a>
<% end %>
</span>
<%= if @current_user do %>
<span>·</span>
<a href={~p"/admin/notes/#{@note}/edit"} class="text-theme-primary font-semibold">Edit</a>
@ -23,8 +32,16 @@
<div class="flex flex-wrap gap-3">
<%= for image <- @note.images do %>
<a href={ChiyaWeb.Uploaders.NoteImage.url({image.path, image}, :full_dithered)} class="lightbox | w-28" data-gallery="note" data-description={ChiyaWeb.Markdown.render(image.content)}>
<img src={ChiyaWeb.Uploaders.NoteImage.url({image.path, image}, :thumb_dithered)} class="rounded"/>
<a
href={ChiyaWeb.Uploaders.NoteImage.url({image.path, image}, :full_dithered)}
class="lightbox | w-28"
data-gallery="note"
data-description={ChiyaWeb.Markdown.render(image.content)}
>
<img
src={ChiyaWeb.Uploaders.NoteImage.url({image.path, image}, :thumb_dithered)}
class="rounded"
/>
</a>
<% end %>
</div>

View file

@ -0,0 +1,20 @@
<div class="mx-auto max-w-xl mx-4 lg:mx-0">
<h1 class="mt-16 text-[2rem] font-semibold font-serif leading-10 tracking-tighter text-theme-heading">
Tagged with &ldquo;<%= @tag.name %>&rdquo;
</h1>
<p class="mt-4 text-base leading-7 text-theme-base">
<%= @tag.content %>
</p>
<div class="w-full mt-6 sm:w-auto flex flex-col gap-1.5">
<%= for note <- @tag.notes do %>
<a
href={~p"/#{note.slug}"}
class="rounded -mx-2 -my-0.5 px-2 py-0.5 hover:bg-theme-primary/10 transition"
>
<span class="text-theme-primary text-lg font-semibold leading-8"><%= note.name %></span>
<span class="text-theme-base text-sm"><%= pretty_date(note.published_at) %></span>
</a>
<% end %>
</div>
</div>

View file

@ -29,10 +29,13 @@ defmodule ChiyaWeb.NoteShowLive do
</.header>
<.list>
<:item title="Published at"><%= pretty_date(@note.published_at) %> <span>(<%= from_now(@note.published_at) %>)</span></:item>
<:item title="Published at">
<%= pretty_date(@note.published_at) %> <span>(<%= from_now(@note.published_at) %>)</span>
</:item>
<:item title="Channels"><%= @channels %></:item>
<:item title="Kind"><%= @note.kind %></:item>
<:item title="Url"><%= @note.url %></:item>
<:item title="Tags"><%= note_tags(@note.tags) %></:item>
<:item title="Links outgoing"><%= note_links(@note.links_to) %></:item>
<:item title="Links incoming"><%= note_links(@note.links_from) %></:item>
</.list>
@ -48,11 +51,7 @@ defmodule ChiyaWeb.NoteShowLive do
class="rounded-lg border border-theme-dim w-28"
src={ChiyaWeb.Uploaders.NoteImage.url({image.path, image}, :thumb_dithered)}
/>
<.button
phx-click="delete_image"
phx-value-id={image.id}
data-confirm="Are you sure?"
>
<.button phx-click="delete_image" phx-value-id={image.id} data-confirm="Are you sure?">
Delete Image
</.button>
</a>
@ -153,7 +152,6 @@ defmodule ChiyaWeb.NoteShowLive do
{:noreply, assign(socket, :note, Notes.get_note_preloaded!(socket.assigns.note.id))}
end
defp note_links(notes) do
Enum.map_join(notes, ", ",fn n -> n.name end)
end
defp note_links(notes), do: Enum.map_join(notes, ", ", fn n -> n.name end)
defp note_tags(tags), do: Enum.map_join(tags, ", ", fn t -> t.name end)
end

View file

@ -21,7 +21,7 @@ defmodule ChiyaWeb.UserLoginLive do
<:actions>
<.input field={@form[:remember_me]} type="checkbox" label="Keep me logged in" />
<.link href={~p"/user/reset_password"} class="text-sm font-semibold">
<.link href={~p"/user/reset_password"} class="text-sm font-semibold dark:text-gray-100">
Forgot your password?
</.link>
</:actions>

View file

@ -117,6 +117,7 @@ defmodule ChiyaWeb.Router do
get "/:slug", PageController, :note
get "/c/:slug", PageController, :channel
get "/t/:slug", PageController, :tag
get "/", PageController, :home
end
end

View file

@ -2,14 +2,16 @@ defmodule Chiya.Repo.Migrations.FixNoteImageReference do
use Ecto.Migration
def up do
drop constraint :note_images, "note_images_note_id_fkey"
drop constraint(:note_images, "note_images_note_id_fkey")
alter table(:note_images) do
modify :note_id, references(:notes, on_delete: :delete_all)
end
end
def down do
drop constraint :note_images, "note_images_note_id_fkey"
drop constraint(:note_images, "note_images_note_id_fkey")
alter table(:note_images) do
modify :note_id, references(:notes, on_delete: :nothing)
end

View file

@ -0,0 +1,24 @@
defmodule Chiya.Repo.Migrations.AddTags do
use Ecto.Migration
def change do
create table(:tags) do
add :name, :string
add :slug, :string
add :content, :text
add :icon, :string
add :regex, :string
timestamps()
end
create table(:notes_tags) do
add :note_id, references(:notes, on_delete: :delete_all)
add :tag_id, references(:tags, on_delete: :delete_all)
end
create unique_index(:tags, [:slug])
create unique_index(:tags, [:name])
create unique_index(:notes_tags, [:note_id, :tag_id])
end
end