agentgate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -0
- package/package.json +63 -0
- package/public/favicon.svg +48 -0
- package/public/icons/bluesky.svg +1 -0
- package/public/icons/fitbit.svg +16 -0
- package/public/icons/github.svg +1 -0
- package/public/icons/google-calendar.svg +1 -0
- package/public/icons/jira.svg +1 -0
- package/public/icons/linkedin.svg +1 -0
- package/public/icons/mastodon.svg +1 -0
- package/public/icons/reddit.svg +1 -0
- package/public/icons/youtube.svg +1 -0
- package/public/logo.svg +52 -0
- package/public/style.css +584 -0
- package/src/cli.js +77 -0
- package/src/index.js +344 -0
- package/src/lib/db.js +325 -0
- package/src/lib/hsyncManager.js +57 -0
- package/src/lib/queueExecutor.js +362 -0
- package/src/routes/bluesky.js +130 -0
- package/src/routes/calendar.js +120 -0
- package/src/routes/fitbit.js +127 -0
- package/src/routes/github.js +72 -0
- package/src/routes/jira.js +77 -0
- package/src/routes/linkedin.js +137 -0
- package/src/routes/mastodon.js +91 -0
- package/src/routes/queue.js +186 -0
- package/src/routes/reddit.js +138 -0
- package/src/routes/ui/bluesky.js +66 -0
- package/src/routes/ui/calendar.js +120 -0
- package/src/routes/ui/fitbit.js +122 -0
- package/src/routes/ui/github.js +60 -0
- package/src/routes/ui/index.js +35 -0
- package/src/routes/ui/jira.js +72 -0
- package/src/routes/ui/linkedin.js +120 -0
- package/src/routes/ui/mastodon.js +140 -0
- package/src/routes/ui/reddit.js +120 -0
- package/src/routes/ui/youtube.js +120 -0
- package/src/routes/ui.js +1077 -0
- package/src/routes/youtube.js +119 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getAccountCredentials, setAccountCredentials } from '../lib/db.js';
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
const YOUTUBE_API = 'https://www.googleapis.com/youtube/v3';
|
|
6
|
+
const GOOGLE_AUTH = 'https://oauth2.googleapis.com';
|
|
7
|
+
|
|
8
|
+
// Service metadata - exported for /api/readme and /api/skill
|
|
9
|
+
export const serviceInfo = {
|
|
10
|
+
key: 'youtube',
|
|
11
|
+
name: 'YouTube',
|
|
12
|
+
shortDesc: 'Channels, videos, subscriptions',
|
|
13
|
+
description: 'YouTube Data API proxy',
|
|
14
|
+
authType: 'oauth',
|
|
15
|
+
docs: 'https://developers.google.com/youtube/v3/docs',
|
|
16
|
+
examples: [
|
|
17
|
+
'GET /api/youtube/{accountName}/channels?part=snippet,statistics&mine=true',
|
|
18
|
+
'GET /api/youtube/{accountName}/videos?part=snippet,statistics&myRating=like',
|
|
19
|
+
'GET /api/youtube/{accountName}/subscriptions?part=snippet&mine=true'
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Get a valid access token, refreshing if needed
|
|
24
|
+
async function getAccessToken(accountName) {
|
|
25
|
+
const creds = getAccountCredentials('youtube', accountName);
|
|
26
|
+
if (!creds) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// If we have an access token and it's not expired, use it
|
|
31
|
+
if (creds.accessToken && creds.expiresAt && Date.now() < creds.expiresAt) {
|
|
32
|
+
return creds.accessToken;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Need to refresh the token
|
|
36
|
+
if (!creds.refreshToken) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch(`${GOOGLE_AUTH}/token`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
45
|
+
},
|
|
46
|
+
body: new URLSearchParams({
|
|
47
|
+
client_id: creds.clientId,
|
|
48
|
+
client_secret: creds.clientSecret,
|
|
49
|
+
refresh_token: creds.refreshToken,
|
|
50
|
+
grant_type: 'refresh_token'
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
console.error('YouTube token refresh failed:', await response.text());
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const tokens = await response.json();
|
|
60
|
+
|
|
61
|
+
// Store the new tokens
|
|
62
|
+
setAccountCredentials('youtube', accountName, {
|
|
63
|
+
...creds,
|
|
64
|
+
accessToken: tokens.access_token,
|
|
65
|
+
expiresAt: Date.now() + (tokens.expires_in * 1000) - 60000 // 1 min buffer
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return tokens.access_token;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('YouTube token refresh failed:', error);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Proxy GET requests to YouTube API
|
|
76
|
+
// Route: /api/youtube/:accountName/*
|
|
77
|
+
router.get('/:accountName/*', async (req, res) => {
|
|
78
|
+
try {
|
|
79
|
+
const { accountName } = req.params;
|
|
80
|
+
const accessToken = await getAccessToken(accountName);
|
|
81
|
+
if (!accessToken) {
|
|
82
|
+
return res.status(401).json({
|
|
83
|
+
error: 'YouTube account not configured',
|
|
84
|
+
message: `Set up YouTube account "${accountName}" in the admin UI`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const path = req.params[0] || '';
|
|
89
|
+
const queryString = new URLSearchParams(req.query).toString();
|
|
90
|
+
const url = `${YOUTUBE_API}/${path}${queryString ? '?' + queryString : ''}`;
|
|
91
|
+
|
|
92
|
+
const response = await fetch(url, {
|
|
93
|
+
headers: {
|
|
94
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
95
|
+
'Accept': 'application/json'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const data = await response.json();
|
|
100
|
+
res.status(response.status).json(data);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
res.status(500).json({ error: 'YouTube API request failed', message: error.message });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Handle root path for account
|
|
107
|
+
router.get('/:accountName', async (req, res) => {
|
|
108
|
+
res.json({
|
|
109
|
+
service: 'youtube',
|
|
110
|
+
account: req.params.accountName,
|
|
111
|
+
description: 'YouTube Data API proxy. Append API path after account name.',
|
|
112
|
+
examples: [
|
|
113
|
+
`GET /api/youtube/${req.params.accountName}/channels?part=snippet,statistics&mine=true`,
|
|
114
|
+
`GET /api/youtube/${req.params.accountName}/playlists?part=snippet&mine=true`
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export default router;
|