improve the import/export process to allow for non-ascii characters.
This commit is contained in:
parent
746b490216
commit
86da6c2b06
10 changed files with 39 additions and 78 deletions
|
|
@ -209,6 +209,7 @@
|
|||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
const emitter = defineEmits(['cancel', 'submit'])
|
||||
import { decode } from '~/utils/importer'
|
||||
|
||||
const props = defineProps({
|
||||
reference: {
|
||||
|
|
@ -321,18 +322,8 @@ const importItem = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (false === val.startsWith('{')) {
|
||||
try {
|
||||
val = base64UrlDecode(val)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error(`Failed to decode string. ${e.message}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const item = JSON.parse(val)
|
||||
const item = decode(val)
|
||||
|
||||
if (!item?._type || 'condition' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'condition', got '${item._type ?? 'unknown'}'.`)
|
||||
|
|
|
|||
|
|
@ -267,6 +267,8 @@
|
|||
|
||||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { decode } from '~/utils/importer'
|
||||
|
||||
const emitter = defineEmits(['cancel', 'submit']);
|
||||
const toast = useNotification();
|
||||
const props = defineProps({
|
||||
|
|
@ -339,18 +341,8 @@ const importItem = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (false === val.startsWith('{')) {
|
||||
try {
|
||||
val = base64UrlDecode(val)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error(`Failed to decode string. ${e.message}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const item = JSON.parse(val)
|
||||
const item = decode(val)
|
||||
|
||||
if ('notification' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'notification', got '${item._type}'.`)
|
||||
|
|
|
|||
|
|
@ -232,6 +232,8 @@
|
|||
|
||||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { decode } from '~/utils/importer'
|
||||
|
||||
const emitter = defineEmits(['cancel', 'submit']);
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -351,18 +353,8 @@ const importItem = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (false === val.startsWith('{')) {
|
||||
try {
|
||||
val = base64UrlDecode(val)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error(`Failed to decode string. ${e.message}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const item = JSON.parse(val)
|
||||
const item = decode(val)
|
||||
|
||||
if (!item?._type || 'preset' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'preset', got '${item._type ?? 'unknown'}'.`)
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@
|
|||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { CronExpressionParser } from 'cron-parser'
|
||||
import { decode } from '~/utils/importer'
|
||||
|
||||
const props = defineProps({
|
||||
reference: {
|
||||
|
|
@ -352,18 +353,8 @@ const importItem = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (false === val.startsWith('{')) {
|
||||
try {
|
||||
val = base64UrlDecode(val)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error(`Failed to decode string. ${e.message}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const item = JSON.parse(val)
|
||||
const item = decode(val)
|
||||
|
||||
if ('task' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'task', got '${item._type}'.`)
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@
|
|||
|
||||
<script setup>
|
||||
import { request } from '~/utils/index'
|
||||
import { encode } from '~/utils/importer'
|
||||
|
||||
const toast = useNotification()
|
||||
const config = useConfigStore()
|
||||
|
|
@ -286,6 +287,6 @@ const exportItem = item => {
|
|||
userData['_type'] = 'condition'
|
||||
userData['_version'] = '1.0'
|
||||
|
||||
return copyText(base64UrlEncode(JSON.stringify(userData)))
|
||||
return copyText(encode(userData))
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ div.is-centered {
|
|||
|
||||
<script setup>
|
||||
import { request } from '~/utils/index'
|
||||
import { encode } from '~/utils/importer'
|
||||
|
||||
const toast = useNotification()
|
||||
const config = useConfigStore()
|
||||
|
|
@ -337,6 +338,6 @@ const exportItem = async item => {
|
|||
data['_type'] = 'notification'
|
||||
data['_version'] = '1.0'
|
||||
|
||||
return copyText(base64UrlEncode(JSON.stringify(data)))
|
||||
return copyText(encode(data))
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ div.is-centered {
|
|||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { request } from '~/utils/index'
|
||||
import { encode } from '~/utils/importer'
|
||||
|
||||
const toast = useNotification()
|
||||
const config = useConfigStore()
|
||||
|
|
@ -400,7 +401,7 @@ const exportItem = item => {
|
|||
userData['_type'] = 'preset'
|
||||
userData['_version'] = '2.5'
|
||||
|
||||
return copyText(base64UrlEncode(JSON.stringify(userData)))
|
||||
return copyText(encode(userData))
|
||||
}
|
||||
|
||||
const calcPath = (path) => {
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ import moment from 'moment'
|
|||
import { useStorage } from '@vueuse/core'
|
||||
import { CronExpressionParser } from 'cron-parser'
|
||||
import { request } from '~/utils/index'
|
||||
import { encode } from '~/utils/importer'
|
||||
import type { task_item, exported_task, error_response } from '~/@types/tasks'
|
||||
|
||||
const box = useConfirm()
|
||||
|
|
@ -496,8 +497,7 @@ const exportItem = async (item: task_item) => {
|
|||
data._type = 'task'
|
||||
data._version = '2.0'
|
||||
|
||||
return copyText(base64UrlEncode(JSON.stringify(data)));
|
||||
return copyText(encode(data));
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
|
|
|||
21
ui/utils/importer.ts
Normal file
21
ui/utils/importer.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const encode = (obj: Record<string, any>): string => {
|
||||
const jsonStr = JSON.stringify(obj);
|
||||
const utf8Bytes = new TextEncoder().encode(jsonStr);
|
||||
const binary = String.fromCharCode(...utf8Bytes);
|
||||
const base64 = btoa(binary);
|
||||
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
const decode = (str: string): object => {
|
||||
const base64 = str
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/')
|
||||
.padEnd(str.length + (4 - str.length % 4) % 4, '=');
|
||||
|
||||
const binary = atob(base64);
|
||||
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
|
||||
const jsonStr = new TextDecoder().decode(bytes);
|
||||
return JSON.parse(jsonStr);
|
||||
}
|
||||
|
||||
export { encode, decode }
|
||||
|
|
@ -406,33 +406,6 @@ const convertCliOptions = async opts => {
|
|||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* URL Safe Base64 Encode.
|
||||
*
|
||||
* @param {String} data The data to encode
|
||||
*
|
||||
* @returns {String} The encoded data
|
||||
*/
|
||||
const base64UrlEncode = data => btoa(data).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
|
||||
/**
|
||||
* URL Safe Base64 Decode.
|
||||
*
|
||||
* @param {String} input The input string to decode
|
||||
*
|
||||
* @returns {String} The decoded data
|
||||
*/
|
||||
const base64UrlDecode = input => {
|
||||
let base64 = input.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const pad = base64.length % 4;
|
||||
if (pad) {
|
||||
base64 += '='.repeat(4 - pad);
|
||||
}
|
||||
|
||||
return atob(base64);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if array or object has data.
|
||||
*
|
||||
|
|
@ -539,8 +512,6 @@ export {
|
|||
makeDownload,
|
||||
formatBytes,
|
||||
convertCliOptions,
|
||||
base64UrlEncode,
|
||||
base64UrlDecode,
|
||||
has_data,
|
||||
toggleClass,
|
||||
cleanObject,
|
||||
|
|
|
|||
Loading…
Reference in a new issue