use devise instead auth0
This commit is contained in:
3
.env
3
.env
@@ -1,3 +0,0 @@
|
||||
AUTH_DOMAIN=dev-l56jkgi4.us.auth0.com
|
||||
AUTH_CLIENT_ID=xuG9f6l8ewKSXdJozsT2Ps8bABtlLbRb
|
||||
AUTH_AUDIENCE=https://xstake.com.br/
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -16,13 +16,11 @@ gem "image_processing", "~> 1.12"
|
||||
|
||||
gem "devise"
|
||||
gem "devise-i18n"
|
||||
gem "devise-bootstrap-views"
|
||||
|
||||
gem "administrate-field-active_storage"
|
||||
gem "tailwindcss-rails"
|
||||
gem "administrate"
|
||||
gem "enumerize"
|
||||
gem "httparty"
|
||||
gem "graphql"
|
||||
gem "pundit"
|
||||
|
||||
|
||||
10
Gemfile.lock
10
Gemfile.lock
@@ -104,7 +104,6 @@ GEM
|
||||
railties (>= 4.1.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-bootstrap-views (1.1.0)
|
||||
devise-i18n (1.10.0)
|
||||
devise (>= 4.8.0)
|
||||
diff-lcs (1.4.4)
|
||||
@@ -121,9 +120,6 @@ GEM
|
||||
graphql (1.12.14)
|
||||
graphql_playground-rails (2.1.0)
|
||||
rails (>= 5.1.0)
|
||||
httparty (0.18.1)
|
||||
mime-types (~> 3.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (1.8.10)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_processing (1.12.1)
|
||||
@@ -155,9 +151,6 @@ GEM
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (1.0.1)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2021.0704)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.0)
|
||||
mini_portile2 (2.6.1)
|
||||
@@ -165,7 +158,6 @@ GEM
|
||||
momentjs-rails (2.20.1)
|
||||
railties (>= 3.1)
|
||||
msgpack (1.4.2)
|
||||
multi_xml (0.6.0)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.12.1)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
@@ -331,13 +323,11 @@ DEPENDENCIES
|
||||
bootsnap (>= 1.4.4)
|
||||
capybara
|
||||
devise
|
||||
devise-bootstrap-views
|
||||
devise-i18n
|
||||
dotenv-rails
|
||||
enumerize
|
||||
graphql
|
||||
graphql_playground-rails
|
||||
httparty
|
||||
image_processing (~> 1.12)
|
||||
listen (~> 3.3)
|
||||
pg (~> 1.1)
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
class ApplicationController < ActionController::Base
|
||||
include Pundit
|
||||
|
||||
before_action :configure_devise_permitted_parameters, if: :devise_controller?
|
||||
|
||||
protected
|
||||
|
||||
def configure_devise_permitted_parameters
|
||||
attributes = [:first_name, :last_name]
|
||||
devise_parameter_sanitizer.permit(:sign_up, keys: attributes)
|
||||
devise_parameter_sanitizer.permit(:account_update, keys: attributes)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
module Authenticable
|
||||
def current_auth
|
||||
@current_auth ||= Auth::Authenticate.new(bearer_token).profile
|
||||
end
|
||||
|
||||
def bearer_token
|
||||
pattern = /^Bearer /
|
||||
header = request.headers["Authorization"]
|
||||
header.gsub(pattern, "") if header&.match(pattern)
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
class GraphqlController < ApplicationController
|
||||
include Authenticable
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
def execute
|
||||
variables = prepare_variables(params[:variables])
|
||||
query = params[:query]
|
||||
operation_name = params[:operationName]
|
||||
context = {
|
||||
current_auth: current_auth,
|
||||
current_user: current_auth&.user,
|
||||
current_user: current_user,
|
||||
}
|
||||
result = XStakeSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
|
||||
render(json: result)
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
// Turbolinks.start()
|
||||
// ActiveStorage.start()
|
||||
|
||||
import "regenerator-runtime";
|
||||
import "stylesheets/application";
|
||||
import "regenerator-runtime";
|
||||
import "../src/index";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import "../src/index";
|
||||
@@ -1,15 +1,21 @@
|
||||
import type { Variables, RequestParameters, CacheConfig } from "relay-runtime";
|
||||
import { Environment, Network, RecordSource, Store } from "relay-runtime";
|
||||
|
||||
async function fetchRelay(
|
||||
export const fetchRelay = async (
|
||||
params: RequestParameters,
|
||||
variables: Variables,
|
||||
_cacheConfig: CacheConfig
|
||||
) {
|
||||
) => {
|
||||
const csrfToken =
|
||||
document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
?.getAttribute("content") ?? "";
|
||||
|
||||
const response = await fetch("/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": csrfToken,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: params.text,
|
||||
@@ -30,7 +36,7 @@ async function fetchRelay(
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
export const environment = new Environment({
|
||||
network: Network.create(fetchRelay),
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
import { graphql } from "babel-plugin-relay/macro";
|
||||
import type { FC } from "react";
|
||||
import React, { Suspense } from "react";
|
||||
import { RelayEnvironmentProvider } from "react-relay";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
import React from "react";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
|
||||
import { environment } from "../relay/environment";
|
||||
import { Navbar } from "./components/Navbar";
|
||||
import { SideNav } from "./components/SideNav";
|
||||
import { AppContext } from "./contexts/AppContext";
|
||||
import { AuthProvider } from "./contexts/AuthProvider";
|
||||
import { Navbar, SideNav } from "./components";
|
||||
import { AppProvider } from "./contexts/AppProvider";
|
||||
import { UserProvider } from "./contexts/UserProvider";
|
||||
import { Routes } from "./Routes";
|
||||
import type { AppQuery } from "./__generated__/AppQuery.graphql";
|
||||
|
||||
export const App: FC = () => {
|
||||
const { currentUser } = useLazyLoadQuery<AppQuery>(
|
||||
graphql`
|
||||
query AppQuery {
|
||||
currentUser {
|
||||
firstName
|
||||
}
|
||||
}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
|
||||
return (
|
||||
<RelayEnvironmentProvider environment={environment}>
|
||||
<Suspense fallback="Carregando...">
|
||||
<Router>
|
||||
<AuthProvider>
|
||||
<AppContext>
|
||||
<AppProvider>
|
||||
<UserProvider user={currentUser}>
|
||||
<main className="min-h-screen w-full bg-gray-50 flex flex-col">
|
||||
<Navbar />
|
||||
<div className="flex flex-grow">
|
||||
@@ -24,10 +31,7 @@ export const App: FC = () => {
|
||||
<Routes />
|
||||
</div>
|
||||
</main>
|
||||
</AppContext>
|
||||
</AuthProvider>
|
||||
</Router>
|
||||
</Suspense>
|
||||
</RelayEnvironmentProvider>
|
||||
</UserProvider>
|
||||
</AppProvider>
|
||||
);
|
||||
};
|
||||
|
||||
97
app/javascript/src/__generated__/AppQuery.graphql.ts
generated
Normal file
97
app/javascript/src/__generated__/AppQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from "relay-runtime";
|
||||
export type AppQueryVariables = {};
|
||||
export type AppQueryResponse = {
|
||||
readonly currentUser: {
|
||||
readonly firstName: string;
|
||||
} | null;
|
||||
};
|
||||
export type AppQuery = {
|
||||
readonly response: AppQueryResponse;
|
||||
readonly variables: AppQueryVariables;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
query AppQuery {
|
||||
currentUser {
|
||||
firstName
|
||||
id
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "firstName",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "AppQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"kind": "LinkedField",
|
||||
"name": "currentUser",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Operation",
|
||||
"name": "AppQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "User",
|
||||
"kind": "LinkedField",
|
||||
"name": "currentUser",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "ecf2bf1a08ead5c3e2edb202242ed8cf",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "AppQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query AppQuery {\n currentUser {\n firstName\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
(node as any).hash = 'ea65c9caf86d93b8ddded2ae3eaa7b0e';
|
||||
export default node;
|
||||
@@ -1,63 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { useAuth0 } from "@auth0/auth0-react";
|
||||
|
||||
import XStakeLogo from "../images/logo.png";
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
|
||||
export const Navbar = () => {
|
||||
const { setSideNavExpanded } = useAppContext();
|
||||
|
||||
const handleExpandSideNav = () => {
|
||||
setSideNavExpanded((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const { loginWithRedirect, logout, isAuthenticated } = useAuth0();
|
||||
|
||||
return (
|
||||
<nav className="fixed w-full h-16 flex bg-white shadow items-center px-4 space-x-2 z-50">
|
||||
<button
|
||||
className="w-10 h-10 xl:hidden"
|
||||
onClick={() => handleExpandSideNav()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-full w-full"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<img
|
||||
src={XStakeLogo}
|
||||
alt="XStake Logo"
|
||||
width={64}
|
||||
placeholder="blurred"
|
||||
/>
|
||||
<h1 className="text-2xl font-bold">XStake</h1>
|
||||
<div className="w-full h-full flex items-center justify-end">
|
||||
{isAuthenticated ? (
|
||||
<button
|
||||
className="cursor-pointer hover:bg-gray-100 h-full px-4 font-bold"
|
||||
onClick={() => logout({ returnTo: window.location.origin })}
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="cursor-pointer hover:bg-gray-100 h-full px-4 font-bold"
|
||||
onClick={loginWithRedirect}
|
||||
>
|
||||
Entrar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
75
app/javascript/src/components/Navbar/Navbar.tsx
Normal file
75
app/javascript/src/components/Navbar/Navbar.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import * as React from "react";
|
||||
|
||||
import XStakeLogo from "../../images/logo.png";
|
||||
import { useApp } from "../../contexts/AppProvider";
|
||||
import { useCurrentUser } from "../../contexts/UserProvider";
|
||||
|
||||
const linkStyles =
|
||||
"cursor-pointer hover:bg-gray-100 h-full px-4 font-bold flex items-center";
|
||||
|
||||
export const Navbar = () => {
|
||||
const { setSideNavExpanded } = useApp();
|
||||
const handleExpandSideNav = () => {
|
||||
setSideNavExpanded((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const { isAuthenticated } = useCurrentUser();
|
||||
|
||||
const csrfToken =
|
||||
document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
?.getAttribute("content") ?? "";
|
||||
|
||||
return (
|
||||
<nav className="fixed w-full h-16 flex bg-white shadow items-center px-4 space-x-2 z-50">
|
||||
<button
|
||||
className="w-10 h-10 xl:hidden"
|
||||
onClick={() => handleExpandSideNav()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-full w-full"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<img
|
||||
src={XStakeLogo}
|
||||
alt="XStake Logo"
|
||||
width={64}
|
||||
placeholder="blurred"
|
||||
/>
|
||||
<h1 className="text-2xl font-bold">XStake</h1>
|
||||
<div className="w-full h-full flex items-center justify-end">
|
||||
{isAuthenticated ? (
|
||||
<form className="h-full" method="post" action="/users/sign_out">
|
||||
<input className={linkStyles} type="submit" value="Sair" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="authenticity_token"
|
||||
defaultValue={csrfToken}
|
||||
/>
|
||||
<input type="hidden" name="_method" value="delete" />
|
||||
</form>
|
||||
) : (
|
||||
<form className="h-full" method="post" action="/users/sign_in">
|
||||
<input className={linkStyles} type="submit" value="Entrar" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="authenticity_token"
|
||||
defaultValue={csrfToken}
|
||||
/>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
1
app/javascript/src/components/Navbar/index.ts
Normal file
1
app/javascript/src/components/Navbar/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./Navbar";
|
||||
@@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import cx from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
import { useApp } from "../contexts/AppProvider";
|
||||
|
||||
type MenuItem = {
|
||||
label: string;
|
||||
@@ -25,7 +25,7 @@ const MenuItems: MenuItem[] = [
|
||||
];
|
||||
|
||||
export const SideNav = () => {
|
||||
const { sideNavExpanded, setSideNavExpanded } = useAppContext();
|
||||
const { sideNavExpanded, setSideNavExpanded } = useApp();
|
||||
|
||||
const handleCloseSideNav = () => {
|
||||
setSideNavExpanded(false);
|
||||
|
||||
2
app/javascript/src/components/index.ts
Normal file
2
app/javascript/src/components/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./Navbar";
|
||||
export * from "./SideNav";
|
||||
@@ -8,7 +8,7 @@ export type AppContext = {
|
||||
|
||||
const Context = createContext<AppContext | null>(null);
|
||||
|
||||
export const useAppContext = (): AppContext => {
|
||||
export const useApp = (): AppContext => {
|
||||
const context = useContext(Context);
|
||||
|
||||
if (context === null) {
|
||||
@@ -18,7 +18,7 @@ export const useAppContext = (): AppContext => {
|
||||
return context;
|
||||
};
|
||||
|
||||
export const AppContext: FC = ({ children }) => {
|
||||
export const AppProvider: FC = ({ children }) => {
|
||||
const [sideNavExpanded, setSideNavExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -1,24 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { Auth0Provider } from "@auth0/auth0-react";
|
||||
import type { FC } from "react";
|
||||
import React from "react";
|
||||
|
||||
export const AuthProvider: FC = ({ children }) => {
|
||||
// @ts-ignore
|
||||
const domain = window.AUTH_DOMAIN;
|
||||
// @ts-ignore
|
||||
const clientId = window.AUTH_CLIENT_ID;
|
||||
// @ts-ignore
|
||||
const audience = window.AUTH_AUDIENCE;
|
||||
|
||||
return (
|
||||
<Auth0Provider
|
||||
domain={domain}
|
||||
clientId={clientId}
|
||||
audience={audience}
|
||||
redirectUri={window.location.origin}
|
||||
>
|
||||
{children}
|
||||
</Auth0Provider>
|
||||
);
|
||||
};
|
||||
32
app/javascript/src/contexts/UserProvider.tsx
Normal file
32
app/javascript/src/contexts/UserProvider.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { FC } from "react";
|
||||
import React, { createContext, useContext } from "react";
|
||||
|
||||
type CurrentUserContext = {
|
||||
user: {
|
||||
readonly firstName: string;
|
||||
} | null;
|
||||
isAuthenticated: boolean;
|
||||
};
|
||||
|
||||
const Context = createContext<CurrentUserContext>({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
});
|
||||
|
||||
export const useCurrentUser = (): CurrentUserContext => {
|
||||
const context = useContext(Context);
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
user: {
|
||||
readonly firstName: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export const UserProvider: FC<Props> = ({ user, children }) => (
|
||||
<Context.Provider value={{ user, isAuthenticated: !!user }}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
);
|
||||
@@ -1,12 +1,21 @@
|
||||
import React from "react";
|
||||
import React, { Suspense } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { RelayEnvironmentProvider } from "react-relay";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
|
||||
import { environment } from "../relay/environment";
|
||||
import { App } from "./App";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<RelayEnvironmentProvider environment={environment}>
|
||||
<Suspense fallback="Carregando...">
|
||||
<Router>
|
||||
<App />
|
||||
</Router>
|
||||
</Suspense>
|
||||
</RelayEnvironmentProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
# index_admin_users_on_reset_password_token (reset_password_token) UNIQUE
|
||||
#
|
||||
class AdminUser < ApplicationRecord
|
||||
# Include default devise modules. Others available are:
|
||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
||||
devise :database_authenticatable, :recoverable,
|
||||
:rememberable, :validatable
|
||||
end
|
||||
|
||||
@@ -5,17 +5,25 @@
|
||||
# Table name: users
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# email :string not null
|
||||
# email :string default(""), not null
|
||||
# encrypted_password :string default(""), not null
|
||||
# first_name :string not null
|
||||
# last_name :string not null
|
||||
# remember_created_at :datetime
|
||||
# reset_password_sent_at :datetime
|
||||
# reset_password_token :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_users_on_email (email) UNIQUE
|
||||
# index_users_on_reset_password_token (reset_password_token) UNIQUE
|
||||
#
|
||||
class User < ApplicationRecord
|
||||
devise :database_authenticatable, :registerable,
|
||||
:recoverable, :rememberable, :validatable
|
||||
|
||||
has_many :documents, class_name: "UserDocument", dependent: :destroy
|
||||
|
||||
validates :first_name, :last_name, :email, presence: true
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
module Auth
|
||||
class Auth0Client
|
||||
class << self
|
||||
def find_profile(token)
|
||||
Profile.new(user_profile_attributes(token))
|
||||
end
|
||||
|
||||
def user_profile_attributes(token)
|
||||
HTTParty.get(
|
||||
"https://#{ENV["AUTH_DOMAIN"]}/userinfo",
|
||||
headers: {
|
||||
"Content-Type" => "application/json",
|
||||
"Authorization": "Bearer #{token}",
|
||||
}
|
||||
).with_indifferent_access
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
module Auth
|
||||
class Authenticate
|
||||
attr_reader :jwt_token
|
||||
|
||||
def initialize(jwt_token)
|
||||
@jwt_token = jwt_token
|
||||
end
|
||||
|
||||
def profile
|
||||
return nil if jwt_token.blank?
|
||||
|
||||
Auth0Client.find_profile(jwt_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,15 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
module Auth
|
||||
class Profile
|
||||
attr_reader :id, :email
|
||||
|
||||
def initialize(attributes)
|
||||
@id = attributes[:sub]
|
||||
@email = attributes[:email]
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= User.find_by(email: email)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,16 @@
|
||||
<h1><%= t('.resend_confirmation_instructions') %></h1>
|
||||
<h2><%= t('.resend_confirmation_instructions') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.resend_confirmation_instructions'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.resend_confirmation_instructions') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<p><%= t('.greeting', recipient: @email) %></p>
|
||||
|
||||
<p><%= t('.instruction') %></p>
|
||||
|
||||
<p><%= link_to t('.action'), confirmation_url(@resource, confirmation_token: @token) %></p>
|
||||
7
app/views/devise/mailer/email_changed.html.erb
Normal file
7
app/views/devise/mailer/email_changed.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<p><%= t('.greeting', recipient: @email) %></p>
|
||||
|
||||
<% if @resource.try(:unconfirmed_email?) %>
|
||||
<p><%= t('.message_unconfirmed', email: @resource.unconfirmed_email) %></p>
|
||||
<% else %>
|
||||
<p><%= t('.message', email: @resource.email) %></p>
|
||||
<% end %>
|
||||
3
app/views/devise/mailer/password_change.html.erb
Normal file
3
app/views/devise/mailer/password_change.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<p><%= t('.greeting', recipient: @resource.email) %></p>
|
||||
|
||||
<p><%= t('.message') %></p>
|
||||
@@ -0,0 +1,8 @@
|
||||
<p><%= t('.greeting', recipient: @resource.email) %></p>
|
||||
|
||||
<p><%= t('.instruction') %></p>
|
||||
|
||||
<p><%= link_to t('.action'), edit_password_url(@resource, reset_password_token: @token) %></p>
|
||||
|
||||
<p><%= t('.instruction_2') %></p>
|
||||
<p><%= t('.instruction_3') %></p>
|
||||
7
app/views/devise/mailer/unlock_instructions.html.erb
Normal file
7
app/views/devise/mailer/unlock_instructions.html.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
<p><%= t('.greeting', recipient: @resource.email) %></p>
|
||||
|
||||
<p><%= t('.message') %></p>
|
||||
|
||||
<p><%= t('.instruction') %></p>
|
||||
|
||||
<p><%= link_to t('.action'), unlock_url(@resource, unlock_token: @token) %></p>
|
||||
@@ -1,26 +1,25 @@
|
||||
<h1><%= t('.change_your_password') %></h1>
|
||||
<h2><%= t('.change_your_password') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
<%= f.hidden_field :reset_password_token %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password, t('.new_password') %>
|
||||
<%= f.password_field :password, autofocus: true, class: 'form-control' %>
|
||||
|
||||
<div class="field">
|
||||
<%= f.label :password, t('.new_password') %><br />
|
||||
<% if @minimum_password_length %>
|
||||
<small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small>
|
||||
<em><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></em><br />
|
||||
<% end %>
|
||||
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password_confirmation, t('.confirm_new_password') %>
|
||||
<%= f.password_field :password_confirmation, autocomplete: 'off', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :password_confirmation, t('.confirm_new_password') %><br />
|
||||
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.change_my_password'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.change_my_password') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<h1><%= t('.forgot_your_password') %></h1>
|
||||
<h2><%= t('.forgot_your_password') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.send_me_reset_password_instructions'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.send_me_reset_password_instructions') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
@@ -1,37 +1,43 @@
|
||||
<h1><%= t('.title', resource: resource_name.to_s.humanize) %></h1>
|
||||
<h2><%= t('.title', resource: resource.model_name.human) %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password %>
|
||||
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
|
||||
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
|
||||
<div><%= t('.currently_waiting_confirmation_for_email', email: resource.unconfirmed_email) %></div>
|
||||
<% end %>
|
||||
|
||||
<small class="form-text text-muted"><%= t('.leave_blank_if_you_don_t_want_to_change_it') %></small>
|
||||
<div class="field">
|
||||
<%= f.label :password %> <i>(<%= t('.leave_blank_if_you_don_t_want_to_change_it') %>)</i><br />
|
||||
<%= f.password_field :password, autocomplete: "new-password" %>
|
||||
<% if @minimum_password_length %>
|
||||
<br />
|
||||
<em><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></em>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password_confirmation %>
|
||||
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :password_confirmation %><br />
|
||||
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :current_password %>
|
||||
<%= f.password_field :current_password, autocomplete: 'current-password', class: 'form-control' %>
|
||||
|
||||
<small class="form-text text-muted"><%= t('.we_need_your_current_password_to_confirm_your_changes') %></small>
|
||||
<div class="field">
|
||||
<%= f.label :current_password %> <i>(<%= t('.we_need_your_current_password_to_confirm_your_changes') %>)</i><br />
|
||||
<%= f.password_field :current_password, autocomplete: "current-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.update'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.update') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p><%= t('.unhappy') %>? <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %>.</p>
|
||||
<h3><%= t('.cancel_my_account') %></h3>
|
||||
|
||||
<%= link_to t('.back'), :back %>
|
||||
<p><%= t('.unhappy') %> <%= button_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %></p>
|
||||
|
||||
<%= link_to t('devise.shared.links.back'), :back %>
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
<h1><%= t('.sign_up') %></h1>
|
||||
<h2><%= t('.sign_up') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :first_name %><br />
|
||||
<%= f.text_field :first_name, autofocus: true %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="field">
|
||||
<%= f.label :last_name %><br />
|
||||
<%= f.text_field :last_name %>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autocomplete: "email" %>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<%= f.label :password %>
|
||||
<%= f.password_field :password, autocomplete: 'current-password', class: 'form-control' %>
|
||||
|
||||
<% if @minimum_password_length %>
|
||||
<small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small>
|
||||
<% end %>
|
||||
<em><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></em>
|
||||
<% end %><br />
|
||||
<%= f.password_field :password, autocomplete: "new-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password_confirmation %>
|
||||
<%= f.password_field :password_confirmation, autocomplete: 'current-password', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :password_confirmation %><br />
|
||||
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.sign_up'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.sign_up') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
<h1><%= t('.sign_in') %></h1>
|
||||
<h2><%= t('.sign_in') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password %>
|
||||
<%= f.password_field :password, autocomplete: 'current-password', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :password %><br />
|
||||
<%= f.password_field :password, autocomplete: "current-password" %>
|
||||
</div>
|
||||
|
||||
<% if devise_mapping.rememberable? %>
|
||||
<div class="form-group form-check">
|
||||
<%= f.check_box :remember_me, class: 'form-check-input' %>
|
||||
<%= f.label :remember_me, class: 'form-check-label' do %>
|
||||
<%= resource.class.human_attribute_name('remember_me') %>
|
||||
<% end %>
|
||||
<div class="field">
|
||||
<%= f.check_box :remember_me %>
|
||||
<%= f.label :remember_me %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.sign_in'), class: 'btn btn-primary' %>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.sign_in') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
15
app/views/devise/shared/_error_messages.html.erb
Normal file
15
app/views/devise/shared/_error_messages.html.erb
Normal file
@@ -0,0 +1,15 @@
|
||||
<% if resource.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<h2>
|
||||
<%= I18n.t("errors.messages.not_saved",
|
||||
count: resource.errors.count,
|
||||
resource: resource.class.model_name.human.downcase)
|
||||
%>
|
||||
</h2>
|
||||
<ul>
|
||||
<% resource.errors.full_messages.each do |message| %>
|
||||
<li><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -1,27 +1,25 @@
|
||||
<div class="form-group">
|
||||
<%- if controller_name != 'sessions' %>
|
||||
<%= link_to t(".sign_in"), new_session_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
|
||||
<%= link_to t(".sign_up"), new_registration_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
||||
<%= link_to t(".forgot_your_password"), new_password_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
|
||||
<%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
|
||||
<%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name) %><br />
|
||||
<% end -%>
|
||||
<% end %>
|
||||
|
||||
<%- if devise_mapping.omniauthable? %>
|
||||
<%- resource_class.omniauth_providers.each do |provider| %>
|
||||
<%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider) %><br />
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
</div>
|
||||
<%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider), method: :post %><br />
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<h1><%= t('.resend_unlock_instructions') %></h1>
|
||||
<h2><%= t('.resend_unlock_instructions') %></h2>
|
||||
|
||||
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
|
||||
<%= bootstrap_devise_error_messages! %>
|
||||
<%= render "devise/shared/error_messages", resource: resource %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
|
||||
<div class="field">
|
||||
<%= f.label :email %><br />
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.submit t('.resend_unlock_instructions'), class: 'btn btn-primary'%>
|
||||
<div class="actions">
|
||||
<%= f.submit t('.resend_unlock_instructions') %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render 'devise/shared/links' %>
|
||||
<%= render "devise/shared/links" %>
|
||||
|
||||
@@ -7,15 +7,7 @@
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
||||
|
||||
<script>
|
||||
window.AUTH_DOMAIN = "<%= ENV['AUTH_DOMAIN'] %>"
|
||||
window.AUTH_CLIENT_ID = "<%= ENV['AUTH_CLIENT_ID'] %>"
|
||||
window.AUTH_AUDIENCE = "<%= ENV['AUTH_AUDIENCE'] %>"
|
||||
</script>
|
||||
|
||||
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
|
||||
<%= javascript_pack_tag 'react', 'data-turbolinks-track': 'reload' %>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
2
config/initializers/i18n.rb
Normal file
2
config/initializers/i18n.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
# frozen_string_literal: true
|
||||
I18n.default_locale = "pt-BR"
|
||||
5
config/locales/pt-BR.yml
Normal file
5
config/locales/pt-BR.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
pt-BR:
|
||||
administrate:
|
||||
navigation:
|
||||
sign_out: Encerrar sessão
|
||||
sign_out_confirmation: Tem certeza que deseja encerrar a sessão?
|
||||
@@ -1,5 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
Rails.application.routes.draw do
|
||||
devise_for :users
|
||||
devise_for :admin_users
|
||||
|
||||
namespace :admin do
|
||||
|
||||
36
db/migrate/20210808153626_add_devise_to_users.rb
Normal file
36
db/migrate/20210808153626_add_devise_to_users.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddDeviseToUsers < ActiveRecord::Migration[6.1]
|
||||
def self.up
|
||||
change_table(:users, bulk: true) do |t|
|
||||
## Database authenticatable
|
||||
# t.string(:email, null: false, default: "")
|
||||
t.string(:encrypted_password, null: false, default: "")
|
||||
|
||||
## Recoverable
|
||||
t.string(:reset_password_token)
|
||||
t.datetime(:reset_password_sent_at)
|
||||
|
||||
## Rememberable
|
||||
t.datetime(:remember_created_at)
|
||||
end
|
||||
|
||||
change_column_default(:users, :email, "")
|
||||
|
||||
# add_index(:users, :email, unique: true)
|
||||
add_index(:users, :reset_password_token, unique: true)
|
||||
end
|
||||
|
||||
def self.down
|
||||
change_column_default(:users, :email, nil)
|
||||
|
||||
change_table(:users, bulk: true) do |t|
|
||||
t.remove(:encrypted_password)
|
||||
t.remove(:reset_password_token)
|
||||
t.remove(:reset_password_sent_at)
|
||||
t.remove(:remember_created_at)
|
||||
end
|
||||
|
||||
remove_index(:users, :reset_password_token)
|
||||
end
|
||||
end
|
||||
9
db/schema.rb
generated
9
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_08_05_000225) do
|
||||
ActiveRecord::Schema.define(version: 2021_08_08_153626) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -66,10 +66,15 @@ ActiveRecord::Schema.define(version: 2021_08_05_000225) do
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "first_name", null: false
|
||||
t.string "last_name", null: false
|
||||
t.string "email", null: false
|
||||
t.string "email", default: "", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.datetime "remember_created_at"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
end
|
||||
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"relay:watch": "relay-compiler --schema app/javascript/__generated__/schema.graphql --src app/javascript/src --extensions tsx --language typescript --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth0/auth0-react": "^1.6.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.14.5",
|
||||
"@rails/actioncable": "^6.0.0",
|
||||
|
||||
@@ -5,15 +5,20 @@
|
||||
# Table name: users
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# email :string not null
|
||||
# email :string default(""), not null
|
||||
# encrypted_password :string default(""), not null
|
||||
# first_name :string not null
|
||||
# last_name :string not null
|
||||
# remember_created_at :datetime
|
||||
# reset_password_sent_at :datetime
|
||||
# reset_password_token :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_users_on_email (email) UNIQUE
|
||||
# index_users_on_reset_password_token (reset_password_token) UNIQUE
|
||||
#
|
||||
require "rails_helper"
|
||||
|
||||
|
||||
56
yarn.lock
generated
56
yarn.lock
generated
@@ -2,26 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@auth0/auth0-react@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@auth0/auth0-react/-/auth0-react-1.6.0.tgz#9175d9d1ee3379aaaacfb6bed4640d56f239f59f"
|
||||
integrity sha512-xjuNJnINMrJFbSpOSr9mjdVwfI9b+JbMKxoZy8R9Tmk3DGpBJ9eo69Im2RAeAWRISdrrxEZxNv9RAh9AQUvxEA==
|
||||
dependencies:
|
||||
"@auth0/auth0-spa-js" "^1.16.1"
|
||||
|
||||
"@auth0/auth0-spa-js@^1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.16.1.tgz#fb35fe233bc1a39db5ce5694e75ed7a2779d5dbf"
|
||||
integrity sha512-LosLwxKmLTVvy7dFwugVUwToRRwMetSgA8B+fHY0rq+hLtQr3mwZuEiu4T7NkmOzHTzGUWxLN38DkU6nYEG0Nw==
|
||||
dependencies:
|
||||
abortcontroller-polyfill "^1.7.1"
|
||||
browser-tabs-lock "^1.2.13"
|
||||
core-js "^3.11.0"
|
||||
es-cookie "^1.3.2"
|
||||
fast-text-encoding "^1.0.3"
|
||||
promise-polyfill "^8.2.0"
|
||||
unfetch "^4.2.0"
|
||||
|
||||
"@babel/code-frame@7.12.11":
|
||||
version "7.12.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||
@@ -1577,11 +1557,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
|
||||
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
|
||||
|
||||
abortcontroller-polyfill@^1.7.1:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5"
|
||||
integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==
|
||||
|
||||
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||
@@ -2145,13 +2120,6 @@ brorand@^1.0.1, brorand@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
browser-tabs-lock@^1.2.13:
|
||||
version "1.2.14"
|
||||
resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.2.14.tgz#f4ba30810d20199a1858c102da1f91e339250728"
|
||||
integrity sha512-ssSpCRcvFe4vc098LDnrJOQDfZiG35KhQGB9hthTbwJk5mmUkePwhcMlW61NH3YuIE2Y9uGLqf9yxEBKbaDlaw==
|
||||
dependencies:
|
||||
lodash ">=4.17.21"
|
||||
|
||||
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
|
||||
@@ -2710,7 +2678,7 @@ core-js-pure@^3.16.0:
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.0.tgz#218e07add3f1844e53fab195c47871fc5ba18de8"
|
||||
integrity sha512-wzlhZNepF/QA9yvx3ePDgNGudU5KDB8lu/TRPKelYA/QtSnkS/cLl2W+TIdEX1FAFcBr0YpY7tPDlcmXJ7AyiQ==
|
||||
|
||||
core-js@^3.11.0, core-js@^3.12.1:
|
||||
core-js@^3.12.1:
|
||||
version "3.16.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.16.0.tgz#1d46fb33720bc1fa7f90d20431f36a5540858986"
|
||||
integrity sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==
|
||||
@@ -3407,11 +3375,6 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-
|
||||
string.prototype.trimstart "^1.0.4"
|
||||
unbox-primitive "^1.0.1"
|
||||
|
||||
es-cookie@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/es-cookie/-/es-cookie-1.3.2.tgz#80e831597f72a25721701bdcb21d990319acd831"
|
||||
integrity sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||
@@ -3883,11 +3846,6 @@ fast-levenshtein@^2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fast-text-encoding@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
|
||||
integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807"
|
||||
@@ -5277,7 +5235,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@>=4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.5:
|
||||
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.5:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -6992,11 +6950,6 @@ promise-inflight@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
|
||||
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
|
||||
|
||||
promise-polyfill@^8.2.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0"
|
||||
integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==
|
||||
|
||||
promise@^7.1.1:
|
||||
version "7.3.1"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
|
||||
@@ -8525,11 +8478,6 @@ unbox-primitive@^1.0.1:
|
||||
has-symbols "^1.0.2"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
unfetch@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
|
||||
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||
|
||||
Reference in New Issue
Block a user