Para buscar una palabra en documentos PDF desde la terminal en Linux, puedes utilizar la herramienta pdfgrep[1][2][4]. Aquí te muestro algunos ejemplos de cómo usarla:
Buscar una palabra específica en un archivo PDF:
pdfgrep palabra archivo.pdf
Buscar una frase en un archivo PDF, ignorando mayúsculas y minúsculas y mostrando el número de línea:
pdfgrep -ni "frase a buscar" archivo.pdf
Buscar una palabra en varios archivos PDF del directorio actual:
pdfgrep -ni palabra *.pdf
Buscar recursivamente en todos los archivos PDF de un directorio y subdirectorios:
Contar cuántas veces aparece una palabra en varios archivos PDF:
pdfgrep -c palabra *.pdf
Algunas características adicionales de pdfgrep:
Es compatible con expresiones regulares para búsquedas más avanzadas
Permite excluir ciertos archivos de la búsqueda con --exclude
Tiene más opciones que puedes consultar con pdfgrep --help
Para instalar pdfgrep en Linux, está disponible en los repositorios de la mayoría de distribuciones[2]. Por ejemplo en Ubuntu/Debian:
sudo apt install pdfgrep
Y en Arch Linux:
sudo pacman -S pdfgrep
En resumen, pdfgrep es una herramienta muy útil para buscar texto en archivos PDF directamente desde la terminal en Linux, con opciones avanzadas y fácil de instalar.
Identificar el disco donde se quiere instalar el gestor de arranque GRUB2. Supongamos que es /dev/sdb.
Configurar GRUB2
Edita el archivo de configuración principal de GRUB2:
```sudo nvim /etc/default/grub
2. Modifica las siguientes opciones:
- `GRUB_DEFAULT=0`: Establece el índice de la entrada de arranque predeterminada (0 para la primera).
- `GRUB_TIMEOUT=5`: Establece el tiempo de espera en segundos antes de arrancar la entrada predeterminada.
- `GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/SuSE-release)"`: Establece el nombre del distribuidor.
- `GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"`: Establece los parámetros adicionales del kernel.
3. Guarda los cambios y cierra el editor.
## Detectar sistemas operativos
1. Ejecuta el siguiente comando para detectar los sistemas operativos instalados:
```sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Este comando generará automáticamente la configuración de GRUB2 basándose en los sistemas operativos detectados.
Instalar GRUB2 en el disco
Instala GRUB2 en el disco seleccionado (/dev/sdb en este ejemplo):
sudo grub2-install /dev/sda
Este comando instalará el gestor de arranque GRUB2 en el disco especificado.
Reiniciar y probar
Reinicia el sistema y deberías ver el menú de arranque de GRUB2 con las entradas para cada sistema operativo detectado.
Selecciona cada entrada para asegurarte de que los sistemas operativos arrancan correctamente.
Recuerda que si tienes problemas específicos con algún sistema operativo, puede ser necesario ajustar la configuración de GRUB2 manualmente editando el archivo /boot/grub2/grub.cfg. Sin embargo, este archivo se genera automáticamente, así que cualquier cambio manual se perderá después de ejecutar grub2-mkconfig.
Si necesitas hacer cambios permanentes, es mejor modificar el archivo /etc/default/grub y luego volver a generar la configuración con grub2-mkconfig.
Identificar el nombre del grupo de volúmenes (VG) que contiene el volumen lógico (LV) que deseas montar.
Listar los volúmenes lógicos:
Ejecutar el comando lvdisplay con el nombre del grupo de volúmenes (VG) para obtener la lista de volúmenes lógicos (LV) disponibles:
bash
$ sudo lvdisplay <nombre-del-grupo-de-volumenes>
$ sudo lvdisplay almalinux
LV Path /dev/almalinux/swap
LV Name swap
VG Name almalinux
LV UUID U6IxH2-WRlu-fe7s-PNSV-BenF-xmtG-7BCvhd
LV Write Access read/write
LV Creation host, time localhost.localdomain, 2024-06-13
21:00:45 +0200
LV Status available
LV Size 7,64 GiB
Current LE 1957
Segments 1
Allocation inherit
Read ahead sectors auto
currently set to 1024
Block device 254:0
--- Logical volume ---
LV Path /dev/almalinux/home
LV Name home
VG Name almalinux
LV UUID z8lIGf-pef1-87Zu-0Sec-T31N-Zb3e-
TQwGdq
LV Write Access read/write
LV Creation host, time localhost.localdomain, 2024-06-13 21:00:45 +0200
LV Status available
LV Size 144,34 GiB
Current LE 36950
Segments 1
Allocation inherit
Read ahead sectors auto
currently set to 1024
Block device 254:1
--- Logical volume ---
LV Path /dev/almalinux/root
LV Name root
VG Name almalinux
LV UUID fGyrxc-MnrI-T8hh-9iTk-fOO2-ObQg-tsINqj
LV Write Access read/write
LV Creation host, time localhost.localdomain, 2024-06-13 21:00:47 +0200
LV Status available
LV Size 70,00 GiB
Current LE 17920
Segments 1
Allocation inherit
Read ahead sectors auto
currently set to 1024
Block device 254:2
Identifica el nombre del volumen lógico (LV) que deseas montar.
Montar el volumen lógico:
Utiliza el comando mount con el nombre del volumen lógico (LV) y la ruta de montaje deseada:
bash
$ sudo mount /dev/<nombre-del-volumen-lógico> <ruta-de-montaje>
Por ejemplo:
bash
$ sudo mount /dev/vgtest/lvdata2 /mnt
En mi caso voy a montar la root
mount /dev/almalinux/root /mnt/almalinux/root
Pero tambien la home:
mount /dev/almalinux/root /mnt/almalinux/root
Verificar el estado del montaje:
Ejecuta el comando mount sin argumentos para verificar la lista de sistemas de archivos montados:
bash
$ mount
/dev/mapper/almalinux-root on /mnt/almalinux/root type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/mapper/almalinux-home on /mnt/almalinux/home type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
Estos pasos te permitirán montar un sistema de archivos LVM de manera efectiva
El primer objetivo es configurar una página web HTML sencilla que proporcione un formulario y una lista de mensajes. Vamos a utilizar el marco web Node.JS express con este fin. Asegurarse tener Node.JS esta instalado.
Primero creemos un package.json archivo de manifiesto que describe nuestro proyecto. Te recomiendo colocarlo en un directorio vacío dedicado (llamaré al mío) socket.io).
package.json
Express inicializa la app para que sea un controlador de funciones que puede proporcionar a un servidor HTTP (como se ve en la línea 5).
Definimos un controlador de ruta / al que se llama cuando llegamos a nuestro sitio web.
Hacemos que el servidor http escuche en el puerto 8080.
Si ejecuta node index.js, debería ver lo siguiente:
Obteniendo en el navegador...localhost:8080 y no el 3000
Sirviendo HTML
Hasta ahora en index.js llamamos a res.send y le pasamos una cadena de HTML. Nuestro código se vería muy confuso si simplemente colocáramos el HTML de toda nuestra aplicación allí, por lo que crearemos un fichero index.html y lo serviremos en su lugar.
Refactoricemos nuestro controlador de rutas para usar sendFile en su lugar.
Si se reinicia el proceso (al presionar Control+C y ejecutar node index.js nuevamente) y actualiza la página, debería verse así:
Integración de Socket.IO
Socket.IO se compone de dos partes:
-Un servidor que se integra (o se monta en) el servidor HTTP Node.JS (el paquete socket.io)
- Una biblioteca cliente que se carga en el lado del navegador (el paquete socket.io-client)
Durante el desarrollo, socket.io nos sirve el cliente automáticamente, como veremos, por lo que por ahora sólo tenemos que instalar un módulo:
npm install socket.io
Eso instalará el módulo y agregará la dependencia al paquete.json. Ahora editemos index.js para agregarlo:
Observe que inicializo una nueva instancia de socket.io pasando el objeto servidor (el servidor HTTP). Luego escucho el evento de conexión de los connection entrantes y lo registro en la consola.
Ahora en index.html agregue el siguiente fragmento antes de
(etiqueta final del cuerpo):
Eso es todo lo que se necesita para cargar el cliente socket.io, que expone un global io (y el punto final (endpoint) GET /socket.io/socket.io.js), y luego conectarse.
Si desea utilizar la versión local del archivo JS del lado del cliente, puede encontrarla en node_modules/socket.io/client-dist/socket.io.js.
Truco:
También puede utilizar un CDN en lugar de los archivos locales (e.g. ).
Note que no se esta especificando ninguna URL cuando se llamo a io(), ya que de forma predeterminada intenta conectarme al host que sirve a la página.
Si ahora reinicia el proceso (presionando Control+C y ejecutando node index.js nuevamente) y luego actualiza la página web, debería ver la consola imprimir “a user connected”.
Intenta abrir varias pestañas y verás varios mensajes.
Cada socket también activa un evento de desconexión especial:
Luego, si actualiza una pestaña varias veces, podrá verla en acción.
Emitir eventos
La idea principal detrás de Socket.IO es que puedes enviar y recibir cualquier evento que desees, con cualquier dato que desees. Cualquier objeto que pueda codificarse como lo hará JSON y también se admiten datos binarios.
Hagámoslo de modo que cuando el usuario escribe un mensaje, el servidor lo recibe como un evento de mensaje de chat. La sección de script en index.html ahora debería verse de la siguiente manera:
Y en index.js imprimimos el evento de mensaje de chat:
io.on('connection', (socket) => {
,socket.on('chat message', (msg) => {
enter code here
console.log('message: ' + msg); });
});
Broadcasting
El próximo objetivo es que emitamos el evento desde el servidor al resto de usuarios.
Para enviar un evento a todos, Socket.IO nos brinda el método io.emit().
// this will emit the event to all connected sockets
io.emit('hello', 'world');
Si desea enviar un mensaje a todos excepto a un determinado socket emisor, tenemos el indicador de transmisión para emitir desde ese socket:
Antes de continuar, hagamos un recorrido rápido por la API proporcionada por Socket.IO:
Common API
Los siguientes métodos están disponibles tanto para el cliente como para el servidor.
Basic emit
Como hemos visto en el paso 4, puedes enviar cualquier dato al otro lado con socket.emit():
Desde el Cliente al Servidor:
lado Cliente -> socket.emit('hello', 'world');
lado Servidor -> io.on('connection', (socket) => {
socket.on('hello', (arg) => {
console.log(arg); // 'world'
});
});
y a la inversa
Desde el Servidor al Cliente:
lado Servidor: io.on('connection', (socket) => {
socket.emit('hello', 'world');
});
lado Cliente: socket.on('hello', (arg) => {
console.log(arg); // 'world'
});
Puede enviar cualquier cantidad de argumentos y se admiten todas las estructuras de datos serializables, incluidos objetos binarios como ArrayBuffer, TypedArray o Buffer (solo Node.js):
Los eventos son geniales, pero en algunos casos es posible que desees una API de solicitud y respuesta más clásica. En Socket.IO, esta función se denomina "reconocimientos".
Viene en dos sabores:
Con función de devolución de llamada
Puede agregar una devolución de llamada como último argumento de la emisión (), y esta devolución de llamada se llamará una vez que la otra parte haya reconocido el evento:
From client to server
Client
socket.timeout(5000).emit('request', { foo: 'bar' }, 'baz', (err, response) => {
if (err) {
// the server did not acknowledge the event in the given delay
} else {
console.log(response.status); // 'ok'
}
});
Server:
io.on('connection', (socket) => {
socket.timeout(5000).emit('request', { foo: 'bar' }, 'baz', (err, response) => {
if (err) {
// the client did not acknowledge the event in the given delay
} else {
console.log(response.status); // 'ok'
}
});
});
El método emitWithAck() proporciona la misma funcionalidad, pero devuelve una promesa que se resolverá una vez que la otra parte reconozca el evento:
From client to server
Client:
try {
const response = await socket.timeout(5000).emitWithAck('request', { foo: 'bar' }, 'baz');
console.log(response.status); // 'ok'
} catch (e) {
// the server did not acknowledge the event in the given delay
}
Como hemos visto en el paso 5, puedes transmitir un evento a todos los clientes conectados con io.emit():
io.emit('hello', 'world');
Rooms
En la jerga de Socket.IO, una habitación (room) es un canal arbitrario al que los sockets pueden unirse y salir. Se puede utilizar para transmitir eventos a un subconjunto de clientes conectados:
¡Eso es básicamente todo! Para referencia futura, toda la API se puede encontrar aquí (servidor) y aquí (cliente).
Handling disconnections
Ahora, resaltemos dos propiedades realmente importantes de Socket.IO:
un cliente Socket.IO no siempre está conectado
un servidor Socket.IO no almacena ningún evento
Lo que significa que su aplicación debe poder sincronizar el estado local del cliente con el estado global del servidor después de una desconexión temporal.
Nota: Lo que significa que su aplicación debe poder sincronizar el estado local del cliente con el estado global del servidor después de una desconexión temporal.
En el contexto de nuestra aplicación de chat, esto implica que un cliente desconectado podría perderse algunos mensajes:
Veremos en los próximos pasos cómo podemos mejorar esto.
Recuperación del estado de conexión
Primero, manejemos las desconexiones fingiendo que no hubo desconexión: esta característica se llama "recuperación del estado de conexión" -> "Connection state recovery".
Esta función almacenará temporalmente todos los eventos que envía el servidor e intentará restaurar el estado de un cliente cuando se vuelva a conectar:
restaurar sus habitaciones
envía cualquier evento perdido
Debe estar habilitado en el lado del servidor:
Incorporar en el index.js:
const io = new Server(server, {
connectionStateRecovery: {}
});
Pero esta es una característica increíble, ¿por qué no está habilitada de forma predeterminada?
Hay varias razones para esto:
no siempre funciona; por ejemplo, si el servidor falla abruptamente o se reinicia, es posible que el estado del cliente no se guarde
no siempre es posible habilitar esta función al ampliarla
Truco:
Dicho esto, es realmente una gran característica ya que no es necesario sincronizar el estado del cliente después de una desconexión temporal (por ejemplo, cuando el usuario cambia de WiFi a 4G).
Exploraremos una solución más general en el siguiente paso.
Entrega del servidor
Hay dos formas comunes de sincronizar el estado del cliente tras la reconexión:
o el servidor envía el estado completo
o el cliente realiza un seguimiento del último evento que ha procesado y el servidor envía las piezas faltantes
Ambas son soluciones totalmente válidas y elegir una dependerá de su caso de uso. En este tutorial iremos con este último.
Primero, persistamos los mensajes de nuestra aplicación de chat. Hoy en día hay muchas opciones excelentes, usaremos SQLite aquí.
Instalemos los paquetes necesarios:
npm install sqlite sqlite3
Simplemente almacenaremos cada mensaje en una tabla SQL:
index.js:
const express = require('express');
const { createServer } = require('node:http');
const { join } = require('node:path');
const { Server } = require('socket.io');
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
async function main() {
// open the database file
const db = await open({
filename: 'chat.db',
driver: sqlite3.Database
});
// create our 'messages' table (you can ignore the 'client_offset' column for now)
await db.exec(`
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_offset TEXT UNIQUE,
content TEXT
);
`);
const app = express();
const server = createServer(app);
const io = new Server(server, {
connectionStateRecovery: {}
});
app.get('/', (req, res) => {
res.sendFile(join(__dirname, 'index.html'));
});
io.on('connection', (socket) => {
socket.on('chat message', async (msg) => {
let result;
try {
// store the message in the database
result = await db.run('INSERT INTO messages (content) VALUES (?)', msg);
} catch (e) {
// TODO handle the failure
return;
}
// include the offset with the message
io.emit('chat message', msg, result.lastID);
});
});
server.listen(3000, () => {
console.log('server running at http://localhost:3000');
});
}
main();
Luego, el cliente realizará un seguimiento del desplazamiento:
Como puede ver en el video de arriba, funciona tanto después de una desconexión temporal como de una actualización de página completa.
Truco:
La diferencia con la función "Recuperación del estado de conexión" es que es posible que una recuperación exitosa no necesite llegar a su base de datos principal (puede recuperar los mensajes de una secuencia Redis, por ejemplo).
Bien, ahora hablemos de la entrega al cliente (client delivery).
Client delivery
Veamos cómo podemos asegurarnos de que el servidor siempre reciba los mensajes enviados por los clientes.
INFO:
De forma predeterminada, Socket.IO
proporciona una garantía de entrega "como máximo una vez" (también conocida como "disparar y olvidar"), lo que significa que no habrá reintento en caso de que el mensaje no llegue al servidor.
Buffered events
Cuando se desconecta un cliente, cualquier llamada a socket.emit() se almacena en búfer hasta la reconexión:
Buffered events
En el vídeo de arriba, el mensaje "en tiempo real" se almacena en el búfer hasta que se restablece la conexión.
Este comportamiento podría ser totalmente suficiente para su aplicación. Sin embargo, hay algunos casos en los que se podría perder un mensaje:
la conexión se corta mientras se envía el evento
el servidor falla o se reinicia mientras se procesa - el evento
la base de datos no está disponible temporalmente
At least once
Podemos implementar una garantía de "al menos una vez:
manualmente con acuse de recibo:
function emit(socket, event, arg) {
socket.timeout(5000).emit(event, arg, (err) => {
if (err) {
// no ack from the server, let's retry
emit(socket, event, arg);
}
});
}
En ambos casos, el cliente volverá a intentar enviar el mensaje hasta obtener un acuse de recibo del servidor:
io.on('connection', (socket) => {
socket.on('hello', (value, callback) => {
// once the event is successfully handled
callback();
});
})
Truco:
Con la opción de reintentos (retries) se garantiza el orden de los mensajes, ya que los mensajes se ponen en cola y se envían uno por uno. Este no es el caso de la primera opción.
Exactly once
El problema con los reintentos es que el servidor ahora podría recibir el mismo mensaje varias veces, por lo que necesita una forma de identificar de forma única cada mensaje y almacenarlo solo una vez en la base de datos.
Veamos cómo podemos implementar una garantía "exactamente una vez" en nuestra aplicación de chat.
Comenzaremos asignando un identificador único a cada mensaje del lado del cliente:
Nota:
El atributo socket.id es un identificador aleatorio de 20 caracteres que se asigna a cada conexión.
También podríamos haber usado getRandomValues() para generar un desplazamiento único.
Y luego almacenamos este desplazamiento junto con el mensaje en el lado del servidor:
index.js
// [...]
io.on('connection', async (socket) => {
socket.on('chat message', async (msg, clientOffset, callback) => {
let result;
try {
result = await db.run('INSERT INTO messages (content, client_offset) VALUES (?, ?)', msg, clientOffset);
} catch (e) {
if (e.errno === 19 /* SQLITE_CONSTRAINT */ ) {
// the message was already inserted, so we notify the client
callback();
} else {
// nothing to do, just let the client retry
}
return;
}
io.emit('chat message', msg, result.lastID);
// acknowledge the event
callback();
});
if (!socket.recovered) {
try {
await db.each('SELECT id, content FROM messages WHERE id > ?',
[socket.handshake.auth.serverOffset || 0],
(_err, row) => {
socket.emit('chat message', row.content, row.id);
}
)
} catch (e) {
// something went wrong
}
}
});
// [...]
De esta manera, la restricción UNIQUE en la columna cliente_offset evita la duplicación del mensaje.
Precaucion:
No olvides reconocer el evento, de lo contrario el cliente seguirá intentándolo (hasta tiempos de reintento).
Nuevamente, la garantía predeterminada ("como máximo una vez") -> ("at most once") puede ser suficiente para su aplicación, pero ahora sabe cómo hacerla más confiable.
Scaling horizontally (Escalado horizontalmente)
Ahora que nuestra aplicación es resistente a interrupciones temporales de la red, veamos cómo podemos escalarla horizontalmente para poder admitir miles de clientes simultáneos.
Nota:
- El escalado horizontal (también conocido como "escalado") significa agregar nuevos servidores a su infraestructura para hacer frente a nuevas demandas
- El escalado vertical (también conocido como "escalado") significa agregar más recursos (potencia de procesamiento, memoria, almacenamiento, ...) a su infraestructura existente
Primer paso: usemos todos los núcleos disponibles del host. De forma predeterminada, Node.js ejecuta su código Javascript en un solo subproceso, lo que significa que incluso con una CPU de 32 núcleos, solo se utilizará un núcleo. Afortunadamente, el módulo de clúster Node.js proporciona una manera conveniente de crear un subproceso de trabajador por núcleo.
También necesitaremos una forma de reenviar eventos entre los servidores Socket.IO. A este componente lo llamamos "Adaptador".
Entonces, instalemos el adaptador del clúster:
npm install @socket.io/cluster-adapter
Ahora lo conectamos:
index.js
const express = require('express');
const { createServer } = require('node:http');
const { join } = require('node:path');
const { Server } = require('socket.io');
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
const { availableParallelism } = require('node:os');
const cluster = require('node:cluster');
const { createAdapter, setupPrimary } = require('@socket.io/cluster-adapter');
if (cluster.isPrimary) {
const numCPUs = availableParallelism();
// create one worker per available core
for (let i = 0; i < numCPUs; i++) {
cluster.fork({
PORT: 3000 + i
});
}
// set up the adapter on the primary thread
return setupPrimary();
}
async function main() {
const app = express();
const server = createServer(app);
const io = new Server(server, {
connectionStateRecovery: {},
// set up the adapter on each worker thread
adapter: createAdapter()
});
// [...]
// each worker will listen on a distinct port
const port = process.env.PORT;
server.listen(port, () => {
console.log(`server running at http://localhost:${port}`);
});
}
main();
¡Eso es todo! Esto generará un subproceso de trabajador por CPU disponible en su máquina. Veámoslo en acción:
Como puede ver en la barra de direcciones, cada pestaña del navegador está conectada a un servidor Socket.IO diferente y el adaptador simplemente reenvía los eventos del mensaje de chat entre ellos.
Truco:
Actualmente existen 5 implementaciones oficiales de adaptadores:
el adaptador Redis
el adaptador Redis Streams
el adaptador MongoDB
el adaptador Postgres
el adaptador de clúster
Para que puedas elegir el que mejor se adapta a tus necesidades. Sin embargo, tenga en cuenta que algunas implementaciones no admiten la función de recuperación del estado de conexión; puede encontrar la matriz de compatibilidad aquí.
Nota:
en la mayoría de los casos, también deberá asegurarse de que todas las solicitudes HTTP de una sesión de Socket.IO lleguen al mismo servidor (también conocido como "sesión adhesiva"). Sin embargo, esto no es necesario aquí, ya que cada servidor Socket.IO tiene su propio puerto.
¡Y eso finalmente completa nuestra aplicación de chat! En este tutorial hemos visto cómo:
enviar un evento entre el cliente y el servidor
transmitir un evento a todos o a un subconjunto de clientes conectados
manejar desconexiones temporales
ampliar
Ahora debería tener una mejor descripción general de las funciones proporcionadas por Socket.IO. ¡Ahora es su momento de crear su propia aplicación en tiempo real!
Notas finales
Código final del servidor
index.js
const express = require('express');
const { createServer } = require('node:http');
const { join } = require('node:path');
const { Server } = require('socket.io');
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
const { availableParallelism } = require('node:os');
const cluster = require('node:cluster');
const { createAdapter, setupPrimary } = require('@socket.io/cluster-adapter');
if (cluster.isPrimary) {
const numCPUs = availableParallelism();
for (let i = 0; i < numCPUs; i++) {
cluster.fork({
PORT: 3000 + i
});
}
return setupPrimary();
}
async function main() {
const db = await open({
filename: 'chat.db',
driver: sqlite3.Database
});
await db.exec(`
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_offset TEXT UNIQUE,
content TEXT
);
`);
const app = express();
const server = createServer(app);
const io = new Server(server, {
connectionStateRecovery: {},
adapter: createAdapter()
});
app.get('/', (req, res) => {
res.sendFile(join(__dirname, 'index.html'));
});
io.on('connection', async (socket) => {
socket.on('chat message', async (msg, clientOffset, callback) => {
let result;
try {
result = await db.run('INSERT INTO messages (content, client_offset) VALUES (?, ?)', msg, clientOffset);
} catch (e) {
if (e.errno === 19 /* SQLITE_CONSTRAINT */ ) {
callback();
} else {
// nothing to do, just let the client retry
}
return;
}
io.emit('chat message', msg, result.lastID);
callback();
});
if (!socket.recovered) {
try {
await db.each('SELECT id, content FROM messages WHERE id > ?',
[socket.handshake.auth.serverOffset || 0],
(_err, row) => {
socket.emit('chat message', row.content, row.id);
}
)
} catch (e) {
// something went wrong
}
}
});
const port = process.env.PORT;
server.listen(port, () => {
console.log(`server running at http://localhost:${port}`);
});
}
main();
Un grupo y un círculo son dos conceptos relacionados con la gestión de usuarios y permisos, pero con funciones y propósitos ligeramente diferentes:
En resumen, usar grupos es preferible cuando se requiere una gestión de usuarios y permisos más estructurada y ampliamente integrada en Nextcloud, mientras que los círculos son más apropiados para colaboraciones informales con opciones de configuración más flexibles pero limitadas a la aplicación de Contactos
Grupos
Funcionalidad: Un grupo es una estructura de organización para usuarios en Nextcloud. Permite asignar permisos y derechos a varios usuarios de manera colectiva.
Uso: Los grupos son útiles para simular departamentos o equipos de trabajo, permitiendo la gestión de permisos y acceso a recursos compartidos de manera eficiente.
Creación: Los grupos se crean en la sección de usuarios del panel de administración de Nextcloud.
Círculos
Funcionalidad: Un círculo es una característica de la aplicación de Contactos en Nextcloud. Es una forma de colaboración informal que permite a los usuarios crear y gestionar grupos de contactos, compartir archivos y carpetas, y participar en conversaciones de Talk.
Uso: Los círculos son ideales para colaboraciones informales, como eventos, sesiones de ideas o talleres, donde se requiere una estructura más flexible que los grupos tradicionales.
Creación: Los círculos se crean en la aplicación de Contactos, donde se pueden agregar miembros, configurar roles y opciones de visibilidad.
En resumen, los grupos son estructuras de organización para usuarios con permisos y derechos definidos, mientras que los círculos son grupos de colaboración informales con roles y opciones de configuración específicas para compartir archivos y carpetas.
Tendremos que descargarnos ambos el Fide-Fada y el FADA, y guardandolos con el Mes en curso y FIDE-FADA o FADA. Hay que tener cuidado porque uno sobreescribiria el otro y son distintos.
Son xls y abrirlo con LibreOffice Calc
Seleccionaremos Guardar como ...
Formato csv
y como ejemplo: Elo-Fide-Fada-05-2024.csv
Nos saldra en que formato guardarlo, odc o csv y elegiremos CSV.
Nos saldra un cuadro de dialogo indicando el Delimitador de campos: y pondremos ;
En el delimitador de cadena, lo dejamos vacio sin ningun espacio dentro..
Y le damos as Aceptar
Una vez guardado nos vamos a Vega y en la sección inferior donde pone archivo. Elegimos que es un Archivo Vega y buscamos el archivo que hicimos anterior.
Para comprobar que esta bien hecho, abrimos o creamos un Torneo e para añadir jugadores abajo a la derecha hay un campo de búsqueda, y con solo tre letras ya aparecen jugadores, señalamos uno, o bien pulsamos enter en ese jugador y se debería añadir con todos sus datos.
Si hemos convertido ambos ficheros anteriores con FADA -FIDE y FADA, lo tenemos tener separados y comprobar que en uno solo añade FADA y en otro ambos.