Instalar a edição Node
A edição Node corre como um serviço permanente: um processo, uma base SQLite no disco, nada mais para instalar — nem base de dados externa, nem cache, nem fila. Este tutorial vai da pasta vazia à instância em produção, incluindo o que a maioria dos guias deixa de fora: o serviço do sistema, o proxy inverso, o TLS, as cópias de segurança e a atualização.
O que é preciso#
| Elemento | Versão | Porquê |
|---|---|---|
| Node.js | 22 ou mais recente | O produto apoia-se no seu executor de testes integrado e em APIs recentes. |
| Um compilador C | build-essential | O better-sqlite3 compila-se na instalação, salvo se existir um binário pronto para a sua plataforma. |
| Um disco local | — | O SQLite e os ficheiros depositados. Nunca uma partilha de rede: veja mais abaixo. |
Uma máquina de um núcleo e 512 MB de memória chega para algumas dezenas de pessoas. O que conta não é a potência mas o disco: tem de ser local e ter cópia de segurança.
1. Obter e instalar#
git clone <o-seu-repositório> /var/www/toutadmin
cd /var/www/toutadmin
npm ci --omit=dev
npm ci em vez de npm install: instala exatamente o que o ficheiro
de bloqueio descreve, sem nunca o reescrever. Num servidor, uma versão que escorrega é uma
avaria que ninguém percebe.
O better-sqlite3 é a única dependência que compila C.
sudo apt install -y build-essential python3 resolve a quase totalidade dos
casos.
2. Configurar#
cp .env.example .env
Tudo se define por variáveis de ambiente — lidas de .env, ou postas pelo seu
gestor de serviços. Nenhuma é obrigatória: sem .env, a instância arranca na
porta 3000 e envia-o ao assistente.
| Variável | Predefinição | O que faz |
|---|---|---|
PORT | 3000 | A porta de escuta. |
NODE_ENV | — | production em produção: cookies estritos, sem rastos detalhados. |
SESSION_SECRET | gerado | Sela as sessões. Deixado vazio, é gerado em data/session.key. |
INSTALL_TOKEN | — | Reclamado pelo assistente antes de instalar. Recomendado num servidor exposto. |
TRUST_PROXY | — | 1 atrás de um proxy de confiança, e só aí. |
DB_PATH | data/app.sqlite | A base. |
UPLOAD_DIR, CV_DIR, VAULT_DIR, SIGN_DIR, DOCS_DIR, BACKUP_DIR | sob data/ | As pastas de ficheiros: fotografias, currículos, cofre, livro de assinaturas, documentos recebidos, arquivos. |
LOGIN_RATE_LIMIT | 10 | Tentativas de acesso por quarto de hora e por endereço. |
GLOBAL_RATE_LIMIT | 300 | Pedidos por minuto e por endereço. |
API_RATE_LIMIT | — | Chamadas de API por minuto e por token. |
SESSION_IDLE_MINUTES | 60 | Inatividade a partir da qual a sessão cai. |
SESSION_MAX_HOURS | 12 | Duração absoluta que nenhuma atividade prolonga. |
ADMIN_EMAIL, ADMIN_PASSWORD | — | Instalação sem interface: cria o administrador no arranque. |
Mudar SESSION_SECRET desliga toda a gente de uma vez. Ponha-o uma vez, gere-o
ao acaso
(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"), e
guarde-o com o resto. Nunca o deixe num repositório git.
Pôr um símbolo de instalação#
Entre o primeiro arranque e a sua passagem pelo assistente, a instância é de quem a
encontrar: o primeiro a chegar cria a conta de administração. Ponha, pois,
INSTALL_TOKEN antes de abrir a porta:
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
# depois em .env:
INSTALL_TOKEN=c3f1…
O assistente compara-o em tempo constante — uma comparação vulgar deixaria adivinhar o símbolo carácter a carácter — e regista cada recusa. Uma vez instalada a instância, o assistente fecha-se sozinho.
Instalar sem interface#
Para uma implantação automatizada, ADMIN_EMAIL e ADMIN_PASSWORD
criam o administrador no arranque, sem passar pelo assistente. Retire-os depois: uma
palavra-passe no ambiente de um serviço é lida por quem lê esse serviço.
3. Permissões e onde vivem os dados#
sudo useradd --system --home /var/www/toutadmin --shell /usr/sbin/nologin toutadmin
sudo chown -R toutadmin:toutadmin /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/data
sudo chmod 640 /var/www/toutadmin/.env
O código pode ficar apenas de leitura; só data/ tem de ser gravável. O SQLite
escreve ficheiros vizinhos (-wal, -shm): o que conta é a
pasta, não apenas a base.
NFS e SMB mentem quanto ao bloqueio de ficheiros. O SQLite conta com esse bloqueio para impedir duas escritas em simultâneo: numa partilha, a base acaba por se corromper sem avisar. Disco local, sempre — e é a cópia de segurança que parte para outro lado, não a base.
4. O serviço#
Lançado à mão, o produto para quando fecha o terminal. Confie-o ao systemd:
# /etc/systemd/system/toutadmin.service
[Unit]
Description=Toutadmin
After=network.target
[Service]
Type=simple
User=toutadmin
WorkingDirectory=/var/www/toutadmin
EnvironmentFile=/var/www/toutadmin/.env
ExecStart=/usr/bin/node src/server.js
Restart=always
RestartSec=5
# O serviço só precisa de escrever em data/.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/toutadmin/data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now toutadmin
sudo systemctl status toutadmin
journalctl -u toutadmin -f
As cinco linhas de endurecimento não são decorativas: ProtectSystem=strict
torna todo o sistema de ficheiros não gravável, e ReadWritePaths reabre a única
pasta que o tem de ser. Uma falha de escrita arbitrária atinge então apenas
data/.
5. O proxy inverso#
Nunca sirva a porta 3000 diretamente na internet: não faz TLS e não tem razão para aprender a fazê-lo.
server {
listen 443 ssl http2;
server_name intranet.exemplo.pt;
ssl_certificate /etc/letsencrypt/live/intranet.exemplo.pt/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/intranet.exemplo.pt/privkey.pem;
client_max_body_size 20M; # depósitos no cofre, documentos recebidos
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name intranet.exemplo.pt;
return 301 https://$host$request_uri;
}
Faça também com que o serviço escute apenas no anel local, para que a porta 3000 só seja alcançável pelo proxy.
TLS#
sudo certbot --nginx -d intranet.exemplo.pt
O produto põe o cabeçalho Strict-Transport-Security assim que a ligação está
cifrada, e nunca em claro: anunciado a partir de uma página não cifrada não seria lido, e
prenderia um ensaio local em https durante seis meses.
TRUST_PROXY anda com o proxy, e só com ele
Esta variável faz o servidor acreditar no endereço de origem anunciado num cabeçalho.
Atrás do nginx é o que é preciso: senão todos os pedidos parecem vir de
127.0.0.1 e os limites por endereço deixam de proteger seja o que for. Sem
proxy à frente é o contrário: qualquer um anuncia o endereço que quiser e contorna os
limites.
6. O assistente#
Abra o seu domínio. Enquanto não existir conta nenhuma, todo o endereço reencaminha para
/installation. Cinco passos:
- O idioma da instância, entre os 16.
- Os pré-requisitos, verificados e mostrados.
- A empresa: o nome mostrado em toda a parte.
- As férias anuais atribuídas a cada novo trabalhador não independente.
- A conta de administração: endereço e palavra-passe de pelo menos doze caracteres.
Logo que exista uma conta, /installation reencaminha para a página de acesso: o
assistente fechou-se sozinho, não há ficheiro nenhum para apagar à mão.
7. A varredura periódica#
Ao contrário da edição PHP, não há nada a agendar: o servidor traz o seu próprio agendador, que acorda de hora a hora e faz oito coisas.
| O que a varredura faz | Garantia |
|---|---|
| Fecha as contas cujo contrato chegou ao fim | Verificado também no acesso e ao abrir o painel |
| Emite as faturas das subscrições vencidas | Um índice único impede faturar duas vezes |
| Transforma os prazos em notificações | Uma chave de desduplicação: um prazo avisa uma vez |
| Limpa as notificações lidas e o registo de auditoria | Conforme a conservação escolhida |
| Recolhe a caixa de correio contabilística (IMAP) | Se a recolha estiver configurada |
| Esvazia a fila dos webhooks, com as suas repetições | Cinco tentativas, depois desistência |
| Cria a cópia automática e externaliza-a | Quando o intervalo decorreu |
Cada operação é idempotente: uma varredura repetida não fatura duas vezes nem notifica duas vezes. É isso que permite reiniciar o serviço a qualquer momento sem pensar nisso.
8. Verificar que tudo funciona#
# os testes (não escrevem na sua base)
npm test
# uma instância de demonstração, para dar uma volta
node scripts/seed-demo.js
Depois, na interface: crie um membro, entre com ele, deposite um anexo, lance uma cópia manual e verifique-a no ecrã Cópias de segurança. Estes quatro gestos tocam na base, nos ficheiros, nas permissões e no arquivo.
9. Atualizar#
# 1. uma cópia de segurança primeiro, sempre — no ecrã Cópias de segurança
# 2. o código
cd /var/www/toutadmin
git pull
npm ci --omit=dev
# 3. reiniciar; a base atualiza-se sozinha no arranque
sudo systemctl restart toutadmin
journalctl -u toutadmin -n 30 --no-pager
O esquema evolui por migrações idempotentes: repetir a atualização não parte nada.
data/ e .env nunca são tocados.
Voltar atrás#
Reponha o código na versão anterior e reinicie. As migrações não apagam colunas: uma base migrada continua legível pela versão anterior, salvo menção explícita no registo de versões. Na dúvida, reponha o arquivo tirado no passo 1.
10. Fazer cópias a sério#
O arquivo tar.gz leva a base e as cinco pastas de ficheiros. A base é copiada
pela cópia em linha do SQLite, que produz uma cópia coerente mesmo durante uma escrita — a
edição PHP obtém o mesmo resultado por VACUUM INTO. Cada ficheiro leva o seu
resumo SHA-256, verificado na reposição.
Um arquivo que fica no servidor que protege não protege nada: configure um destino remoto
(FTPS ou Google Drive) no ecrã Cópias de segurança, e subscreva um webhook a
sauvegarde.echec para ser avisado quando a externalização falha. O pormenor
está na página Cópias de segurança e reposição.
Experimentar primeiro localmente#
npm install
cp .env.example .env
npm run dev # reinicia a cada alteração
Abra http://localhost:3000. Localmente, deixe NODE_ENV vazio: em
production, os cookies são marcados «seguros» e não serão conservados numa
ligação em claro — andaria às voltas na página de acesso.
Passar à edição PHP, ou vir dela#
As duas edições partilham o mesmo esquema — 142 tabelas,
1311 colunas — e o mesmo formato de palavra-passe. Pare uma, copie
app.sqlite e as pastas de ficheiros, arranque a outra: não há conversão. Veja
As duas edições.
Documentação do Toutadmin — construída em 2026-09-13. Site autónomo, independente do software.