Refactor to ES module with localStorage profile and data-bind
This commit is contained in:
11
main.go
11
main.go
@@ -21,7 +21,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
templates = template.Must(template.ParseGlob("static/*.html"))
|
templates = template.Must(template.New("").ParseGlob("static/*.html"))
|
||||||
|
template.Must(templates.ParseGlob("static/*.js"))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
db, err = sql.Open("postgres", os.Getenv("PGCONN"))
|
db, err = sql.Open("postgres", os.Getenv("PGCONN"))
|
||||||
@@ -63,13 +64,17 @@ func handleStatic(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
name := strings.TrimPrefix(path, "/")
|
name := strings.TrimPrefix(path, "/")
|
||||||
|
|
||||||
if strings.HasSuffix(name, ".html") {
|
if strings.HasSuffix(name, ".html") || strings.HasSuffix(name, ".js") {
|
||||||
t := templates.Lookup(name)
|
t := templates.Lookup(name)
|
||||||
if t == nil {
|
if t == nil {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html")
|
if strings.HasSuffix(name, ".html") {
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "application/javascript")
|
||||||
|
}
|
||||||
t.Execute(w, templateData())
|
t.Execute(w, templateData())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
85
static/app.js
Normal file
85
static/app.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
const CLIENT_ID = '{{.env.GOOGLE_CLIENT_ID}}';
|
||||||
|
|
||||||
|
function getProfile() {
|
||||||
|
const data = localStorage.getItem('profile');
|
||||||
|
return data ? JSON.parse(data) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProfile(profile) {
|
||||||
|
localStorage.setItem('profile', JSON.stringify(profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout() {
|
||||||
|
localStorage.removeItem('profile');
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind(data) {
|
||||||
|
document.querySelectorAll('[data-bind]').forEach(el => {
|
||||||
|
const key = el.dataset.bind;
|
||||||
|
const value = key.split('.').reduce((o, k) => o?.[k], data);
|
||||||
|
if (el.tagName === 'IMG') {
|
||||||
|
el.src = value;
|
||||||
|
} else {
|
||||||
|
el.textContent = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForGoogle() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (typeof google !== 'undefined') {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const check = setInterval(() => {
|
||||||
|
if (typeof google !== 'undefined') {
|
||||||
|
clearInterval(check);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function auth() {
|
||||||
|
let profile = getProfile();
|
||||||
|
if (profile) {
|
||||||
|
bind(profile);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
await waitForGoogle();
|
||||||
|
|
||||||
|
const signin = document.getElementById('signin');
|
||||||
|
signin.style.display = 'block';
|
||||||
|
document.body.style.opacity = 1;
|
||||||
|
|
||||||
|
profile = await new Promise((resolve) => {
|
||||||
|
google.accounts.id.initialize({
|
||||||
|
client_id: CLIENT_ID,
|
||||||
|
callback: async (response) => {
|
||||||
|
const res = await fetch('/auth/google/callback', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
body: 'credential=' + encodeURIComponent(response.credential)
|
||||||
|
});
|
||||||
|
const profile = await res.json();
|
||||||
|
setProfile(profile);
|
||||||
|
signin.style.display = 'none';
|
||||||
|
resolve(profile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
google.accounts.id.renderButton(signin, {
|
||||||
|
type: 'standard',
|
||||||
|
size: 'large',
|
||||||
|
theme: 'outline',
|
||||||
|
text: 'sign_in_with',
|
||||||
|
shape: 'rectangular',
|
||||||
|
logo_alignment: 'left'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
bind(profile);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
@@ -31,79 +31,29 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="opacity: 0">
|
<body style="opacity: 0">
|
||||||
<div id="signin">
|
<div id="signin"></div>
|
||||||
<div id="g_id_onload"
|
|
||||||
data-client_id="{{.env.GOOGLE_CLIENT_ID}}"
|
|
||||||
data-callback="handleCredentialResponse"
|
|
||||||
data-use_fedcm_for_button="true"
|
|
||||||
data-auto_prompt="false">
|
|
||||||
</div>
|
|
||||||
<div class="g_id_signin"
|
|
||||||
data-type="standard"
|
|
||||||
data-size="large"
|
|
||||||
data-theme="outline"
|
|
||||||
data-text="sign_in_with"
|
|
||||||
data-shape="rectangular"
|
|
||||||
data-logo_alignment="left">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<wa-card id="welcome">
|
<wa-card id="welcome">
|
||||||
<div class="profile" slot="header">
|
<div class="profile" slot="header">
|
||||||
<img id="profile-picture" src="" alt="Profile">
|
<img data-bind="picture" alt="Profile">
|
||||||
<div>
|
<div>
|
||||||
<div><strong id="profile-name"></strong></div>
|
<div><strong data-bind="name"></strong></div>
|
||||||
<div id="profile-email"></div>
|
<div data-bind="email"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Welcome to HCA Tickets!</p>
|
<p>Welcome to HCA Tickets!</p>
|
||||||
<wa-button variant="neutral" onclick="logout()">Switch User</wa-button>
|
<wa-button variant="neutral" id="logout-btn">Switch User</wa-button>
|
||||||
</wa-card>
|
</wa-card>
|
||||||
<script>
|
|
||||||
function getProfile() {
|
|
||||||
const data = localStorage.getItem('profile');
|
|
||||||
return data ? JSON.parse(data) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setProfile(profile) {
|
|
||||||
localStorage.setItem('profile', JSON.stringify(profile));
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout() {
|
|
||||||
localStorage.removeItem('profile');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCredentialResponse(response) {
|
|
||||||
const res = await fetch('/auth/google/callback', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
|
||||||
body: 'credential=' + encodeURIComponent(response.credential)
|
|
||||||
});
|
|
||||||
const profile = await res.json();
|
|
||||||
setProfile(profile);
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
const profile = getProfile();
|
|
||||||
if (profile) {
|
|
||||||
document.getElementById('profile-picture').src = profile.picture;
|
|
||||||
document.getElementById('profile-name').textContent = profile.name;
|
|
||||||
document.getElementById('profile-email').textContent = profile.email;
|
|
||||||
document.getElementById('signin').style.display = 'none';
|
|
||||||
document.getElementById('welcome').style.display = 'block';
|
|
||||||
} else {
|
|
||||||
document.getElementById('signin').style.display = 'block';
|
|
||||||
document.getElementById('welcome').style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render();
|
|
||||||
</script>
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
|
import { auth, logout } from '/app.js';
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||||
document.documentElement.className = e.matches ? 'wa-dark' : 'wa-light';
|
document.documentElement.className = e.matches ? 'wa-dark' : 'wa-light';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await auth();
|
||||||
|
document.getElementById('welcome').style.display = 'block';
|
||||||
|
document.getElementById('logout-btn').addEventListener('click', logout);
|
||||||
|
|
||||||
await customElements.whenDefined('wa-card');
|
await customElements.whenDefined('wa-card');
|
||||||
document.body.style.opacity = 1;
|
document.body.style.opacity = 1;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user