diff --git a/.gitignore b/.gitignore index 5205805..a3d67c8 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +.idea +package-lock.json diff --git a/README.md b/README.md index 54d0743..a59e6d6 100644 --- a/README.md +++ b/README.md @@ -12,5 +12,25 @@ clone github-contributions-chart & github-contributions-canvas - browse to http://localhost:3000/ - select `JankariTech` theme - enter GitHub username +- optionally enter the GitLab URL, username and access token (see below) - click `Generate` - download image + +## Include contributions from a private GitLab instance + +The chart can additionally contain the contributions a person made on a private GitLab instance. + +All GitLab settings are entered directly in the form, no configuration file is needed: + +- `GitLab URL` - the base URL of the GitLab instance, e.g. `https://gitlab.example.com` +- `GitLab Username` - the username on that instance +- `GitLab Access Token` - a personal access token with the `read_user` scope, + only needed if the instance / the profile is not publicly readable + +The contributions are read from `/users//calendar.json` by the server +(the token is sent to the local API route in a header and only used for that request) +and are added to the GitHub contributions of the same day. +The daily counts and the yearly totals are summed up and the colour levels are recalculated. + +Note: only days that are part of the GitHub contribution calendar of the user are shown, +so GitLab contributions of years in which the person had no GitHub activity at all are not displayed. diff --git a/src/pages/api/v1/[username].js b/src/pages/api/v1/[username].js index 8487fcd..16d9710 100644 --- a/src/pages/api/v1/[username].js +++ b/src/pages/api/v1/[username].js @@ -1,8 +1,20 @@ import { fetchDataForAllYears } from '../../../utils/api/fetch' export default async (req, res) => { - const { username, format } = req.query; - const data = await fetchDataForAllYears(username, format); - res.setHeader('Cache-Control', 's-maxage=3600, stale-while-revalidate') - res.json(data); -} \ No newline at end of file + const { username, format, gitlabUsername, gitlabUrl } = req.query; + // the token is sent in a header so that it does not end up in URLs / logs + const gitlabToken = req.headers['x-gitlab-token']; + try { + const data = await fetchDataForAllYears( + username, + format, + gitlabUsername, + gitlabUrl, + gitlabToken + ); + res.setHeader('Cache-Control', 's-maxage=3600, stale-while-revalidate') + res.json(data); + } catch (error) { + res.status(500).json({ error: error.message }); + } +} diff --git a/src/pages/index.js b/src/pages/index.js index 82da52a..29b783a 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -5,6 +5,8 @@ import { fetchData, downloadJSON, cleanUsername, + cleanGitlabUsername, + cleanGitlabUrl, share, copyToClipboard } from "../utils/export"; @@ -16,6 +18,9 @@ const App = () => { const contentRef = useRef(); const [loading, setLoading] = useState(false); const [username, setUsername] = useState(""); + const [gitlabUsername, setGitlabUsername] = useState(""); + const [gitlabUrl, setGitlabUrl] = useState(""); + const [gitlabToken, setGitlabToken] = useState(""); const [theme, setTheme] = useState("standard"); const [data, setData] = useState(null); const [error, setError] = useState(null); @@ -31,15 +36,23 @@ const App = () => { e.preventDefault(); setUsername(cleanUsername(username)); + setGitlabUsername(cleanGitlabUsername(gitlabUsername)); + setGitlabUrl(cleanGitlabUrl(gitlabUrl)); setLoading(true); setError(null); setData(null); - fetchData(cleanUsername(username)) + fetchData(cleanUsername(username), { + username: cleanGitlabUsername(gitlabUsername), + url: cleanGitlabUrl(gitlabUrl), + token: gitlabToken.trim() + }) .then((data) => { setLoading(false); - if (data.years.length === 0) { + if (data.error) { + setError(data.error); + } else if (data.years.length === 0) { setError("Could not find your profile"); } else { setData(data); @@ -87,8 +100,8 @@ const App = () => { username: username, themeName: theme, scaleFactor: 12, - startingDate: '2020-12-08', - endDate: '2024-01-14', + startingDate: '2023-04-14', + endDate: '2026-07-31', footerText: "Made by @sallar & friends - github-contributions.vercel.app" }); contentRef.current.scrollIntoView({ @@ -182,6 +195,32 @@ const App = () => { autoCapitalize="none" autoFocus /> + setGitlabUrl(e.target.value)} + value={gitlabUrl} + id="gitlab-url" + autoCorrect="off" + autoCapitalize="none" + /> + setGitlabUsername(e.target.value)} + value={gitlabUsername} + id="gitlab-username" + autoCorrect="off" + autoCapitalize="none" + /> + setGitlabToken(e.target.value)} + value={gitlabToken} + id="gitlab-token" + autoComplete="off" + autoCorrect="off" + autoCapitalize="none" + />