small works
This commit is contained in:
@@ -5,14 +5,21 @@ import { inject as service } from '@ember/service';
|
||||
export default class ApplicationAdapter extends JSONAPIAdapter {
|
||||
@service session;
|
||||
|
||||
namespace = 'api/v1';
|
||||
get namespace() {
|
||||
if (this.session.isAuthenticated) {
|
||||
return 'api/v1';
|
||||
} else {
|
||||
return 'api/v1/public';
|
||||
}
|
||||
}
|
||||
|
||||
@computed('session.{data.authenticated.access_token,isAuthenticated}')
|
||||
get headers() {
|
||||
let headers = {};
|
||||
if (this.session.isAuthenticated) {
|
||||
// OAuth 2
|
||||
headers['Authorization'] = `Bearer ${this.session.data.authenticated.access_token}`;
|
||||
headers['Authorization'] =
|
||||
`Bearer ${this.session.data.authenticated.access_token}`;
|
||||
}
|
||||
|
||||
return headers;
|
||||
|
||||
@@ -82,7 +82,7 @@ export default BaseAuthenticator.extend({
|
||||
*/
|
||||
restore(data) {
|
||||
if (this._refreshTokenTimeout) {
|
||||
run.cancel(this._refreshTokenTimeout);
|
||||
cancel(this._refreshTokenTimeout);
|
||||
delete this._refreshTokenTimeout;
|
||||
}
|
||||
return this._refreshAccessToken(data);
|
||||
@@ -106,13 +106,14 @@ export default BaseAuthenticator.extend({
|
||||
*/
|
||||
authenticate(identification, password) {
|
||||
return new RSVP.Promise((resolve, reject) => {
|
||||
const {
|
||||
identificationAttributeName,
|
||||
passwordAttributeName
|
||||
} = this.getProperties('identificationAttributeName', 'passwordAttributeName');
|
||||
const { identificationAttributeName, passwordAttributeName } =
|
||||
this.getProperties(
|
||||
'identificationAttributeName',
|
||||
'passwordAttributeName',
|
||||
);
|
||||
const data = {
|
||||
[identificationAttributeName]: identification,
|
||||
[passwordAttributeName]: password
|
||||
[passwordAttributeName]: password,
|
||||
};
|
||||
const serverTokenEndpoint = this.get('serverTokenEndpoint');
|
||||
|
||||
@@ -141,7 +142,7 @@ export default BaseAuthenticator.extend({
|
||||
@public
|
||||
*/
|
||||
invalidate() {
|
||||
run.cancel(this._refreshTokenTimeout);
|
||||
cancel(this._refreshTokenTimeout);
|
||||
delete this._refreshTokenTimeout;
|
||||
return RSVP.Promise.resolve();
|
||||
},
|
||||
@@ -161,11 +162,12 @@ export default BaseAuthenticator.extend({
|
||||
const options = {
|
||||
body: JSON.stringify(data),
|
||||
headers,
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
return new RSVP.Promise((resolve, reject) => {
|
||||
fetch(url, options).then((response) => {
|
||||
fetch(url, options)
|
||||
.then((response) => {
|
||||
response.text().then((text) => {
|
||||
let json = text ? JSON.parse(text) : {};
|
||||
if (!response.ok) {
|
||||
@@ -176,7 +178,8 @@ export default BaseAuthenticator.extend({
|
||||
resolve(json);
|
||||
}
|
||||
});
|
||||
}).catch(reject);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -219,7 +222,7 @@ export default BaseAuthenticator.extend({
|
||||
|
||||
const offset = 1000; // Refresh 1 sec before JWT expires
|
||||
const now = Date.now();
|
||||
const waitMs = (jwtPayloadExpiresAt * 1000) - now - offset; //expiresAt is in sec
|
||||
const waitMs = jwtPayloadExpiresAt * 1000 - now - offset; //expiresAt is in sec
|
||||
|
||||
if (this._refreshTokenTimeout) {
|
||||
cancel(this._refreshTokenTimeout);
|
||||
@@ -228,12 +231,18 @@ export default BaseAuthenticator.extend({
|
||||
|
||||
// Reschedule if the JWT is still valid
|
||||
if (waitMs > 0) {
|
||||
this._refreshTokenTimeout = later(this, this._refreshAccessToken, data, waitMs);
|
||||
this._refreshTokenTimeout = later(
|
||||
this,
|
||||
this._refreshAccessToken,
|
||||
data,
|
||||
waitMs,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_refreshAccessToken(data) {
|
||||
var timeElapsedSinceLastRefresh = Date.now() - window.localStorage.getItem('jwtLastRefreshAt')
|
||||
var timeElapsedSinceLastRefresh =
|
||||
Date.now() - window.localStorage.getItem('jwtLastRefreshAt');
|
||||
if (timeElapsedSinceLastRefresh <= this.get('refreshTokenAfter')) {
|
||||
// Request attempted too soon! Reschedule
|
||||
return this._validateTokenAndScheduleRefresh(data);
|
||||
@@ -242,7 +251,9 @@ export default BaseAuthenticator.extend({
|
||||
const serverRefreshTokenEndpoint = this.get('serverRefreshTokenEndpoint');
|
||||
|
||||
return new RSVP.Promise((resolve, reject) => {
|
||||
this.makeRequest(serverRefreshTokenEndpoint, data, { Authorization:data["access_token"] })
|
||||
this.makeRequest(serverRefreshTokenEndpoint, data, {
|
||||
Authorization: data['access_token'],
|
||||
})
|
||||
.then((response) => {
|
||||
return this._validateTokenAndScheduleRefresh(response);
|
||||
})
|
||||
@@ -257,7 +268,7 @@ export default BaseAuthenticator.extend({
|
||||
reason = JSON.stringify(reason.responseJSON);
|
||||
}
|
||||
warn(`JWT token could not be refreshed: ${reason}.`, false, {
|
||||
id: 'ember-simple-auth-jwt.failedJWTTokenRefresh'
|
||||
id: 'ember-simple-auth-jwt.failedJWTTokenRefresh',
|
||||
});
|
||||
|
||||
reject();
|
||||
@@ -267,10 +278,12 @@ export default BaseAuthenticator.extend({
|
||||
|
||||
_validateTokenAndScheduleRefresh(response) {
|
||||
if (!this._validate(response)) {
|
||||
return RSVP.Promise.reject('token is missing or invalid in server response');
|
||||
return RSVP.Promise.reject(
|
||||
'token is missing or invalid in server response',
|
||||
);
|
||||
}
|
||||
|
||||
this._scheduleTokenRefresh(response);
|
||||
return RSVP.Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
2
app/components/topbar/item.hbs
Normal file
2
app/components/topbar/item.hbs
Normal file
@@ -0,0 +1,2 @@
|
||||
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
|
||||
<a href={{@href}} class="rounded-md px-3 py-2 text-sm font-medium text-gray-300 hover:bg-gray-700 hover:text-white" aria-current="page">{{@title}}</a>
|
||||
218
app/components/topbar/navbar.hbs
Normal file
218
app/components/topbar/navbar.hbs
Normal file
@@ -0,0 +1,218 @@
|
||||
<nav class="bg-gray-800">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="border-b border-gray-700">
|
||||
<div class="flex h-16 items-center justify-between px-4 sm:px-0">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0">
|
||||
<img
|
||||
class="size-8"
|
||||
src="https://tailwindui.com/plus-assets/img/logos/mark.svg?color=indigo&shade=500"
|
||||
alt="Your Company"
|
||||
/>
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
<div class="ml-10 flex items-baseline space-x-4">
|
||||
<Topbar::Item @title="Dashboard" @href="#" />
|
||||
<Topbar::Item @title="Team" @href="#" />
|
||||
<Topbar::Item @title="Projects" @href="#" />
|
||||
<Topbar::Item @title="Calendar" @href="#" />
|
||||
<Topbar::Item @title="Reports" @href="#" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
<div class="ml-4 flex items-center md:ml-6">
|
||||
<button
|
||||
type="button"
|
||||
class="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
|
||||
>
|
||||
<span class="absolute -inset-1.5"></span>
|
||||
<span class="sr-only">View notifications</span>
|
||||
<svg
|
||||
class="size-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Profile dropdown -->
|
||||
<div class="relative ml-3">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="relative flex max-w-xs items-center rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
|
||||
id="user-menu-button"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<span class="absolute -inset-1.5"></span>
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<img
|
||||
class="size-8 rounded-full"
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Dropdown menu, show/hide based on menu state.
|
||||
|
||||
Entering: "transition ease-out duration-100"
|
||||
From: "transform opacity-0 scale-95"
|
||||
To: "transform opacity-100 scale-100"
|
||||
Leaving: "transition ease-in duration-75"
|
||||
From: "transform opacity-100 scale-100"
|
||||
To: "transform opacity-0 scale-95"
|
||||
-->
|
||||
<div
|
||||
class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="user-menu-button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Topbar::ProfileItem @title="Your Profile" />
|
||||
<Topbar::ProfileItem @title="Settings" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="-mr-2 flex md:hidden">
|
||||
<!-- Mobile menu button -->
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
|
||||
aria-controls="mobile-menu"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="absolute -inset-0.5"></span>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<!-- Menu open: "hidden", Menu closed: "block" -->
|
||||
<svg
|
||||
class="block size-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||
/>
|
||||
</svg>
|
||||
<!-- Menu open: "block", Menu closed: "hidden" -->
|
||||
<svg
|
||||
class="hidden size-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M6 18 18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile menu, show/hide based on menu state. -->
|
||||
<div class="border-b border-gray-700 md:hidden" id="mobile-menu">
|
||||
<div class="space-y-1 px-2 py-3 sm:px-3">
|
||||
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md bg-gray-900 px-3 py-2 text-base font-medium text-white"
|
||||
aria-current="page"
|
||||
>Dashboard</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
|
||||
>Team</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
|
||||
>Projects</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
|
||||
>Calendar</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white"
|
||||
>Reports</a>
|
||||
</div>
|
||||
<div class="border-t border-gray-700 pb-3 pt-4">
|
||||
<div class="flex items-center px-5">
|
||||
<div class="shrink-0">
|
||||
<img
|
||||
class="size-10 rounded-full"
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<div class="text-base/5 font-medium text-white">Tom Cook</div>
|
||||
<div class="text-sm font-medium text-gray-400">tom@example.com</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="relative ml-auto shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
|
||||
>
|
||||
<span class="absolute -inset-1.5"></span>
|
||||
<span class="sr-only">View notifications</span>
|
||||
<svg
|
||||
class="size-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
data-slot="icon"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3 space-y-1 px-2">
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white"
|
||||
>Your Profile</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white"
|
||||
>Settings</a>
|
||||
<a
|
||||
href="#"
|
||||
class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white"
|
||||
>Sign out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
8
app/components/topbar/profile-item.hbs
Normal file
8
app/components/topbar/profile-item.hbs
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- Active: "bg-gray-100 outline-none", Not Active: "" -->
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
id="user-menu-item-0"
|
||||
>{{@title}}</a>
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Holygamesapp</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
|
||||
|
||||
{{content-for "head"}}
|
||||
|
||||
|
||||
@@ -7,6 +7,23 @@
|
||||
<LinkTo @route="login">Login</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="main">
|
||||
|
||||
<div class="min-h-full">
|
||||
<div class="bg-gray-800 pb-32">
|
||||
<Topbar::Navbar />
|
||||
|
||||
<header class="py-10">
|
||||
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-white">Dashboard</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<main class="-mt-32">
|
||||
<div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
|
||||
<div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user