| | |
| | | } |
| | | }) |
| | | |
| | | /////// SEARCH //////////////////////////////////////////////////////////////// |
| | | |
| | | const searchLib = require("./lib/search") |
| | | searchLib.doIndex().catch(console.error) |
| | | fastify.get("/api/search/user", async function (req, res) { |
| | | console.log(req.query) |
| | | const search = req.query?.search |
| | | if (!search) { |
| | | return res.code(422).send({status: "error", msg: "no search"}) |
| | | } |
| | | else { |
| | | console.log(search) |
| | | const data = await searchLib.search(search) |
| | | return res.send(data) |
| | | } |
| | | }) |
| | | |
| | | fastify |
| | | /////// USER //////////////////////////////////////////////////////////////// |
| | | .get('/api/users', async function (req, res) { |
| | | const {offset, limit} = req.query |
| | | const users = await db.getUsers(offset, limit) |
| | | const {offset, limit, search} = req.query |
| | | const users = await db.getUsers(offset, limit, search) |
| | | return res.send(users) |
| | | }) |
| | | .get("/api/users/count", async function (req, res) { |
| | |
| | | } |
| | | }) |
| | | |
| | | /////// SEARCH //////////////////////////////////////////////////////////////// |
| | | |
| | | const searchLib = require("./lib/search") |
| | | searchLib.doIndex().catch(console.error) |
| | | fastify.get("/api/search/user", async function (req, res) { |
| | | console.log(req.query) |
| | | const search = req.query?.search |
| | | if (!search) { |
| | | return res.code(422).send({status: "error", msg: "no search"}) |
| | | } |
| | | else { |
| | | console.log(search) |
| | | const data = await searchLib.search(search) |
| | | return res.send(data) |
| | | } |
| | | }) |
| | | |
| | | /////// STATIC //////////////////////////////////////////////////////////////// |
| | | |
| | |
| | | const mysql = require("mysql2/promise") |
| | | |
| | | const searchLib = require("./search") |
| | | const {host, port, user, database, password} = require("./../settings").db |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | | |
| | | async function getUsers (offset = 0, limit = 10) { |
| | | limit = Number(limit) || 10 |
| | | offset = Number(offset) || 0 |
| | | function getUserSearchQuery (offset, limit, search) { |
| | | const sel = `usr_id, login, firstname, lastname, gender, email, institution, street, city, zipcode, country, department, active` |
| | | // TODO user defined fields |
| | | // TODO check args for SQL Injection |
| | | |
| | | const pool = await poolP |
| | | const [results, fields] = await pool.query( |
| | | `SELECT ${sel} |
| | | if (!search || search === '') { |
| | | return `SELECT ${sel} |
| | | FROM ${database}.usr_data AS ud |
| | | WHERE login REGEXP '^[0-9]+$' |
| | | LIMIT ${limit} |
| | | OFFSET ${offset} |
| | | ` |
| | | ) |
| | | const count = await getUserCount() |
| | | } |
| | | else { |
| | | const ids = searchLib.search(search) |
| | | if (!ids.length) return getUserSearchQuery(offset, limit) // nothing found |
| | | return `SELECT ${sel} |
| | | FROM ${database}.usr_data AS ud |
| | | WHERE login REGEXP '^[0-9]+$' |
| | | AND usr_id IN (${ids.join(",")}) |
| | | LIMIT ${limit} |
| | | OFFSET ${offset} |
| | | ` |
| | | } |
| | | } |
| | | |
| | | async function getUsers (offset = 0, limit = 10, search = null) { |
| | | console.log("++++++++++ get users", offset, limit, search) |
| | | limit = Number(limit) || 10 |
| | | offset = Number(offset) || 0 |
| | | // TODO check args for SQL Injection |
| | | |
| | | const pool = await poolP |
| | | |
| | | let userSearchQuery = getUserSearchQuery(offset, limit, search) |
| | | // console.log(userSearchQuery) |
| | | const [results, fields] = await pool.query(userSearchQuery) |
| | | const count = await getUserCount(offset, limit, search) |
| | | return { |
| | | total: count, |
| | | offset, limit, |
| | |
| | | } |
| | | } |
| | | |
| | | async function getUserCount () { |
| | | async function getUserCount (offset, limit, search) { |
| | | const pool = await poolP |
| | | const [results, fields] = await pool.query( |
| | | `SELECT COUNT(*) |
| | | FROM ${database}.usr_data AS ud |
| | | WHERE login REGEXP '^[0-9]+$'` |
| | | ) |
| | | return results[0]["COUNT(*)"] |
| | | // const q = getUserSearchQuery(offset, limit, search) |
| | | const q = getUserSearchQuery(0, 1000000, search) // hier darf kein Limit sein, offset=0 |
| | | const q2 = `SELECT COUNT(*) AS count |
| | | FROM (${q}) AS X` |
| | | const [results, fields] = await pool.query(q2) |
| | | return results[0].count |
| | | |
| | | // const [results, fields] = await pool.query( |
| | | // `SELECT COUNT(*) |
| | | // FROM ${database}.usr_data AS ud |
| | | // WHERE login REGEXP '^[0-9]+$'` |
| | | // ) |
| | | // return results[0]["COUNT(*)"] |
| | | } |
| | | |
| | | async function getUserByLogin (login) { |
| | |
| | | async function getKursItems2 (ref_id) { |
| | | const pool = await poolP |
| | | const q = ` |
| | | WITH RECURSIVE tree (parent_id, obj_id, ref_id, title, type ) AS ( |
| | | SELECT ci.parent_id, or2.obj_id , ci.obj_id as ref_id, od.title, od.type |
| | | WITH RECURSIVE tree (parent_id, obj_id, ref_id, title, type) AS (SELECT ci.parent_id, |
| | | or2.obj_id, |
| | | ci.obj_id as ref_id, |
| | | od.title, |
| | | od.type |
| | | FROM ${database}.crs_items ci |
| | | INNER JOIN ${database}.object_reference or2 ON or2.ref_id = ci.obj_id |
| | | INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id |
| | |
| | | |
| | | UNION ALL |
| | | |
| | | SELECT child.parent_id, or2.obj_id , child.obj_id as ref_id, od.title, od.type |
| | | SELECT child.parent_id, |
| | | or2.obj_id, |
| | | child.obj_id as ref_id, |
| | | od.title, |
| | | od.type |
| | | FROM ${database}.crs_items child |
| | | INNER JOIN ${database}.object_reference or2 ON or2.ref_id = child.obj_id |
| | | INNER JOIN ${database}.object_data od ON od.obj_id = or2.obj_id |
| | | JOIN tree ON child.parent_id = tree.ref_id |
| | | WHERE or2.deleted is NULL |
| | | ) |
| | | SELECT * FROM tree |
| | | WHERE or2.deleted is NULL) |
| | | SELECT * |
| | | FROM tree |
| | | ORDER BY tree.ref_id |
| | | ` |
| | | const [results] = await pool.query(q) |
| | |
| | | } |
| | | |
| | | function search (query) { |
| | | return idxUser.search(query) |
| | | const searchLimit = 10000 |
| | | return idxUser.search(query, searchLimit) |
| | | } |
| | | |
| | | function searchUsers (query, user) { |
| | |
| | | |
| | | <div class="pagination"> |
| | | <div class="pagination-buttons"> |
| | | |
| | | <input type="button" @click="goStart()" class="start" value="«"> |
| | | <input type="button" @click="goPrev()" class="prev" value="‹"> |
| | | <span class="current">{{ offset }} - {{ offset + limit }} / {{ total }}</span> |
| | |
| | | |
| | | /////// USER //////////////////////////////////////////////////////////////// |
| | | |
| | | export async function getUsers (offset, limit) { |
| | | const res = await fetch(`${apiBase}/users?offset=${offset}&limit=${limit}&token=${apiToken.value}`) |
| | | export async function getUsers (offset, limit, search) { |
| | | const res = await fetch(`${apiBase}/users?offset=${offset}&limit=${limit}&search=${search}&token=${apiToken.value}`) |
| | | return res.json() |
| | | } |
| | | |
| | |
| | | const res = await fetch(`${apiBase}/user/teilnahmen/${userId}?token=${apiToken.value}`) |
| | | return await res.json() |
| | | } |
| | | |
| | | /////// SEARCH //////////////////////////////////////////////////////////////// |
| | | |
| | | export async function searchUsers (query) { |
| | | const res = await fetch(`${apiBase}/search/user?search=${query}&token=${apiToken.value}`) |
| | | return await res.json() |
| | | } |
| | | |
| | |
| | | import {onMounted, reactive, ref} from "vue" |
| | | import {useRoute} from "vue-router" |
| | | import Pagination from "../components/Pagination.vue" |
| | | import {getUsers, routerBase} from "@/lib/api" |
| | | import {getUsers, routerBase, searchUsers} from "@/lib/api" |
| | | import {useRouteQuery} from '@vueuse/router' |
| | | import {onKeyStroke} from "@vueuse/core" |
| | | import {onKeyStroke, useDebounceFn} from "@vueuse/core" |
| | | |
| | | const route = useRoute() |
| | | const userId = route.params.userId |
| | |
| | | |
| | | onMounted(() => init(offset.value)) |
| | | |
| | | /////// SEARCH //////////////////////////////////////////////////////////////// |
| | | |
| | | const search = useRouteQuery("search", "",)// {transform: s => s.trim()}) |
| | | async function _doSearch (search) { |
| | | console.log("doSEarch", search) |
| | | return init(offset.value, search) |
| | | } |
| | | const doSearch = useDebounceFn(_doSearch, 500) |
| | | |
| | | ///////////////////////////////////////////////////////////////////////// |
| | | |
| | | async function init (noffset = 0) { |
| | | const data = await getUsers(noffset, limit) |
| | | async function init (noffset = 0, search) { |
| | | console.log(">>> INIT", noffset, search) |
| | | const data = await getUsers(noffset, limit, search) |
| | | console.log(data) |
| | | Object.assign(users, data) |
| | | offset.value = noffset |
| | |
| | | |
| | | function go (offset) { |
| | | console.log("go to offset", offset) |
| | | return init(offset) |
| | | return init(offset, search.value) |
| | | } |
| | | |
| | | |
| | | |
| | | </script> |
| | |
| | | <div class="users-header"> |
| | | <div class="search"> |
| | | Search |
| | | <input type="text" size="30"> |
| | | <input @keyup="doSearch(search)" v-model="search" type="text" size="30"> |
| | | </div> |
| | | <Pagination :offset="users.offset" :limit="users.limit" :total="users.total" @go="go" /> |
| | | </div> |