Refactor to remove sign up and JWT env variables (#65)
closes #63 closes #66
This commit is contained in:
parent
e787e751d9
commit
3324bd960f
@ -1,2 +1 @@
|
|||||||
API_BASE_URL='https://btrix.webrecorder.net/api'
|
API_BASE_URL=https://btrix.webrecorder.net/api
|
||||||
REGISTRATION_ENABLED=false
|
|
||||||
|
@ -37,7 +37,6 @@ follow instructions for deploying to a local Docker instance. Update `API_BASE_U
|
|||||||
| `format` | formats js, html and css files |
|
| `format` | formats js, html and css files |
|
||||||
| `localize:extract` | generate XLIFF file to be translated |
|
| `localize:extract` | generate XLIFF file to be translated |
|
||||||
| `localize:build` | output a localized version of strings/templates |
|
| `localize:build` | output a localized version of strings/templates |
|
||||||
| `env:sync` | sync environment variables with API settings |
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
@ -19,15 +19,14 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "web-test-runner \"src/**/*.test.{ts,js}\" --node-resolve --playwright --browsers chromium",
|
"test": "web-test-runner \"src/**/*.test.{ts,js}\" --node-resolve --playwright --browsers chromium",
|
||||||
"prebuild": "del-cli ./dist && npm run env:sync",
|
"prebuild": "del-cli ./dist",
|
||||||
"build": "webpack --config webpack.prod.js",
|
"build": "webpack --config webpack.prod.js",
|
||||||
"build-dev": "webpack --mode development",
|
"build-dev": "webpack --mode development",
|
||||||
"start": "webpack serve --mode=development",
|
"start": "webpack serve --mode=development",
|
||||||
"lint": "eslint --fix \"src/**/*.{ts,js}\"",
|
"lint": "eslint --fix \"src/**/*.{ts,js}\"",
|
||||||
"format": "prettier --write \"**/*.{ts,js,html,css}\"",
|
"format": "prettier --write \"**/*.{ts,js,html,css}\"",
|
||||||
"localize:extract": "lit-localize extract",
|
"localize:extract": "lit-localize extract",
|
||||||
"localize:build": "lit-localize build",
|
"localize:build": "lit-localize build"
|
||||||
"env:sync": "node ./scripts/get-settings.mjs"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||||
|
@ -1,2 +1 @@
|
|||||||
API_BASE_URL='http://btrix.cloud'
|
API_BASE_URL=https://btrix-dev.webrecorder.net/api
|
||||||
REGISTRATION_ENABLED=true
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import fetch from "node-fetch";
|
|
||||||
import updateDotenv from "update-dotenv";
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
try {
|
|
||||||
const resp = await fetch(`${process.env.API_BASE_URL}/settings`);
|
|
||||||
const body = await resp.json();
|
|
||||||
|
|
||||||
const newEnv = await updateDotenv({
|
|
||||||
REGISTRATION_ENABLED: Boolean(body.enabled).toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
".env file updated:",
|
|
||||||
`REGISTRATION_ENABLED=${newEnv["REGISTRATION_ENABLED"]}`
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
console.log(
|
|
||||||
"could not update .env file, env is now:",
|
|
||||||
`REGISTRATION_ENABLED=${process.env.REGISTRATION_ENABLED}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
@ -19,8 +19,6 @@ import "./shoelace";
|
|||||||
import "./components";
|
import "./components";
|
||||||
import "./pages";
|
import "./pages";
|
||||||
|
|
||||||
const REGISTRATION_ENABLED = process.env.REGISTRATION_ENABLED === "true";
|
|
||||||
|
|
||||||
type DialogContent = {
|
type DialogContent = {
|
||||||
label?: TemplateResult | string;
|
label?: TemplateResult | string;
|
||||||
body?: TemplateResult | string;
|
body?: TemplateResult | string;
|
||||||
@ -56,6 +54,12 @@ export class App extends LiteElement {
|
|||||||
@query("#globalDialog")
|
@query("#globalDialog")
|
||||||
private globalDialog!: SlDialog;
|
private globalDialog!: SlDialog;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private isAppSettingsLoaded: boolean = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private isRegistrationEnabled?: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -93,10 +97,18 @@ export class App extends LiteElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
async firstUpdated() {
|
||||||
if (this.authService.authState) {
|
if (this.authService.authState) {
|
||||||
this.updateUserInfo();
|
this.updateUserInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settings = await this.getAppSettings();
|
||||||
|
|
||||||
|
if (settings) {
|
||||||
|
this.isRegistrationEnabled = settings.registrationEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isAppSettingsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateUserInfo() {
|
private async updateUserInfo() {
|
||||||
@ -118,6 +130,20 @@ export class App extends LiteElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAppSettings(): Promise<{ registrationEnabled: boolean } | void> {
|
||||||
|
const resp = await fetch("/api/settings", {
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const body = await resp.json();
|
||||||
|
|
||||||
|
return body;
|
||||||
|
} else {
|
||||||
|
console.debug(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
navigate(newViewPath: string) {
|
navigate(newViewPath: string) {
|
||||||
if (newViewPath.startsWith("http")) {
|
if (newViewPath.startsWith("http")) {
|
||||||
const url = new URL(newViewPath);
|
const url = new URL(newViewPath);
|
||||||
@ -261,7 +287,12 @@ export class App extends LiteElement {
|
|||||||
|
|
||||||
switch (this.viewState.route) {
|
switch (this.viewState.route) {
|
||||||
case "signUp": {
|
case "signUp": {
|
||||||
if (REGISTRATION_ENABLED) {
|
if (!this.isAppSettingsLoaded) {
|
||||||
|
return html`<div
|
||||||
|
class="w-full md:bg-gray-100 flex items-center justify-center"
|
||||||
|
></div>`;
|
||||||
|
}
|
||||||
|
if (this.isRegistrationEnabled) {
|
||||||
return html`<btrix-sign-up
|
return html`<btrix-sign-up
|
||||||
class="w-full md:bg-gray-100 flex items-center justify-center"
|
class="w-full md:bg-gray-100 flex items-center justify-center"
|
||||||
@navigate="${this.onNavigateTo}"
|
@navigate="${this.onNavigateTo}"
|
||||||
|
@ -5,6 +5,7 @@ export type Auth = {
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: string;
|
Authorization: string;
|
||||||
};
|
};
|
||||||
|
/** Timestamp (milliseconds) when token expires */
|
||||||
tokenExpiresAt: number;
|
tokenExpiresAt: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,6 +15,12 @@ type Session = {
|
|||||||
|
|
||||||
export type AuthState = (Auth & Session) | null;
|
export type AuthState = (Auth & Session) | null;
|
||||||
|
|
||||||
|
type JWT = {
|
||||||
|
user_id: string;
|
||||||
|
aud: string[];
|
||||||
|
exp: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type LoggedInEventDetail = Auth & {
|
export type LoggedInEventDetail = Auth & {
|
||||||
api?: boolean;
|
api?: boolean;
|
||||||
firstLogin?: boolean;
|
firstLogin?: boolean;
|
||||||
@ -26,9 +33,6 @@ export interface LoggedInEvent<T = LoggedInEventDetail> extends CustomEvent {
|
|||||||
|
|
||||||
// Check for token freshness every 5 minutes
|
// Check for token freshness every 5 minutes
|
||||||
const FRESHNESS_TIMER_INTERVAL = 60 * 1000 * 5;
|
const FRESHNESS_TIMER_INTERVAL = 60 * 1000 * 5;
|
||||||
// TODO get expires at from server
|
|
||||||
// Hardcode 1hr expiry for now
|
|
||||||
const ACCESS_TOKEN_LIFETIME = 1000 * 60 * 60;
|
|
||||||
// Hardcode 24h expiry for now
|
// Hardcode 24h expiry for now
|
||||||
const SESSION_LIFETIME = 1000 * 60 * 60 * 24;
|
const SESSION_LIFETIME = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
@ -74,17 +78,27 @@ export default class AuthService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const authHeaders = AuthService.parseAuthHeaders(await resp.json());
|
const data = await resp.json();
|
||||||
|
const token = AuthService.decodeToken(data.access_token);
|
||||||
|
const authHeaders = AuthService.parseAuthHeaders(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
username: email,
|
username: email,
|
||||||
headers: authHeaders,
|
headers: authHeaders,
|
||||||
// TODO get expires at from server
|
tokenExpiresAt: token.exp * 1000,
|
||||||
// Hardcode 1hr expiry for now
|
|
||||||
tokenExpiresAt: Date.now() + ACCESS_TOKEN_LIFETIME,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode JSON web token returned as access token
|
||||||
|
*/
|
||||||
|
private static decodeToken(token: string): JWT {
|
||||||
|
return JSON.parse(window.atob(token.split(".")[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build authorization headers from login response
|
||||||
|
*/
|
||||||
private static parseAuthHeaders(data: {
|
private static parseAuthHeaders(data: {
|
||||||
token_type: string;
|
token_type: string;
|
||||||
access_token: string;
|
access_token: string;
|
||||||
@ -169,6 +183,7 @@ export default class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.info("Session expired, logging out");
|
||||||
this.logout();
|
this.logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,13 +208,13 @@ export default class AuthService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const authHeaders = AuthService.parseAuthHeaders(await resp.json());
|
const data = await resp.json();
|
||||||
|
const token = AuthService.decodeToken(data.access_token);
|
||||||
|
const authHeaders = AuthService.parseAuthHeaders(data);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
headers: authHeaders,
|
headers: authHeaders,
|
||||||
// TODO get expires at from server
|
tokenExpiresAt: token.exp * 1000,
|
||||||
// Hardcode 1hr expiry for now
|
|
||||||
tokenExpiresAt: Date.now() + ACCESS_TOKEN_LIFETIME,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,9 @@ require("dotenv").config({
|
|||||||
path: dotEnvPath,
|
path: dotEnvPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO actual prod URL
|
const backendUrl = new URL(
|
||||||
const backendUrl = new URL(process.env.API_BASE_URL || "http://btrix.cloud/");
|
process.env.API_BASE_URL || "https://btrix.webrecorder.net/"
|
||||||
|
);
|
||||||
const shoelaceAssetsSrcPath = path.resolve(
|
const shoelaceAssetsSrcPath = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||||
|
Loading…
Reference in New Issue
Block a user