diff --git a/.gitignore b/.gitignore index 38e566f..31a0f45 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,4 @@ malarkey-*.tar npm-debug.log /assets/node_modules/ -priv/ \ No newline at end of file +priv/static/uploads/ \ No newline at end of file diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po new file mode 100644 index 0000000..844c4f5 --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -0,0 +1,112 @@ +## `msgid`s in this file come from POT (.pot) files. +## +## Do not add, change, or remove `msgid`s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use `mix gettext.extract --merge` or `mix gettext.merge` +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en\n" + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} byte(s)" +msgid_plural "should be %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} byte(s)" +msgid_plural "should be at least %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} byte(s)" +msgid_plural "should be at most %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot new file mode 100644 index 0000000..eef2de2 --- /dev/null +++ b/priv/gettext/errors.pot @@ -0,0 +1,109 @@ +## This is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here has no +## effect: edit them in PO (`.po`) files instead. +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} byte(s)" +msgid_plural "should be %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} byte(s)" +msgid_plural "should be at least %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} byte(s)" +msgid_plural "should be at most %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/priv/repo/migrations/.formatter.exs b/priv/repo/migrations/.formatter.exs new file mode 100644 index 0000000..49f9151 --- /dev/null +++ b/priv/repo/migrations/.formatter.exs @@ -0,0 +1,4 @@ +[ + import_deps: [:ecto_sql], + inputs: ["*.exs"] +] diff --git a/priv/repo/migrations/20251029000001_create_users.exs b/priv/repo/migrations/20251029000001_create_users.exs new file mode 100644 index 0000000..7fbf60c --- /dev/null +++ b/priv/repo/migrations/20251029000001_create_users.exs @@ -0,0 +1,28 @@ +defmodule Malarkey.Repo.Migrations.CreateUsers do + use Ecto.Migration + + def change do + create table(:users, primary_key: false) do + add :id, :binary_id, primary_key: true + add :email, :string + add :username, :string, null: false + add :display_name, :string + add :bio, :text + add :location, :string + add :website, :string + add :avatar_url, :string + add :header_url, :string + add :hashed_password, :string + add :verified, :boolean, default: false + add :followers_count, :integer, default: 0 + add :following_count, :integer, default: 0 + add :posts_count, :integer, default: 0 + add :confirmed_at, :naive_datetime + + timestamps() + end + + create unique_index(:users, [:email]) + create unique_index(:users, [:username]) + end +end diff --git a/priv/repo/migrations/20251029000002_create_users_auth_tables.exs b/priv/repo/migrations/20251029000002_create_users_auth_tables.exs new file mode 100644 index 0000000..4fa5907 --- /dev/null +++ b/priv/repo/migrations/20251029000002_create_users_auth_tables.exs @@ -0,0 +1,35 @@ +defmodule Malarkey.Repo.Migrations.CreateUsersAuthTables do + use Ecto.Migration + + def change do + # Users tokens for sessions, email confirmation, password reset + create table(:users_tokens, primary_key: false) do + add :id, :binary_id, primary_key: true + add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + add :token, :binary, null: false + add :context, :string, null: false + add :sent_to, :string + timestamps(updated_at: false) + end + + create index(:users_tokens, [:user_id]) + create unique_index(:users_tokens, [:context, :token]) + + # OAuth identities table + create table(:oauth_identities, primary_key: false) do + add :id, :binary_id, primary_key: true + add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + add :provider, :string, null: false + add :provider_uid, :string, null: false + add :provider_email, :string + add :provider_login, :string + add :provider_token, :text + add :provider_meta, :map + + timestamps() + end + + create index(:oauth_identities, [:user_id]) + create unique_index(:oauth_identities, [:provider, :provider_uid]) + end +end diff --git a/priv/repo/migrations/20251029000003_create_posts.exs b/priv/repo/migrations/20251029000003_create_posts.exs new file mode 100644 index 0000000..b5348a2 --- /dev/null +++ b/priv/repo/migrations/20251029000003_create_posts.exs @@ -0,0 +1,27 @@ +defmodule Malarkey.Repo.Migrations.CreatePosts do + use Ecto.Migration + + def change do + create table(:posts, primary_key: false) do + add :id, :binary_id, primary_key: true + add :content, :text, null: false + add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + add :reply_to_id, references(:posts, type: :binary_id, on_delete: :nilify_all) + add :repost_of_id, references(:posts, type: :binary_id, on_delete: :delete_all) + add :quote_post_id, references(:posts, type: :binary_id, on_delete: :nilify_all) + add :media_urls, {:array, :string}, default: [] + add :likes_count, :integer, default: 0 + add :reposts_count, :integer, default: 0 + add :replies_count, :integer, default: 0 + add :views_count, :integer, default: 0 + + timestamps() + end + + create index(:posts, [:user_id]) + create index(:posts, [:reply_to_id]) + create index(:posts, [:repost_of_id]) + create index(:posts, [:quote_post_id]) + create index(:posts, [:inserted_at]) + end +end diff --git a/priv/repo/migrations/20251029000004_create_likes.exs b/priv/repo/migrations/20251029000004_create_likes.exs new file mode 100644 index 0000000..571de3c --- /dev/null +++ b/priv/repo/migrations/20251029000004_create_likes.exs @@ -0,0 +1,17 @@ +defmodule Malarkey.Repo.Migrations.CreateLikes do + use Ecto.Migration + + def change do + create table(:likes, primary_key: false) do + add :id, :binary_id, primary_key: true + add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + add :post_id, references(:posts, type: :binary_id, on_delete: :delete_all), null: false + + timestamps(updated_at: false) + end + + create index(:likes, [:user_id]) + create index(:likes, [:post_id]) + create unique_index(:likes, [:user_id, :post_id]) + end +end diff --git a/priv/repo/migrations/20251029000005_create_follows.exs b/priv/repo/migrations/20251029000005_create_follows.exs new file mode 100644 index 0000000..aed1ef4 --- /dev/null +++ b/priv/repo/migrations/20251029000005_create_follows.exs @@ -0,0 +1,20 @@ +defmodule Malarkey.Repo.Migrations.CreateFollows do + use Ecto.Migration + + def change do + create table(:follows, primary_key: false) do + add :id, :binary_id, primary_key: true + add :follower_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + add :following_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false + + timestamps(updated_at: false) + end + + create index(:follows, [:follower_id]) + create index(:follows, [:following_id]) + create unique_index(:follows, [:follower_id, :following_id]) + + # Ensure users can't follow themselves + create constraint(:follows, :cannot_follow_self, check: "follower_id != following_id") + end +end diff --git a/priv/repo/migrations/20251031160042_add_media_types_to_posts.exs b/priv/repo/migrations/20251031160042_add_media_types_to_posts.exs new file mode 100644 index 0000000..e2184d5 --- /dev/null +++ b/priv/repo/migrations/20251031160042_add_media_types_to_posts.exs @@ -0,0 +1,9 @@ +defmodule Malarkey.Repo.Migrations.AddMediaTypesToPosts do + use Ecto.Migration + + def change do + alter table(:posts) do + add :media_types, {:array, :string}, default: [] + end + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs new file mode 100644 index 0000000..5c906d7 --- /dev/null +++ b/priv/repo/seeds.exs @@ -0,0 +1,11 @@ +# Script for populating the database. You can run it as: +# +# mix run priv/repo/seeds.exs +# +# Inside the script, you can read and write to any of your +# repositories directly: +# +# Malarkey.Repo.insert!(%Malarkey.SomeSchema{}) +# +# We recommend using the bang functions (`insert!`, `update!` +# and so on) as they will fail if something goes wrong. diff --git a/priv/static/favicon.ico b/priv/static/favicon.ico new file mode 100644 index 0000000..7f372bf Binary files /dev/null and b/priv/static/favicon.ico differ diff --git a/priv/static/images/logo.svg b/priv/static/images/logo.svg new file mode 100644 index 0000000..9f26bab --- /dev/null +++ b/priv/static/images/logo.svg @@ -0,0 +1,6 @@ + diff --git a/priv/static/robots.txt b/priv/static/robots.txt new file mode 100644 index 0000000..26e06b5 --- /dev/null +++ b/priv/static/robots.txt @@ -0,0 +1,5 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: /