????

Your IP : 216.73.216.127


Current Path : C:/opt/pgsql/pgAdmin 4/web/pgadmin/tools/psql/static/js/components/
Upload File :
Current File : C:/opt/pgsql/pgAdmin 4/web/pgadmin/tools/psql/static/js/components/PsqlComponent.jsx

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////

import React, { useEffect } from 'react';
import { Box, useTheme } from '@mui/material';
import url_for from 'sources/url_for';
import PropTypes from 'prop-types';

import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { SearchAddon } from 'xterm-addon-search';
import { io } from 'socketio';
import { copyToClipboard } from '../../../../../static/js/clipboard';
import 'pgadmin.browser.keyboard';
import gettext from 'sources/gettext';

function psql_socket_io(socket, is_enable, sid, db, server_type, fitAddon, term, role){
  // Listen all the socket events emit from server.
  let init_psql = true;
  socket.on('pty-output', function(data){
    if(data.error) {
      term.write('\r\n');
    }
    term.write(data.result);
    if(data.error) {
      term.write('\r\n');
    }
    if (init_psql && data && role) {
      // setting role if available
      socket.emit('socket_set_role',{'role': _.unescape(role)});
      init_psql = false;
    }
  });
  // Connect socket
  socket.on('connect', () => {
    if(is_enable){
      socket.emit('start_process', {'sid': sid, 'db': db, 'stype': server_type });
    }
    fitAddon.fit();
    socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
  });

  socket.on('conn_error', (response) => {
    term.write(response.error);
    fitAddon.fit();
    socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
  });

  socket.on('conn_not_allow', () => {
    term.write('PSQL connection not allowed');
    fitAddon.fit();
    socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
  });

  socket.on('disconnect-psql', () => {
    socket.emit('server-disconnect', {'sid': sid});
    term.write('\r\nServer disconnected, Connection terminated, To create new connection please open another psql tool.');
  });
}

function psql_terminal_io(term, socket, platform, pgAdmin) {
  // Listen key press event from terminal and emit socket event.
  term.attachCustomKeyEventHandler(e => {
    e.stopPropagation();
    if(e.type=='keydown' && (e.metaKey || e.ctrlKey) && (e.key == 'c' || e.key == 'C')) {
      let selected_text = term.getSelection();
      navigator.permissions.query({ name: 'clipboard-write' }).then(function(result) {
        if(result.state === 'granted' || result.state === 'prompt') {
          copyToClipboard(selected_text);
        } else {
          pgAdmin.Browser.notifier.alert(gettext('Clipboard write permission required'), gettext('To copy data from PSQL terminal, Clipboard write permission required.'));
        }
      });
    } else {
      self.pgAdmin.Browser.keyboardNavigation.triggerIframeEventsBroadcast(e,true);
    }

    return !(e.ctrlKey && platform == 'win32');
  });

  term.textarea.addEventListener('paste', function() {
    navigator.permissions.query({ name: 'clipboard-read' }).then(function(result) {
      if(result.state === 'granted' || result.state === 'prompt') {
        navigator.clipboard.readText().then( clipText => {
          let selected_text = clipText;
          if (selected_text.length > 0) {
            socket.emit('socket_input', {'input': selected_text, 'key_name': 'paste'});
          }
        });
      } else{
        pgAdmin.Browser.notifier.alert(gettext('Clipboard read permission required'), gettext('To paste data on the PSQL terminal, Clipboard read permission required.'));
      }
    });
  });

  term.onKey(function (ev) {
    socket.emit('socket_input', {'input': ev.key, 'key_name': ev.domEvent.code});
  });
}

function psql_Addon(term) {
  const fitAddon = new FitAddon();
  term.loadAddon(fitAddon);

  const webLinksAddon = new WebLinksAddon();
  term.loadAddon(webLinksAddon);

  const searchAddon = new SearchAddon();
  term.loadAddon(searchAddon);

  fitAddon.fit();
  term.resize(15, 50);
  fitAddon.fit();
  return fitAddon;
}

function psql_socket() {
  return io('/pty', {
    path: `${url_for('pgadmin.root')}/socket.io`,
    pingTimeout: 120000,
    pingInterval: 25000
  });
}

export default function  PsqlComponent({ params, pgAdmin }) {
  const theme = useTheme();
  const termRef = React.useRef(null);
  const containerRef = React.useRef(null);

  const initializePsqlTool = (params)=>{
    const term = new Terminal({
      cursorBlink: true,
      scrollback: 5000,
    });
    /* Addon for fitAddon, webLinkAddon, SearchAddon */
    const fitAddon = psql_Addon(term);
  
    term.open(containerRef.current);

    /*  Socket */
    const socket = psql_socket();
  
    psql_socket_io(socket, params.is_enable, params.sid, params.db, params.server_type, fitAddon, term, params.role);
    
    psql_terminal_io(term, socket, params.platform, pgAdmin);
  
    /*  Set terminal size */
    setTimeout(function(){
      socket.emit('resize', {'cols': term.cols, 'rows': term.rows});
    }, 1000);
    return [term, socket];
  };

  useEffect(()=>{
    const [term, socket] = initializePsqlTool(params);
    termRef.current = term;  

    termRef.current?.setOption('theme',  {
      background: '#ff0000'});

    return () => {
      term.dispose();
      socket.disconnect();
    };

  }, []);

  useEffect(()=>{
    let psqlTheme = {
      background: theme.palette.background.default,
      foreground: theme.palette.text.primary,
      cursor: theme.palette.text.primary,
      cursorAccent: theme.palette.text.primary,
      selection: theme.palette.primary.main
    };
    termRef.current?.setOption('theme', psqlTheme );
  },[theme]);


  return (
    <Box width="100%" height="100%" display="flex" flexDirection="column" flexGrow="1" tabIndex="0" ref={containerRef}>
    </Box>
  );
}

PsqlComponent.propTypes = {
  params:PropTypes.shape({
    is_enable: PropTypes.Boolean,
    sid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
    db: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
    server_type: PropTypes.string,
    role: PropTypes.string,
    platform: PropTypes.string
  }),
  pgAdmin: PropTypes.object.isRequired,
};