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.
Files changed (40) hide show
  1. package/README.md +216 -0
  2. package/package.json +63 -0
  3. package/public/favicon.svg +48 -0
  4. package/public/icons/bluesky.svg +1 -0
  5. package/public/icons/fitbit.svg +16 -0
  6. package/public/icons/github.svg +1 -0
  7. package/public/icons/google-calendar.svg +1 -0
  8. package/public/icons/jira.svg +1 -0
  9. package/public/icons/linkedin.svg +1 -0
  10. package/public/icons/mastodon.svg +1 -0
  11. package/public/icons/reddit.svg +1 -0
  12. package/public/icons/youtube.svg +1 -0
  13. package/public/logo.svg +52 -0
  14. package/public/style.css +584 -0
  15. package/src/cli.js +77 -0
  16. package/src/index.js +344 -0
  17. package/src/lib/db.js +325 -0
  18. package/src/lib/hsyncManager.js +57 -0
  19. package/src/lib/queueExecutor.js +362 -0
  20. package/src/routes/bluesky.js +130 -0
  21. package/src/routes/calendar.js +120 -0
  22. package/src/routes/fitbit.js +127 -0
  23. package/src/routes/github.js +72 -0
  24. package/src/routes/jira.js +77 -0
  25. package/src/routes/linkedin.js +137 -0
  26. package/src/routes/mastodon.js +91 -0
  27. package/src/routes/queue.js +186 -0
  28. package/src/routes/reddit.js +138 -0
  29. package/src/routes/ui/bluesky.js +66 -0
  30. package/src/routes/ui/calendar.js +120 -0
  31. package/src/routes/ui/fitbit.js +122 -0
  32. package/src/routes/ui/github.js +60 -0
  33. package/src/routes/ui/index.js +35 -0
  34. package/src/routes/ui/jira.js +72 -0
  35. package/src/routes/ui/linkedin.js +120 -0
  36. package/src/routes/ui/mastodon.js +140 -0
  37. package/src/routes/ui/reddit.js +120 -0
  38. package/src/routes/ui/youtube.js +120 -0
  39. package/src/routes/ui.js +1077 -0
  40. 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;