Source: stores/account.js

/*
 * Copyright © 2024-2025 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD license.
 * See file LICENSE for details.
 */
import { defineStore } from 'pinia'
import { jwtDecode } from "jwt-decode"

/**
 * This is the account store.
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
export const useAccountStore = defineStore('account', {
  state: () => ({
    /**
     * The account name if the current user is logged in.
     */
    account: window.localStorage.getItem('ctan.account') || '',
    /**
     *
     */
    error: '',
    /**
     *
     */
    message: null,
    /**
     *
     */
    user: (window.localStorage.getItem('ctan.user')
      ? JSON.parse(window.localStorage.getItem('ctan.user'))
      : {is: {}}
    )
    /*
    // locale: window.localStorage.getItem('ctan.locale') || 'en',
    // theme: window.localStorage.getItem('ctan.theme') || 'light',
    // listType: window.localStorage.getItem('ctan.list.type') || 'list',
    // files: {
    //   showSpecial
    // }
    */
  }),

  actions: {
    /**
     * Reset the error status. 
     */
    clearError () {
      this.error = ''
    },

    /**
     * Initialise. 
     */
    init (message) {
      this.message = message
    },

    /**
     * Has the current user the admin role? 
     */
    isAdmin () {
      if (this.account === null ||
        this.user === null ||
        this.user.is === null ||
        !this.user.is.admin) {
        throw showError({
          fatal: true,
          statusCode: 404,
          message: 'Page not found.'
        })
      } 
    },

    /**
     * Has the current user the given role? 
     */
    is (role) {
      return this.user !== null &&
        this.user.is !== null &&
        this.user.is[role]
    },

    /**
     * Is the current user logged in?
     */
    isLoggedIn () {
      return !!this.account
    },

    /**
     * Retrieve the current JWT.
     */
    getToken () {
      if (this.user === null) {
        return null;
      }
      return this.user.token
    },

    /**
     * Try to login.
     *
     * @param {*} account the account name
     * @param {*} passwd the password
     */
    async login (account, password) {
      this.message.beginLoading()
      this.error = ''
      try {
        const data = await $fetch('/api/3.0/user/login', {
          method: 'POST',
          body: {
            account: account,
            password: password
          }
        })
        if (data) {
          this.account = data.account
          this.user = data
        } else {
          this.error = '401'
          this.account = ''
          this.user = null
        }
      } catch (error) {
        if (error.toString().includes(' 404 ')) {
          this.error = '404'
        } else {
          this.error = '500'
        }
        this.account = ''
        this.user = null
      } finally {
        this.message.endLoading()
      }

      window.localStorage.setItem('ctan.account', this.account)
      window.localStorage.setItem('ctan.user', JSON.stringify(this.user))
    },

    /**
     * Sign-out the current user.
     * The stored account data is cleared.
     */
    logout () {
      this.account = ''
      this.user = null
      window.localStorage.setItem('ctan.account', this.account)
      window.localStorage.setItem('ctan.user', JSON.stringify(this.user))
    },

    /**
     * Try to refresh an account.
     *
     * @param {*} email the email
     */
    async refresh (email) {
      // TODO

      throw showError({
        fatal: true,
        statusCode: 501,
        message: 'Not Implemented'
      })
    },

    /**
     * Try to register an account.
     *
     * @param {*} account the account name
     * @param {*} email the email
     */
    async register (account, email, name, gender, lang) {
      this.message.beginLoading()
      this.error = ''
      try {
        const data = await $fetch('/api/3.0/user/register', {
          method: 'POST',
          body: {
            account: account,
            email: email,
            gender: gender,
            lang: lang,
            name: name
          }
        })
      } catch (error) {
        this.error = error.toString().replaceAll(/.*: ([0-9]+) .*/g, '$1')
      } finally {
        this.message.endLoading()
      }
    },

    /**
     * Try to remove an account.
     *
     * @param {*} message the message
     */
    async remove (message) {
      message.beginLoading()
      this.error = ''
      try {
        await $fetch('/api/3.0/user', {
          method: 'DELETE',
          body: {
            account: this.user.account,
            token: this.user.token
          }
        })
        this.logout()
      } catch (error) {
        this.error = error
        this.account = ''
        this.user = null
      } finally {
        message.endLoading()
      }
    },

    /**
     * Try to reset the password.
     *
     * @param {*} account the account name
     * @param {*} email the email
     */
    async reset (account, email, locale) {
      this.message.beginLoading()
      this.error = ''
      try {
        await $fetch('/api/3.0/user/passwd', {
          method: 'POST',
          body: {
            account: account,
            email: email,
            locale: locale
          }
        })
      } catch (error) {
        if (error.toString().includes(' 404 ')) {
          this.error = '404'
        } else {
          this.error = '500'
        }
      } finally {
        this.message.endLoading()
      }
    },
    
    /**
     * Try to set an attribute to a value -- in the client and in the server.
     *
     * @param {*} attribute the name of the attribute
     * @param {*} value the new value
     */
    async set (attribute, value) {
        
      const jwtPayload = JSON.parse(window.atob(this.user.token.split('.')[1]))

      if (attribute === 'country') {
        value = value.value
      }
      this.message.beginLoading()
      this.error = ''
      try {
        await $fetch('/api/3.0/user/set', {
          method: 'POST',
          body: {
            token: this.user.token,
            attribute: attribute,
            value: value
          }
        })
        this.user[attribute] = value
        window.localStorage.setItem('ctan.user', JSON.stringify(this.user))
      } catch (error) {
        this.error = error
      } finally {
        this.message.endLoading()
      }
    }
  }
})