[mod] merge lsp-web-poc
This commit is contained in:
parent
368930cbf2
commit
1c758567d4
18 changed files with 1243 additions and 2 deletions
56
webapp/lsp/lsp-util.xqm
Normal file
56
webapp/lsp/lsp-util.xqm
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
(:~
|
||||
: Simple WebSocket chat. Utility functions.
|
||||
: @author BaseX Team, BSD License
|
||||
:)
|
||||
module namespace chat-util = 'chat/util';
|
||||
|
||||
import module namespace session = 'http://basex.org/modules/session';
|
||||
import module namespace ws = 'http://basex.org/modules/ws';
|
||||
|
||||
(:~ User id (bound to sessions and WebSockets). :)
|
||||
declare variable $chat-util:id := 'id';
|
||||
|
||||
(:~
|
||||
: Sends a users list (all, active) to all registered clients.
|
||||
:)
|
||||
declare function chat-util:users() as empty-sequence() {
|
||||
ws:emit({
|
||||
'type': 'users',
|
||||
'users': array { sort(user:list()) },
|
||||
'active': array { distinct-values(
|
||||
for $id in ws:ids()
|
||||
return ws:get($id, $chat-util:id)
|
||||
)}
|
||||
})
|
||||
};
|
||||
|
||||
(:~
|
||||
: Sends a message to all clients, or to the clients of a specific user.
|
||||
: @param $text text to be sent
|
||||
: @param $to receiver of a private message (optional)
|
||||
:)
|
||||
declare function chat-util:message(
|
||||
$text as xs:string,
|
||||
$to as xs:string?
|
||||
) as empty-sequence() {
|
||||
let $ws-ids := ws:ids()[not($to) or ws:get(., $chat-util:id) = $to]
|
||||
return ws:send({
|
||||
'type': 'message',
|
||||
'text': serialize($text),
|
||||
'from': ws:get(ws:id(), $chat-util:id),
|
||||
'date': format-time(current-time(), '[H02]:[m02]:[s02]'),
|
||||
'private': boolean($to)
|
||||
}, $ws-ids)
|
||||
};
|
||||
|
||||
(:~
|
||||
: Closes all WebSocket connections from the specified user.
|
||||
: @param $name username
|
||||
:)
|
||||
declare function chat-util:close(
|
||||
$name as xs:string
|
||||
) as empty-sequence() {
|
||||
for $id in ws:ids()
|
||||
where ws:get($id, $chat-util:id) = $name
|
||||
return ws:close($id)
|
||||
};
|
||||
46
webapp/lsp/lsp-ws.xqm
Normal file
46
webapp/lsp/lsp-ws.xqm
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
(:~
|
||||
: Simple WebSocket chat. WebSocket functions.
|
||||
: @author BaseX Team, BSD License
|
||||
:)
|
||||
module namespace chat-ws = 'chat-ws';
|
||||
|
||||
import module namespace chat-util = 'chat/util' at 'lsp-util.xqm';
|
||||
import module namespace request = "http://exquery.org/ns/request";
|
||||
|
||||
(:~
|
||||
: Creates a WebSocket connection. Registers the user and notifies all clients.
|
||||
:)
|
||||
declare
|
||||
%ws:connect('/chat')
|
||||
function chat-ws:connect() as empty-sequence() {
|
||||
ws:set(ws:id(), $chat-util:id, session:get($chat-util:id)),
|
||||
chat-util:users()
|
||||
};
|
||||
|
||||
(:~
|
||||
: Processes a WebSocket message.
|
||||
: @param $message message
|
||||
:)
|
||||
declare
|
||||
%ws:message('/chat', '{$message}')
|
||||
function chat-ws:message(
|
||||
$message as xs:string
|
||||
) as empty-sequence() {
|
||||
let $json := parse-json($message)
|
||||
let $type := $json?type
|
||||
return if($type = 'message') then (
|
||||
chat-util:message($json?text, $json?to)
|
||||
) else if($type = 'ping') then(
|
||||
(: do nothing :)
|
||||
) else error()
|
||||
};
|
||||
|
||||
(:~
|
||||
: Closes a WebSocket connection. Unregisters the user and notifies all clients.
|
||||
:)
|
||||
declare
|
||||
%ws:close('/chat')
|
||||
function chat-ws:close() as empty-sequence() {
|
||||
ws:delete(ws:id(), $chat-util:id),
|
||||
chat-util:users()
|
||||
};
|
||||
144
webapp/lsp/lsp.xqm
Normal file
144
webapp/lsp/lsp.xqm
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
(:~
|
||||
: Simple WebSocket chat. RESTXQ functions.
|
||||
: @author BaseX Team, BSD License
|
||||
:)
|
||||
module namespace chat = 'chat';
|
||||
|
||||
import module namespace chat-util = 'chat/util' at 'lsp-util.xqm';
|
||||
|
||||
(:~
|
||||
: Login or main page.
|
||||
: @return HTML page
|
||||
:)
|
||||
declare
|
||||
%rest:path('/lsp')
|
||||
%output:method('html')
|
||||
function chat:chat() as element() {
|
||||
if(session:get($chat-util:id)) then (
|
||||
chat:main()
|
||||
) else (
|
||||
chat:login()
|
||||
)
|
||||
};
|
||||
|
||||
(:~
|
||||
: Checks the user input, registers the user and reloads the chat.
|
||||
: @param $name username
|
||||
: @param $pass password
|
||||
: @return redirection
|
||||
:)
|
||||
declare
|
||||
%rest:POST
|
||||
%rest:path('/lsp/login-check')
|
||||
%rest:form-param('name', '{$name}')
|
||||
%rest:form-param('pass', '{$pass}')
|
||||
function chat:login-check(
|
||||
$name as xs:string,
|
||||
$pass as xs:string
|
||||
) as element(rest:response) {
|
||||
try {
|
||||
user:check($name, $pass),
|
||||
session:set($chat-util:id, $name)
|
||||
} catch user:* {
|
||||
(: login fails: no session info is set :)
|
||||
},
|
||||
web:redirect('/chat')
|
||||
};
|
||||
|
||||
(:~
|
||||
: Logs out the current user, notifies all WebSocket clients, and redirects to the login page.
|
||||
: @return redirection
|
||||
:)
|
||||
declare
|
||||
%rest:path('/lsp/logout')
|
||||
function chat:logout() as element(rest:response) {
|
||||
session:get($chat-util:id) ! chat-util:close(.),
|
||||
session:delete($chat-util:id),
|
||||
web:redirect('/chat')
|
||||
};
|
||||
|
||||
(:~
|
||||
: Returns the HTML login page.
|
||||
: @return HTML page
|
||||
:)
|
||||
declare %private function chat:login() as element(html) {
|
||||
chat:wrap((
|
||||
<div class='warning'>Please enter your credentials:</div>,
|
||||
<form action='/chat/login-check' method='post'>
|
||||
<div class='small'/>
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>Name:</b></td>
|
||||
<td>
|
||||
<input size='30' name='name' id='user' autofocus=''/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Password:</b></td>
|
||||
<td>{
|
||||
<input size='30' type='password' name='pass'/>,
|
||||
<button type='submit'>Login</button>
|
||||
}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
), ())
|
||||
};
|
||||
|
||||
(:~
|
||||
: Returns the HTML main page.
|
||||
: @return HTML page
|
||||
:)
|
||||
declare %private function chat:main() as element(html) {
|
||||
chat:wrap((
|
||||
<p>
|
||||
<input type='text' size='60' autofocus='true' placeholder='Message to all users…'
|
||||
id='input' onkeydown='keyDown(event)' autocomplete='off'/>
|
||||
</p>,
|
||||
<table width='100%'>
|
||||
<tr>
|
||||
<td width='100'>
|
||||
<div class='note'>USERS (<b>online</b>)</div>
|
||||
<div id='users'/>
|
||||
</td>
|
||||
<td class='vertical'/>
|
||||
<td>
|
||||
<div class='note'>CHAT MESSAGES</div>
|
||||
<div id='messages'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
), <script type='text/javascript' src='/static/chat.js'/>)
|
||||
};
|
||||
|
||||
(:~
|
||||
: Returns an HTML page.
|
||||
: @param $contents page contents
|
||||
: @param $login login page
|
||||
: @return HTML page
|
||||
:)
|
||||
declare %private function chat:wrap(
|
||||
$contents as item()*,
|
||||
$headers as element()*
|
||||
) as element(html) {
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>BaseX WebSocket Chat</title>
|
||||
<meta name='author' content='BaseX Team, BSD License'/>
|
||||
<link rel='stylesheet' type='text/css' href='/static/style.css'/>
|
||||
{ $headers }
|
||||
</head>
|
||||
<body>
|
||||
<span class='right'>
|
||||
{
|
||||
for $id in session:get($chat-util:id)
|
||||
return <span><b>{ $id }</b> (<a href='/chat/logout'>logout</a>)</span>
|
||||
}
|
||||
  <a href='/'><img src='static/basex.svg' class='img'/></a>
|
||||
</span>
|
||||
<h1>BaseX WebSocket Chat</h1>
|
||||
{ $contents }
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue