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'
|
||||
REGISTRATION_ENABLED=false
|
||||
API_BASE_URL=https://btrix.webrecorder.net/api
|
||||
|
@ -37,7 +37,6 @@ follow instructions for deploying to a local Docker instance. Update `API_BASE_U
|
||||
| `format` | formats js, html and css files |
|
||||
| `localize:extract` | generate XLIFF file to be translated |
|
||||
| `localize:build` | output a localized version of strings/templates |
|
||||
| `env:sync` | sync environment variables with API settings |
|
||||
|
||||
## Testing
|
||||
|
||||
|
@ -19,15 +19,14 @@
|
||||
},
|
||||
"scripts": {
|
||||
"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-dev": "webpack --mode development",
|
||||
"start": "webpack serve --mode=development",
|
||||
"lint": "eslint --fix \"src/**/*.{ts,js}\"",
|
||||
"format": "prettier --write \"**/*.{ts,js,html,css}\"",
|
||||
"localize:extract": "lit-localize extract",
|
||||
"localize:build": "lit-localize build",
|
||||
"env:sync": "node ./scripts/get-settings.mjs"
|
||||
"localize:build": "lit-localize build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||
|
@ -1,2 +1 @@
|
||||
API_BASE_URL='http://btrix.cloud'
|
||||
REGISTRATION_ENABLED=true
|
||||
API_BASE_URL=https://btrix-dev.webrecorder.net/api
|
||||
|
@ -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 "./pages";
|
||||
|
||||
const REGISTRATION_ENABLED = process.env.REGISTRATION_ENABLED === "true";
|
||||
|
||||
type DialogContent = {
|
||||
label?: TemplateResult | string;
|
||||
body?: TemplateResult | string;
|
||||
@ -56,6 +54,12 @@ export class App extends LiteElement {
|
||||
@query("#globalDialog")
|
||||
private globalDialog!: SlDialog;
|
||||
|
||||
@state()
|
||||
private isAppSettingsLoaded: boolean = false;
|
||||
|
||||
@state()
|
||||
private isRegistrationEnabled?: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -93,10 +97,18 @@ export class App extends LiteElement {
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
async firstUpdated() {
|
||||
if (this.authService.authState) {
|
||||
this.updateUserInfo();
|
||||
}
|
||||
|
||||
const settings = await this.getAppSettings();
|
||||
|
||||
if (settings) {
|
||||
this.isRegistrationEnabled = settings.registrationEnabled;
|
||||
}
|
||||
|
||||
this.isAppSettingsLoaded = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (newViewPath.startsWith("http")) {
|
||||
const url = new URL(newViewPath);
|
||||
@ -261,7 +287,12 @@ export class App extends LiteElement {
|
||||
|
||||
switch (this.viewState.route) {
|
||||
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
|
||||
class="w-full md:bg-gray-100 flex items-center justify-center"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
|
@ -5,6 +5,7 @@ export type Auth = {
|
||||
headers: {
|
||||
Authorization: string;
|
||||
};
|
||||
/** Timestamp (milliseconds) when token expires */
|
||||
tokenExpiresAt: number;
|
||||
};
|
||||
|
||||
@ -14,6 +15,12 @@ type Session = {
|
||||
|
||||
export type AuthState = (Auth & Session) | null;
|
||||
|
||||
type JWT = {
|
||||
user_id: string;
|
||||
aud: string[];
|
||||
exp: number;
|
||||
};
|
||||
|
||||
export type LoggedInEventDetail = Auth & {
|
||||
api?: boolean;
|
||||
firstLogin?: boolean;
|
||||
@ -26,9 +33,6 @@ export interface LoggedInEvent<T = LoggedInEventDetail> extends CustomEvent {
|
||||
|
||||
// Check for token freshness every 5 minutes
|
||||
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
|
||||
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 {
|
||||
username: email,
|
||||
headers: authHeaders,
|
||||
// TODO get expires at from server
|
||||
// Hardcode 1hr expiry for now
|
||||
tokenExpiresAt: Date.now() + ACCESS_TOKEN_LIFETIME,
|
||||
tokenExpiresAt: token.exp * 1000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: {
|
||||
token_type: string;
|
||||
access_token: string;
|
||||
@ -169,6 +183,7 @@ export default class AuthService {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.info("Session expired, logging out");
|
||||
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 {
|
||||
headers: authHeaders,
|
||||
// TODO get expires at from server
|
||||
// Hardcode 1hr expiry for now
|
||||
tokenExpiresAt: Date.now() + ACCESS_TOKEN_LIFETIME,
|
||||
tokenExpiresAt: token.exp * 1000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,9 @@ require("dotenv").config({
|
||||
path: dotEnvPath,
|
||||
});
|
||||
|
||||
// TODO actual prod URL
|
||||
const backendUrl = new URL(process.env.API_BASE_URL || "http://btrix.cloud/");
|
||||
const backendUrl = new URL(
|
||||
process.env.API_BASE_URL || "https://btrix.webrecorder.net/"
|
||||
);
|
||||
const shoelaceAssetsSrcPath = path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||
|
Loading…
Reference in New Issue
Block a user