diff options
Diffstat (limited to 'app')
| -rw-r--r-- | app/imap.js | 66 | ||||
| -rw-r--r-- | app/src/App.vue | 16 | ||||
| -rw-r--r-- | app/src/components/Panel.vue | 40 | ||||
| -rw-r--r-- | app/src/pages/Home.vue | 66 | ||||
| -rw-r--r-- | app/src/store.js | 2 | 
5 files changed, 154 insertions, 36 deletions
diff --git a/app/imap.js b/app/imap.js index 181d9cf..4041089 100644 --- a/app/imap.js +++ b/app/imap.js @@ -5,6 +5,13 @@ ipcMain.on('imap:listTree:from', listTreeFrom);  ipcMain.on('imap:listTree:to', listTreeTo);  ipcMain.on('imap:migrate', migrate); +/** + * Connect to server + * + * @param {object} options + * + * @returns {Promise<ImapFlow>} + */  async function connect (options) {    const client = new ImapFlow({      host: options.server, @@ -13,6 +20,7 @@ async function connect (options) {        user: options.username,        pass: options.password,      }, +    tls: options.tls,    });    await client.connect(); @@ -20,6 +28,15 @@ async function connect (options) {    return client;  } +/** + * Turn fetch response into array + * + * @param {string} range + * @param {object} query + * @param {object} options + * + * @returns {Promise<*[]>} + */  ImapFlow.prototype.fetchArray = async function (range, query, options = {}) {    const msgsGenerator = await this.fetch(range, query, options);    const msgs = []; @@ -30,45 +47,80 @@ ImapFlow.prototype.fetchArray = async function (range, query, options = {}) {    return msgs;  }; +/** + * @param {IpcMainEvent} event + * @param {object} options + */  async function listTreeFrom (event, options) { -  const client = await connect(options); +  try { +    const client = await connect(options); -  event.reply('imap:listTree:from:reply', await client.listTree()); -  await client.logout(); +    event.reply('imap:listTree:from:reply', await client.listTree()); +    await client.logout(); +  } catch (e) { +    event.reply('imap:from:error', e); +  }  } +/** + * @param {IpcMainEvent} event + * @param {object} options + */  async function listTreeTo (event, options) { -  const client = await connect(options); +  try { +    const client = await connect(options); -  event.reply('imap:listTree:to:reply', await client.listTree()); -  await client.logout(); +    event.reply('imap:listTree:to:reply', await client.listTree()); +    await client.logout(); +  } catch (e) { +    event.reply('imap:to:error', e); +  }  } +/** + * @param {IpcMainEvent} event + * @param {object} from + * @param {object} to + */  async function migrate (event, { from, to }) {    const fromClient = await connect(from);    const toClient = await connect(to); +  event.reply('imap:migrate:progress', 'Getting folders');    const fromFolders = (await fromClient.list()).filter((folder) => folder.subscribed);    for (const fromFolder of fromFolders) { +    event.reply('imap:migrate:progress', `Working on folder "${fromFolder.path}"`); +      try {        await toClient.mailboxCreate(fromFolder.path);      } catch (e) {} +    const appendCommands = [];      const fromMailbox = await fromClient.getMailboxLock(fromFolder.path);      const toMailbox = await toClient.getMailboxLock(fromFolder.path);      try { +      event.reply('imap:migrate:progress', '- Collecting messages from target for comparison');        const toMsgs = await toClient.fetchArray('1:*', { flags: true, envelope: true, source: true }); + +      event.reply('imap:migrate:progress', '- Collecting messages from source');        const fromMsgs = await fromClient.fetchArray('1:*', { flags: true, envelope: true, source: true }); +      event.reply('imap:migrate:progress', '- Comparing messages');        const msgs = fromMsgs.filter((fromMsg) => !toMsgs.some((toMsg) => Buffer.compare(toMsg.source, fromMsg.source) === 0)); +      event.reply('imap:migrate:progress', `- Message status: Found ${fromMsgs.length} messages total and ${msgs.length} new messages to migrate`); +        await toClient.noop();        for (const msg of msgs) { -        toClient.append(fromFolder.path, msg.source, Array.from(msg.flags), msg.envelope.date); +        appendCommands.push(toClient.append(fromFolder.path, msg.source, Array.from(msg.flags), msg.envelope.date));        }      } finally { +      await Promise.all(appendCommands); +      event.reply('imap:migrate:progress', '- Done');        fromMailbox.release();        toMailbox.release();      }    } + +  event.reply('imap:migrate:progress', 'Done');  } diff --git a/app/src/App.vue b/app/src/App.vue index 98240ae..87365a2 100644 --- a/app/src/App.vue +++ b/app/src/App.vue @@ -1,3 +1,19 @@  <template>    <router-view />  </template> + +<script> +import 'foundation-sites/dist/js/foundation.cjs'; + +export default {}; +</script> + +<style lang="scss"> +@import '~foundation-sites/dist/css/foundation.min.css'; + +body { +  font-size: 1rem; +  font-family: Helvetica, sans-serif; +  padding: 1rem; +} +</style> diff --git a/app/src/components/Panel.vue b/app/src/components/Panel.vue index 3ae4000..527a8a9 100644 --- a/app/src/components/Panel.vue +++ b/app/src/components/Panel.vue @@ -1,21 +1,35 @@  <template> -  <div class="panel"> -    <label> -      Server <input type="text" :value="modelValue.server" @input="emit('server', $event.target.value)"> -    </label> -    <label> -      Port <input type="text" :value="modelValue.port" @input="emit('port', $event.target.value)"> -    </label> +  <form data-abide @submit.prevent="$emit('connect')"> +    <div class="grid-x"> +      <div class="cell"> +        <label> +          Server <input type="text" :value="modelValue.server" required @input="emit('server', $event.target.value)"> +        </label> +      </div> +    </div> +    <div class="grid-x"> +      <div class="cell"> +        <label> +          Port <input type="text" :value="modelValue.port" required @input="emit('port', $event.target.value)"> +        </label> +      </div> +    </div>      <label>        Username <input type="text" :value="modelValue.username" @input="emit('username', $event.target.value)">      </label>      <label> -      Passwort <input type="text" :value="modelValue.password" @input="emit('password', $event.target.value)"> +      Passwort <input type="password" :value="modelValue.password" @input="emit('password', $event.target.value)">      </label> -    <button @click="$emit('connect')"> +    <div class="grid-x"> +      <div class="cell"> +        <input :id="`tls-${uid}`" type="checkbox" :checked="modelValue.tls" @input="emit('tls', $event.target.checked)"> +        <label :for="`tls-${uid}`">TLS enabled</label> +      </div> +    </div> +    <button class="button" type="submit">        Connect      </button> -  </div> +  </form>  </template>  <script> @@ -27,6 +41,12 @@ export default {    ],    emits: ['connect'], + +  data () { +    return { +      uid: crypto.getRandomValues(new Uint8Array(1)), +    }; +  },  };  </script> diff --git a/app/src/pages/Home.vue b/app/src/pages/Home.vue index bfe92bf..8385a46 100644 --- a/app/src/pages/Home.vue +++ b/app/src/pages/Home.vue @@ -1,22 +1,38 @@  <template>    <div class="wrap">      <div class="panels"> -      <Panel v-model="from" @connect="connectFrom" /> -      <Panel v-model="to" @connect="connectTo" /> +      <div> +        <h1>FROM</h1> +        {{ from.error }} +        <Panel v-model="from" @connect="connectFrom" /> +        <ul v-if="from.folders"> +          <Folders :folder="from.folders" /> +        </ul> +      </div> + +      <div> +        <h1>TO</h1> +        {{ to.error }} +        <Panel v-model="to" @connect="connectTo" /> +        <ul v-if="to.folders"> +          <Folders :folder="to.folders" /> +        </ul> +      </div>      </div> -    <button @click="migrate"> + +    <button class="button" @click="migrate">        Migrate!      </button> -    <h1>FROM</h1> -    <ul v-if="from.folders"> -      <Folders :folder="from.folders" /> -    </ul> - -    <h1>TO</h1> -    <ul v-if="to.folders"> -      <Folders :folder="to.folders" /> -    </ul> +    <div class="progress-screen"> +      <b>{{ progress }}</b> +      <br> +      <div class="progress-screen__log"> +        <div v-for="(msg, idx) in log" :key="idx"> +          {{ msg }} +        </div> +      </div> +    </div>    </div>  </template> @@ -31,7 +47,10 @@ export default {    },    data () { -    return {}; +    return { +      progress: '', +      log: [], +    };    },    computed: { @@ -61,6 +80,18 @@ export default {      this.$electron.ipcRenderer.on('imap:listTree:to:reply', (event, folders) => {        this.to.folders = folders;      }); + +    this.$electron.ipcRenderer.on('imap:from:error', (event, error) => { +      this.from.error = error; +    }); +    this.$electron.ipcRenderer.on('imap:to:error', (event, error) => { +      this.to.error = error; +    }); + +    this.$electron.ipcRenderer.on('imap:migrate:progress', (event, progress) => { +      this.progress = progress; +      this.log.push(progress); +    });    },    methods: { @@ -73,6 +104,9 @@ export default {      },      async migrate () { +      this.progress = ''; +      this.log = []; +        this.$electron.ipcRenderer.send('imap:migrate', {          from: JSON.parse(JSON.stringify(this.from)),          to: JSON.parse(JSON.stringify(this.to)), @@ -83,12 +117,6 @@ export default {  </script>  <style lang="scss"> -body { -  margin: 0; -  font-size: 1rem; -  font-family: Helvetica, sans-serif; -} -  .panels {    display: flex;    justify-content: space-between; diff --git a/app/src/store.js b/app/src/store.js index 818689b..294380c 100644 --- a/app/src/store.js +++ b/app/src/store.js @@ -8,12 +8,14 @@ export default createStore({          port: 3143,          username: 'from@example.org',          password: 'password', +        tls: false,        },        to: {          server: 'localhost',          port: 31432,          username: 'to@example.org',          password: 'password', +        tls: false,        },      };    },  | 
