code:
#!/bin/bash
if [ ! -d ".git" ]; then
	#not running in git repo, so can't use git commands :-)
	echo "No .git repo found - skipping sanity checks"
	exit 0
fi

WARNING='\033[93m'
WARNING='\033[93m'
ENDC='\033[0m'

myprint() {
	while read data; do
		echo -n -e "[$1]$WARNING"
		echo "$data"
	done
}

GREP="git grep -n --color"
# Check for missing right angle bracket: <</if>
$GREP "<</[^>]*>[^>]" -- 'game/*'  | myprint "MissingClosingAngleBracket"
$GREP "<<[^>()]*>[^()<>"$'\r]*\r'"\?$" -- 'game/*' | myprint "MissingClosingAngleBracket"
# Check for missing left angle bracket: </if>>
$GREP "\([^<]\|^\)</\?\(if\|else\|case\|set\|print\|elseif\)" -- 'game/*' | myprint "MissingOpeningAngleBracket2"
# Check for accidental assignment.  e.g.:   <<if $foo = "hello">>
$GREP "<<[ ]*if[^>=]*[^><\!=]=[^=][^>]*>>" -- 'game/*' | myprint "AccidentalAssignmentInIf"
# Check for accidental assignment.  e.g.:   <<elseif $foo = "hello">>
$GREP "<<[ ]*elseif[^>=]*[^><\!=]=[^=][^>]*>>" -- 'game/*' | myprint "AccidentalAssignmentInElseIf"
# Check for missing ".  e.g.:   <<if $foo == "hello>>
# $GREP -e "<<[^\"<>]*\"[^\"<>]*>>" -- 'game/*' | myprint "MissingSpeechMark"
# Check for missing ".  e.g.:   <<if $foo = "hello)
# $GREP -e "<<[^\"<>]*\([^\"<>]*\"[^><\"]*\"\| [<>] \)*\"\([^\"<>]*\"[^><\"]*\"\| [<>] \)*\([^\"<>]\| [<>] \)*>>" --and --not -e "*[^']*" -- 'game/*' | myprint "MissingSpeechMark2"
# Check for colours like: @@color:red   - should be @@.red
$GREP -e "@@color:" --and --not -e  "@@color:rgb([0-9 ]\+,[0-9 ]\+,[0-9 ]\+)" -- "game/*" | myprint "UseCssColors"
# Check for missing $ in activeSlave or PC
$GREP "<<[ ]*[^\$><_\[]*\(activeSlave\|PC\)[.]"  -- "game/*" | myprint "MissingDollar"
# Check for closing bracket without opening bracket.  e.g.:  <<if foo)>>	  (but  <<case "foo")>>   is valid, so ignore those
$GREP -e "<<[ a-zA-Z]\+\([^()<>]\|[^()<>][<>][^()<>]\)*)" --and --not -e "<< *case"  -- "game/*" | myprint "MissingOpeningBracket"
# Check for opening bracket without closing bracket.  e.g.:  <<if (foo>>
$GREP -e "<<[ a-zA-Z]\([^<>]\|[^<>][<>][^<>]\)\+(\([^()<>]\|[^<>()][<>][^<>()]\|([^<>()]*])\)*>>" -- "game/*" | myprint "MissingClosingBracket"
# Check for two closing brackets but one opening bracket.  e.g.:  <<if (foo))>>
$GREP -e "<<[ a-zA-Z]\+[^()<>]*([^()]*)[^()]*)[^()<>]*>>"  -- "game/*" | myprint "MissingOpeningBracket2"
# Check for one closing bracket but two opening brackets.  e.g.:  <<if ((foo)>>
$GREP -e "<<[ a-zA-Z]\+[^()<>]*([^()]*([^()]*)[^()<>]*>>"  -- "game/*" | myprint "MissingClosingBracket2"
$GREP -e "<<.*[(][^<>)]*[(][^<>)]*)\?[^<>)]*>>" -- "game/*" | myprint "MissingClosingBracket3"
# Check for missing >>.  e.g.:   <<if $foo
$GREP "<<[^<>]*[^,\"\[{"$'\r]\r'"\?$" -- 'game/*' | myprint "MissingClosingAngleBrackets"
# Check for too many >>>.  e.g.: <</if>>>
$GREP "<<[^<>\"]*[<>]\?[^<>\"]*>>>" -- "game/*" | myprint "TooManyAngleBrackets"
# Check for too many <<<.  e.g.: <<</if>>
$GREP "<<<[^<>\"]*[<>]\?[^<>\"]*>>" -- "game/*" | myprint "TooManyAngleBrackets2"
# Check for wrong capitalisation on 'activeslave' and other common typos
$GREP  "\(csae\|[a-z] She \|attepmts\|youreslf\|advnaces\|canAcheive\|setBellySize\|SetbellySize\|setbellySize\|bellypreg\|pregBelly\|bellyimplant\|bellyfluid\|pronounCaps\|carress\)" -- 'game/*' | myprint "SpellCheck"
$GREP  "\(recieve\|recieves\)" -- 'game/*' | myprint "PregmodderCannotSpellReceive"
$GREP "\$slave\[" -- 'game/*' | myprint "ShouldBeSlaves"
# Check for strange spaces e.g.  $slaves[$i]. lips
$GREP "\$slaves\[\$i\]\. " -- 'game/*' | myprint "MissingPropertyAfterSlaves"
# Check, e.g., <<//if>>
$GREP "<</[a-zA-Z]*[^a-zA-Z<>]\+[a-zA-Z]*>>" -- 'game/*' | myprint "DoubleSlash"
# Check, e.g.  <<else $foo==4
$GREP "<<else >\?[^>]" -- 'game/*' | myprint "ShouldBeElseIf"
# Check, e.g., =to
$GREP "=to" -- 'game/*' | myprint "EqualAndTo"
# Check, e.g.  <<set foo == 4>>
$GREP "<<set[^{>=]*==" -- 'game/*' | myprint "DoubleEqualsInSet"
# Check for, e.g   <<if slaves[foo]>>
$GREP "<<\([^>]\|[^>]>[^>]\)*[^$]slaves\[" -- 'game/*' | myprint "MissingDollar"
# Check for missing $ or _ in variable name:
$GREP -e "<<[a-zA-Z]\([^>\"]\|[^>]>[^>]\|\"[^\"]*\"\)* [a-zA-Z]\+ * =" -- game/* | myprint "MissingDollar2"
# Check for missing command, e.g.  <<foo =
$GREP -e "<<[a-zA-Z]* = *" -- game/* | myprint "BadCommand"
# Check for duplicate words, e.g. with with
$GREP -e  " \(\b[a-zA-Z][a-zA-Z]\+\) \1\b " --and --not -e " her her " --and --not -e " you you " --and --not -e " New New " --and --not -e "Slave Slave " --and --not -e " that that " --and --not -e " in in " --and --not -e " is is " -- 'game/*' | myprint "Duplicate words"
# Check for obsolete SugarCube macros
$GREP -E "<<display |<<click|<<.*\.contains" -- game/* | myprint "ObsoleteMacro"
# Check for double articles
$GREP -E "\Wa an\W" -- game/* | myprint "DoubleArticle"
# Check for incorrect articles
$GREP -i -E "\Wa (a|e|i|o|u)." -- game/* | grep -a -i -vE "\Wa (un|eu|us|ut|on|ur|in)." | grep -a -i -vE "(&|<<s>>|UM)." | myprint "IncorrectArticle"
$GREP -i -E "\Wan (b|c|d|f|g|j|k|l|m|n|p|q|r|s|t|v|w|x|y|z)\w." -- game/* | grep -a -i -vE "[A-Z]{3}" | myprint "IncorrectArticle"
# Check for $ sign mid-word
$GREP -i "\w$\w" -- game/* | myprint "VarSignMidWord"
# check for $ sign at beginning of macro
$GREP '<<\s*\$' -- 'game/*'  | myprint "VarSignAtMacroStart"
# check for missing ; before statement
$GREP 'if $ ' -- 'game/*'  | myprint "missing ; before statement"
$GREP 'elseif $ ' -- 'game/*'  | myprint "missing ; before statement"
# Check that we do not have any variables that we use only once.   e.g.	 $onlyUsedOnce
# Ignore  *Nationalities
(
cd game/
find . -name "*.twee" -exec cat '{}' ';' | tr -c '$a-zA-Z' '\n'  | sed -n '/^[$]/p' | sort | uniq -u | sed 's/^[$]/-e[$]/' | sed 's/$/\\\\W/' | xargs -r  git grep -n --color | myprint "OnlyUsedOnce"
find . -name "*.twee" -exec cat '{}' ';' | tr -c '.$a-zA-Z[]_' '\n' | sed 's/SugarCube\.State\.variables\./$/g' | sed -n -e 's/^[$]\(PC\|activeSlave\|\(slaves\|tanks\)\[[^]]*\]*\)[.]\([a-zA-Z]\+\).*$/[.]\3/p' | sort | uniq -u |sed 's/^\(.*\)$/-e\1\\\\\b/'  | xargs -r git grep -n --color | myprint "SlaveAttributeUsedOnce"
$GREP "\$\(PC\|activeSlave\|slaves\|tanks\)[.][^a-zA-Z]" | myprint "UnexpectedCharAfterDot"

#Find all the variables listed in init.twee
VARIABLELIST=$(cat base-system/init.twee | tr -c '$a-zA-Z' '\n'  | sed -n '/^[$]/p' | sort | uniq)
# Find all variables anywhere.  Commented out because the output is too noisy currently
#VARIABLELIST=$(find . -name "*.twee" -exec cat '{}' ';' | tr -c '$a-zA-Z' '\n'  | sed -n '/^[$]/p' | sort | uniq)
MISSINGFROMVERSIONUPDATE=$(for variable in $VARIABLELIST; do grep -q "$variable" base-system/version-update.twee || echo "$variable"; done)
echo -e "base-system/version-update.twee$ENDC: $(echo $MISSINGFROMVERSIONUPDATE)" | myprint "MissingFromVersionUpdate"
)

git ls-files "game/*.twee" | xargs -d '\n'  ./devTools/check.py

path: sanityCheck


code:
/*CSS override*/
/*Remove comments to apply*/
/*#stats {
	top: 256px;
}

body.has-images #story-caption {
	margin-top: calc(278px - 2.5em);
}*/

repo_name: fizzyboi/degrees

license: mit


code:
# App artifacts
/_build
/db
/deps
/*.ez

# Generated on crash by the VM
erl_crash.dump

# Files matching config/*.secret.exs pattern contain sensitive
# data and you should not commit them into version control.
#
# Alternatively, you may comment the line below and commit the
# secrets files as long as you replace their contents by environment
# variables.
/config/*.secret.exs

repo_name: spam/pleroma

path: .gitignore

license: unknown


code:
# This file is a template, and might need editing before it works on your project.
image: elixir:latest

# Pick zero or more services to be used on all builds.
# Only needed when using a docker container to run your tests in.
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service
services:
  - postgres:latest

before_script:
  - mix local.rebar --force
  - mix local.hex --force
  - mix deps.get

mix:
  script:
  - mix test


code:
# Pleroma

To start your Phoenix server:

  * Install dependencies with `mix deps.get`
  * Create and migrate your database with `mix ecto.create && mix ecto.migrate`
  * Start Phoenix endpoint with `mix phx.server`

Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.

Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment).

## Learn more

  * Official website: http://www.phoenixframework.org/
  * Guides: http://phoenixframework.org/docs/overview
  * Docs: https://hexdocs.pm/phoenix
  * Mailing list: http://groups.google.com/group/phoenix-talk
  * Source: https://github.com/phoenixframework/phoenix


code:
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.
use Mix.Config

# General application configuration
config :pleroma,
  ecto_repos: [Pleroma.Repo]

# Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
  render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
  pubsub: [name: Pleroma.PubSub,
           adapter: Phoenix.PubSub.PG2]

# Configures Elixir's Logger
config :logger, :console,
  format: "$time $metadata[$level] $message\n",
  metadata: [:request_id]

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env}.exs"


code:
use Mix.Config

# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources.
config :pleroma, Pleroma.Web.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: []

# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# command from your terminal:
#
#     openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem
#
# The `http:` config above can be replaced with:
#
#     https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
#
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

# Configure your database
config :pleroma, Pleroma.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "pleroma_dev",
  hostname: "localhost",
  pool_size: 10


code:
use Mix.Config

# For production, we often load configuration from external
# sources, such as your system environment. For this reason,
# you won't find the :http configuration below, but set inside
# Pleroma.Web.Endpoint.load_from_system_env/1 dynamically.
# Any dynamic configuration should be moved to such function.
#
# Don't forget to configure the url host to something meaningful,
# Phoenix uses this information when generating URLs.
#
# Finally, we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the mix phoenix.digest task
# which you typically run after static files are built.
config :pleroma, Pleroma.Web.Endpoint,
  on_init: {Pleroma.Web.Endpoint, :load_from_system_env, []},
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json"

# Do not print debug messages in production
config :logger, level: :info

# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
#
#     config :pleroma, Pleroma.Web.Endpoint,
#       ...
#       url: [host: "example.com", port: 443],
#       https: [:inet6,
#               port: 443,
#               keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
#               certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
#
# Where those two env variables return an absolute path to
# the key and cert in disk or a relative path inside priv,
# for example "priv/ssl/server.key".
#
# We also recommend setting `force_ssl`, ensuring no data is
# ever sent via http, always redirecting to https:
#
#     config :pleroma, Pleroma.Web.Endpoint,
#       force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.

# ## Using releases
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start the server for all endpoints:
#
#     config :phoenix, :serve_endpoints, true
#
# Alternatively, you can configure exactly which server to
# start per endpoint:
#
#     config :pleroma, Pleroma.Web.Endpoint, server: true
#

# Finally import the config/prod.secret.exs
# which should be versioned separately.
import_config "prod.secret.exs"


code:
use Mix.Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :pleroma, Pleroma.Web.Endpoint,
  http: [port: 4001],
  server: false

# Print only warnings and errors during test
config :logger, level: :warn

# Configure your database
config :pleroma, Pleroma.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "pleroma_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox


code:
defmodule Pleroma.Application do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec

    # Define workers and child supervisors to be supervised
    children = [
      # Start the Ecto repository
      supervisor(Pleroma.Repo, []),
      # Start the endpoint when the application starts
      supervisor(Pleroma.Web.Endpoint, []),
      # Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3)
      # worker(Pleroma.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Pleroma.Supervisor]
    Supervisor.start_link(children, opts)
  end
end


code:
defmodule Pleroma.Repo do
  use Ecto.Repo, otp_app: :pleroma

  @doc """
  Dynamically loads the repository url from the
  DATABASE_URL environment variable.
  """
  def init(_, opts) do
    {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
  end
end


code:
defmodule Pleroma.Web.UserSocket do
  use Phoenix.Socket

  ## Channels
  # channel "room:*", Pleroma.Web.RoomChannel

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket
  # transport :longpoll, Phoenix.Transports.LongPoll

  # Socket params are passed from the client and can
  # be used to verify and authenticate a user. After
  # verification, you can put default assigns into
  # the socket that will be set for all channels, ie
  #
  #     {:ok, assign(socket, :user_id, verified_user_id)}
  #
  # To deny connection, return `:error`.
  #
  # See `Phoenix.Token` documentation for examples in
  # performing token verification on connect.
  def connect(_params, socket) do
    {:ok, socket}
  end

  # Socket id's are topics that allow you to identify all sockets for a given user:
  #
  #     def id(socket), do: "user_socket:#{socket.assigns.user_id}"
  #
  # Would allow you to broadcast a "disconnect" event and terminate
  # all active sockets and channels for a given user:
  #
  #     Pleroma.Web.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
  #
  # Returning `nil` makes this socket anonymous.
  def id(_socket), do: nil
end


code:
defmodule Pleroma.Web.Endpoint do
  use Phoenix.Endpoint, otp_app: :pleroma

  socket "/socket", Pleroma.Web.UserSocket

  # Serve at "/" the static files from "priv/static" directory.
  #
  # You should set gzip to true if you are running phoenix.digest
  # when deploying your static files in production.
  plug Plug.Static,
    at: "/", from: :pleroma, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

  # Code reloading can be explicitly enabled under the
  # :code_reloader configuration of your endpoint.
  if code_reloading? do
    plug Phoenix.CodeReloader
  end

  plug Plug.RequestId
  plug Plug.Logger

  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Poison

  plug Plug.MethodOverride
  plug Plug.Head

  # The session will be stored in the cookie and signed,
  # this means its contents can be read but not tampered with.
  # Set :encryption_salt if you would also like to encrypt it.
  plug Plug.Session,
    store: :cookie,
    key: "_pleroma_key",
    signing_salt: "CqaoopA2"

  plug Pleroma.Web.Router

  @doc """
  Dynamically loads configuration from the system environment
  on startup.

  It receives the endpoint configuration from the config files
  and must return the updated configuration.
  """
  def load_from_system_env(config) do
    port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
    {:ok, Keyword.put(config, :http, [:inet6, port: port])}
  end
end


code:
defmodule Pleroma.Web.Gettext do
  @moduledoc """
  A module providing Internationalization with a gettext-based API.

  By using [Gettext](https://hexdocs.pm/gettext),
  your module gains a set of macros for translations, for example:

      import Pleroma.Web.Gettext

      # Simple translation
      gettext "Here is the string to translate"

      # Plural translation
      ngettext "Here is the string to translate",
               "Here are the strings to translate",
               3

      # Domain-based translation
      dgettext "errors", "Here is the error message to translate"

  See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
  """
  use Gettext, otp_app: :pleroma
end


code:
defmodule Pleroma.Web.Router do
  use Pleroma.Web, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/api", Pleroma.Web do
    pipe_through :api
  end
end


code:
defmodule Pleroma.Web.ErrorHelpers do
  @moduledoc """
  Conveniences for translating and building error messages.
  """

  @doc """
  Translates an error message using gettext.
  """
  def translate_error({msg, opts}) do
    # Because error messages were defined within Ecto, we must
    # call the Gettext module passing our Gettext backend. We
    # also use the "errors" domain as translations are placed
    # in the errors.po file.
    # Ecto will pass the :count keyword if the error message is
    # meant to be pluralized.
    # On your own code and templates, depending on whether you
    # need the message to be pluralized or not, this could be
    # written simply as:
    #
    #     dngettext "errors", "1 file", "%{count} files", count
    #     dgettext "errors", "is invalid"
    #
    if count = opts[:count] do
      Gettext.dngettext(Pleroma.Web.Gettext, "errors", msg, msg, count, opts)
    else
      Gettext.dgettext(Pleroma.Web.Gettext, "errors", msg, opts)
    end
  end
end


code:
defmodule Pleroma.Web.ErrorView do
  use Pleroma.Web, :view

  def render("404.json", _assigns) do
    %{errors: %{detail: "Page not found"}}
  end

  def render("500.json", _assigns) do
    %{errors: %{detail: "Internal server error"}}
  end

  # In case no render clause matches or no
  # template is found, let's render it as 500
  def template_not_found(_template, assigns) do
    render "500.json", assigns
  end
end


code:
defmodule Pleroma.Web do
  @moduledoc """
  A module that keeps using definitions for controllers,
  views and so on.

  This can be used in your application as:

      use Pleroma.Web, :controller
      use Pleroma.Web, :view

  The definitions below will be executed for every view,
  controller, etc, so keep them short and clean, focused
  on imports, uses and aliases.

  Do NOT define functions inside the quoted expressions
  below.
  """

  def controller do
    quote do
      use Phoenix.Controller, namespace: Pleroma.Web
      import Plug.Conn
      import Pleroma.Web.Router.Helpers
      import Pleroma.Web.Gettext
    end
  end

  def view do
    quote do
      use Phoenix.View, root: "lib/pleroma/web/templates",
                        namespace: Pleroma.Web

      # Import convenience functions from controllers
      import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]

      import Pleroma.Web.Router.Helpers
      import Pleroma.Web.ErrorHelpers
      import Pleroma.Web.Gettext
    end
  end

  def router do
    quote do
      use Phoenix.Router
      import Plug.Conn
      import Phoenix.Controller
    end
  end

  def channel do
    quote do
      use Phoenix.Channel
      import Pleroma.Web.Gettext
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end


code:
defmodule Pleroma.Mixfile do
  use Mix.Project

  def project do
    [app: :pleroma,
     version: "0.0.1",
     elixir: "~> 1.4",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     start_permanent: Mix.env == :prod,
     aliases: aliases(),
     deps: deps()]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [mod: {Pleroma.Application, []},
     extra_applications: [:logger, :runtime_tools]]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [{:phoenix, "~> 1.3.0-rc"},
     {:phoenix_pubsub, "~> 1.0"},
     {:phoenix_ecto, "~> 3.2"},
     {:postgrex, ">= 0.0.0"},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"}]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
     "ecto.reset": ["ecto.drop", "ecto.setup"],
     "test": ["ecto.create --quiet", "ecto.migrate", "test"]]
  end
end


code:
## `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_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 be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have %{count} item(s)"
msgid_plural "should have %{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 have at least %{count} item(s)"
msgid_plural "should have at least %{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 have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(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 ""


code:
## This file 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 as 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_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 be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""

msgid "should have %{count} item(s)"
msgid_plural "should have %{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 have at least %{count} item(s)"
msgid_plural "should have at least %{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 have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(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 ""


code:
# 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:
#
#     Pleroma.Repo.insert!(%Pleroma.SomeSchema{})
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.


code:
defmodule Pleroma.Web.ChannelCase do
  @moduledoc """
  This module defines the test case to be used by
  channel tests.

  Such tests rely on `Phoenix.ChannelTest` and also
  import other functionality to make it easier
  to build common datastructures and query the data layer.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with channels
      use Phoenix.ChannelTest

      # The default endpoint for testing
      @endpoint Pleroma.Web.Endpoint
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()})
    end
    :ok
  end

end


code:
defmodule Pleroma.Web.ConnCase do
  @moduledoc """
  This module defines the test case to be used by
  tests that require setting up a connection.

  Such tests rely on `Phoenix.ConnTest` and also
  import other functionality to make it easier
  to build common datastructures and query the data layer.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with connections
      use Phoenix.ConnTest
      import Pleroma.Web.Router.Helpers

      # The default endpoint for testing
      @endpoint Pleroma.Web.Endpoint
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()})
    end
    {:ok, conn: Phoenix.ConnTest.build_conn()}
  end

end


code:
defmodule Pleroma.DataCase do
  @moduledoc """
  This module defines the setup for tests requiring
  access to the application's data layer.

  You may define functions here to be used as helpers in
  your tests.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      alias Pleroma.Repo

      import Ecto
      import Ecto.Changeset
      import Ecto.Query
      import Pleroma.DataCase
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)

    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()})
    end

    :ok
  end

  @doc """
  A helper that transform changeset errors to a map of messages.

      changeset = Accounts.create_user(%{password: "short"})
      assert "password is too short" in errors_on(changeset).password
      assert %{password: ["password is too short"]} = errors_on(changeset)

  """
  def errors_on(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
      Enum.reduce(opts, message, fn {key, value}, acc ->
        String.replace(acc, "%{#{key}}", to_string(value))
      end)
    end)
  end
end


code:
ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)


code:
defmodule Pleroma.Web.ErrorViewTest do
  use Pleroma.Web.ConnCase, async: true

  # Bring render/3 and render_to_string/3 for testing custom views
  import Phoenix.View

  test "renders 404.json" do
    assert render(Pleroma.Web.ErrorView, "404.json", []) ==
           %{errors: %{detail: "Page not found"}}
  end

  test "render 500.json" do
    assert render(Pleroma.Web.ErrorView, "500.json", []) ==
           %{errors: %{detail: "Internal server error"}}
  end

  test "render any other" do
    assert render(Pleroma.Web.ErrorView, "505.json", []) ==
           %{errors: %{detail: "Internal server error"}}
  end
end


code:
# Top 7 Christian Gifts that Inspire Faith and Encourage Growth All Year Long

## Getting started

To make it easy for you to get started with GitLab, here's a list of recommended next steps.

Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!

## Add your files

- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:

```
cd existing_repo
git remote add origin https://gitgud.io/mutgoz/top-7-christian-gifts-that-inspire-faith-and-encourage-growth-all-year-long.git
git branch -M master
git push -uf origin master
```

## Integrate with your tools

- [ ] [Set up project integrations](https://gitgud.io/mutgoz/top-7-christian-gifts-that-inspire-faith-and-encourage-growth-all-year-long/-/settings/integrations)

## Collaborate with your team

- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)

## Test and Deploy

Use the built-in continuous integration in GitLab.

- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)

***

# Editing this README

When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.

## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.

## Name
Choose a self-explaining name for your project.

## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.

## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.

## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.

## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.

## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.

## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.

## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.

## Contributing
State if you are open to contributions and what your requirements are for accepting them.

For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.

You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.

## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.

## License
For open source projects, say how it is licensed.

## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.

repo_name: mutgoz/top


code:
# gentoo_configs

A collection of my currently-used files in case I ever need to install another Gentoo or other Linux system.


code:
node_modules/
static/
docker/jschan/Dockerfile
docker/jschan/Dockerfile-reset
docker/nginx/Dockerfile
docker/static/
tools/
!tools/festrings.json
gulp/res/js/socket.io.js

repo_name: diegolegion/algochan

path: .dockerignore


ecmaVersion: latest

block-spacing: error always

space-before-blocks: error always

linebreak-style: error unix

quotes: error single

semi: error always


code:
node_modules/
backup.sh

#secrets
configs/*.json
configs/*.js

# ignore jschan tmp and static
tmp/
static/*
docker/static/*

# ignore some gulp resources
/gulp/res/css/codethemes
gulp/res/css/locals.css
gulp/res/css/custom.css
gulp/res/icons/*

# no compiledclients js files generated from .pug in gulpfile
gulp/res/js/locals.js
gulp/res/js/post.js
gulp/res/js/modal.js
gulp/res/js/captchaformsection.js
gulp/res/js/pugfilters.js
gulp/res/js/threadwatcher.js
gulp/res/js/watchedthread.js
gulp/res/js/pugruntime.js
gulp/res/js/uploaditem.js
gulp/res/js/socket.io.js
gulp/res/js/web3.js

# some ide and testing files, artefacts, etc
.idea/
coverage/
junit.xml


code:
image: node:latest

# Cache modules in between jobs
cache:
  key: $CI_COMMIT_REF_SLUG
  paths:
    - .npm/

before_script:
  - npm ci --cache .npm --prefer-offline

# Code linting
eslint:
  stage: .pre
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
  script:
    - 'npm i -g eslint-cli'
    - 'npm i eslint --save-dev'
    - 'node node_modules/eslint/bin/eslint.js ./'
  allow_failure: true

# Run tests
unit-tests:
  stage: test
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
  before_script:
    - apt-get update -y && apt-get install -y fonts-urw-base35 gsfonts fonts-dejavu
  script:
    - 'npm install'
    - 'npm install jest -g'
    - 'cp configs/secrets.js.example configs/secrets.js'
    - 'jest --ci --verbose --reporters=default --reporters=jest-junit --testPathIgnorePatterns=./test/ --collectCoverage --coverageReporters text --coverageReporters cobertura'
  coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
  artifacts:
    when: always
    reports:
      junit:
        - junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  #TODO: stage: integration-tests


code:
- [ ] I have searched the issues to make sure I am not opening a duplicate
- [ ] If this is a jschan installation/setup issue, I have followed the INSTALLATION.md
- [ ] I am asking a [SMART QUESTION](http://www.catb.org/~esr/faqs/smart-questions.html)
- [ ] Leave this box unchecked to prove you actually bothered to read

## Issue report

### Description
A clear and concise description of what the problem is.

### Steps to reproduce (if applicable)

1. Click on '...'
3. Scroll down to '....'
4. See error

### Expected result
What did you expect to happen?

### Actual result
What actually happened?

### Possible solution
Optional, if you have a suggestion.

### Additional information
For example, screenshots and log excerpts.

### Environment
Device:
Operating System and version:
Browser and version:


code:
### 1.6.0
  - Filters now have a "replace" mode, by @disco.
  - Global account management now has an option delete all boards owned by an account when deleting it.
  - Bugfix moving posts to non existing board not correctly returning an error sometimes.

### 1.5.0
  - Thanks very much to @disco and @some_random_guy for valuable contributions for this release while I have been busy and on vacation.
  - Remove spamhaus ZEN from the default included DNSBLs.
  - Improved handling of video thumbnailing with transparent vp8 and vp9 codecs.
    - Video codec now included in processedFile data.
  - Fix frontend bug of hovering quotes on some global/manage pages.
  - Added a tool to restore deleted posts from a backup.
  - Fix issue with scheduled file pruning.
  - Fix minor issue of text wrapping on board tables.
  - Update vendorised dependencies (gm, express-fileupload, etc).
  - Add a few more eslint rules to more strictly enforce code style.
  - No longer exlude the tools folder from linting.
  - Npm audit fix.

### 1.4.1
  - Update the INSTALLATION.md (install instructions).
  - Add a "Bypass DNSBL" account/role permission.
  - Add a "Bypass Anonymizer Restrictions" account/role permission.
  - Update dependencies.
  - Npm audit fix.

### 1.4.0
  - Add a new global management feature to clear all blockbypasses or all login sessions.
  - Make (You)s sync over multiple tabs when posting or clearing.
  - Update dependencies.
  - Npm audit fix.

### 1.3.3
  - Fix the "Board Defaults" section in globamanagement settings being inaccessible, and update the template to be slightly more maintainable (especially for forks).
  - Make the message box focused by js when a page is opened with #postform.
  - Fix an installation issue with a folder not getting created properly.

### 1.3.2
  - Global management board list links now link to boards in manage mode.
  - Bugfix live editing posts, when editing last post of a thread it no longer
    moves to the top of a thread.
  - pt-PT and pt-BR language improvements.
  - Styling tweaks to web3 signatures.
  - Update jschan-docs.
  - Npm audit.

### 1.3.1
  - Pt language fixes and updates.
  - Fix a bug in live posts not displaying web3 address/signatures.

### 1.3.0
  - Web3 integration with MetaMask, development generously funded by @0xBekket
    - Users with the MetaMask browser extension can now register/login to accounts, and sign their messages with MetaMask.
    - Signed post messages will display a signature and link to their wallet at the bottom of the post.
    - The link uses etherscan by default but can be changed to another url with %s substituting the address, like other URL settings.
    - A board-level and global option is available to disable web3 features.
    - `signature` and `address` are available as new properties on signed posts through the API.
    - No third party server communication or details are required for signing messages or authing.
    - This is a very exciting feature with a lot of possibilities for expanding in future. Stay tuned.
  - Client side filters can now be applied against flags or custom flags.
  - Fix order of Tegaki upload so files come before replays so catalog shows the thumbnail.
  - Video embeds now support youtube shorts and odysee.com.
  - New global option for URI decoding filenames, to display some characters such as quotation mark (") better in filenames.
  - Minor pt-PT language fixes.
  - Small color adjustments to Yotsuba theme to match 4chan colors more closely.
  - Update nginx config for new embeds in CSP, and some more file type support.
  - Npm audit.

### 1.2.2
  - Add a global setting to try and URI decode filenames
  - Minor pt-PT and pt-BR language fixes
  - Remove some unused dependencies
  - Npm audit

### 1.2.1
  - Bugfix ban upgrades not applying correctly due to bans format change.

### 1.2.0
  - Massive filter system overhaul, huge thanks to @disco.
    - Filters are no longer just a text box with some options that apply to all filters.
    - Filters can now be created/edited/deleted on separate global and board pages.
    - All previous filter options e.g. strict matching, ban/block mode, ban message, ban duration, and appealability are customisable per-filter.
    - The PR for this, #532 on gitgud is an excellent example of how to add features to jschan and fits into the design patterns perfectly.
    - This gives the flexibility to add a filter "replace" mode in a future release.
  - Added Italian locale (it-IT) courtesy of @zocram from niuchan.org
  - Improved Portuguese translations thanks to @l29utp0 from ptchan.org
  - Improved Russian translations thanks to Kusankot
  - Minor addition to INSTALLATION.md for those using Ubuntu rather than Debian.
  - Npm audit & dependencies updated.

### 1.1.1
  - Bugfix live post updates triggering the disconnect and removal of postforms on non-OP delete/move.
  - Improve text wrapping on long table data.
  - Npm audit.

### 1.1.0
  - Posts edits and "USER WAS BANNED FOR THIS POST" messages will now be updated live through the websocket.
  - Added Yandex Smart Captcha support, as an additional captcha type option.
    - NOTE: This is still a WIP because Yandex does not support registration from my country and I am unable to get an API key. If you are able to share an API key for development purposes, please reach out via contact detaiils in README.md
  - Fix pluralisation of thread stickying message.
  - Fix some language mixups in translations.

### 1.0.6
  - Bugfix in translation of blocked country message when posting.
  - Some Improvements to the Russian translation courtesy of an anon from bbs.quietplace.xyz. Handling of the complex plural rules in Russian will be improved in a future version.
  - Npm audit.

### 1.0.5
  - Add pt-BR language pack, by @thugking from Rchan.
  - Fix bypass not auto closing and submitting on non english locale.
  - Actually fix Tor hidden service country name not displaying correctly.
  - Make the error handling for "possible corrupt or unsupported file" catch more bad files and processing errors.
  - Make animated webp thumbnailing at least work and not give the above error (not supporting animated thumbs like .gif yet).
  - Add "ttf" extension to nginx snippets because some themes include fonts.
  - Reorganize <input> styling, use accent-color property on <input> elements, by @disco.
  - Add globalmanage setting for board default language.
  - Fix an issue with the referer because of translation, and prevent invalid settings for it from breaking the web panel.
  - Fix broken migration for board defaults causing language issues on newly created boards after updating from pre 1.0.0.
  - Npm audit.

### 1.0.4
  - Translation improvements.
  - Fix Tor hidden service country name not displaying correctly.
  - Make global disabling of anonymizer file posting apply to board banners, flags, custompages.

### 1.0.3
  - Translation improvements.
  - Add back missing "Lock+Unlist" mode for handling ianctive boards.
  - Npm audit.

### 1.0.2
  - Translation improvements.

### 1.0.1
  - Translation improvements.
  - Add a link to board in mod view e.g. /test/ on globalmanage recent posts to give some context.
  - Fix dockerignore for docker testing/development, was broken due to translations.

### 1.0.0
Version 1.0.0 is here. jschan is stable and mature enough and has been for a while. There's no need to go to version 0.9999.9999 and I am comfortable enough to call it "Version 1".
Of course, that doesn't mean jschan is perfect or finished by any means. I want to keep improving jschan and there are plenty of new features and changes planned for versions 2, 3, and beyond.
Thank you so much to all the jschan site admins, users & contributors for your help along the way. Your contributions have been invaluable to making this software great.
Special shoutout to l29utp0 & loynet (ptchan.org), Homicide (94chan.org) and some_random_guy (trashchan.xyz).

-Tom

Now, back to the program. Here are the changes for 1.0.0:
  - **Multiple language support.**

jschan now supports language packs. There is a global and board-level language setting which completely translates the interface to another language. Using server side rendering and logic, so it remains noscript compatible.
An effort has been made to translate everything, but given there is almost 4 years of code, some things may have slipped through the cracks. If something isn't translated, please report it on gitgud.

1.0.0 includes three* complete language packs:
  - English (en-GB)
  - Portuguese (pt-PT)
  - Russian (ru-RU) *Machine translated, may not be accurate.

Huge credit to the ptchan.org admins for providing the Portuguese translation.
Contributions for new language packs or improvements to existing ones are very welcome! **Reach out via email, IRC, or session (See README.md) to discuss imbursement for contributing language packs.**
Note: "global" pages e.g. overboards will adhere to global language setting. In a future iteration, these will be updated to (optionally, with global setting) support adhering to users browser language header for better UX going between a non-global language board and global pages.

  - Notify the user when making a playlist from a thread if there were no files, rather than just silently logging.
  - Improves the css and markup to only show the appropriate wording e.g "tap" or "click" in tooltips depending if you are on mobile/desktop.
  - Improve the installation process to fix a potential issue with the database connection settings.
  - Customflags will now show correctly when editing a post on a board with custom flags enabled.
  - Security improvement to the 2FA validation flow during login.
  - Log a few more errors related to hcaptcha/recaptcha, for debugging purposes. (already caught and returned in a friendly manner)
  - Remove showing language and relevance data when auto detecting highlighted code block language.
  - Add more themes (cybhub, digi, favela, ptchan, teletext).
  - More minor bugfixes to permissions pages displays.

### 0.11.4
  - Bugfix for the message stating how many banners were deleted when deleting banners.
  - Add an option to limit the total resolution of an image/video (width*height).

### 0.11.3
  - Fix max vs total upload count in controller for uploading board assets, flags and banners.
  - Move css theme assets to themes/assets instead of all lumped in one folder.
  - Adjust the letter spacing and line spacing of the 2FA QR code.

### 0.11.2
  - Convert the assets page form handling to the newer checkSchema code.
  - Don't show the "Edit" option in the post dropdowns for public pages.
  - No longer apply permissions inheritance after editing to prevent confusion.
  - Improve duplicate checking when editing roles to only explicitly match updated roles rather than applying inheritance first.
  - Bugfix "my permission" page not displaying correctly and board staff permission editing not applying.
  - Improve required parent permission display to show "requires X" in the tooltip of disabled checkboxes.

### 0.11.1
  - Bugfix non logged in users not able to use actions because of an issue in move post logic (thanks @some_random_guy).
  - Bugfix editing posts from the actions form (not the new dropdown "Edit" button) always redirecting to OP.

### 0.11.0
  - Improve the fallback behaviour of live posts to work on connect_error instead of only reconnect_failed, and also attempt to upgrade again to the socket.
  - The post form in modview now shows the full email field even with "sage only email", same as how the name field shows in modview with "force anon"
  - Globals or users who are staff on multiple boards can move posts between boards
  - IP/ban type display and suffixes improved to make IPv4/6/Bypass/Pruned IP clearer for more informed moderation.
  - Post editing is now available as a link in the post dropdown, and makes a GET for the page (like editcustompage/editnews)
  - Labelled link format markdown now supports mailto.
  - "Hide name" set by default when using public actions form.
  - Bugfix saveoverboard script so it works on catalog instead of only index, and improve the code to be more general and allow /catalog.html in navbar link.
  - Bugfix BYPASS_CAPTCHA permission not able to be applied to roles.
  - Add a small note on global account/role permission editing about the implication of setting "board management" permissions globally.
  - Update README with some new related projects and change demo/test instance to 94chan.
  - Globalmanage roles are now available in json format in the API.
  - Improve some of the templates and refactor some repetitive bits into includes/mixins.
  - Add and update some tests.
  - Npm audit & dependencies updated.
  - Buon Natale e buon anno 🎉

### 0.10.2
  - Bugfix to 2fa, code-reuse prevention was blocking all codes rather than only a recently used correct code.

### 0.10.1
  - Minor nginx configuration change for twofactor.html.

### 0.10.0
  - Add account two factor authenticaiton (TOTP).
  - Update dependencies.

### 0.9.4
  - Ability to add internal notes to bans. These are only seen by staff and not shown to the banned user.
  - Wordfilter auto bans will set an internal note with which wordfilter was triggered.
  - Fix zooming in on tabbed settings pages breaking when zoomed in on some browsers.
  - Npm audit fix.

### 0.9.3
  - Vastly improve installation instructions & automated nginx configuration.

### 0.9.2
  - Bugfix an issue with bypasses/captcha related to difference between renewing vs getting new bypass.

### 0.9.1
  - Allow customisable font for grid captcha.
  - Default grid captcha to a common system font instead of bundling an enormous 24MB font file.
  - Bugfix some tegaki issues.

### 0.9.0
  - Add board option to hide banners from header and link in board nav bar.
  - Add global option to remove reverse image search links from the overboard.
  - Tegaki now supports replay upload and playback.
  - Completing /bypass.html form will now renew your existing bypass if you had one.
  - Replaced "no subject" with #postId in several places.
  - New public API endpoints
    - Public board logs.
    - Board banners (previously undocumented).
    - Board custompages.
    - Select board settings and global settings (ones that are useful/necessary for 3rd party app integration).
  - Bugfix log pruning not removing old logs from mod view logs page (public log pages were unaffected and hid the old logs).
  - Bugfix deleting board assets/banners returning the wrong number in the success message.

### 0.8.2
  - Migrate to new socket.io redis adapter package.
  - Fix issue with pre checking boxes in ban modal conflicting with grid captcha.

### 0.8.1
  - Fix incorrectly displaying inactive accounts action settings in global settings.
  - Fix captcha gridv2 not appearing in board captcha slot with js enabled.

### 0.8.0
  - Settings pages are now more organised and use tabs, and still works without javascript.
  - Captcha improvements
    - New captcha image effects, and customisation for their value/strength.
    - Customisable font for text captcha.
    - Improved grid captcha, the character sets and question are now customisable.
    - Added new captcha type "grid v2" with a different challenge involving pointing arrows. Also customisable like grid v1.
  - Added a new account level permission to bypass captchas including blockbypass.
  - Anonymizers dummy bypass cookies given while blockbypass is disabled are no longer valid once blockbypass is enabled.
  - Add a new option for automatically forfeiting board staff position and/or deleting inactive accounts with customisable time.
  - Add a new option to automatically lock, lock+unlist, or delete boards that have no board owner.
  - Homepage hot threads have a new option for maximum thread age, and are no longer bound to a static 7 days.
  - Homepage hot threads score formula is now `score=replies in last 7 days*(1-(current thread age/max thread age))` to better represent more recently active threads, rather than simply sorting by reply count.
  - Add unofficial Typescript SDK + typings for jschan api to README, (thanks to Michell/ussaohelcim).
  - Add "playlist" button in OP dropdown to download thread audio/video as m3u playlist (thanks to Michell/ussaohelcim).
  - Switch youtube embeds to use youtube-nocookie domain.

### 0.7.3
  - Bugfix some captcha generations causing server error due non-integer argument to randomRange.

### 0.7.2
  - Add playlist generating bookmarklet script to README
  - Bugfix some captcha generations causing server error due to bad random number range.
  - Fix UTF8-filenames not being decoded corectly (busboy made this not the default in a recent update).

### 0.7.1
  - Show sticky level on hover (title property) of pin icon.
  - Reduce frontend script size by ~10KB. More improvement to come in future updates.
  - Change homepage hot threads to show creation date rather than last bumped.
  - Don't allow setting global limit for post password length below what the frontend automatically generates.
  - When strict mime validation is enabled, actually tell the user what the server thinks the mime is in the mismatch error message.
  - Make IP pruning schedule also apply to modlogs.
  - Fix incorrectly hiding "create board"/"register an account" on account page because of permission issue.
  - Fix the nginx configuration script putting commas in CSP if you say yes to recaptcha/hcaptcha.
  - Fix recaptcha/hcaptcha causing an issue for scrolling to new posts in some (chrom*) browsers.
  - Fix the generate-favicon gulp task not versioning correctly causing cache issues.
  - Small frontend and backend refactors.
  - Add screenshot image animated with light/dark theme in README.
  - Publish some dependencies to npmjs for faster installation.
  - Update dependencies.

### 0.7.0
  - ESLint has been added as a dev dependency. This does "linting" to enforce code style and find and fix some mistakes.
  - Updated CONTRIBUTING.md information.
  - Bugfixes.

### 0.6.6
  - Post editing bugfix.

### 0.6.5
  - Portrait mode support for tegaki, improves the experience particularly on mobile devices.
  - Small refactor of filtering.
  - Modlog pruning now only deletes log entries older than the specified date when new modlogs are added, as originally intended.
  - Add a few extra things to server logs when debugLogs is true.
  - Minor bugfixes.

### 0.6.4
  - Add "adaptive" theme that switches between "clear" and "tomorrow" based on the device system theme, using prefers-color-scheme media query.
  - Minor bugfixes.

### 0.6.3
  - Bugfix when setting empty board tags, and other multiline settings.

### 0.6.2
  - Hotfix webring.

### 0.6.1
  - +/- button to expand a thread and see the omitted posts from index pages.
  - Option for global filter bans to be not appealable.

### 0.6.0
  - Bans+Appeal form will now appear in the modal popup when js enabled, instead of a dodgy workaround which often caused posting bugs and broke over Tor.
  - Bans can now be "upgraded" retroactively to expand single IP bans to qrange/hrange bans.
  - Ban table now shows columns for "Type" (IP/Bypass/Pruned) and "Range" (Single/Narrow/Wide).
  - Global setting to show some popular threads from listed boards on the homepage, with adjustible limit (0=disabled) and threshold. (courtesy ptchan.org)
  - Big refactor of backend, the awfully named and disorganised "helpers" is now the more appropriately named and better organised "lib".
  - Some form and input handling code made more robust based on test feedback.
  - More tests.
  - Bugfixes.

### 0.5.3
  - Add testing framework. Allows to test individual code, simulated requests and check for regressions after changes. Will be made more comprehensive over time.
  - Make "Manage" navbar link go to the current page in modview, instead of defaulting to reports page.
  - Fix some obscure bugs with incorrect post ordering/pages not rebuilding after making a post, or submitting post actions.
  - Many bugfixes.

### 0.5.2
  - Fix regression in remarkup of posts during moving, deleting, cyclic threads, etc due to permissions.
  - Small internal change to how dicerolls are processed.

### 0.5.1
  - Small change to expanding spoilered files.
  - Bugfix outdated bans template mixin.

### 0.5.0
  - Add tegaki.js (oekaki). Has board toggle and settings option for default canvas size.

### 0.4.1
  - Fix not properly skipping dnsbl with block bypass when configured to do so, give the better frame popup, and skip if already solved board captcha, etc.
  - Fix sessions list page showing expiry dates far in the past
  - Minor bugfix to flag saving.

### 0.4.0
  - Hashed IPs now use an irc-style "cloaking".
    - Ranges are now combined in the IP instead of being only available in the JSON and not shown in management pages.
    - IPs will end with .IP, bypasses will end in .BP and pruned ips will end in .PRUNED
  - Users can now list/delete their active login sessions.
  - New permission system. No more "levels" shit.
    - Boards no longer have "moderators" but "staff".
    - New "staff" page in board management, to add/remove/edit staff.
    - The global "accounts" and board "staff" pages allow editing individual permissions.
    - There is now more fine permission/access control system for global and board-level stuff.
    - Users with permission to edit accounts can edit accounts on a global level and grant them permissions.
    - Users with permission to edit board staff can do the same on a per-board level.
  - Better continuity between pages like "news", "accounts", "custompages", "staff" and the associated editieng page. The manage/globalmaange navbar stays in place with the appropriate section highlighted.
  - More linking between moderation interfaces e.g. globals board list will now show direct link to view the owners account.
  - Made some board and page titles more consistent.
  - Custom flag selection is now saved like name and post password.
  - Bugfixes.

### 0.3.4
  - Minor bugfix, saving board settings with empty custom CSS was incorrectly returning an error if there were global filters for css.

### 0.3.3
  - Minor bugfix to filenames of expanded images being incorrectly truncated in some circumstances, when "image loading bars" is enabled.

### 0.3.2
  - Minor bugfix to post moving.

### 0.3.1
  - Small board list optimisation, no longer try to fetch webring site names if webring not enabled.
  - Make webring blacklisting also apply to endpoints after being fetched, in case the URL is different (alt domain, onions, etc).
  - Webring.json and site names cache is now removed when webring is disabled.
  - More reasonable webring timeout for fetching each site.
  - Webring bugfixes.

### 0.3.0
  - Ban durations are now editable. Staff can set a new ban duration (starting from the current date, not the original ban issue date).
  - Minor bugfixes.

### 0.2.0
  - From now on, versioning = major.minor.patch. significant changes = major, feature updates = minor, bugfixes/small stuff = patch.
  - Update instructions about nginx changes when upgrading.
  - Add an endpoint for getting the csrf token separately from html pages. See API docs for more details.
  - Add post "marking" so moved/deleted posts info is sent over websocket. Frontend will handle them. Deleted threads and moved OPs will now also disconnect the socket and remove the post form.
  - Block bypasses are now locked to where they were created (anonymizer or clearnet) to prevent some forms of 'smuggling'. This will be improved further in upcoming releases.
  - Code highlighting now supports all highlight.js languages when explicitly specified. The whitelist now only applies to auto-detection, as originally intended.
  - Quotes for post references in modlog now have the proper quote class, and will show when hovered like any other quote.
  - Bugfixes
  - [jschan-docs](https://fatchan.gitgud.site/jschan-docs/):
    - API docs improvements, now includes csrf token, posting, post actions (and mod variants), and more. It should be enough documentation for somebody to write a mobile app integration.
  - [globalafk](https://gitgud.io/fatchan/globalafk/) (my fork):
    - On android with termux, tapping the notification will open the post (in mod view) and the notification has new shortcut buttons to quickly delete, delete+ban or delete+global ban.

### 0.1.10
  - Add thread watcher/watchlist, so users can save list of threads to a little window and see unread count
  - There are now API docs available at https://fatchan.gitgud.site/jschan-docs/
  - Improved installation nginx script. Confirms overwriting and doesnt break when run more than once
  - Some visual tweaks (file "(u)" being on newline, "x" -> "×")
  - Bugfixes

### 0.1.9
  - Fix "improved" global settings changes not regenerating custom pages properly
  - Postmenu dropdown for filters/moderate added to catalog tiles
  - Notifications are no longer sent for posts when clicking "view full text"
  - Make handling files with no/incorrect extension better
  - Image count from OP is included in catalog tiles file count

### 0.1.8
  - Much improved nginx configuration for installation, script does most of the work
  - Fixed settings form asking to save password -.-
  - Multiple files & post flags now shown in catalog view
  - Faster, more efficient global settings changes
  - Add option for board owner to prevent OP deleting threads that are too old or have too many replies

### 0.1.7
  - Update inconsistent wording of custom overboard subtitle from 0.1.6
  - Dont show "local first" checkbox in boardlist when webring isn't even enabled
  - Bugfix to code markup, remove only leading blank lines, keeping leading whitespace on first non-empty line
  - Make overboard settings save somewhat in localstorage to help stupid zoomers who dont know what a BOOKMARK is

#### 0.1.6
  - Mongodb v5 and node driver v4 update
  - IP rangebans use more reasonable range for ipv6
  - Fix ip randomising schedule
  - Global announcements now show on overboard (both views)
  - Overboard upgrade, now users can set to add and remove any boards, global setting to enable/disable. Form is collapsible
  - Add an extra "new reply" button to the end of threads for convenience
  - Users with scripts will now see proper video thumbnails (depending on browser supported codecs) in the upload list
  - Make fuzzy image hash option and hashes visible to globals, for filtering
  - Add board option to enable reverse image search link next to filenames
  - Add board option to enable an external "archive" link with the other nav links
  - Add global option to configure the URL for archive/reverse image serach links
  - Improved API for some global moderation interfaces, to allow more custom integration to reports and more
  - Staff who hide their name from public logs will also be hidden in the ban when shown to banned user
  - Image previews now shown in notifications
  - Strict filtering improvements
  - Many bugfixes

#### 0.1.5
  - Try to fallback thumbnail generation for video with horribly broken encoding
  - Country blocklist now can actually fit all countries
  - Make "auth level" text box into "account type" dropdown in accounts page, easier to understand
  - Board owners can now edit custom pages
  - Board owners can now manage custom assets
  - Show a little message and disable reply form on full threads (hit reply limit)
  - Allow longer language names for code blocks
  - User settings import and export option

#### 0.1.4
  - Banner uploading bugfix

#### 0.1.3
  - Script optimizations, improves page load speed especially on longer threads
  - Extra (u) download link for no reason
  - favicon, webmanifest, browserconfig, etc for browsers made into gulp task
  - Webring now sends and checks for ppd stat
  - Board search improved (prefix matches)
  - Update code for form submission and data validation, faster and easier to maintain
  - Make it more obvious to users with a blacklisted IP if block bypass dnsbl is enabled
  - Add new themes
  - Improved README
  - Many bugfixes

#### 0.1.2
  - Merge webring and local board list and improve webring search and filter functionality
  - New stat section of homepage
  - Replaced and removed some fatchan-specific media
  - Fixed undefined hcaptcha site key bug
  - Updated README with info about nginx CSP for 3rd party captcha providers
  - Update socket-io 2.x to 4.x

#### 0.1.1
  - Added changelog
  - Version now changes with some kind of meaning
  - Animated gif thumbnails no longer generate static image for images < thumbnail dimensions
  - Boards management "Banners" page is now "Assets"
  - Boards can have custom flags


code:
# Contributing

Thanks for considering contributing.

Some links:
  * [Kanban board](https://gitgud.io/fatchan/jschan/-/boards/4780) already has issues in swim lanes.
  * IRC: [irc.fatpeople.lol #general](ircs://irc.fatpeople.lol:6697/general) OR: [webchat](https://irc-web.fatpeople.lol/#general)

## LICENSE?

See [LICENSE](https://gitgud.io/fatchan/jschan/-/blob/master/LICENSE)

## Making changes

  * Bug? Open an issue.
  * Request? Open an issue and ask. Saves wasting your time making changes that will be denied for being bad or unwanted.

When contributing, make a merge request with a clear list of what you've done.

Write clear commit messages. One line is fine for smaller changes, or multiple lines for bigger changes. Be clear and concise.

    $ git commit -m "A brief summary of the commit
    >
    > A paragraph describing what changed and its impact."

## Coding style

  - Tab indentation
    - Switch cases indented 1 level
    - Member expressions indented 1 level
    - Comment indentation ignored
  - Unix linebreaks
  - Single quotes
  - Always include semicolon

ESLint will enforce this (and pick up some other minor code issues). Run ESLint:

```bash
#whole project
eslint ./

#specific directory/file
eslint /path/to/whatever
```

## Running tests

Make sure these still pass after your changes, or adjust them to meet the new expected results.

There is a "jschan-test" service in the `docker-compose.yml` file that will run all the tests in a jschan instance using the docker instance. See the advanced section of installation for some instruction on how to use this.

You can also Run them locally if you have an instance setup (or for quickly running unit tests):

```bash
#unit tests
npm run test
# OR npm run test:unit

#integration tests
TEST_ADMIN_PASSWORD=<password from jschan-reset docker> npm run test:integration

#all tests
npm run test:all

#specific test(s)
npm run test:all <filename|regex>
```

Linting, code coverage and test results are reported in merge requests. Improvements to add tests and/or improve test coverage are welcomed.

Thanks,
Tom


code:
## Installation
##### These instructions are not for the uninitiated and assume you have a brain. Follow them carefully and you will have a nice working imageboard by the end.

##### Requirements
- Linux - Debian used in this example
- Node.js - the application runtime
- MongoDB >= 4.4 - the database
- Redis - session store, task queue, locks, caching, websocket message arbiter
- Nginx - webserver/proxy, serve static files, handle https, GeoIP lookup
- Certbot/letsencrypt - to get a free https certificate
- Graphicsmagick+Imagemagick - identify and thumbnail images, generate captchas
- Ffmpeg - identify and thumbnail audio, video and gifs

**0. Read the LICENSE**

**1. Setup server with some basics**

- Separate, unpriveliged user to run the application.
- Basic security:
  - Sshd root login disabled, key login only, listen only on desired interface.
  - Firewall (ufw works) to deny all incoming on ports besides http/s and sshd.
  - Setup unattended-upgrades for security patches.
- Set the timezone to UTC.
- Clone the repo somewhere. The homedir for the user you setup or /var/www should work.

**2. Install dependencies.**

```bash
sudo apt update -y
sudo apt install curl wget libgeoip-dev gnupg ffmpeg imagemagick graphicsmagick fontconfig fonts-dejavu -y
```

**3. Install MongoDB**

NOTE: As per the MongoDB Documentation, since MongoDB 5.0, the AVX instruction set is required.

> MongoDB 5.0 requires use of the AVX instruction set, available on [select Intel and AMD processors](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#CPUs_with_AVX).

Please ensure your hardware is supported before reporting issues. The complete platform support matrix is available [here](https://www.mongodb.com/docs/manual/administration/production-notes/#platform-support-matrix).

[MongoDB Installation](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/#install-mongodb-community-edition-on-debian):
```bash
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb http://repo.mongodb.org/apt/debian $(lsb_release -sc)/mongodb-org/7.0 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
hsudo apt install -y mongodb-org
sudo systemctl enable --now mongod
```

NOTE: If at this point, mongod doesn't start or has an error, fix the permissions ([stackoverflow](https://stackoverflow.com/questions/64608581/mongodb-code-exited-status-14-failed-but-not-any-clear-errors/66107451#66107451)):
```bash
sudo chown -R mongodb:mongodb /var/lib/mongodb
sudo chown mongodb:mongodb /tmp/mongodb-27017.sock
sudo service mongod restart
```

[Enable authentication](https://www.mongodb.com/features/mongodb-authentication):
```bash
#NOTE: change "CHANGE-ME-YOUR-SECURE-MONGODB-PASSWORD" to something secure.
mongosh admin --eval "db.getSiblingDB('jschan').createUser({user: 'jschan', pwd: 'CHANGE-ME-YOUR-SECURE-MONGODB-PASSWORD', roles: [{role:'readWrite', db:'jschan'}]})"
sudo sh -c "cat > /etc/mongod.conf" <<'EOF'
storage:
  dbPath: /var/lib/mongodb
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
net:
  port: 27017
  bindIp: 127.0.0.1
processManagement:
  timeZoneInfo: /usr/share/zoneinfo
security:
  authorization: "enabled"
EOF

sudo systemctl restart mongod
#NOTE: to access to DB directly in future:
#mongosh "mongodb://jschan:CHANGE-ME-YOUR-SECURE-MONGODB-PASSWORD@127.0.0.1:27017/jschan"
```

**4. Install Redis**

[Redis Installation](https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-debian-10):
```bash
sudo apt update -y
sudo apt install redis-server -y
sed -i 's/supervised no/supervised systemd/' /etc/redis/redis.conf
sudo systemctl enable --now redis-server
```

Enable authentication:
```
echo "requirepass CHANGE-ME-YOUR-SECURE-REDIS-PASSWORD" | sudo tee -a /etc/redis/redis.conf
sudo systemctl restart redis-server
```

**5. Install Node.js**

For easy installation, use [node version manager](https://github.com/nvm-sh/nvm#installing-and-updating) "nvm":
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
```

Then run the following command to get the LTS version of nodejs.
```bash
nvm install --lts
```

Installing Node.js yourself without nvm is possible, but unsupported by this guide.

**6. (Optional) If you want a .onion address (Tor) and/or .loki address (Lokinet)**

Install Tor, setup a hidden service, and output your .onion address:
```bash
sudo apt install tor -y
sudo systemctl enable --now tor
sudo sh -c "cat > /etc/tor/torrc" <<'EOF'
HiddenServiceDir /var/lib/tor/jschan/
HiddenServiceVersion 3
HiddenServicePort 80 unix:/var/run/nginx-tor.sock
EOF

sudo systemctl restart tor
until [ -f /var/lib/tor/jschan/hostname ]
do
     sleep 1
done
sudo cat /var/lib/tor/jschan/hostname
```

Install Lokinet, setup a SNApp, and find your .loki address:
```bash
sudo curl -so /etc/apt/trusted.gpg.d/oxen.gpg https://deb.oxen.io/pub.gpg
echo "deb https://deb.oxen.io $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/oxen.list
sudo apt update -y
sudo apt install lokinet -y
sudo systemctl enable --now lokinet
sudo sh -c "cat > /var/lib/lokinet/lokinet.ini" <<'EOF'
[router]
[network]
keyfile=/var/lib/lokinet/snappkey.private
[paths]
[dns]
[bind]
[api]
[bootstrap]
[logging]
EOF

sudo systemctl restart lokinet
nslookup -type=cname localhost.loki # Your loki address is the "canonical name".
```

For Lokinet, make sure to firewall all ports except 80 on `lokitun0` interface. If you use `ufw` for example, you could do:
```bash
sudo ufw deny in on lokitun0 to any
sudo ufw allow in on lokitun0 to any port 80 proto tcp
```

Note down the .loki and .onion address for the next step.

**7. Setup nginx**

For standard installations, run `configs/nginx/nginx.sh`. This will prompt you for installation directory, domains, onion/lokinet, enable geoip, install a letsencrypt certificate with certbot and more:
```bash
wget https://raw.githubusercontent.com/fatchan/nginx-autoinstall/master/nginx-autoinstall.sh
chmod +x nginx-autoinstall.sh
sudo su
HEADLESS=y OPTION=1 NGINX_VER=STABLE SUBFILTER=y RTMP=y ./nginx-autoinstall.sh
echo "You can safely ignore that error about restarting nginx ^"
exit
rm nginx-autoinstall.sh
sudo bash configs/nginx/nginx.sh
```

For non-standard installations like using a CDN, see [configs/nginx/README.md](configs/nginx/README.md) and DIY.

**8. Get the backend setup & running**

1. Copy the example secrets file and edit it with your mongodb+redis credentials, cookie secrets, etc:
```bash
cp configs/secrets.js.example configs/secrets.js && editor configs/secrets.js
```

2. Edit the rules and FAQ page if desired:
```bash
editor views/custompages/faq.pug views/custompages/rules.pug
```

3. Install nodejs dependencies:
```bash
npm install
```

4. Replace `gulp/res/icons/master.png` with your desired favicon image.

5. Run the setup script. This will install `pm2` (nodejs process manager) and `gulp` (task system) and runs some gulp tasks.
```bash
npm run-script setup
```

6. Run gulp reset to initialize the database and folder structure, and create the admin account. **Default admin account with random password will be printed to the command line.** NOTE: dont run gulp reset again unless you want to completely irreversibly wipe everything.
```bash
gulp reset
```

7. Make pm2 a system service and load on system startup. **NOTE: This will also output some additional commands you need to run to complete the process. Read the command output carefully.**
```
pm2 startup
```

8. Start all the backend processes.
```bash
npm run-script start
gulp
pm2 save
```

Some commands you may need to use in future/find helpful:
- `pm2 list` - Lists running pm2 processes.
- `pm2 logs` - Start tailing logs.
- `pm2 reload ecosystem.config.js` - Reload all backend processes.
- `gulp password` - Reset the admin account password if you forgot it.
- `gulp` - Run the default gulp task.

More information and commands for customisation or advanced use is in the ADVANCED section.

**9. (Optional) if you plan to use the webring and want to make requests with a proxy to mask your origin server IP**

EITHER:

- Use the socks proxy provided by a non-docker tor daemon, which is probably already setup on port 9050 if you have tor installed for a hidden service.
- Install docker and run torproxy in a container: https://github.com/dperson/torproxy (of course, audit the docker image yourself).
- Use your own socks proxy

Either of the first two options will allow you to follow .onions in your webring follow list.

To enable the proxy, tick "Use Socks Proxy" in global management settings and set the appropriate proxy address, e.g. `socks5h://127.0.0.1:9050`, then save settings.

**10. All done!**

## Updating

0. `cd` to your jschan installation folder.

1. Stop the running backend:
```bash
pm2 stop ecosystem.config.js
```

2. (Optional) Update node.js to the latest LTS node version, latest npm, and reinstall global packages. NOTE: Only works if you installed node.js "nvm" as per the recommendation in the installation instructions.
```bash
nvm install --lts --reinstall-packages-from=$(node --version) --latest-npm
```

3. Pull the latest changes from git:
```bash
git pull
```

4. Install nodejs dependencies again in case any have updated or changed
```bash
npm install
```

5. Check if the nginx config needs updating, comparing the version you updated from with the current version:
```bash
git diff v0.9.2 v0.8.0 configs/nginx
```
If the output was blank, goto step 6.

If the output showed changes and you used `configs/nginx/nginx.sh` to setup nginx, run it again to reconfigure nginx and overwrite your old configuration.

If you have a non-standard nginx config, update your nginx config yourself.

6. Run the gulp migrate task. This will run "migrations" such as updating the database schema or file structure:
```bash
gulp migrate && gulp
```

7. Start the backend:
```bash
pm2 restart ecosystem.config.js --env production
```

At this point, your installation is updated. If something is broken, check and read the logs, they will help figure out what went wrong:
```bash
pm2 logs
```

## Help! Something didn't work!!!1!!1

If you are sure you did everything correctly and you still can't get it working, you can ask for help in the IRC (linked in [README](README.md)).

Be polite, be patient, ask [smart questions](http://www.catb.org/~esr/faqs/smart-questions.html), and keep in mind nobody is obliged to help you.

Paid support is available at a rate of $50USD/hr, payable in cryptocurrency (BTC/XMR) only. Email the address on my [Gitgud profile](https://gitgud.io/fatchan) to inquire.

## Advanced

This is an optional section for people who know what they are doing.

Here are some additional "advanced" things you can do with your jschan installation:

<details><summary>Advanced</summary>

#### Performance tweaks

**Using unix sockets for nginx, tor, redis, and mongodb:**

If you run an instance with local redis, mongodb, tor, nginx, etc then you can (usually) net some performance improvement by making them communicate with unix sockets rather than TCP. No more localhost. The biggest disadvantage is making sure each component that communicates using a given socket has the correct permissions set. Generally, creating a socket that is owned by a group to which you add both components is good enough.

This example is for nginx <-> tor daemon communication. You should be able to work out redis and mongodb yourself based on this.

In the nginx `server {}` block:

```nginx
listen unix:/var/run/nginx-tor.sock;
allow "unix:";
deny all;
```

In your torrc:

```
HiddenServicePort 80 unix:/var/run/nginx-tor.sock
```

sudo systemctl restart tor && sudo systemctl restart nginx

**Don't use pm2/nodejs clustering:**

By default, the "chan" pm2 process (the main web serving component of jschan backend) will be "clustered". In nodejs this means that you can have multiple processes e.g. sharing the same port, whereby the "master" will accept connections and pass the handlers over to each process in a round-robin fashion. The pm2 master process handles all this and allows for nice things like zero downtime reloads. The downside is that there is a slight overhead associated with having that single thread accept connections and delegate them to each worker. Instead, you can run each chan process on a sequential port, starting from the port in configs/secrets.js. Then you can configure multiple ip:ports in nginx to connect to each nodejs process independently.

**Note: pm2 reload will no longer give zero downtime restarts this way.**

1. Change the `exec_mode` in ecosystem.config.js and some code in server.js for listening to the ports:

```diff
diff --git a/ecosystem.config.js b/ecosystem.config.js
index fc000094..f2b53c1b 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -30,0 +31 @@ module.exports = {
+               exec_mode: 'fork',
diff --git a/server.js b/server.js
index f9e19dbc..1630dce0 100644
--- a/server.js
+++ b/server.js
@@ -152 +152 @@ const express = require('express')
-       server.listen(port, '127.0.0.1', () => {
+       server.listen(port+parseInt(process.env.NODE_APP_INSTANCE), '127.0.0.1', () => {
```

2. Change the `upstream` block in your jschan sites-available nginx config to have a backend for each chan process, like so:

```nginx
upstream chan {
	server 127.0.0.1:7000;
	server 127.0.0.1:7001;
	server 127.0.0.1:7002;
	server 127.0.0.1:7003;
}
```

You will need to match the number of servers to however many chan processes you are running, which by default is (number of cpu threads/2)

3. pm2 restart ecosystem.config.js --env production && sudo systemctl restart nginx

#### Customisation

Customisation is pretty easy. As long as you format whatever you are customising properly e.g. pug templates, css, etc and have correct syntax for javascript files, the build system will handle the rest for you. Here are some things you can do:

**Custom pages**

To add additional custom pages which will be at the root of your site, add a .pug file to views/custompages/. See rules.pug or faq.pug as an example. These will get added to the root of your site just like /rules.html and /faq.html.

Pug template language reference: https://pugjs.org/api/getting-started.html

To build all custompages, run `gulp custompages`.

**Custom CSS & themes**

All css files in gulp/res/css/ will get combined and minified into the main style.css for the site. If you want to edit the css, it is advised to not edit gulp/res/css/style.css directly as this can change between updates. Instead, add a "custom.css" (example name only) to the same folder with what ever css you want. This will be included after style.css so rules will take precedence.

Theme files in gulp/res/css/themes/ can also be edited, if desired. You can also create new themes copying their general format of including variables inside `:root{}`.

To build all css files, run `gulp css`. For some situations, such as adding or removing themes, you should run `gulp` and `pm2 restart all` because scripts and templates containing the theme selector dropdowns and server-side checks for valid theme names will need to be updated.

#### Handy nginx stuff

For detecting and automatically updating Tor exit node lists, see [tools/update_tor_exits.sh](tools/update_tor_exits.sh)

For updating the GeoIP database for nginx, see [tools/update_geoip.sh](tools/update_geoip.sh)

#### Docker

Experimental, strictly for development only.

Basically:

```bash
docker-compose up -d mongodb redis

#on the first run, or to "gulp reset" later:
docker-compose up jschan-reset

docker-compose up -d jschan

docker-compose up -d nginx
```

</details>


code:
# jschan

Anonymous imageboard software that doesn't suck.

Mirror(s):
 - https://gitgud.io/fatchan/jschan
 - https://git.basedflare.com/fatchan/jschan

Live instances (Unofficial):
 - 🇺🇸 https://94chan.org
 - 🇵🇹/🇧🇷 https://ptchan.org
 - 🇮🇳 https://indiachan.net

Contact via:
 - [Session](https://getsession.org/): `051b2ff270769d20764fa1b8e6bc3240b0a3c28ffb3242e7cce60db479b23ef427`
 - IRC: [open in client](ircs://irc.fatpeople.lol:6697/general) or [webchat](https://irc-web.fatpeople.lol/#general)
 - Email: see my gitgud profile

## Features
 - [x] Multiple language support (🇬🇧 🇵🇹 🇧🇷 🇷🇺 🇮🇹)
 - [x] Optional user created boards
 - [x] Multiple files per post
 - [x] Antispam/Anti-flood & DNSBL
 - [x] 3 customisable inbuilt captchas + 3 third party captchas (hcaptcha, recaptcha, yandex smartcaptcha)
 - [x] Two factor authentication (TOTP) for accounts
 - [x] Manage everything from the web panel
 - [x] Granular account permissions
 - [x] Works properly with anonymizer networks (Tor, Lokinet, etc)
 - [x] Web3 integration - register, login, and sign posts with [MetaMask](https://metamask.io)
 - [x] [Tegaki](https://github.com/desuwa/tegaki) applet with drawing and replays
 - [x] [API documentation](https://fatchan.gitgud.site/jschan-docs/)
 - [x] Built-in webring (compatible w/ [lynxchan](https://gitlab.com/alogware/LynxChanAddon-Webring) & [infinity](https://gitlab.com/Tenicu/infinityaddon-webring))
 - [x] Beautiful bundled frontend with lots of themes and options, see below:

![screenshots](collage.gif "screenshots")

## License
GNU AGPLv3, see [LICENSE](LICENSE).

## Installation & Upgrading
See [INSTALLATION.md](INSTALLATION.md) for instructions on setting up a jschan instance or upgrading to a newer version.

## Changelog
See [CHANGELOG.md](CHANGELOG.md) for changes between versions.

## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.

## Related Projects

Official:
 - [fatchan/jschan-docs](https://gitgud.io/fatchan/jschan-docs/) - API Documentation for jschan ([gitgud pages](https://fatchan.gitgud.site/jschan-docs/#introduction))
 - [fatchan/jschan-api-go](https://gitgud.io/fatchan/jschan-api-go) - WIP Golang API Client for jschan ([gitgud pages](https://fatchan.gitgud.site/jschan-api-go/pkg/jschan/))
 - [jschan-antispam group](https://gitgud.io/jschan-antispam/) - Multiple projects that are compatible with jschan and implement more sophisticated antispam capabilities.

Unofficial: **Not guaranteed to work or be safe, use at your own risk.**
 - [globalafk](https://git.ptchan.org/globalafk/) - "A simple python script that sends ugly notifications when something happens on a jschan imageboard that you moderate."
 - [reporter](https://git.ptchan.org/reporter/) - news bot that fetches news from a provider and posts a snippet of it on a configurable board of a configurable jschan imageboard
 - [yacam](https://git.ptchan.org/yacam/) - a bot that tries to detect (dumb) spam on jschan imageboards and does something about it

## For generous people

Bitcoin (BTC): [`bc1q4elrlz5puak4m9xy3hfvmpempnpqpu95v8s9m6`](bitcoin:bc1q4elrlz5puak4m9xy3hfvmpempnpqpu95v8s9m6)

Monero (XMR): [`89J9DXPLUBr5HjNDNZTEo4WYMFTouSsGjUjBnUCCUxJGUirthnii4naZ8JafdnmhPe4NP1nkWsgcK82Uga7X515nNR1isuh`](monero:89J9DXPLUBr5HjNDNZTEo4WYMFTouSsGjUjBnUCCUxJGUirthnii4naZ8JafdnmhPe4NP1nkWsgcK82Uga7X515nNR1isuh)

Oxen (OXEN): `LBjExqjDKCFT6Tj198CfK8auAzBERJX1ogtcsjuKZ6AYWTFxwEADLgf2zZ8NHvWCa1UW7vrtY8DJmPYFpj3MEE69CryCvN6`


code:
`sites-available.example` is a sample /etc/nginx/sites-available/example.com file
`snippets/*` are sample snippets for /etc/nginx/snippets/

For standard installs, run nginx.sh for easy configuration with prompts.

For non-standard installations, DIY.

If you use cloudflare, please read [these](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-) [articles](https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-Cloudflare-IP-Geolocation) to setup proper IP forwarding and geolocation headers. Similar steps would apply to other CDNs/reverse proxies.