Source: Router/list/service.js

// @ts-check
/**
 * @import {ExpressRequestAuthorized} from '../../types.js'
 */
import {query,pool,UUID2hex,HEX2uuid} from '@commtool/sql-query'
import {renderObject} from '../../utils/renderTemplates.js'
import { Templates } from '../../utils/compileTemplates.js';
import {getUID} from '../../utils/UUIDs.js'
import {addUpdateEntry, addUpdateList} from '../../server.ws.js'
import { addVisibility, deleteVisibility, rebuildListVisibility } from '../../utils/listVisibilty.js';
import { isObjectAdmin, isListAdmin } from '../../utils/authChecks.js';
import {requestUpdateLogger, readLogger, errorLoggerRead, errorLoggerUpdate} from '../../utils/requestLogger.js'
import { publishEvent } from '../../utils/events.js';

/**
 * Insert a new list or dlist
 * @param {ExpressRequestAuthorized} req
 * @param {string} ownerUID
 * @param {'list'|'dlist'} type
 * @returns {Promise<{success:boolean,result?:any,message?:string}>}
 */
export const insertList=async (req,ownerUID,type)=>
{
    try {
        const UID=await getUID(req)
        let memberUID=UUID2hex(req.session.user)
        if(req.query.user )
        {
            memberUID=UUID2hex(req.query.user)
        }
        const template= Templates[req.session.root][type]
        const object=await renderObject(template,req.body,req)
        let resOwner=await query (`SELECT ObjectBase.UID, Member.Display,Member.Data, ObjectBase.Title       
            FROM ObjectBase 
            INNER JOIN Member ON (Member.UID=ObjectBase.UID)
            WHERE ObjectBase.UID=? AND ObjectBase.Type IN ('group','event')`,[ownerUID],{cast:['UUID','json']})
        if(resOwner.length===0)
        {
            resOwner=await query (`SELECT ObjectBase.UID, Member.Display, Member.Data, ObjectBase.Title 
                FROM ObjectBase 
                INNER JOIN Member ON(Member.UID=ObjectBase.UID)
                WHERE ObjectBase.UID=? AND ObjectBase.Type='group'`,
                [UUID2hex(req.session.root)],
                {cast:['UUID','json']})
        }
        const owner=resOwner[0]
        const [list] = await query(`SELECT Data,dindex FROM ObjectBase WHERE UID=? `,[UID])
        if(!list)
        {
            await query(`
            INSERT INTO ObjectBase (UID,Type,UIDBelongsTo,Title, dindex)
            VALUES (?,?,?,?,?)
            `,
            [object.UID,type,object.UID,object.Title,object.dindex])

            await query(`
            INSERT INTO Member (UID,Display,SortName, FullTextIndex, Data)
            VALUES (?,?,?,?,?)
            `,
            [object.UID,object.Display,object.SortIndex,object.FullTextIndex,JSON.stringify({...req.body,visibility:undefined})])
            
            await query(`INSERT IGNORE INTO Links (UID,Type,UIDTarget) VALUES (?,?,?)`,
                [
                    [object.UID,'memberA',ownerUID],
                    [object.UID,'member',memberUID]
                ],
                {batch:true}
            )
            await query(`INSERT INTO Visible (UID,UIDUser,Type) VALUES(?,?,'admin')`,[UID,memberUID])
            if(!req.query.private)
                addVisibility(req,UID,owner)
        }
        else 
        {
            if(!await isListAdmin(req,UID))
            {
                return {message: 'user not authorized', success: false}
            }
            await query( `UPDATE ObjectBase,Member SET 
                    ObjectBase.Title=?,Member.Display=?,Member.SortName=?,Member.FullTextIndex=?,ObjectBase.dindex=?,Member.Data=?
                    WHERE ObjectBase.UID=? AND Member.UID=ObjectBase.UID`, 
            [object.Title,object.Display,object.SortIndex,object.FullTextIndex,object.dindex,JSON.stringify(req.body),UID]) 

            const oldOwner= await query(`SELECT ObjectBase.UID, ObjectBase.Type FROM
                ObjectBase INNER JOIN Links ON (ObjectBase.UID = Links.UIDTarget AND Links.Type='memberA')
                WHERE ObjectBase.Type IN ('person','group') AND Links.UID=? `,[UID],{cast:['json','UUID']}) 

            if(oldOwner.length===1 && oldOwner[0].UID!==owner.UID)
            {
                if(!await isListAdmin(req,UID))
                {
                    return {message: 'user not authorized for ownership update', success: false}
                }
                await query(`UPDATE Links SET UIDTarget=? WHERE UID=? AND Type='memberA'`,[ownerUID,UID])
                await deleteVisibility(UID)
                addVisibility(req,UID,owner)

            }
            const oldMember= await query(`SELECT ObjectBase.UID, ObjectBase.Type FROM
                ObjectBase INNER JOIN Links ON (ObjectBase.UID = Links.UIDTarget AND Links.Type='member')
                WHERE ObjectBase.Type IN ('person','group') AND Links.UID=? `,[UID],{cast:['json','UUID']}) 

            const resMember=await query (`SELECT Member.UID, Member.Display, Member.Data, ObjectBase.Title 
                FROM ObjectBase 
                INNER JOIN Member ON (ObjectBase.UIDBelongsTo = Member.UID)
                WHERE ObjectBase.UID=?`,[memberUID],{cast:['json','UUID']})
            const member=resMember[0]
            if(oldMember.length===1  && oldMember[0].UID!==member.UID)
            {
                if(!await isListAdmin(req,UID))
                {
                    return {message: 'user not authorized for ownership update', success: false}
                }

                await query(`UPDATE Links SET UIDTarget=? WHERE UID=? AND Type='member'`,[memberUID,UID])
                rebuildListVisibility(req,UID)
            }
            object.admin=await isListAdmin(req,UID)
            object.writeable=await isObjectAdmin(req,UID)
            addUpdateEntry(UID,{Data:{...object,Data:req.body,UID: HEX2uuid(object.UID),owner, member}})
        }
        return {success: true,result:{...object,Data: {...req.body,visibility:undefined},UID: HEX2uuid(object.UID)}}
    }
    catch(e)
    {
        errorLoggerUpdate(e)
    }
}

/**
 * Update existing list/dlist
 * @param {ExpressRequestAuthorized} req
 * @param {string} UID
 * @param {'list'|'dlist'} type
 * @param {boolean} [admin=false]
 * @returns {Promise<{success:boolean,message?:string}>}
 */
export const updateList=async (req,UID,type,admin=false)=>
{
    try {
          let sqlType= `AND ObjectBase.Type='${type}'`    
        if(req.query.type==='list_dlist')
        {
            sqlType=   `AND ObjectBase.Type IN('list','dlist')`    
        }
        const [list] = await query(`SELECT Member.Data,ObjectBase.dindex FROM ObjectBase
            INNER JOIN Member ON (Member.UID=ObjectBase.UID)
            WHERE ObjectBase.UID=? ${sqlType} `,[UID],{cast:['json']})
        if(!list)
        {
            return {success: false, message:'List not found'}
        }
        const Data={...list.Data,...req.body}
        const template= Templates[req.session.root][type]
        const object=await renderObject(template,Data,req)
        await query( `UPDATE ObjectBase,Member SET 
        ObjectBase.Title=?,Member.Display=?,Member.SortName=?,Member.FullTextIndex=?,ObjectBase.dindex=?,Member.Data=?
        WHERE ObjectBase.UID=? AND Member.UID=ObjectBase.UID`, 
            [object.Title,object.Display,object.SortIndex,object.FullTextIndex,object.dindex,JSON.stringify(Data),UID]) 
        const resMember=await query (`SELECT Member.UID, Member.Display, Member.Data, ObjectBase.Title 
            FROM ObjectBase 
            INNER JOIN Member ON (ObjectBase.UIDBelongsTo = Member.UID)
            INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='member')
            WHERE Links.UID=?`,[UID],{cast:['UUID','json']})
        const member= resMember[0]
        const resOwner=await query (`SELECT Member.UID, Member.Display, Member.Data, ObjectBase.Title 
            FROM ObjectBase 
            INNER JOIN Member ON (ObjectBase.UIDBelongsTo = Member.UID)
            INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='memberA')
            WHERE Links.UID=? AND ObjectBase.Type IN('group','event')`,[UID],{cast:['UUID','json']})
        const owner=resOwner[0]
        object.admin=await isListAdmin(req,UID)
        object.writeable=await isObjectAdmin(req,UID)
        addUpdateEntry(UID,{Data:{...object,Data:Data,UID: HEX2uuid(object.UID),owner, member}})
       // addUpdateList(UID)
        return {success: true }
    }
    catch(e)
    {
        errorLoggerUpdate(e)
    }

}

/**
 * Get list/dlist details
 * @param {ExpressRequestAuthorized} req
 * @param {string} UID
 * @param {'list'|'dlist'} type
 * @returns {Promise<{success:boolean,result?:any,message?:string}>}
 */
export const getList=async(req,UID,type)=>
{
    let sqlType= `AND ObjectBase.Type='${type}'`    
    if(req.query.type==='list_dlist')
    {
        sqlType=   `AND ObjectBase.Type IN('list','dlist')`    
    }
    const resList=await query(`SELECT ObjectBase.UID,Member.Data,Member.Display,ObjectBase.dindex 
        FROM ObjectBase 
        INNER JOIN Member ON (Member.UID=ObjectBase.UID)
        WHERE ObjectBase.UID=? ${sqlType}`,[UID,UUID2hex(req.session.user)],{cast:['UUID','json']})
    if(resList && resList.length===1)
    {
        const list=resList[0]
        
        const resMember=await query (`SELECT Member.UID, Member.Display, Member.Data, ObjectBase.Title 
            FROM ObjectBase 
            INNER JOIN Member ON (ObjectBase.UIDBelongsTo = Member.UID)
            INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='member')
            WHERE Links.UID=?`,[UID],{cast:['UUID','json']})
        const member= resMember[0]
        const resOwner=await query (`SELECT Member.UID, Member.Display, Member.Data, ObjectBase.Title 
            FROM ObjectBase 
            INNER JOIN Member ON (ObjectBase.UIDBelongsTo = Member.UID)
            INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='memberA')
            WHERE Links.UID=? AND ObjectBase.Type IN ('group','event')`,[UID],{cast:['UUID','json']})
        const owner= resOwner[0]
        list.admin=await isListAdmin(req,UID)
        list.writeable=await isObjectAdmin(req,UID)
        return{success:true,result:{...list,owner:owner, member:member}}
    }
    else
    {
        return {success:false,message: type+' not found or user not authorized'}
    }
}

/**
 * Delete a list/dlist
 * @param {ExpressRequestAuthorized} req
 * @param {'list'|'dlist'} type
 * @returns {Promise<{success:boolean,message?:string}>}
 */
export const deleteList=async(req,type)=>
{
    try {
        const UID=UUID2hex(req.params.UID)
        let sqlType= `AND ObjectBase.Type='${type}'`
        if(req.query.type==='list_dlist')
        {
            sqlType=   `AND ObjectBase.Type IN('list','dlist')`    
        }
        const lres=await query(`SELECT ObjectBase.UIDBelongsTo,ObjectBase.UID FROM ObjectBase
                WHERE ObjectBase.UID =? ${sqlType}`,[UID])
        if(lres.length===0)
        {
            return({success:false,message:type+' not found'})
        }
        const list=lres[0]

        const hasLinks = await query(`SELECT Links.UID,ObjectBase.Display FROM Links 
                INNER JOIN ObjectBase ON (Links.UID=ObjectBase.UID) 
                WHERE  Links.Type IN ('filter','member','memberA') AND Links.UIDTarget=? `,[UID])
        if(hasLinks.length && !req.query.force)
        {
            return({success:false,message:'has still members or active filters'})
        }
        else
        {
          
            if(list)
            {
                query(`DELETE ObjectBase,Member FROM ObjectBase LEFT JOIN Member ON (Member.UID=ObjectBase.UID) WHERE ObjectBase.UID=?  ${sqlType}`,[list.UID])
                query(`DELETE Links FROM Links WHERE UID=? AND Type IN ('memberA','member')`,[UID])
                if(req.query.force)
                {
                    query(`DELETE ObjectBase,Links FROM ObjectBase,Links 
                            WHERE ObjectBase.Type IN ('entry','guest','filter') 
                            AND Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA','filter')
                            AND Links.UIDTarget=?`,[UID] )
                }
                deleteVisibility(UID)
                const actions= await query(`SELECT ObjectBase.UID,ObjectBase.Data, ObjectBase.Display, Links.UID AS UIDtemplate
                    FROM ObjectBase 
                    INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='action')    
                    WHERE ObjectBase.UIDBelongsTo=? AND ObjectBase.Type='action'`, [list.UID], { cast: ['json', 'UUID'] });
                if(actions.length>0)
                {
                    await query(`DELETE ObjectBase,Links 
                        FROM ObjectBase 
                        LEFT JOIN Links 
                        ON (Links.UID=ObjectBase.UID AND Links.Type='action') 
                        WHERE ObjectBase.UID IN (?) AND ObjectBase.Type='action'`, [actions.map(a => a.UID)]);
                    for(const action of actions)
                    {
                        publishEvent(`/remove/actionT/action/${HEX2uuid(action.UIDtemplate)}`, {
                            organization: req.session.root,
                            data: HEX2uuid(UID)
                        });
                    }
                }

                return {success:true}
            }
            else
            {
                return {success:false, message:'list not found'}
            }
        }
    }
    catch(e)
    {
        errorLoggerUpdate(e)
    }
}