Tabla de Contenidos
Memos
Características
- Centrada en privacidad. Todos los datos se almacenan en un fichero SQLite que te pertenece.
- Notas públicas o privadas.
- Texto plano con Markdown.
- Soporte de varios idiomas (Inglés, español, francés, italiano…)
- Ligero, pero potente. La aplicación se compone de Go + React.js + SQLite (en últimas versiones ya soporta MySQL y PostgreSQL)
- Personalizable. Puedes configurar el nombre del servidor, icono, descripción…
- Multi usuario
- Open source.
- Gratis para siempre.
- Publicación desde Telegram
- API RESTful para integración de terceros
- Diseñado para autoalojarlo con Docker fácilmente.
Instalación
Docker
Ejemplo para Docker Compose:
--- version: '3.0' services: memos: image: neosmemo/memos:stable container_name: memos volumes: - ./app/:/var/opt/memos ports: - 5230:5230
Configuración
La primera vez que accedemos podremos crear una cuenta que será la de administración.
Telegram
memos permite enviar mensajes desde un bot de Telegram y se publicará en nuestra instancia de memos.
Debemos crearnos un bot y poner su token en Settings → Sistema → Telegram Bot Token
Luego debemos indicar cuál es nuestro chat ID (nuestro identificador de usuario en Telegram) en Settings → Preferencias → Telegram UserID.
A partir de ese momento, todos los mensajes que le enviemos a nuestro bot, serán publicadas como notas privadas en nuestra instancia de memos.
Personalización de estilos
Desde Configuración → Sistema → Estilos adicionales, podemos añadir reglas CSS para personalizar la apariencia de nuestra instancia de Memos. Por ejemplo:
p { margin-bottom: 1rem !important; line-heigth: 1.5; } ul.list-inside { margin-left: 1rem; margin-bottom: 1rem!important; } br { display: none; } .text-5xl { font-size: 1.5rem; } .text-3xl { font-size: 1.3rem; } .text-xl { font-size: 1.1rem; } .text-xl span { font-weight: bold; } h1 { margin-bottom: 1rem !important; } h2 { border-bottom: 1px solid #d7d7d7; margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; padding-bottom: 1rem; } h3 { padding-top: 0.5rem; } .leading-normal { line-height: 1.1; }
Soporte Markdown y otros
Memos soporta sintaxis Markdown y más para los siguientes elementos:
- Encabezados
- Negrita
- Cursiva / itálica
- Citas
- Listas ordenadas
- Listas desordenadas
- Código / texto preformateado
- Regla horizontal
- Enlaces
- Imágenes
- Bloques de código
- Tachado
- Lista de tareas
- Código HTML personalizado
- Tablas
Otros:
- Diagramas mermaid
- LaTeX
- Subíndice y superíndice (como en H₂O y 5⁵)
- Spoiler
Estructura de la base de datos
SQLite
-- drop all tables first DROP TABLE IF EXISTS migration_history; DROP TABLE IF EXISTS system_setting; DROP TABLE IF EXISTS user; DROP TABLE IF EXISTS user_setting; DROP TABLE IF EXISTS memo; DROP TABLE IF EXISTS memo_organizer; DROP TABLE IF EXISTS memo_relation; DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS tag; DROP TABLE IF EXISTS activity; DROP TABLE IF EXISTS storage; DROP TABLE IF EXISTS idp; DROP TABLE IF EXISTS inbox; DROP TABLE IF EXISTS webhook; -- migration_history CREATE TABLE migration_history ( version TEXT NOT NULL PRIMARY KEY, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')) ); -- system_setting CREATE TABLE system_setting ( name TEXT NOT NULL, value TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', UNIQUE(name) ); -- user CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL', username TEXT NOT NULL UNIQUE, role TEXT NOT NULL CHECK (role IN ('HOST', 'ADMIN', 'USER')) DEFAULT 'USER', email TEXT NOT NULL DEFAULT '', nickname TEXT NOT NULL DEFAULT '', password_hash TEXT NOT NULL, avatar_url TEXT NOT NULL DEFAULT '' ); CREATE INDEX idx_user_username ON user (username); -- user_setting CREATE TABLE user_setting ( user_id INTEGER NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, UNIQUE(user_id, key) ); -- memo CREATE TABLE memo ( id INTEGER PRIMARY KEY AUTOINCREMENT, creator_id INTEGER NOT NULL, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL', content TEXT NOT NULL DEFAULT '', visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE' ); CREATE INDEX idx_memo_creator_id ON memo (creator_id); CREATE INDEX idx_memo_content ON memo (content); CREATE INDEX idx_memo_visibility ON memo (visibility); -- memo_organizer CREATE TABLE memo_organizer ( memo_id INTEGER NOT NULL, user_id INTEGER NOT NULL, pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0, UNIQUE(memo_id, user_id) ); -- memo_relation CREATE TABLE memo_relation ( memo_id INTEGER NOT NULL, related_memo_id INTEGER NOT NULL, type TEXT NOT NULL, UNIQUE(memo_id, related_memo_id, type) ); -- resource CREATE TABLE resource ( id INTEGER PRIMARY KEY AUTOINCREMENT, creator_id INTEGER NOT NULL, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), filename TEXT NOT NULL DEFAULT '', blob BLOB DEFAULT NULL, external_link TEXT NOT NULL DEFAULT '', type TEXT NOT NULL DEFAULT '', size INTEGER NOT NULL DEFAULT 0, internal_path TEXT NOT NULL DEFAULT '', memo_id INTEGER ); CREATE INDEX idx_resource_creator_id ON resource (creator_id); CREATE INDEX idx_resource_memo_id ON resource (memo_id); -- tag CREATE TABLE tag ( name TEXT NOT NULL, creator_id INTEGER NOT NULL, UNIQUE(name, creator_id) ); -- activity CREATE TABLE activity ( id INTEGER PRIMARY KEY AUTOINCREMENT, creator_id INTEGER NOT NULL, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), type TEXT NOT NULL DEFAULT '', level TEXT NOT NULL CHECK (level IN ('INFO', 'WARN', 'ERROR')) DEFAULT 'INFO', payload TEXT NOT NULL DEFAULT '{}' ); -- storage CREATE TABLE storage ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, type TEXT NOT NULL, config TEXT NOT NULL DEFAULT '{}' ); -- idp CREATE TABLE idp ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, type TEXT NOT NULL, identifier_filter TEXT NOT NULL DEFAULT '', config TEXT NOT NULL DEFAULT '{}' ); -- inbox CREATE TABLE inbox ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), sender_id INTEGER NOT NULL, receiver_id INTEGER NOT NULL, status TEXT NOT NULL, message TEXT NOT NULL DEFAULT '{}' ); -- webhook CREATE TABLE webhook ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL', creator_id INTEGER NOT NULL, name TEXT NOT NULL, url TEXT NOT NULL ); CREATE INDEX idx_webhook_creator_id ON webhook (creator_id);

