mirror of
https://codeberg.org/timelimit/timelimit-server-ui.git
synced 2025-10-03 17:19:15 +02:00
Add support for the new premium unlock API
This commit is contained in:
parent
c70c0d131a
commit
5d310c21d8
7 changed files with 180 additions and 5 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Server Admin UI
|
* TimeLimit Server Admin UI
|
||||||
* Copyright (C) 2020 Jonas Lochmann
|
* Copyright (C) 2020 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as
|
* it under the terms of the GNU General Public License as
|
||||||
|
@ -22,6 +22,10 @@ export interface Api {
|
||||||
mail: string
|
mail: string
|
||||||
duration: 'month' | 'year'
|
duration: 'month' | 'year'
|
||||||
}): Promise<void>
|
}): Promise<void>
|
||||||
|
unlockPremium2 (params: {
|
||||||
|
purchaseId: string
|
||||||
|
purchaseToken: string
|
||||||
|
}): Promise<object>
|
||||||
getStatusMessage (): Promise<string>
|
getStatusMessage (): Promise<string>
|
||||||
setStatusMessage (newMessage: string): Promise<void>
|
setStatusMessage (newMessage: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,16 @@ class DummyApiImpl implements Api {
|
||||||
await delay(100 + Math.random() * 400)
|
await delay(100 + Math.random() * 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async unlockPremium2 () {
|
||||||
|
await this.checkAuth()
|
||||||
|
await delay(100 + Math.random() * 400)
|
||||||
|
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
detail: 'dummy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getStatusMessage () {
|
async getStatusMessage () {
|
||||||
await this.checkAuth()
|
await this.checkAuth()
|
||||||
await delay(100 + Math.random() * 400)
|
await delay(100 + Math.random() * 400)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Server Admin UI
|
* TimeLimit Server Admin UI
|
||||||
* Copyright (C) 2020 Jonas Lochmann
|
* Copyright (C) 2020 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as
|
* it under the terms of the GNU General Public License as
|
||||||
|
@ -110,6 +110,33 @@ class HttpApiImpl implements Api {
|
||||||
await response.json()
|
await response.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async unlockPremium2 ({ purchaseId, purchaseToken }: {
|
||||||
|
purchaseId: string
|
||||||
|
purchaseToken: string
|
||||||
|
}): Promise<object> {
|
||||||
|
const response = await fetch(this.serverUrl + '/unlock-premium-v2', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ purchaseId, purchaseToken }),
|
||||||
|
mode: 'cors',
|
||||||
|
cache: 'no-cache',
|
||||||
|
referrerPolicy: 'no-referrer',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Basic ' + btoa(':' + this.token),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw new IllegalAccessDataError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('unexpected response - ' + response.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json()
|
||||||
|
}
|
||||||
|
|
||||||
async getStatusMessage (): Promise<string> {
|
async getStatusMessage (): Promise<string> {
|
||||||
const response = await fetch(this.serverUrl + '/status-message', {
|
const response = await fetch(this.serverUrl + '/status-message', {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Server Admin UI
|
* TimeLimit Server Admin UI
|
||||||
* Copyright (C) 2020 Jonas Lochmann
|
* Copyright (C) 2020 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as
|
* it under the terms of the GNU General Public License as
|
||||||
|
@ -124,6 +124,32 @@ export const initAppController = ({ target, apiCreator }: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view.unlockPremium2.listener = {
|
||||||
|
onUnlockPremiumRequested: async ({ token, id }) => {
|
||||||
|
view.unlockPremium2.isEnabled = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await api.unlockPremium2({
|
||||||
|
purchaseId: id,
|
||||||
|
purchaseToken: token
|
||||||
|
})
|
||||||
|
|
||||||
|
view.unlockPremiumLog2.addMessage(
|
||||||
|
id + ': ' + JSON.stringify(response, null, 2),
|
||||||
|
1000 * 60 * 5
|
||||||
|
)
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex instanceof IllegalAccessDataError) {
|
||||||
|
showAccessDataWrong()
|
||||||
|
} else {
|
||||||
|
view.unlockPremiumLog2.addMessage('could not unlock', view.unlockPremiumLog.short)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
view.unlockPremium2.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this catches exceptions by itself
|
// this catches exceptions by itself
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
;(async () => {
|
;(async () => {
|
||||||
|
|
|
@ -20,11 +20,14 @@ import { LogView } from './logview'
|
||||||
import { StatusView } from './status'
|
import { StatusView } from './status'
|
||||||
import { StatusMessageForm } from './statusmessage'
|
import { StatusMessageForm } from './statusmessage'
|
||||||
import { UnlockPremiumForm } from './unlockpremiumform'
|
import { UnlockPremiumForm } from './unlockpremiumform'
|
||||||
|
import { UnlockPremiumForm2 } from './unlockpremiumform2'
|
||||||
|
|
||||||
export class DetailScreen {
|
export class DetailScreen {
|
||||||
status = new StatusView()
|
status = new StatusView()
|
||||||
unlockPremium = new UnlockPremiumForm()
|
unlockPremium = new UnlockPremiumForm()
|
||||||
unlockPremiumLog = new LogView()
|
unlockPremiumLog = new LogView()
|
||||||
|
unlockPremium2 = new UnlockPremiumForm2()
|
||||||
|
unlockPremiumLog2 = new LogView()
|
||||||
statusMessage = new StatusMessageForm()
|
statusMessage = new StatusMessageForm()
|
||||||
statusMessageLog = new LogView()
|
statusMessageLog = new LogView()
|
||||||
root = document.createElement('div')
|
root = document.createElement('div')
|
||||||
|
@ -36,6 +39,10 @@ export class DetailScreen {
|
||||||
this.unlockPremium.root,
|
this.unlockPremium.root,
|
||||||
this.unlockPremiumLog.root
|
this.unlockPremiumLog.root
|
||||||
),
|
),
|
||||||
|
wrapInBorder(
|
||||||
|
this.unlockPremium2.root,
|
||||||
|
this.unlockPremiumLog2.root
|
||||||
|
),
|
||||||
wrapInBorder(
|
wrapInBorder(
|
||||||
this.statusMessage.root,
|
this.statusMessage.root,
|
||||||
this.statusMessageLog.root
|
this.statusMessageLog.root
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Server Admin UI
|
* TimeLimit Server Admin UI
|
||||||
* Copyright (C) 2020 Jonas Lochmann
|
* Copyright (C) 2020 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as
|
* it under the terms of the GNU General Public License as
|
||||||
|
@ -33,7 +33,7 @@ export class LogView {
|
||||||
|
|
||||||
view.innerText = message
|
view.innerText = message
|
||||||
|
|
||||||
this.root.appendChild(view)
|
this.root.prepend(view)
|
||||||
|
|
||||||
if (duration) {
|
if (duration) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
101
src/view/unlockpremiumform2.ts
Normal file
101
src/view/unlockpremiumform2.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Server Admin UI
|
||||||
|
* Copyright (C) 2020 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation, version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createHeader } from './header'
|
||||||
|
import { RadioButton } from './radio'
|
||||||
|
|
||||||
|
function getPurchaseIdPlaceholder(): string {
|
||||||
|
const now = new Date()
|
||||||
|
|
||||||
|
const day = now.getDate().toString().padStart(2, '0')
|
||||||
|
const month = (now.getMonth() + 1).toString().padStart(2, '0')
|
||||||
|
const year = now.getFullYear().toString()
|
||||||
|
|
||||||
|
return [year, month, day, 'XYZ'].join('-')
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnlockPremiumForm2 {
|
||||||
|
listener?: UnlockPremiumFormListener = undefined
|
||||||
|
readonly root: HTMLElement
|
||||||
|
private readonly form: HTMLFormElement
|
||||||
|
private readonly purchaseIdField: HTMLInputElement
|
||||||
|
private readonly purchaseTokenField: HTMLTextAreaElement
|
||||||
|
private readonly submitButton: HTMLInputElement
|
||||||
|
private enabled: boolean = true
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this.form = document.createElement('form')
|
||||||
|
this.purchaseIdField = document.createElement('input')
|
||||||
|
this.purchaseTokenField = document.createElement('textarea')
|
||||||
|
this.submitButton = document.createElement('input')
|
||||||
|
|
||||||
|
this.purchaseIdField.placeholder = getPurchaseIdPlaceholder()
|
||||||
|
this.purchaseIdField.value = getPurchaseIdPlaceholder()
|
||||||
|
|
||||||
|
this.purchaseTokenField.rows = 10
|
||||||
|
|
||||||
|
this.submitButton.type = 'submit'
|
||||||
|
this.submitButton.value = 'Unlock'
|
||||||
|
|
||||||
|
this.reset()
|
||||||
|
|
||||||
|
this.form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const id = this.purchaseIdField.value
|
||||||
|
const token = this.purchaseTokenField.value
|
||||||
|
|
||||||
|
if (id.endsWith('XYZ')) return alert('placeholder token used')
|
||||||
|
|
||||||
|
this.listener?.onUnlockPremiumRequested({ token, id })
|
||||||
|
})
|
||||||
|
|
||||||
|
this.form.append(
|
||||||
|
createHeader('Unlock premium version (new)'),
|
||||||
|
this.purchaseIdField,
|
||||||
|
this.purchaseTokenField,
|
||||||
|
this.submitButton
|
||||||
|
)
|
||||||
|
|
||||||
|
this.root = this.form
|
||||||
|
}
|
||||||
|
|
||||||
|
reset () {
|
||||||
|
this.purchaseIdField.value = getPurchaseIdPlaceholder()
|
||||||
|
this.purchaseTokenField.value = ''
|
||||||
|
this.isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
set isEnabled (value: boolean) {
|
||||||
|
if (value === this.enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.enabled = value
|
||||||
|
|
||||||
|
this.purchaseIdField.disabled = !value
|
||||||
|
this.purchaseTokenField.isEnabled = value
|
||||||
|
this.submitButton.disabled = !value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UnlockPremiumFormListener {
|
||||||
|
onUnlockPremiumRequested (params: {
|
||||||
|
token: string
|
||||||
|
id: string
|
||||||
|
}): void
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue