add waffle library, handle file upload, authenticate user with oauth and see if they have the link's email associated to their account
This commit is contained in:
parent
5de53e23ea
commit
cac3757723
|
@ -14,15 +14,17 @@ import "../css/app.css"
|
|||
// 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"
|
||||
import "phoenix_html";
|
||||
import {Socket} from "phoenix";
|
||||
import topbar from "topbar";
|
||||
import {LiveSocket} from "phoenix_live_view";
|
||||
|
||||
import SplashPage from './pages/SplashPage';
|
||||
import JustPage from './pages/JustPage'
|
||||
import ForPage from './pages/ForPage'
|
||||
import YouPage from './pages/YouPage'
|
||||
import JustPage from './pages/JustPage';
|
||||
import ForPage from './pages/ForPage';
|
||||
import YouPage from './pages/YouPage';
|
||||
import AuthPage from './pages/AuthPage';
|
||||
import RevealPage from './pages/RevealPage';
|
||||
|
||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
|
||||
|
@ -42,5 +44,5 @@ liveSocket.connect()
|
|||
window.liveSocket = liveSocket
|
||||
|
||||
window.Components = {
|
||||
SplashPage, JustPage, ForPage, YouPage
|
||||
SplashPage, JustPage, ForPage, YouPage, AuthPage, RevealPage
|
||||
}
|
||||
|
|
|
@ -1,16 +1,54 @@
|
|||
// import HexMix from "../utils/hexmix";
|
||||
// const fragmentData = window.location.hash.split('.');
|
||||
// if (fragmentData.length <= 0) {
|
||||
// alert("No key found in fragment URI");
|
||||
// return;
|
||||
// }
|
||||
// const key = HexMix.hexToUint8(fragmentData[0]);
|
||||
// const iv = HexMix.hexToUint8(fragmentData[1]);
|
||||
import { Button, CenteredContainer, GlobalStyle, Header2, Header3, Input, Label, Spacer, TextAlignWrapper } from "@intended/intended-ui";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
// const importedKey = await window.crypto.subtle.importKey(
|
||||
// 'raw',
|
||||
// key,
|
||||
// 'AES-GCM',
|
||||
// true,
|
||||
// ['encrypt', 'decrypt']
|
||||
// );
|
||||
type AuthPageProps = {
|
||||
csrf: string,
|
||||
service: string,
|
||||
recipient: string
|
||||
}
|
||||
|
||||
const AuthPage = (props: AuthPageProps) => {
|
||||
const { service, recipient } = props;
|
||||
// const [recipientInput, setRecipientInput] = useState("");
|
||||
// const [serviceSelect, setServiceSelect] = useState("github");
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<GlobalStyle />
|
||||
<CenteredContainer fullscreen>
|
||||
<CenteredContainer>
|
||||
<Header2 style={{ margin: ".4rem" }}>Someone sent you a secret</Header2>
|
||||
<Header3 small>
|
||||
Please verify your identity to reveal this message.
|
||||
</Header3>
|
||||
<Spacer space="3rem" />
|
||||
<TextAlignWrapper align="left">
|
||||
<Label htmlFor="usernameEmail">Username / Email</Label>
|
||||
</TextAlignWrapper>
|
||||
<Input
|
||||
variant="disabled-medium"
|
||||
id="usernameEmail"
|
||||
value={recipient}
|
||||
/>
|
||||
<Spacer space="3rem" />
|
||||
<TextAlignWrapper align="left">
|
||||
<Label htmlFor="service">Service</Label>
|
||||
</TextAlignWrapper>
|
||||
<Input variant="disabled-medium" id="service" value={service} />
|
||||
<Spacer space="3rem" />
|
||||
<a href={`https://intended.link/auth/${service}`}>
|
||||
<Button variant="primary" wide onClick={() => {}}>
|
||||
Verify
|
||||
</Button>
|
||||
</a>
|
||||
</CenteredContainer>
|
||||
</CenteredContainer>
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthPage;
|
||||
|
|
|
@ -3,9 +3,13 @@ import React, { useState } from "react";
|
|||
import { ProgressIndicator, Header2, Button, IconArrow, Label, Input, Select, CenteredContainer, SpaceBetweenContainer, Spacer, TextAlignWrapper, GlobalStyle } from "@intended/intended-ui";
|
||||
|
||||
|
||||
const ForPage = () => {
|
||||
type ForPageProps = {
|
||||
csrf: string
|
||||
}
|
||||
|
||||
const ForPage = (props: ForPageProps) => {
|
||||
const [recipientInput, setRecipientInput] = useState("");
|
||||
const [serviceSelect, setServiceSelect] = useState("");
|
||||
const [serviceSelect, setServiceSelect] = useState("github");
|
||||
|
||||
const handleRecipientInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
|
@ -18,14 +22,14 @@ const ForPage = () => {
|
|||
};
|
||||
|
||||
const postContacts = async () => {
|
||||
const fragmentData = window.location.hash.split('.');
|
||||
if (fragmentData.length <= 0) {
|
||||
alert("No key found in fragment URI");
|
||||
return;
|
||||
}
|
||||
// const fragmentData = window.location.hash.split('.');
|
||||
// if (fragmentData.length <= 0) {
|
||||
// alert("No key found in fragment URI");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const linkId = sessionStorage.getItem("link_id");
|
||||
if (linkId == null || linkId == "") {
|
||||
if (!linkId) {
|
||||
alert("No created link found in storage");
|
||||
return;
|
||||
}
|
||||
|
@ -36,10 +40,18 @@ const ForPage = () => {
|
|||
formData.append("link_id", linkId);
|
||||
|
||||
try {
|
||||
await fetch(`${window.location.origin}/just/for`, {
|
||||
const results = await fetch(`${window.location.origin}/just/for`, {
|
||||
headers: {
|
||||
"X-CSRF-Token": props.csrf
|
||||
},
|
||||
body: formData,
|
||||
method: "POST"
|
||||
});
|
||||
if (!results.ok) {
|
||||
throw new Error('Network response was not OK');
|
||||
}
|
||||
|
||||
await results.json();
|
||||
window.location.href = `${window.location.origin}/just/for/you`;
|
||||
} catch (err: any) {
|
||||
alert(err.message);
|
||||
|
@ -74,7 +86,9 @@ const ForPage = () => {
|
|||
id="serviceSelector"
|
||||
onChange={handleServiceChange}
|
||||
value={serviceSelect}
|
||||
/>
|
||||
>
|
||||
<option value='github'>Github</option>
|
||||
</Select>
|
||||
<Spacer space="3rem" />
|
||||
<SpaceBetweenContainer>
|
||||
<Button variant="secondary" onClick={() => window.location.href = "/just"}>
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { ProgressIndicator, Header2, Button, IconArrow, Label, FileInput, TextArea, CenteredContainer, Spacer, TextAlignWrapper, GlobalStyle } from '@intended/intended-ui';
|
||||
import HexMix from "../utils/hexmix";
|
||||
|
||||
const JustPage = (props) => {
|
||||
type JustPageProps = {
|
||||
csrf: string
|
||||
}
|
||||
|
||||
const JustPage = (props: JustPageProps) => {
|
||||
const [secretInput, setSecretInput] = useState("");
|
||||
const [fileInput, setFileInput] = useState<File | null>(null);
|
||||
const [fileName, setFileName] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
sessionStorage.clear();
|
||||
}, [])
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setSecretInput(e.target.value);
|
||||
};
|
||||
|
@ -51,18 +59,20 @@ const JustPage = (props) => {
|
|||
const blobData = new Blob([encrypted]);
|
||||
|
||||
formData.append('text_content', blobData);
|
||||
formData.append('filetype', 'text/plain');
|
||||
formData.append('filename', 'secret.txt');
|
||||
// formData.append('filetype', 'text/plain');
|
||||
// formData.append('filename', 'secret.txt');
|
||||
|
||||
try {
|
||||
const link: unknown = await fetch(`${window.location.origin}/just`, {
|
||||
const link: Response = await fetch(`${window.location.origin}/just`, {
|
||||
headers: {
|
||||
"X-CSRF-Token": props.csrf
|
||||
},
|
||||
body: formData,
|
||||
method: "POST"
|
||||
});
|
||||
sessionStorage.setItem("link_id", (link as Link).id);
|
||||
const { id: link_id } = await link.json()
|
||||
|
||||
sessionStorage.setItem("link_id", link_id);
|
||||
sessionStorage.setItem("key_hex", keyHex);
|
||||
sessionStorage.setItem("iv_hex", ivHex);
|
||||
window.location.href = `${window.location.origin}/just/for`;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import { Button, CenteredContainer, GlobalStyle, Header2, Header3, InputButtonWithIcon, Label, SpaceBetweenContainer, Spacer, TextAlignWrapper, TextAreaParagraph } from "@intended/intended-ui";
|
||||
import React, { useEffect, useState } from "react";
|
||||
// import HexMix from "../utils/hexmix";
|
||||
// const fragmentData = window.location.hash.split('.');
|
||||
// if (fragmentData.length <= 0) {
|
||||
// alert("No key found in fragment URI");
|
||||
// return;
|
||||
// }
|
||||
// const key = HexMix.hexToUint8(fragmentData[0]);
|
||||
// const iv = HexMix.hexToUint8(fragmentData[1]);
|
||||
|
||||
// const importedKey = await window.crypto.subtle.importKey(
|
||||
// 'raw',
|
||||
// key,
|
||||
// 'AES-GCM',
|
||||
// true,
|
||||
// ['encrypt', 'decrypt']
|
||||
// );
|
||||
|
||||
|
||||
const RevealPage = () => {
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<GlobalStyle />
|
||||
<CenteredContainer fullscreen>
|
||||
<CenteredContainer>
|
||||
<Header2 style={{ margin: ".4rem" }}>Someone sent you a secret</Header2>
|
||||
<Header3 small>
|
||||
Please verify your identity to reveal this message.
|
||||
</Header3>
|
||||
<Spacer space="3rem" />
|
||||
|
||||
<SpaceBetweenContainer>
|
||||
<Label htmlFor="secretMessage">Secret message</Label>
|
||||
<Label htmlFor="secretMessage">Sent 8/24/21 @ 1:27pm</Label>
|
||||
</SpaceBetweenContainer>
|
||||
<TextAreaParagraph id="secretMessage">
|
||||
"Sup. What are you doing for lunch?"
|
||||
</TextAreaParagraph>
|
||||
|
||||
<Spacer space="3rem" />
|
||||
<TextAlignWrapper align="left">
|
||||
<Label htmlFor="service">Secret File</Label>
|
||||
</TextAlignWrapper>
|
||||
<InputButtonWithIcon
|
||||
variant="download"
|
||||
id="downloadfile"
|
||||
value="1780983.jpg"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<Spacer space="3rem" />
|
||||
<Button variant="secondary" wide onClick={() => {}}>
|
||||
Send a secret
|
||||
</Button>
|
||||
</CenteredContainer>
|
||||
</CenteredContainer>
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
export default RevealPage;
|
|
@ -8,11 +8,10 @@ const YouPage = () => {
|
|||
const keyHex = sessionStorage.getItem("key_hex");
|
||||
const ivHex = sessionStorage.getItem("iv_hex");
|
||||
|
||||
`${window.location.origin}/just/for/you/${linkId}#${keyHex}.${ivHex}`
|
||||
return "";
|
||||
return `${window.location.origin}/just/for/you/${linkId}#${keyHex}.${ivHex}`;
|
||||
};
|
||||
|
||||
const [url, setUrl] = useState(calculateUrl());
|
||||
const [url, _setUrl] = useState(calculateUrl());
|
||||
|
||||
const copyUrl = async () => {
|
||||
try {
|
||||
|
@ -51,7 +50,7 @@ const YouPage = () => {
|
|||
<Input
|
||||
variant="disabled-light"
|
||||
id="encodedSecret"
|
||||
value="\4RÏÇmÄyÆFÕ¬Ð$CÑÓÃyÛ"
|
||||
value={url}
|
||||
/>
|
||||
<Spacer space="3rem" />
|
||||
<SpaceBetweenContainer>
|
||||
|
|
|
@ -1079,6 +1079,7 @@
|
|||
"version": "7.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
|
||||
"integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
|
@ -1142,9 +1143,9 @@
|
|||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"@intended/intended-ui": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/@intended/intended-ui/-/intended-ui-0.1.19.tgz",
|
||||
"integrity": "sha512-dOQZBvBm5UyKhyilTE6y/3KL1suyjbAuu+kn4b3tKhpy/Y23AglVlHLj4oi/AUgGRMtcRT/o9eySJWkNWQ9weA==",
|
||||
"version": "0.1.21",
|
||||
"resolved": "https://registry.npmjs.org/@intended/intended-ui/-/intended-ui-0.1.21.tgz",
|
||||
"integrity": "sha512-rNyJOGLOw8iKP0AaSYSytZXFssPhlE1FbUjxEze8F4iOgXxs6iu7B9+gSIF4QJg/IrQUVu76tkevBGDk2nTQNg==",
|
||||
"requires": {
|
||||
"polished": "^4.1.3",
|
||||
"react": "^17.0.2",
|
||||
|
@ -1719,9 +1720,9 @@
|
|||
}
|
||||
},
|
||||
"babel-plugin-styled-components": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.1.tgz",
|
||||
"integrity": "sha512-U3wmORxerYBiqcRCo6thItIosEIga3F+ph0jJPkiOZJjyhpZyUZFQV9XvrZ2CbBIihJ3rDBC/itQ+Wx3VHMauw==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz",
|
||||
"integrity": "sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.16.0",
|
||||
"@babel/helper-module-imports": "^7.16.0",
|
||||
|
@ -5896,11 +5897,21 @@
|
|||
}
|
||||
},
|
||||
"polished": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/polished/-/polished-4.1.3.tgz",
|
||||
"integrity": "sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/polished/-/polished-4.1.4.tgz",
|
||||
"integrity": "sha512-Nq5Mbza+Auo7N3sQb1QMFaQiDO+4UexWuSGR7Cjb4Sw11SZIJcrrFtiZ+L0jT9MBsUsxDboHVASbCLbE1rnECg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.14.0"
|
||||
"@babel/runtime": "^7.16.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
|
||||
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"posix-character-classes": {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"watch": "webpack --mode development --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intended/intended-ui": "0.1.19",
|
||||
"@intended/intended-ui": "0.1.21",
|
||||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html",
|
||||
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
||||
|
|
|
@ -33,6 +33,9 @@ config :ueberauth, Ueberauth,
|
|||
github: {Ueberauth.Strategy.Github, [default_scope: "user:email", allow_private_emails: true]}
|
||||
]
|
||||
|
||||
config :waffle,
|
||||
storage: Waffle.Storage.Local
|
||||
|
||||
# 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"
|
||||
|
|
|
@ -18,7 +18,7 @@ config :entendu, Entendu.Repo,
|
|||
config :entendu, EntenduWeb.Endpoint,
|
||||
http: [port: System.get_env("PORT", "4000")],
|
||||
https: [
|
||||
port: 4001,
|
||||
port: 443,
|
||||
cipher_suite: :strong,
|
||||
certfile: "priv/cert/selfsigned.pem",
|
||||
keyfile: "priv/cert/selfsigned_key.pem"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
defmodule Entendu.Links.Link do
|
||||
use Ecto.Schema
|
||||
use Waffle.Ecto.Schema
|
||||
alias Entendu.EncryptedLink
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, Ecto.UUID, autogenerate: true}
|
||||
|
@ -9,8 +11,8 @@ defmodule Entendu.Links.Link do
|
|||
field :expires, :utc_datetime
|
||||
field :filename, :string
|
||||
field :filetype, :string
|
||||
field :text_content, :string
|
||||
field :file_content, :string
|
||||
field :text_content, EncryptedLink.Type
|
||||
field :file_content, EncryptedLink.Type
|
||||
field :recipient, :string
|
||||
field :service, :string
|
||||
|
||||
|
@ -25,10 +27,10 @@ defmodule Entendu.Links.Link do
|
|||
:burn_after_reading,
|
||||
:filename,
|
||||
:filetype,
|
||||
:text_content,
|
||||
:file_content,
|
||||
:recipient,
|
||||
:service
|
||||
])
|
||||
|> cast_attachments(attrs, [:text_content, :file_content])
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Entendu.UserFromAuth do
|
|||
require Jason
|
||||
|
||||
alias Ueberauth.Auth
|
||||
alias Entendu.Links.Link
|
||||
|
||||
def find_or_create(%Auth{} = auth) do
|
||||
{:ok, basic_info(auth)}
|
||||
|
@ -24,9 +25,15 @@ defmodule Entendu.UserFromAuth do
|
|||
nil
|
||||
end
|
||||
|
||||
defp emails_from_auth(%Auth{ extra: %Auth.Extra{ raw_info: %{ user: %{ "emails" => emails}}}}), do: emails
|
||||
|
||||
defp emails_from_auth(%Auth{ info: %{ email: email }}), do: [email]
|
||||
|
||||
defp emails_from_auth(_auth), do: []
|
||||
|
||||
defp basic_info(auth) do
|
||||
IO.inspect(auth)
|
||||
%{id: auth.uid, name: name_from_auth(auth), avatar: avatar_from_auth(auth)}
|
||||
%{id: auth.uid, name: name_from_auth(auth), avatar: avatar_from_auth(auth), emails: emails_from_auth(auth)}
|
||||
end
|
||||
|
||||
defp name_from_auth(auth) do
|
||||
|
@ -44,4 +51,9 @@ defmodule Entendu.UserFromAuth do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_access?(recipient, emails) do
|
||||
emails
|
||||
|> Enum.any?(&( &1["verified"] == true and &1["email"] == recipient))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,18 +23,27 @@ defmodule EntenduWeb.AuthController do
|
|||
end
|
||||
|
||||
def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
|
||||
case UserFromAuth.find_or_create(auth) do
|
||||
{:ok, user} ->
|
||||
# TODO: turn this into plug that only proceeds if current_link session var exists
|
||||
%{ id: link_id, recipient: recipient } = get_session(conn, :current_link)
|
||||
|
||||
with {:ok, user} <- UserFromAuth.find_or_create(auth),
|
||||
true <- UserFromAuth.can_access?(recipient, user.emails) do
|
||||
# TODO: send over encrypted data that the frontend can decrypt
|
||||
conn
|
||||
|> put_flash(:info, "Successfully authenticated.")
|
||||
|> put_session(:current_user, user)
|
||||
|> configure_session(renew: true)
|
||||
|> redirect(to: "/")
|
||||
|> redirect(to: "/just/for/you/#{link_id}")
|
||||
|
||||
else
|
||||
false ->
|
||||
conn
|
||||
|> put_flash(:error, "#{recipient} was not found in your list of verified emails")
|
||||
|> redirect(to: "/just/for/you/#{link_id}")
|
||||
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_flash(:error, reason)
|
||||
|> redirect(to: "/")
|
||||
|> redirect(to: "/just/for/you/#{link_id}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,6 @@ defmodule EntenduWeb.LinkController do
|
|||
|
||||
alias Entendu.Links
|
||||
alias Links.Link
|
||||
alias Ecto.Changeset
|
||||
alias EntenduWeb.FallbackController
|
||||
|
||||
action_fallback(FallbackController)
|
||||
|
@ -17,23 +16,13 @@ defmodule EntenduWeb.LinkController do
|
|||
render(conn, "just.html")
|
||||
end
|
||||
|
||||
defparams(
|
||||
first_step(%{
|
||||
burn_after_reading: [field: :boolean, default: false],
|
||||
expires: :utc_datetime,
|
||||
filename: :string,
|
||||
filetype: :string,
|
||||
text_content: :string,
|
||||
file_content: :string
|
||||
})
|
||||
)
|
||||
|
||||
def just(conn, params) do
|
||||
with %Changeset{valid?: true} = changeset <- first_step(params),
|
||||
link_params <- Params.to_map(changeset),
|
||||
{:ok, %Link{} = link} <- Links.create_link(link_params) do
|
||||
with {:ok, %Link{} = link} <- Links.create_link(params) do
|
||||
conn
|
||||
|> render("show_authorized.json", %{link: link})
|
||||
else
|
||||
test ->
|
||||
IO.inspect(test)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,18 +30,9 @@ defmodule EntenduWeb.LinkController do
|
|||
render(conn, "for.html")
|
||||
end
|
||||
|
||||
defparams(
|
||||
second_step(%{
|
||||
service: :string,
|
||||
recipient: :string
|
||||
})
|
||||
)
|
||||
|
||||
def for(conn, %{link_id: link_id} = params) do
|
||||
with %Changeset{valid?: true} = changeset <- first_step(params),
|
||||
link_params <- Params.to_map(changeset),
|
||||
%Link{} = link <- Links.get_link(link_id),
|
||||
Links.update_link(link, link_params) do
|
||||
def for(conn, %{"link_id" => link_id, "recipient" => recipient, "service" => service}) do
|
||||
with %Link{} = link <- Links.get_link(link_id),
|
||||
Links.update_link(link, %{ recipient: recipient, service: service}) do
|
||||
conn
|
||||
|> render("show_authorized.json", %{link: link})
|
||||
end
|
||||
|
@ -61,4 +41,12 @@ defmodule EntenduWeb.LinkController do
|
|||
def you_page(conn, _params) do
|
||||
render(conn, "you.html")
|
||||
end
|
||||
|
||||
def auth_page(conn, %{ "id" => link_id}) do
|
||||
with %Link{service: service, recipient: recipient} = link <- Links.get_link(link_id) do
|
||||
conn
|
||||
|> put_session(:current_link, link)
|
||||
|> render("auth.html", %{ service: service, recipient: recipient })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,7 @@ defmodule EntenduWeb.Router do
|
|||
get "/just/for", LinkController, :for_page
|
||||
post "/just/for", LinkController, :for
|
||||
get "/just/for/you", LinkController, :you_page
|
||||
get "/just/for/you/:id", LinkController, :auth_page
|
||||
end
|
||||
|
||||
scope "/auth", EntenduWeb do
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<%= react_component("Components.AuthPage", %{ csrf: Plug.CSRFProtection.get_csrf_token(), service: @service, recipient: @recipient, user: @current_user }) %>
|
|
@ -1 +1 @@
|
|||
<%= react_component("Components.ForPage") %>
|
||||
<%= react_component("Components.ForPage", %{ csrf: Plug.CSRFProtection.get_csrf_token() }) %>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
defmodule Entendu.EncryptedLink do
|
||||
use Waffle.Definition
|
||||
use Waffle.Ecto.Definition
|
||||
|
||||
# Include ecto support (requires package waffle_ecto installed):
|
||||
# use Waffle.Ecto.Definition
|
||||
|
||||
@versions [:original]
|
||||
|
||||
# To add a thumbnail version:
|
||||
# @versions [:original, :thumb]
|
||||
|
||||
# Override the bucket on a per definition basis:
|
||||
# def bucket do
|
||||
# :custom_bucket_name
|
||||
# end
|
||||
|
||||
# Whitelist file extensions:
|
||||
# def validate({file, _}) do
|
||||
# file_extension = file.file_name |> Path.extname() |> String.downcase()
|
||||
#
|
||||
# case Enum.member?(~w(.jpg .jpeg .gif .png), file_extension) do
|
||||
# true -> :ok
|
||||
# false -> {:error, "invalid file type"}
|
||||
# end
|
||||
# end
|
||||
|
||||
# Define a thumbnail transformation:
|
||||
# def transform(:thumb, _) do
|
||||
# {:convert, "-strip -thumbnail 250x250^ -gravity center -extent 250x250 -format png", :png}
|
||||
# end
|
||||
|
||||
# Override the persisted filenames:
|
||||
# def filename(version, _) do
|
||||
# version
|
||||
# end
|
||||
|
||||
# Override the storage directory:
|
||||
# def storage_dir(version, {file, scope}) do
|
||||
# "uploads/user/avatars/#{scope.id}"
|
||||
# end
|
||||
|
||||
# Provide a default URL if there hasn't been a file uploaded
|
||||
# def default_url(version, scope) do
|
||||
# "/images/avatars/default_#{version}.png"
|
||||
# end
|
||||
|
||||
# Specify custom headers for s3 objects
|
||||
# Available options are [:cache_control, :content_disposition,
|
||||
# :content_encoding, :content_length, :content_type,
|
||||
# :expect, :expires, :storage_class, :website_redirect_location]
|
||||
#
|
||||
# def s3_object_headers(version, {file, scope}) do
|
||||
# [content_type: MIME.from_path(file.file_name)]
|
||||
# end
|
||||
end
|
4
mix.exs
4
mix.exs
|
@ -51,7 +51,9 @@ defmodule Entendu.MixProject do
|
|||
{:ueberauth, "~> 0.7.0"},
|
||||
{:ueberauth_github, "~> 0.8.1"},
|
||||
{:react_phoenix, "~> 1.3"},
|
||||
{:params, "~> 2.2"}
|
||||
{:params, "~> 2.2"},
|
||||
{:waffle, "~> 1.1"},
|
||||
{:waffle_ecto, "~> 0.0.11"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -42,4 +42,6 @@
|
|||
"ueberauth": {:hex, :ueberauth, "0.7.0", "9c44f41798b5fa27f872561b6f7d2bb0f10f03fdd22b90f454232d7b087f4b75", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2efad9022e949834f16cc52cd935165049d81fa9e925690f91035c2e4b58d905"},
|
||||
"ueberauth_github": {:hex, :ueberauth_github, "0.8.1", "0be487b5afc29bc805fa5e31636f37c8f09d5159ef73fc08c4c7a98c9cfe2c18", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "143d6130b945ea9bdbd0ef94987f40788f1d7e8090decbfc0722773155e7a74a"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
"waffle": {:hex, :waffle, "1.1.5", "11b8b41c9dc46a21c8e1e619e1e9048d18d166b57b33d1fada8e11fcd4e678b3", [:mix], [{:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:ex_aws_s3, "~> 2.1", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "68e6f92b457b13c71e33cc23f7abb60446a01515dc6618b7d493d8cd466b1f39"},
|
||||
"waffle_ecto": {:hex, :waffle_ecto, "0.0.11", "3d9581b3dfc83964ad968ef6bbf31132b5e6959c542a74c49e2a2245a9521048", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:waffle, "~> 1.0", [hex: :waffle, repo: "hexpm", optional: false]}], "hexpm", "626c2832ba94e20840532e609d3af70526d18ff9dfe1b352afb3fbabedb31a7e"},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue