update to phoenix 1.6
This commit is contained in:
parent
f9e66d1336
commit
094e214a55
4
.gitignore
vendored
4
.gitignore
vendored
@ -25,7 +25,7 @@ cannery-*.tar
|
||||
# If NPM crashes, it generates a log, let's ignore it too.
|
||||
npm-debug.log
|
||||
|
||||
# The directory NPM downloads your dependencies sources to.
|
||||
# Ignore assets that are produced by build tools.
|
||||
/assets/node_modules/
|
||||
|
||||
# Since we are building assets from assets/,
|
||||
@ -36,4 +36,4 @@ npm-debug.log
|
||||
.elixir_ls/
|
||||
|
||||
# direnv
|
||||
.envrc
|
||||
.envrc
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Cannery is a personal ammo manager that adjusts to your own needs.
|
||||
|
||||
* Easy to Use: Cannery lets you easily keep an eye on your ammo levels after range day
|
||||
* Easy to Use: Cannery lets you easily keep an eye on your ammo levels before and after range day
|
||||
* Secure: Self-host your own instance, or use an instance from someone you trust.
|
||||
* Simple: Access from any internet-capable device
|
||||
* Simple: Access from any internet-capable device
|
||||
|
@ -10,25 +10,7 @@ $fa-font-path: "@fortawesome/fontawesome-free/webfonts";
|
||||
|
||||
@import "components";
|
||||
|
||||
/* LiveView specific classes for your customizations */
|
||||
.phx-no-feedback.invalid-feedback,
|
||||
.phx-no-feedback .invalid-feedback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.phx-click-loading {
|
||||
opacity: 0.5;
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
|
||||
.phx-disconnected{
|
||||
cursor: wait;
|
||||
}
|
||||
.phx-disconnected *{
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Alerts and form errors */
|
||||
/* Alerts and form errors used by phx.new */
|
||||
.alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
@ -60,4 +42,88 @@ $fa-font-path: "@fortawesome/fontawesome-free/webfonts";
|
||||
color: #a94442;
|
||||
display: block;
|
||||
margin: -1rem 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* LiveView specific classes for your customization */
|
||||
.phx-no-feedback.invalid-feedback,
|
||||
.phx-no-feedback .invalid-feedback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.phx-click-loading {
|
||||
opacity: 0.5;
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
|
||||
.phx-loading{
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.phx-modal {
|
||||
opacity: 1!important;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.phx-modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15vh auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.phx-modal-close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.phx-modal-close:hover,
|
||||
.phx-modal-close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fade-in-scale {
|
||||
animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys;
|
||||
}
|
||||
|
||||
.fade-out-scale {
|
||||
animation: 0.2s ease-out 0s normal forwards 1 fade-out-scale-keys;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: 0.2s ease-out 0s normal forwards 1 fade-in-keys;
|
||||
}
|
||||
.fade-out {
|
||||
animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys;
|
||||
}
|
||||
|
||||
@keyframes fade-in-scale-keys{
|
||||
0% { scale: 0.95; opacity: 0; }
|
||||
100% { scale: 1.0; opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fade-out-scale-keys{
|
||||
0% { scale: 1.0; opacity: 1; }
|
||||
100% { scale: 0.95; opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes fade-in-keys{
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fade-out-keys{
|
||||
0% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
@ -1,31 +1,33 @@
|
||||
// We need to import the CSS so that webpack will load it.
|
||||
// The MiniCssExtractPlugin is used to separate it out into
|
||||
// its own CSS file.
|
||||
// We import the CSS which is extracted to its own file by esbuild.
|
||||
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
|
||||
import "../css/app.scss"
|
||||
|
||||
// webpack automatically bundles all modules in your
|
||||
// entry points. Those entry points can be configured
|
||||
// in "webpack.config.js".
|
||||
//
|
||||
// Import deps with the dep name or local files with a relative path, for example:
|
||||
//
|
||||
// import {Socket} from "phoenix"
|
||||
// import socket from "./socket"
|
||||
//
|
||||
import "phoenix_html"
|
||||
import {Socket} from "phoenix"
|
||||
import topbar from "topbar"
|
||||
import {LiveSocket} from "phoenix_live_view"
|
||||
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
|
||||
// to get started and then uncomment the line below.
|
||||
// import "./user_socket.js"
|
||||
|
||||
let Hooks = {};
|
||||
Hooks.MaintainAttrs = {
|
||||
attrs(){ return this.el.getAttribute("data-attrs").split(", ") },
|
||||
beforeUpdate(){ this.prevAttrs = this.attrs().map(name => [name, this.el.getAttribute(name)]) },
|
||||
updated(){ this.prevAttrs.forEach(([name, val]) => this.el.setAttribute(name, val)) }
|
||||
};
|
||||
// You can include dependencies in two ways.
|
||||
//
|
||||
// The simplest option is to put them in assets/vendor and
|
||||
// import them using relative paths:
|
||||
//
|
||||
// import "../vendor/some-package.js"
|
||||
//
|
||||
// Alternatively, you can `npm install some-package --prefix assets` and import
|
||||
// them using a path starting with the package name:
|
||||
//
|
||||
// import "some-package"
|
||||
//
|
||||
|
||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||||
import "phoenix_html"
|
||||
// Establish Phoenix Socket and LiveView configuration.
|
||||
import {Socket} from "phoenix"
|
||||
import {LiveSocket} from "phoenix_live_view"
|
||||
import topbar from "../vendor/topbar"
|
||||
|
||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}})
|
||||
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
||||
@ -40,4 +42,3 @@ liveSocket.connect()
|
||||
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
||||
// >> liveSocket.disableLatencySim()
|
||||
window.liveSocket = liveSocket
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
// NOTE: The contents of this file will only be executed if
|
||||
// you uncomment its entry in "assets/js/app.js".
|
||||
|
||||
// To use Phoenix channels, the first step is to import Socket,
|
||||
// and connect at the socket path in "lib/web/endpoint.ex".
|
||||
//
|
||||
// Pass the token on params as below. Or remove it
|
||||
// from the params if you are not using authentication.
|
||||
import {Socket} from "phoenix"
|
||||
|
||||
let socket = new Socket("/socket", {params: {token: window.userToken}})
|
||||
|
||||
// When you connect, you'll often need to authenticate the client.
|
||||
// For example, imagine you have an authentication plug, `MyAuth`,
|
||||
// which authenticates the session and assigns a `:current_user`.
|
||||
// If the current user exists you can assign the user's token in
|
||||
// the connection for use in the layout.
|
||||
//
|
||||
// In your "lib/web/router.ex":
|
||||
//
|
||||
// pipeline :browser do
|
||||
// ...
|
||||
// plug MyAuth
|
||||
// plug :put_user_token
|
||||
// end
|
||||
//
|
||||
// defp put_user_token(conn, _) do
|
||||
// if current_user = conn.assigns[:current_user] do
|
||||
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
|
||||
// assign(conn, :user_token, token)
|
||||
// else
|
||||
// conn
|
||||
// end
|
||||
// end
|
||||
//
|
||||
// Now you need to pass this token to JavaScript. You can do so
|
||||
// inside a script tag in "lib/web/templates/layout/app.html.eex":
|
||||
//
|
||||
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
|
||||
//
|
||||
// You will need to verify the user token in the "connect/3" function
|
||||
// in "lib/web/channels/user_socket.ex":
|
||||
//
|
||||
// def connect(%{"token" => token}, socket, _connect_info) do
|
||||
// # max_age: 1209600 is equivalent to two weeks in seconds
|
||||
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
|
||||
// {:ok, user_id} ->
|
||||
// {:ok, socket |> assign(:user, user_id)}
|
||||
// {:error, reason} ->
|
||||
// :error
|
||||
// end
|
||||
// end
|
||||
//
|
||||
// Finally, connect to the socket:
|
||||
socket.connect()
|
||||
|
||||
// Now that you are connected, you can join channels with a topic:
|
||||
let channel = socket.channel("topic:subtopic", {})
|
||||
channel.join()
|
||||
.receive("ok", resp => { console.log("Joined successfully", resp) })
|
||||
.receive("error", resp => { console.log("Unable to join", resp) })
|
||||
|
||||
export default socket
|
88
assets/package-lock.json
generated
88
assets/package-lock.json
generated
@ -1109,9 +1109,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
||||
"integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==",
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.0.tgz",
|
||||
"integrity": "sha512-JUYa/5JwoqikCy7O7jKtuNe9Z4ZZt615G+1EKfaDGSNEpzaA2OwbV/G1v08Oa7fd1XzlFoSCvt9ePl9/6FyAug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "*",
|
||||
@ -1119,9 +1119,9 @@
|
||||
}
|
||||
},
|
||||
"@types/eslint-scope": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz",
|
||||
"integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==",
|
||||
"version": "3.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
|
||||
"integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint": "*",
|
||||
@ -1382,9 +1382,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz",
|
||||
"integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-node": {
|
||||
@ -3190,9 +3190,9 @@
|
||||
}
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz",
|
||||
"integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==",
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
|
||||
"integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@ -3200,9 +3200,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"tapable": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
|
||||
"integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -3244,9 +3244,9 @@
|
||||
}
|
||||
},
|
||||
"es-module-lexer": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz",
|
||||
"integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==",
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
"integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"escalade": {
|
||||
@ -3287,9 +3287,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"estraverse": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
||||
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -9401,9 +9401,9 @@
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz",
|
||||
"integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
|
||||
"integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
@ -9420,9 +9420,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.50.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz",
|
||||
"integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==",
|
||||
"version": "5.67.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.67.0.tgz",
|
||||
"integrity": "sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
@ -9434,12 +9434,12 @@
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.8.0",
|
||||
"es-module-lexer": "^0.7.1",
|
||||
"enhanced-resolve": "^5.8.3",
|
||||
"es-module-lexer": "^0.9.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"json-parse-better-errors": "^1.0.2",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
@ -9447,14 +9447,20 @@
|
||||
"schema-utils": "^3.1.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.2.0",
|
||||
"webpack-sources": "^3.2.0"
|
||||
"watchpack": "^2.3.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
|
||||
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.9",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
|
||||
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
@ -9469,15 +9475,15 @@
|
||||
}
|
||||
},
|
||||
"tapable": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
|
||||
"integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"webpack-sources": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.0.tgz",
|
||||
"integrity": "sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw==",
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
"style-loader": "^3.2.1",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"webpack": "^5.50.0",
|
||||
"webpack": "^5.67.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
||||
# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
||||
#
|
||||
# To ban all spiders from the entire site uncomment the next two lines:
|
||||
# User-agent: *
|
||||
|
157
assets/vendor/topbar.js
vendored
Normal file
157
assets/vendor/topbar.js
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @license MIT
|
||||
* topbar 1.0.0, 2021-01-06
|
||||
* https://buunguyen.github.io/topbar
|
||||
* Copyright (c) 2021 Buu Nguyen
|
||||
*/
|
||||
(function (window, document) {
|
||||
"use strict";
|
||||
|
||||
// https://gist.github.com/paulirish/1579671
|
||||
(function () {
|
||||
var lastTime = 0;
|
||||
var vendors = ["ms", "moz", "webkit", "o"];
|
||||
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
||||
window.requestAnimationFrame =
|
||||
window[vendors[x] + "RequestAnimationFrame"];
|
||||
window.cancelAnimationFrame =
|
||||
window[vendors[x] + "CancelAnimationFrame"] ||
|
||||
window[vendors[x] + "CancelRequestAnimationFrame"];
|
||||
}
|
||||
if (!window.requestAnimationFrame)
|
||||
window.requestAnimationFrame = function (callback, element) {
|
||||
var currTime = new Date().getTime();
|
||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
||||
var id = window.setTimeout(function () {
|
||||
callback(currTime + timeToCall);
|
||||
}, timeToCall);
|
||||
lastTime = currTime + timeToCall;
|
||||
return id;
|
||||
};
|
||||
if (!window.cancelAnimationFrame)
|
||||
window.cancelAnimationFrame = function (id) {
|
||||
clearTimeout(id);
|
||||
};
|
||||
})();
|
||||
|
||||
var canvas,
|
||||
progressTimerId,
|
||||
fadeTimerId,
|
||||
currentProgress,
|
||||
showing,
|
||||
addEvent = function (elem, type, handler) {
|
||||
if (elem.addEventListener) elem.addEventListener(type, handler, false);
|
||||
else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
|
||||
else elem["on" + type] = handler;
|
||||
},
|
||||
options = {
|
||||
autoRun: true,
|
||||
barThickness: 3,
|
||||
barColors: {
|
||||
0: "rgba(26, 188, 156, .9)",
|
||||
".25": "rgba(52, 152, 219, .9)",
|
||||
".50": "rgba(241, 196, 15, .9)",
|
||||
".75": "rgba(230, 126, 34, .9)",
|
||||
"1.0": "rgba(211, 84, 0, .9)",
|
||||
},
|
||||
shadowBlur: 10,
|
||||
shadowColor: "rgba(0, 0, 0, .6)",
|
||||
className: null,
|
||||
},
|
||||
repaint = function () {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = options.barThickness * 5; // need space for shadow
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.shadowBlur = options.shadowBlur;
|
||||
ctx.shadowColor = options.shadowColor;
|
||||
|
||||
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
||||
for (var stop in options.barColors)
|
||||
lineGradient.addColorStop(stop, options.barColors[stop]);
|
||||
ctx.lineWidth = options.barThickness;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, options.barThickness / 2);
|
||||
ctx.lineTo(
|
||||
Math.ceil(currentProgress * canvas.width),
|
||||
options.barThickness / 2
|
||||
);
|
||||
ctx.strokeStyle = lineGradient;
|
||||
ctx.stroke();
|
||||
},
|
||||
createCanvas = function () {
|
||||
canvas = document.createElement("canvas");
|
||||
var style = canvas.style;
|
||||
style.position = "fixed";
|
||||
style.top = style.left = style.right = style.margin = style.padding = 0;
|
||||
style.zIndex = 100001;
|
||||
style.display = "none";
|
||||
if (options.className) canvas.classList.add(options.className);
|
||||
document.body.appendChild(canvas);
|
||||
addEvent(window, "resize", repaint);
|
||||
},
|
||||
topbar = {
|
||||
config: function (opts) {
|
||||
for (var key in opts)
|
||||
if (options.hasOwnProperty(key)) options[key] = opts[key];
|
||||
},
|
||||
show: function () {
|
||||
if (showing) return;
|
||||
showing = true;
|
||||
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
|
||||
if (!canvas) createCanvas();
|
||||
canvas.style.opacity = 1;
|
||||
canvas.style.display = "block";
|
||||
topbar.progress(0);
|
||||
if (options.autoRun) {
|
||||
(function loop() {
|
||||
progressTimerId = window.requestAnimationFrame(loop);
|
||||
topbar.progress(
|
||||
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
|
||||
);
|
||||
})();
|
||||
}
|
||||
},
|
||||
progress: function (to) {
|
||||
if (typeof to === "undefined") return currentProgress;
|
||||
if (typeof to === "string") {
|
||||
to =
|
||||
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
|
||||
? currentProgress
|
||||
: 0) + parseFloat(to);
|
||||
}
|
||||
currentProgress = to > 1 ? 1 : to;
|
||||
repaint();
|
||||
return currentProgress;
|
||||
},
|
||||
hide: function () {
|
||||
if (!showing) return;
|
||||
showing = false;
|
||||
if (progressTimerId != null) {
|
||||
window.cancelAnimationFrame(progressTimerId);
|
||||
progressTimerId = null;
|
||||
}
|
||||
(function loop() {
|
||||
if (topbar.progress("+.1") >= 1) {
|
||||
canvas.style.opacity -= 0.05;
|
||||
if (canvas.style.opacity <= 0.05) {
|
||||
canvas.style.display = "none";
|
||||
fadeTimerId = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fadeTimerId = window.requestAnimationFrame(loop);
|
||||
})();
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof module === "object" && typeof module.exports === "object") {
|
||||
module.exports = topbar;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
define(function () {
|
||||
return topbar;
|
||||
});
|
||||
} else {
|
||||
this.topbar = topbar;
|
||||
}
|
||||
}.call(this, window, document));
|
@ -1,5 +1,5 @@
|
||||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Mix.Config module.
|
||||
# and its dependencies with the aid of the Config module.
|
||||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
@ -8,7 +8,8 @@
|
||||
import Config
|
||||
|
||||
config :cannery,
|
||||
ecto_repos: [Cannery.Repo]
|
||||
ecto_repos: [Cannery.Repo],
|
||||
generators: [binary_id: true]
|
||||
|
||||
# Configures the endpoint
|
||||
config :cannery, CanneryWeb.Endpoint,
|
||||
@ -25,6 +26,28 @@ config :cannery, :generators,
|
||||
binary_id: true,
|
||||
sample_binary_id: "11111111-1111-1111-1111-111111111111"
|
||||
|
||||
# Configures the mailer
|
||||
#
|
||||
# By default it uses the "Local" adapter which stores the emails
|
||||
# locally. You can see the emails in your browser, at "/dev/mailbox".
|
||||
#
|
||||
# For production it's recommended to configure a different adapter
|
||||
# at the `config/runtime.exs`.
|
||||
config :cannery, Cannery.Mailer, adapter: Swoosh.Adapters.Local
|
||||
|
||||
# Swoosh API client is needed for adapters other than SMTP.
|
||||
config :swoosh, :api_client, false
|
||||
|
||||
# Configure esbuild (the version is required)
|
||||
# config :esbuild,
|
||||
# version: "0.14.0",
|
||||
# default: [
|
||||
# args:
|
||||
# ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
|
||||
# cd: Path.expand("../assets", __DIR__),
|
||||
# env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
|
||||
# ]
|
||||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, :console,
|
||||
format: "$time $metadata[$level] $message\n",
|
||||
|
@ -5,6 +5,7 @@ config :cannery, Cannery.Repo,
|
||||
url:
|
||||
System.get_env("DATABASE_URL") ||
|
||||
"ecto://postgres:postgres@localhost/cannery_dev",
|
||||
show_sensitive_data_on_connection_error: true,
|
||||
pool_size: 10
|
||||
|
||||
# For development, we disable any cache and enable
|
||||
@ -12,12 +13,18 @@ config :cannery, Cannery.Repo,
|
||||
#
|
||||
# The watchers configuration can be used to run external
|
||||
# watchers to your application. For example, we use it
|
||||
# with webpack to recompile .js and .css sources.
|
||||
# with esbuild to bundle .js and .css sources.
|
||||
config :cannery, CanneryWeb.Endpoint,
|
||||
debug_errors: true,
|
||||
code_reloader: true,
|
||||
# Binding to loopback ipv4 address prevents access from other machines.
|
||||
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
|
||||
http: [ip: {0, 0, 0, 0}, port: 4000],
|
||||
check_origin: false,
|
||||
code_reloader: true,
|
||||
debug_errors: true,
|
||||
secret_key_base: "dg2lccMgaY3+ZeKppR+ondk4ZRaANZGIN0LMZT1u1uzscH4jO5W9a9b9V9BkC+MW",
|
||||
watchers: [
|
||||
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
|
||||
# esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
|
||||
node: [
|
||||
"node_modules/webpack/bin/webpack.js",
|
||||
"--mode",
|
||||
@ -58,8 +65,8 @@ config :cannery, CanneryWeb.Endpoint,
|
||||
patterns: [
|
||||
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
|
||||
~r"priv/gettext/.*(po)$",
|
||||
~r"lib/cannery_web/.*(live|views)/.*(ex|leex)$",
|
||||
~r"lib/cannery_web/templates/.*(eex|leex)$"
|
||||
~r"lib/cannery_web/(live|views)/.*(ex)$",
|
||||
~r"lib/cannery_web/templates/.*(eex)$"
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -25,14 +25,14 @@ config :logger, level: :info
|
||||
# to the previous section and set your `:url` port to 443:
|
||||
#
|
||||
# config :cannery, CanneryWeb.Endpoint,
|
||||
# ...
|
||||
# url: [host: "localhost", port: 443],
|
||||
# ...,
|
||||
# url: [host: "example.com", port: 443],
|
||||
# https: [
|
||||
# ...,
|
||||
# port: 443,
|
||||
# cipher_suite: :strong,
|
||||
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
|
||||
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
|
||||
# transport_options: [socket_opts: [:inet6]]
|
||||
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
|
||||
# ]
|
||||
#
|
||||
# The `cipher_suite` is set to `:strong` to support only the
|
||||
|
@ -1,33 +0,0 @@
|
||||
# In this file, we load production configuration and secrets
|
||||
# from environment variables. You can also hardcode secrets,
|
||||
# although such is generally not recommended and you have to
|
||||
# remember to add this file to your .gitignore.
|
||||
import Config
|
||||
|
||||
database_url =
|
||||
System.get_env("DATABASE_URL") ||
|
||||
"ecto://postgres:postgres@cannery-db/cannery"
|
||||
|
||||
config :cannery, Cannery.Repo,
|
||||
# ssl: true,
|
||||
url: database_url,
|
||||
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
|
||||
|
||||
secret_key_base =
|
||||
System.get_env("SECRET_KEY_BASE") ||
|
||||
raise """
|
||||
environment variable SECRET_KEY_BASE is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
"""
|
||||
|
||||
host = System.get_env("HOST") || "localhost"
|
||||
|
||||
config :cannery, CanneryWeb.Endpoint,
|
||||
url: [scheme: "https", host: host, port: "443"],
|
||||
http: [
|
||||
port: String.to_integer(System.get_env("PORT") || "4000"),
|
||||
transport_options: [socket_opts: [:inet6]]
|
||||
],
|
||||
secret_key_base: secret_key_base,
|
||||
server: true,
|
||||
registration: System.get_env("REGISTRATION") || "invite"
|
83
config/runtime.exs
Normal file
83
config/runtime.exs
Normal file
@ -0,0 +1,83 @@
|
||||
import Config
|
||||
|
||||
# config/runtime.exs is executed for all environments, including
|
||||
# during releases. It is executed after compilation and before the
|
||||
# system starts, so it is typically used to load production configuration
|
||||
# and secrets from environment variables or elsewhere. Do not define
|
||||
# any compile-time configuration in here, as it won't be applied.
|
||||
# The block below contains prod specific runtime configuration.
|
||||
|
||||
# Start the phoenix server if environment is set and running in a release
|
||||
if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do
|
||||
config :cannery, CanneryWeb.Endpoint, server: true
|
||||
end
|
||||
|
||||
if config_env() == :prod do
|
||||
database_url =
|
||||
System.get_env("DATABASE_URL") ||
|
||||
"ecto://postgres:postgres@cannery-db/cannery"
|
||||
|
||||
maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: []
|
||||
|
||||
config :cannery, Cannery.Repo,
|
||||
# ssl: true,
|
||||
url: database_url,
|
||||
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
|
||||
socket_options: maybe_ipv6
|
||||
|
||||
# The secret key base is used to sign/encrypt cookies and other secrets.
|
||||
# A default value is used in config/dev.exs and config/test.exs but you
|
||||
# want to use a different value for prod and you most likely don't want
|
||||
# to check this value into version control, so we use an environment
|
||||
# variable instead.
|
||||
secret_key_base =
|
||||
System.get_env("SECRET_KEY_BASE") ||
|
||||
raise """
|
||||
environment variable SECRET_KEY_BASE is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
"""
|
||||
|
||||
host = System.get_env("HOST") || "localhost"
|
||||
|
||||
config :cannery, CanneryWeb.Endpoint,
|
||||
url: [scheme: "https", host: host, port: 443],
|
||||
http: [
|
||||
# Enable IPv6 and bind on all interfaces.
|
||||
# Set it to {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
|
||||
# See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
|
||||
# for details about using IPv6 vs IPv4 and loopback vs public addresses.
|
||||
ip: {0, 0, 0, 0, 0, 0, 0, 0},
|
||||
port: String.to_integer(System.get_env("PORT") || "4000")
|
||||
],
|
||||
secret_key_base: secret_key_base,
|
||||
server: true,
|
||||
registration: System.get_env("REGISTRATION") || "invite"
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you are doing OTP releases, you need to instruct Phoenix
|
||||
# to start each relevant endpoint:
|
||||
#
|
||||
# config :cannery, CanneryWeb.Endpoint, server: true
|
||||
#
|
||||
# Then you can assemble a release by calling `mix release`.
|
||||
# See `mix help release` for more information.
|
||||
|
||||
# ## Configuring the mailer
|
||||
#
|
||||
# In production you need to configure the mailer to use a different adapter.
|
||||
# Also, you may need to configure the Swoosh API client of your choice if you
|
||||
# are not using SMTP. Here is an example of the configuration:
|
||||
#
|
||||
# config :cannery, Cannery.Mailer,
|
||||
# adapter: Swoosh.Adapters.Mailgun,
|
||||
# api_key: System.get_env("MAILGUN_API_KEY"),
|
||||
# domain: System.get_env("MAILGUN_DOMAIN")
|
||||
#
|
||||
# For this example you need include a HTTP client required by Swoosh API client.
|
||||
# Swoosh supports Hackney and Finch out of the box:
|
||||
#
|
||||
# config :swoosh, :api_client, Swoosh.ApiClient.Hackney
|
||||
#
|
||||
# See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
|
||||
end
|
@ -11,15 +11,23 @@ config :bcrypt_elixir, :log_rounds, 1
|
||||
config :cannery, Cannery.Repo,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "cannery_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||
hostname: "localhost",
|
||||
pool: Ecto.Adapters.SQL.Sandbox
|
||||
database: "cannery_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||
pool: Ecto.Adapters.SQL.Sandbox,
|
||||
pool_size: 10
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
config :cannery, CanneryWeb.Endpoint,
|
||||
http: [port: 4002],
|
||||
http: [ip: {127, 0, 0, 1}, port: 4002],
|
||||
secret_key_base: "S3qq9QtUdsFtlYej+HTjAVN95uP5i5tf2sPYINWSQfCKJghFj2B1+wTAoljZyHOK",
|
||||
server: false
|
||||
|
||||
# In test we don't send emails.
|
||||
config :cannery, Cannery.Mailer, adapter: Swoosh.Adapters.Test
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
|
||||
# Initialize plugs at runtime for faster test compilation
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
@ -5,19 +5,21 @@ defmodule Cannery.Application do
|
||||
|
||||
use Application
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
children = [
|
||||
# Start the Ecto repository
|
||||
Cannery.Repo,
|
||||
Cannery.Repo.Migrator,
|
||||
# Start the Telemetry supervisor
|
||||
CanneryWeb.Telemetry,
|
||||
# Start the PubSub system
|
||||
{Phoenix.PubSub, name: Cannery.PubSub},
|
||||
# Start the Endpoint (http/https)
|
||||
CanneryWeb.Endpoint
|
||||
CanneryWeb.Endpoint,
|
||||
# Start a worker by calling: Cannery.Worker.start_link(arg)
|
||||
# {Cannery.Worker, arg}
|
||||
# {Cannery.Worker, arg},
|
||||
# Automatically migrate on start
|
||||
Cannery.Repo.Migrator
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
@ -28,6 +30,7 @@ defmodule Cannery.Application do
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
# whenever the application is updated.
|
||||
@impl true
|
||||
def config_change(changed, _new, removed) do
|
||||
CanneryWeb.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
|
3
lib/cannery/mailer.ex
Normal file
3
lib/cannery/mailer.ex
Normal file
@ -0,0 +1,3 @@
|
||||
defmodule Cannery.Mailer do
|
||||
use Swoosh.Mailer, otp_app: :cannery
|
||||
end
|
@ -46,7 +46,7 @@ defmodule CanneryWeb do
|
||||
quote do
|
||||
use Phoenix.LiveView,
|
||||
layout: {CanneryWeb.LayoutView, "live.html"}
|
||||
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
@ -59,6 +59,14 @@ defmodule CanneryWeb do
|
||||
end
|
||||
end
|
||||
|
||||
def component do
|
||||
quote do
|
||||
use Phoenix.Component
|
||||
|
||||
unquote(view_helpers())
|
||||
end
|
||||
end
|
||||
|
||||
def router do
|
||||
quote do
|
||||
use Phoenix.Router
|
||||
@ -81,7 +89,7 @@ defmodule CanneryWeb do
|
||||
# Use all HTML functionality (forms, tags, etc)
|
||||
use Phoenix.HTML
|
||||
|
||||
# Import LiveView helpers (live_render, live_component, live_patch, etc)
|
||||
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
|
||||
import Phoenix.LiveView.Helpers
|
||||
import CanneryWeb.LiveHelpers
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
defmodule CanneryWeb.UserSocket do
|
||||
use Phoenix.Socket
|
||||
|
||||
## Channels
|
||||
# channel "room:*", CanneryWeb.RoomChannel
|
||||
|
||||
# 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, socket |> assign(:user_id, verified_user_id)}
|
||||
#
|
||||
# To deny connection, return `:error`.
|
||||
#
|
||||
# See `Phoenix.Token` documentation for examples in
|
||||
# performing token verification on connect.
|
||||
@impl true
|
||||
def connect(_params, socket, _connect_info) 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:
|
||||
#
|
||||
# CanneryWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
|
||||
#
|
||||
# Returning `nil` makes this socket anonymous.
|
||||
@impl true
|
||||
def id(_socket), do: nil
|
||||
end
|
@ -3,6 +3,7 @@ defmodule CanneryWeb.UserAuth do
|
||||
import Phoenix.Controller
|
||||
|
||||
alias Cannery.Accounts
|
||||
alias CanneryWeb.{HomeLive}
|
||||
alias CanneryWeb.Router.Helpers, as: Routes
|
||||
|
||||
# Make the remember me cookie valid for 60 days.
|
||||
@ -138,7 +139,7 @@ defmodule CanneryWeb.UserAuth do
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Used for routes that require the user to be an admin.
|
||||
"""
|
||||
@ -149,7 +150,7 @@ defmodule CanneryWeb.UserAuth do
|
||||
conn
|
||||
|> put_flash(:error, "You are not authorized to view this page.")
|
||||
|> maybe_store_return_to()
|
||||
|> redirect(to: Routes.home_path(conn, :index))
|
||||
|> redirect(to: Routes.live_path(conn, HomeLive))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
@ -3,7 +3,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
||||
|
||||
alias Cannery.{Accounts, Invites}
|
||||
alias Cannery.Accounts.User
|
||||
alias CanneryWeb.UserAuth
|
||||
alias CanneryWeb.{HomeLive, UserAuth}
|
||||
|
||||
def new(conn, %{"invite" => invite_token}) do
|
||||
invite = Invites.get_invite_by_token(invite_token)
|
||||
@ -13,7 +13,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Sorry, this invite was not found or expired")
|
||||
|> redirect(to: Routes.home_path(CanneryWeb.Endpoint, :index))
|
||||
|> redirect(to: Routes.live_path(CanneryWeb.Endpoint, HomeLive))
|
||||
end
|
||||
end
|
||||
|
||||
@ -23,7 +23,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Sorry, public registration is disabled")
|
||||
|> redirect(to: Routes.home_path(CanneryWeb.Endpoint, :index))
|
||||
|> redirect(to: Routes.live_path(CanneryWeb.Endpoint, HomeLive))
|
||||
end
|
||||
end
|
||||
|
||||
@ -41,7 +41,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Sorry, this invite was not found or expired")
|
||||
|> redirect(to: Routes.home_path(CanneryWeb.Endpoint, :index))
|
||||
|> redirect(to: Routes.live_path(CanneryWeb.Endpoint, HomeLive))
|
||||
end
|
||||
end
|
||||
|
||||
@ -51,7 +51,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Sorry, public registration is disabled")
|
||||
|> redirect(to: Routes.home_path(CanneryWeb.Endpoint, :index))
|
||||
|> redirect(to: Routes.live_path(CanneryWeb.Endpoint, HomeLive))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2,7 +2,7 @@ defmodule CanneryWeb.UserSettingsController do
|
||||
use CanneryWeb, :controller
|
||||
|
||||
alias Cannery.Accounts
|
||||
alias CanneryWeb.UserAuth
|
||||
alias CanneryWeb.{HomeLive, UserAuth}
|
||||
|
||||
plug :assign_email_and_password_changesets
|
||||
|
||||
@ -70,7 +70,7 @@ defmodule CanneryWeb.UserSettingsController do
|
||||
|
||||
conn
|
||||
|> put_flash(:error, "Your account has been deleted")
|
||||
|> redirect(to: Routes.home_path(conn, :index))
|
||||
|> redirect(to: Routes.live_path(conn, HomeLive))
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Unable to delete user")
|
||||
|
@ -7,13 +7,9 @@ defmodule CanneryWeb.Endpoint do
|
||||
@session_options [
|
||||
store: :cookie,
|
||||
key: "_cannery_key",
|
||||
signing_salt: "fxAnJltS"
|
||||
signing_salt: "N8eMKwCG"
|
||||
]
|
||||
|
||||
socket "/socket", CanneryWeb.UserSocket,
|
||||
websocket: true,
|
||||
longpoll: false
|
||||
|
||||
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
|
||||
|
||||
# Serve at "/" the static files from "priv/static" directory.
|
||||
|
@ -13,7 +13,7 @@
|
||||
<li class="flex flex-row justify-center space-x-2">
|
||||
<b>Easy to Use:</b>
|
||||
<p>
|
||||
Cannery lets you easily keep an eye on your ammo levels after range day
|
||||
Cannery lets you easily keep an eye on your ammo levels before and after range day
|
||||
</p>
|
||||
</li>
|
||||
<li class="flex flex-row justify-center space-x-2">
|
||||
|
@ -26,7 +26,7 @@ defmodule CanneryWeb.ModalComponent do
|
||||
|
||||
<%# modal content %>
|
||||
<div class="w-full flex flex-col space-y-4 justify-center items-center">
|
||||
<%= live_component @socket, @component, @opts %>
|
||||
<%= live_component @component, @opts %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,7 +24,7 @@ defmodule CanneryWeb.Router do
|
||||
scope "/", CanneryWeb do
|
||||
pipe_through :browser
|
||||
|
||||
live "/", HomeLive, :index
|
||||
live "/", HomeLive
|
||||
end
|
||||
|
||||
## Authentication routes
|
||||
@ -94,4 +94,16 @@ defmodule CanneryWeb.Router do
|
||||
post "/users/confirm", UserConfirmationController, :create
|
||||
get "/users/confirm/:token", UserConfirmationController, :confirm
|
||||
end
|
||||
|
||||
# Enables the Swoosh mailbox preview in development.
|
||||
#
|
||||
# Note that preview only shows emails that were sent by the same
|
||||
# node running the Phoenix server.
|
||||
if Mix.env() == :dev do
|
||||
scope "/dev" do
|
||||
pipe_through :browser
|
||||
|
||||
forward "/mailbox", Plug.Swoosh.MailboxPreview
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -31,11 +31,27 @@ defmodule CanneryWeb.Telemetry do
|
||||
),
|
||||
|
||||
# Database Metrics
|
||||
summary("cannery.repo.query.total_time", unit: {:native, :millisecond}),
|
||||
summary("cannery.repo.query.decode_time", unit: {:native, :millisecond}),
|
||||
summary("cannery.repo.query.query_time", unit: {:native, :millisecond}),
|
||||
summary("cannery.repo.query.queue_time", unit: {:native, :millisecond}),
|
||||
summary("cannery.repo.query.idle_time", unit: {:native, :millisecond}),
|
||||
summary("cannery.repo.query.total_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The sum of the other measurements"
|
||||
),
|
||||
summary("cannery.repo.query.decode_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent decoding the data received from the database"
|
||||
),
|
||||
summary("cannery.repo.query.query_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent executing the query"
|
||||
),
|
||||
summary("cannery.repo.query.queue_time",
|
||||
unit: {:native, :millisecond},
|
||||
description: "The time spent waiting for a database connection"
|
||||
),
|
||||
summary("cannery.repo.query.idle_time",
|
||||
unit: {:native, :millisecond},
|
||||
description:
|
||||
"The time the connection spent waiting before being checked out for the query"
|
||||
),
|
||||
|
||||
# VM Metrics
|
||||
summary("vm.memory.total", unit: {:byte, :kilobyte}),
|
||||
|
@ -2,20 +2,20 @@
|
||||
<header class="mb-4 px-8 py-4 w-full bg-primary-400">
|
||||
<%= render "topbar.html", assigns %>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="mx-8 my-2 flex flex-col space-y-4 text-center">
|
||||
<%= if get_flash(@conn, :info) do %>
|
||||
<p class="alert alert-info" role="alert">
|
||||
<%= get_flash(@conn, :info) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= if get_flash(@conn, :error) do %>
|
||||
<p class="alert alert-danger" role="alert">
|
||||
<%= get_flash(@conn, :error) %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
||||
<%= @inner_content %>
|
||||
</main>
|
@ -1,16 +1,21 @@
|
||||
<main role="main" class="container min-w-full min-h-full">
|
||||
|
||||
<main class="container min-w-full min-h-full">
|
||||
<header>
|
||||
<%= live_component CanneryWeb.Live.Component.Topbar, current_user: assigns[:current_user] %>
|
||||
|
||||
|
||||
<div class="mx-8 my-2 flex flex-col space-y-4 text-center">
|
||||
<%= if live_flash(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert">
|
||||
<p class="alert alert-info" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="info">
|
||||
<%= live_flash(@flash, :info) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= if live_flash(@flash, :error) do %>
|
||||
<p class="alert alert-danger" role="alert">
|
||||
<p class="alert alert-danger" role="alert"
|
||||
phx-click="lv:clear-flash"
|
||||
phx-value-key="error">
|
||||
<%= live_flash(@flash, :error) %>
|
||||
</p>
|
||||
<% end %>
|
@ -6,8 +6,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<%= csrf_meta_tag() %>
|
||||
<%= live_title_tag assigns[:page_title] || "Cannery", suffix: "" %>
|
||||
<link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
|
||||
<script defer phx-track-static type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
|
||||
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/css/app.css")}/>
|
||||
<script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/js/app.js")}></script>
|
||||
</head>
|
||||
<body class="m-0 p-0 min-w-full min-h-full">
|
||||
<%= @inner_content %>
|
@ -1,6 +1,6 @@
|
||||
<nav role="navigation">
|
||||
<div class="flex flex-row justify-between items-center space-x-4">
|
||||
<%= link to: Routes.home_path(CanneryWeb.Endpoint, :index) do %>
|
||||
<%= link to: Routes.live_path(CanneryWeb.Endpoint, HomeLive) do %>
|
||||
<h1 class="leading-5 text-xl text-white hover:underline">Cannery</h1>
|
||||
<% end %>
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
defmodule CanneryWeb.LayoutView do
|
||||
use CanneryWeb, :view
|
||||
alias Cannery.{Accounts}
|
||||
|
||||
alias CanneryWeb.{HomeLive}
|
||||
|
||||
# Phoenix LiveDashboard is available only in development by default,
|
||||
# so we instruct Elixir to not warn if the dashboard route is missing.
|
||||
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
|
||||
|
||||
def get_title(conn) do
|
||||
if conn.assigns |> Map.has_key?(:title) do
|
||||
"Cannery | #{conn.assigns.title}"
|
||||
|