create initial api (add, list endpoints) - NOT SECURE
This commit is contained in:
parent
6f05cfc340
commit
edc18c2c18
4
.gitignore
vendored
4
.gitignore
vendored
@ -130,3 +130,7 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# extra
|
||||
.idea/
|
||||
bun.lockb
|
||||
|
||||
|
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM oven/bun:latest
|
||||
WORKDIR /app
|
||||
COPY package*.json /app/
|
||||
RUN bun install
|
||||
COPY . /app
|
||||
EXPOSE 3000
|
||||
CMD [ "bun", "run", "server.js" ]
|
72
README.md
72
README.md
@ -1,3 +1,73 @@
|
||||
# mail-connect
|
||||
|
||||
API bridge for docker-mailserver
|
||||
API bridge for docker-mailserver
|
||||
|
||||
*mail-connect is still in early beta*
|
||||
|
||||
## What is it
|
||||
|
||||
mail-connect aims to connect your `docker-mailserver` to *anything* you can imagine, through the power of an API. Despite being used as a core component of LibreCloud, you can still implement mail-connect any way you wish!
|
||||
|
||||
We provide an extendable API which interacts with the `setup` utility via a Docker socket. While this offers advantages, mail-connect is still slow on some functions (such as listing accounts), as it's merely executing pre-made commands and parsing the output.
|
||||
|
||||
## Features
|
||||
|
||||
All features marked with an **E** are extended features, and are not a part of the original `setup` utility.
|
||||
|
||||
### Email
|
||||
|
||||
- [X] Create email
|
||||
- [X] List emails
|
||||
- [ ] **E** Create email from file
|
||||
- [ ] Change password
|
||||
- [ ] Delete email
|
||||
- [ ] Restrict email
|
||||
|
||||
### Alias
|
||||
|
||||
- [ ] Create alias
|
||||
- [ ] List aliases
|
||||
- [ ] Delete alias
|
||||
|
||||
### Quotas
|
||||
|
||||
- [ ] Set quota
|
||||
- [ ] Delete quota
|
||||
|
||||
### dovecot-master
|
||||
|
||||
- [ ] Add
|
||||
- [ ] Update
|
||||
- [ ] Delete
|
||||
- [ ] List
|
||||
|
||||
### Config
|
||||
|
||||
- [ ] DKIM
|
||||
|
||||
### Relay
|
||||
|
||||
- [ ] Add auth
|
||||
- [ ] Add domain
|
||||
- [ ] Exclude domain
|
||||
|
||||
### Fail2Ban
|
||||
|
||||
- [ ] Ban IP
|
||||
- [ ] Unban IP
|
||||
- [ ] Ban log
|
||||
- [ ] Fail2Ban status
|
||||
|
||||
### Debug
|
||||
|
||||
- [ ] Fetchmail
|
||||
- [ ] Login
|
||||
- [ ] Show mail logs
|
||||
|
||||
## Future Improvements
|
||||
|
||||
I plan to implement a *much* more powerful API, when everything else has settled. I will be taking a look at the setup utility itself, and seeing how a more efficient approach can be taken.
|
||||
|
||||
Since `docker-mailserver` is built on Dovecot and Postfix, I am confident we can improve this API to be speedy and efficient as ever.
|
||||
|
||||
## To-Do
|
||||
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
||||
services:
|
||||
mail-connect:
|
||||
build: .
|
||||
container_name: mail-connect
|
||||
ports:
|
||||
- "6723:3000"
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
27
jsconfig.json
Normal file
27
jsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "mail-connect",
|
||||
"module": "server.js",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"child_process": "^1.0.2",
|
||||
"dockerode": "^4.0.4",
|
||||
"express": "^4.21.2"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"protobufjs"
|
||||
]
|
||||
}
|
108
server.js
Normal file
108
server.js
Normal file
@ -0,0 +1,108 @@
|
||||
const express = require('express');
|
||||
const Docker = require('dockerode');
|
||||
|
||||
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
function listAccounts() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const container = docker.getContainer('mailserver');
|
||||
container.exec({
|
||||
Cmd: ['setup', 'email', 'list'],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
}, (err, exec) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
exec.start((err, stream) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let output = '';
|
||||
stream.on('data', (chunk) => {
|
||||
output += chunk.toString();
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
// Remove control characters
|
||||
const cleanOutput = output.replace(/[\u0000-\u001F]+/g, '');
|
||||
const regex = /\*\s*(\S+)\s*\(\s*([^\s\/]+)\s*\/\s*([^)]+)\s*\)\s*\[(\d+)%]/g;
|
||||
const accounts = [];
|
||||
|
||||
for (const match of cleanOutput.matchAll(regex)) {
|
||||
accounts.push({
|
||||
email: match[1],
|
||||
used: match[2].trim() === '~' ? 'Unlimited' : match[2].trim(),
|
||||
capacity: match[3].trim() === '~' ? 'Unlimited' : match[3].trim(),
|
||||
percentage: match[4]
|
||||
});
|
||||
}
|
||||
|
||||
resolve(accounts);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/list', (req, res) => {
|
||||
listAccounts()
|
||||
.then(accounts => res.json({ accounts }))
|
||||
.catch(err => res.status(500).json({ error: err.message }));
|
||||
});
|
||||
|
||||
app.post('/add', (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({ error: "Email and password are required." });
|
||||
}
|
||||
|
||||
const container = docker.getContainer('mailserver');
|
||||
container.exec({
|
||||
Cmd: ['setup', 'email', 'add', email, password],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
}, (err, exec) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: err.message });
|
||||
}
|
||||
|
||||
exec.start((err, stream) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: err.message });
|
||||
}
|
||||
|
||||
let output = '';
|
||||
stream.on('data', (chunk) => {
|
||||
output += chunk.toString();
|
||||
});
|
||||
|
||||
stream.on('end', async () => {
|
||||
const cleanOutput = output.replace(/[\u0000-\u001F]+/g, '').trim();
|
||||
if (cleanOutput === '') {
|
||||
return res.json({ success: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const accounts = await listAccounts();
|
||||
const accountFound = accounts.find(acc => acc.email === email);
|
||||
if (accountFound) {
|
||||
return res.json({ success: true });
|
||||
} else {
|
||||
return res.json({ success: false, message: "Account creation failed" });
|
||||
}
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log(`API listening on port 3000`);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user