feat: posimai-dev — aurora terminal, systemd service, atlas sync, master-architecture update

This commit is contained in:
posimai 2026-03-31 00:25:44 +09:00
parent f38b76a9e9
commit b61831d3a2
4 changed files with 175 additions and 2 deletions

View File

@ -37,6 +37,9 @@
║ diff / clean / timer / digest / think / site ║
║ events / maps / tech-events / analytics / roadmap ║
║ ║
║ 【セルフホスト】posimai-devUbuntu PC / Tailscale
║ https://ubuntu-pc-pc-mkm21cz79ys4.tail72e846.ts.net:3333║
║ ║
║ 【計画中】*.posimai.soar-enrich.com ワイルドカード DNS ║
║ → Passkey の rpID 問題を解決・Eiji に依頼予定 ║
╚══════════════════════════════════════════════════════════╝

View File

@ -36,9 +36,11 @@
:root {
--accent: #A78BFA;
--accent-dim: rgba(167, 139, 250, 0.15);
--dev-bg: #0C1221;
}
[data-theme="light"] {
--accent: #7C3AED;
--dev-bg: #0C1221;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
@ -48,7 +50,7 @@
flex-direction: column;
height: 100dvh;
overflow: hidden;
background: var(--bg);
background: var(--dev-bg);
}
.header {
@ -107,6 +109,10 @@
overflow: hidden;
padding: 12px;
min-height: 0;
background:
radial-gradient(ellipse at 15% 60%, rgba(34, 211, 238, 0.07) 0%, transparent 55%),
radial-gradient(ellipse at 85% 25%, rgba(167, 139, 250, 0.07) 0%, transparent 55%),
var(--dev-bg);
}
#terminal-container .xterm {
@ -184,8 +190,9 @@
fontSize: 14,
lineHeight: 1.4,
cursorBlink: true,
allowTransparency: true,
theme: {
background: '#0D0D0D',
background: 'rgba(12, 18, 33, 0.0)',
foreground: '#F3F4F6',
cursor: '#A78BFA',
selectionBackground: 'rgba(167, 139, 250, 0.3)',

View File

@ -0,0 +1,15 @@
[Unit]
Description=posimai-dev portal
After=network.target tailscaled.service
[Service]
Type=simple
User=ubuntu-pc
WorkingDirectory=/home/ubuntu-pc/posimai-project/posimai-dev
ExecStart=/home/ubuntu-pc/.npm-global/bin/node server.js
Restart=on-failure
RestartSec=5
Environment=PATH=/home/ubuntu-pc/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
[Install]
WantedBy=multi-user.target

148
server.js
View File

@ -2141,6 +2141,154 @@ ${excerpt}
}
});
// ── Atlas: GitHub scan proxy ───────────────────────────────────
r.get('/atlas/github-scan', (req, res) => {
const token = req.query.token;
const org = req.query.org || '';
if (!token) return res.status(400).json({ error: 'token required' });
const https = require('https');
function ghRequest(path, cb) {
const options = {
hostname: 'api.github.com',
path,
method: 'GET',
family: 4,
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/vnd.github+json',
'User-Agent': 'Posimai-Atlas/1.0',
'X-GitHub-Api-Version': '2022-11-28',
},
timeout: 12000,
};
const r2 = https.request(options, (resp) => {
let body = '';
resp.on('data', chunk => { body += chunk; });
resp.on('end', () => cb(null, resp.statusCode, body));
});
r2.on('timeout', () => { r2.destroy(); cb(new Error('Timeout')); });
r2.on('error', cb);
r2.end();
}
const orgPath = org ? `/orgs/${encodeURIComponent(org)}/repos?per_page=100&sort=updated` : null;
const userPath = `/user/repos?per_page=100&sort=updated&affiliation=owner`;
function handleResult(status, body) {
if (status !== 200) return res.status(status).json({ error: body });
try { res.json(JSON.parse(body)); }
catch (e) { res.status(500).json({ error: 'Invalid JSON' }); }
}
if (orgPath) {
ghRequest(orgPath, (err, status, body) => {
if (err) return res.status(500).json({ error: err.message });
// If org not accessible, fall back to user repos
if (status === 404 || status === 403) {
ghRequest(userPath, (err2, status2, body2) => {
if (err2) return res.status(500).json({ error: err2.message });
// Signal to client that we fell back
if (status2 === 200) {
try {
const data = JSON.parse(body2);
return res.json({ repos: data, fallback: true });
} catch (e) { return res.status(500).json({ error: 'Invalid JSON' }); }
}
handleResult(status2, body2);
});
} else {
handleResult(status, body);
}
});
} else {
ghRequest(userPath, (err, status, body) => {
if (err) return res.status(500).json({ error: err.message });
handleResult(status, body);
});
}
});
// ── Atlas: Vercel scan proxy ───────────────────────────────────
r.get('/atlas/vercel-scan', (req, res) => {
const token = req.query.token;
if (!token) return res.status(400).json({ error: 'token required' });
const https = require('https');
const options = {
hostname: 'api.vercel.com',
path: '/v9/projects?limit=100',
method: 'GET',
family: 4,
headers: {
Authorization: `Bearer ${token}`,
'User-Agent': 'Posimai-Atlas/1.0',
},
timeout: 12000,
};
const req2 = https.request(options, (r2) => {
let body = '';
r2.on('data', chunk => { body += chunk; });
r2.on('end', () => {
if (r2.statusCode !== 200) return res.status(r2.statusCode).json({ error: body });
try { res.json(JSON.parse(body)); }
catch (e) { res.status(500).json({ error: 'Invalid JSON' }); }
});
});
req2.on('timeout', () => { req2.destroy(); res.status(500).json({ error: 'Timeout' }); });
req2.on('error', (e) => { res.status(500).json({ error: e.message, code: e.code }); });
req2.end();
});
// ── Atlas: Tailscale scan proxy ────────────────────────────────
r.get('/atlas/tailscale-scan', (req, res) => {
const token = req.query.token;
if (!token) return res.status(400).json({ error: 'token required' });
const https = require('https');
const options = {
hostname: 'api.tailscale.com',
path: '/api/v2/tailnet/-/devices',
method: 'GET',
family: 4, // force IPv4; container IPv6 to tailscale times out
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'User-Agent': 'Posimai-Atlas/1.0',
},
timeout: 12000,
};
const req2 = https.request(options, (r2) => {
let body = '';
r2.on('data', chunk => { body += chunk; });
r2.on('end', () => {
if (r2.statusCode !== 200) {
return res.status(r2.statusCode).json({ error: body });
}
try {
res.json(JSON.parse(body));
} catch (e) {
res.status(500).json({ error: 'Invalid JSON from Tailscale' });
}
});
});
req2.on('timeout', () => {
req2.destroy();
res.status(500).json({ error: 'Request timed out' });
});
req2.on('error', (e) => {
console.error('[atlas/tailscale-scan] error:', e.code, e.message);
res.status(500).json({ error: e.message, code: e.code });
});
req2.end();
});
return r;
}