Add multi-user support via token-based routing and users.json config
Each user gets a unique secret token embedded in their webhook URL (/webhook/:token). Credentials are loaded from users.json at startup instead of environment variables, making it easy to add or remove users without redeploying. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
902cd59d11
commit
68be646f37
4 files changed
+86
-22
No files matched your search
@@ -1,2 +1,3 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
.env
|
.env
|
||||||
|
users.json
|
||||||
@@ -1,27 +1,65 @@
|
|||||||
# AudioPen Nextcloud Webhook
|
# AudioPen Nextcloud Webhook
|
||||||
|
|
||||||
A small Node.js webhook service that receives notes from AudioPen and uploads them to a folder in Nextcloud using WebDAV.
|
A small Node.js webhook service that receives notes from AudioPen and uploads them to a folder in Nextcloud using WebDAV. Supports multiple users, each with their own Nextcloud credentials and a unique webhook token.
|
||||||
|
|
||||||
When AudioPen sends a webhook request, the service:
|
When AudioPen sends a webhook request, the service:
|
||||||
|
|
||||||
1. Receives the note content
|
1. Validates the token in the URL
|
||||||
2. Creates a timestamped Markdown file
|
2. Receives the note content
|
||||||
3. Uploads the file to the configured Nextcloud folder
|
3. Creates a timestamped Markdown file
|
||||||
|
4. Uploads the file to the configured Nextcloud folder
|
||||||
|
|
||||||
Example filename:
|
Example filename:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
note-2026-05-19T22-15-10.123Z.md
|
2026-05-19T22-15-10.123Z-my-note-title.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example `.env`
|
## Setup
|
||||||
|
|
||||||
Create a file named `.env` in the project root directory:
|
### 1. Configure users
|
||||||
|
|
||||||
|
Copy `users.json.example` to `users.json` and fill in credentials for each user:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"abc123secrettoken": {
|
||||||
|
"nextcloudUrl": "https://nextcloud.example.com",
|
||||||
|
"username": "alice",
|
||||||
|
"password": "alice-app-password",
|
||||||
|
"folder": "AudioPen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each key is a secret token that identifies a user. Generate one with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -e "console.log(crypto.randomUUID())"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure the server
|
||||||
|
|
||||||
|
Optionally create a `.env` file to set the port or a custom path to `users.json`:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
NEXTCLOUD_URL=https://nextcloud.example.com
|
|
||||||
NEXTCLOUD_USERNAME=myuser
|
|
||||||
NEXTCLOUD_PASSWORD=my-app-password
|
|
||||||
NEXTCLOUD_FOLDER=AudioPen
|
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
USERS_FILE=users.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3. Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
node server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure AudioPen
|
||||||
|
|
||||||
|
In AudioPen's webhook settings, set the URL to:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://your-server/webhook/<token>
|
||||||
|
```
|
||||||
|
|
||||||
|
Each user gets their own URL containing their unique token.
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
@@ -10,14 +11,24 @@ app.use(express.text({ type: '*/*' }))
|
|||||||
|
|
||||||
const PORT = process.env.PORT || 3000
|
const PORT = process.env.PORT || 3000
|
||||||
|
|
||||||
const NEXTCLOUD_URL = process.env.NEXTCLOUD_URL
|
const USERS_FILE = process.env.USERS_FILE || 'users.json'
|
||||||
const NEXTCLOUD_USERNAME = process.env.NEXTCLOUD_USERNAME
|
let users = {}
|
||||||
const NEXTCLOUD_PASSWORD = process.env.NEXTCLOUD_PASSWORD
|
try {
|
||||||
const NEXTCLOUD_FOLDER = process.env.NEXTCLOUD_FOLDER || 'AudioPen'
|
users = JSON.parse(readFileSync(USERS_FILE, 'utf8'))
|
||||||
|
console.log(`Loaded ${Object.keys(users).length} user(s) from ${USERS_FILE}`)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to load ${USERS_FILE}: ${err.message}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post('/webhook/:token', async (req, res) => {
|
||||||
|
const user = users[req.params.token]
|
||||||
|
if (!user) {
|
||||||
|
return res.status(401).json({ error: 'Invalid token' })
|
||||||
|
}
|
||||||
|
|
||||||
app.post('/webhook', async (req, res) => {
|
|
||||||
try {
|
try {
|
||||||
console.log('Received webhook')
|
console.log(`Received webhook for user: ${user.username}`)
|
||||||
|
|
||||||
console.log('HEADERS:', req.headers)
|
console.log('HEADERS:', req.headers)
|
||||||
console.log('RAW BODY:', req.body)
|
console.log('RAW BODY:', req.body)
|
||||||
@@ -63,16 +74,16 @@ ${transcript}
|
|||||||
`
|
`
|
||||||
|
|
||||||
const webdavUrl =
|
const webdavUrl =
|
||||||
`${process.env.NEXTCLOUD_URL}` +
|
`${user.nextcloudUrl}` +
|
||||||
`/remote.php/dav/files/` +
|
`/remote.php/dav/files/` +
|
||||||
`${process.env.NEXTCLOUD_USERNAME}/` +
|
`${user.username}/` +
|
||||||
`${process.env.NEXTCLOUD_FOLDER}/` +
|
`${user.folder}/` +
|
||||||
`${filename}`
|
`${filename}`
|
||||||
|
|
||||||
await axios.put(webdavUrl, markdown, {
|
await axios.put(webdavUrl, markdown, {
|
||||||
auth: {
|
auth: {
|
||||||
username: process.env.NEXTCLOUD_USERNAME,
|
username: user.username,
|
||||||
password: process.env.NEXTCLOUD_PASSWORD
|
password: user.password
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/markdown'
|
'Content-Type': 'text/markdown'
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"abc123secrettoken": {
|
||||||
|
"nextcloudUrl": "https://nextcloud.example.com",
|
||||||
|
"username": "alice",
|
||||||
|
"password": "alice-app-password",
|
||||||
|
"folder": "AudioPen"
|
||||||
|
},
|
||||||
|
"xyz789anothertoken": {
|
||||||
|
"nextcloudUrl": "https://nextcloud.example.com",
|
||||||
|
"username": "bob",
|
||||||
|
"password": "bob-app-password",
|
||||||
|
"folder": "AudioPen"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user