[+] Complete transfer logic

pull/131/head
Azalea 2025-03-20 06:06:35 -04:00
parent c6af5b7d87
commit c93f47744b
2 changed files with 166 additions and 27 deletions

View File

@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import { slide } from "svelte/transition"; import { slide } from "svelte/transition";
import { ts } from "../../libs/i18n"; import { t, ts } from "../../libs/i18n";
import TransferServer from "./TransferServer.svelte"; import TransferServer from "./TransferServer.svelte";
import { DATA_HOST } from "../../libs/config"; import { DATA_HOST } from "../../libs/config";
import type { ConfirmProps } from "../../libs/generalTypes";
let tabs = ['chu3', 'mai2', 'ongeki'] let tabs = ['chu3', 'mai2', 'ongeki']
@ -13,16 +14,15 @@
} }
let tab = 0 let tab = 0
let src = JSON.parse(localStorage.getItem('src') ?? JSON.stringify({ let src = JSON.parse(localStorage.getItem('src') ?? `{"dns": "", "card": "", "keychip": ""}`)
dns: "", let dst = JSON.parse(localStorage.getItem('dst') ?? `{"dns": "", "card": "", "keychip": ""}`)
card: "", let [srcTested, dstTested] = [false, false]
keychip: "" let gameInfo = JSON.parse(localStorage.getItem('gameInfo') ?? `{"game": "", "version": ""}`)
}))
let dst = JSON.parse(localStorage.getItem('dst') ?? `{ card: "", server: "", keychip: "" }`) let srcEl: TransferServer, dstEl: TransferServer
let gameInfo = JSON.parse(localStorage.getItem('gameInfo') ?? JSON.stringify({ let srcExportedData: string
game: "", let loading: boolean = false
version: "", let confirm: ConfirmProps | null = null
}))
function defaultGame() { function defaultGame() {
gameInfo.game = game[tabs[tab]].game gameInfo.game = game[tabs[tab]].game
@ -35,6 +35,27 @@
localStorage.setItem('gameInfo', JSON.stringify(gameInfo)) localStorage.setItem('gameInfo', JSON.stringify(gameInfo))
} }
function startTransfer() {
if (!(srcTested && dstTested)) return alert("Please test both servers first!")
if (loading) return alert("Transfer already in progress!")
console.log("Starting transfer...")
loading = true
if (!dstEl.exportedData) {
// Ask user to make sure to backup their data
if (!confirm("It seems like you haven't backed up your destination data. Are you sure you want to proceed? (This will overwrite your destination server's data)")) {
loading = false
return
}
}
srcEl.pull()
.then(() => dstEl.push(srcExportedData))
.then(() => alert("Transfer successful!"))
.catch(e => alert(`Transfer failed: ${e}`))
.finally(() => loading = false)
}
defaultGame() defaultGame()
</script> </script>
@ -56,15 +77,21 @@
<p>👋 Welcome to the AquaTrans™ server data transfer tool!</p> <p>👋 Welcome to the AquaTrans™ server data transfer tool!</p>
<p>You can use this to export data from any server, and input data into any server using the connection credentials (card number, server address, and keychip id).</p> <p>You can use this to export data from any server, and input data into any server using the connection credentials (card number, server address, and keychip id).</p>
<p>This tool will simulate a game client and pull your data from the source server, and push your data to the destination server.</p> <p>This tool will simulate a game client and pull your data from the source server, and push your data to the destination server.</p>
<p>Please fill out the form below to get started!</p> <p>Please fill out the info below to get started!</p>
</div>
<TransferServer bind:src={src} bind:gameInfo={gameInfo} on:change={onChange}
bind:tested={srcTested} bind:this={srcEl} bind:exportedData={srcExportedData} />
<div class="arrow" class:disabled={!(srcTested && dstTested)}>
<img src="{DATA_HOST}/d/DownArrow.png" alt="arrow" on:click={startTransfer}>
</div> </div>
<TransferServer bind:src={src} bind:gameInfo={gameInfo} on:change={onChange} />
<div class="arrow"><img src="{DATA_HOST}/d/DownArrow.png" alt="arrow"></div>
<TransferServer bind:src={dst} bind:gameInfo={gameInfo} on:change={onChange} <TransferServer bind:src={dst} bind:gameInfo={gameInfo} on:change={onChange}
isSrc={false} /> bind:tested={dstTested} bind:this={dstEl} isSrc={false} />
</main> </main>
<style lang="sass"> <style lang="sass">
.arrow .arrow
width: 100% width: 100%
@ -72,11 +99,14 @@
justify-content: center justify-content: center
margin-top: -40px margin-top: -40px
margin-bottom: -40px margin-bottom: -40px
z-index: 0 z-index: 1
// CSS animation to let the image opacity breathe &.disabled
.arrow img filter: grayscale(1)
animation: breathe 1s infinite alternate
// CSS animation to let the image opacity breathe
img
animation: breathe 1s infinite alternate
@keyframes breathe @keyframes breathe
0% 0%

View File

@ -1,18 +1,90 @@
<script lang="ts"> <script lang="ts">
import StatusOverlays from "../../components/StatusOverlays.svelte"; import StatusOverlays from "../../components/StatusOverlays.svelte";
import { TRANSFER } from "../../libs/sdk"; import { TRANSFER } from "../../libs/sdk";
import { download, selectJsonFile } from "../../libs/ui";
import InputTextShort from "./InputTextShort.svelte"; import InputTextShort from "./InputTextShort.svelte";
export let src: AllNetSrc export let src: AllNetSrc
export let gameInfo: AllNetGame export let gameInfo: AllNetGame
export let isSrc: boolean = true export let isSrc: boolean = true
let tested: boolean = false export let tested: boolean = false
let [loading, error, expectedError] = [false, "", ""]
function testConnection() {
if (loading) return
// Preliminiary checks
if (!src.dns || !src.keychip || !src.card || !gameInfo.game || !gameInfo.version) {
error = "Please fill out all fields"
return
}
loading = true
console.log("Testing connection...")
TRANSFER.check({...src, ...gameInfo}).then(res => {
console.log("Connection test result:", res)
tested = true
}).catch(err => expectedError = err.message).finally(() => loading = false)
}
let messages: string[] = []
export let exportedData: string = ""
export function pull(): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (loading || !tested) return reject("Please test connection first")
if (exportedData) return resolve(exportedData)
console.log("Exporting data...")
TRANSFER.pull({...src, ...gameInfo}, (msg: TrStreamMessage) => {
console.log("Export progress: ", JSON.stringify(msg))
if ('message' in msg) messages = [...messages, msg.message]
if ('error' in msg) {
expectedError = msg.error
reject(msg.error)
}
if ('data' in msg) {
// file name: Export YYYY-MM-DD {server host} {game} {card last 6}.json
let date = new Date().toISOString().split('T')[0]
let host = new URL(src.dns).hostname
download(msg.data, `Export ${date} ${host} ${gameInfo.game} ${src.card.slice(-6)}.json`)
exportedData = msg.data
resolve(msg.data)
}
}).catch(err => { expectedError = err; reject(err) })
})
}
function pushBtn() {
if (loading || !tested) return
selectJsonFile().then(obj => push(JSON.stringify(obj)))
}
export function push(data: string) {
if (loading || !tested) return
console.log("Import data...")
loading = true
TRANSFER.push({...src, ...gameInfo}, data).then(() => {
console.log("Data imported successfully")
messages = ["Data imported successfully"]
}).catch(err => expectedError = err.message).finally(() => loading = false)
}
</script> </script>
<div class="server source" class:src={isSrc}> <StatusOverlays {loading} {error} />
<div class="server source" class:src={isSrc} class:hasError={expectedError} class:tested={tested}>
<h3>{isSrc ? "Source" : "Target"} Server</h3> <h3>{isSrc ? "Source" : "Target"} Server</h3>
{#if expectedError}
<blockquote class="error-msg">{expectedError}</blockquote>
{/if}
<!-- First input line --> <!-- First input line -->
<div class="inputs"> <div class="inputs">
<InputTextShort desc="Server Address" placeholder="e.g. http://aquadx.hydev.org" <InputTextShort desc="Server Address" placeholder="e.g. http://aquadx.hydev.org"
@ -33,12 +105,22 @@
bind:value={src.card} on:change disabled={tested} /> bind:value={src.card} on:change disabled={tested} />
</div> </div>
<!-- Streaming messages -->
{#if messages.length > 0}
<div class="stream-messages">
{#each messages.slice(Math.max(messages.length - 5, 0), undefined) as msg}
<p>{msg}</p>
{/each}
</div>
{/if}
<!-- Buttons --> <!-- Buttons -->
<div class="inputs buttons"> <div class="inputs buttons">
{#if !tested} {#if !tested}
<button class="flex-1" on:click={() => {}}>Test Connection</button> <button class="flex-1" on:click={testConnection} disabled={loading}>Test Connection</button>
{:else} {:else}
<button class="flex-1" on:click={() => {}}>Export Data</button> <button class="flex-1" on:click={pull}>Export Data</button>
<button class="flex-1" on:click={pushBtn}>Import Data</button>
{/if} {/if}
</div> </div>
</div> </div>
@ -47,14 +129,28 @@
@use "../../vars" @use "../../vars"
@use "sass:color" @use "sass:color"
.error-msg
white-space: pre-wrap
margin: 0
.server .server
display: flex display: flex
flex-direction: column flex-direction: column
gap: 1rem gap: 1rem
--c-src: 255, 174, 174 // --c-src: 202, 168, 252
&.src --c-src: 179, 198, 255
--c-src: 173, 192, 247 // animation: hue-rotate 10s infinite linear
// &.src
// --c-src: 173, 192, 247
// animation: hue-rotate 10s infinite linear reverse
&.tested
--c-src: 169, 255, 186
&.hasError
--c-src: 255, 174, 174
animation: none
padding: 1rem padding: 1rem
border-radius: vars.$border-radius border-radius: vars.$border-radius
@ -71,6 +167,12 @@
text-align: center text-align: center
// @keyframes hue-rotate
// 0%
// filter: hue-rotate(0deg)
// 100%
// filter: hue-rotate(360deg)
.inputs .inputs
display: flex display: flex
flex-wrap: wrap flex-wrap: wrap
@ -85,5 +187,12 @@
width: 100px width: 100px
&.buttons &.buttons
margin-top: 1rem margin-top: 0.5rem
.stream-messages
font-size: 0.8rem
opacity: 0.8
margin-top: 0.5rem
padding: 0 0.5rem
</style> </style>