mirror of
https://github.com/tiennm99/ghglance.git
synced 2026-10-11 03:13:20 +00:00
`ghglance -serve :8080` runs a web UI from the same binary: submit a username, a background job renders every card in every theme, and /u/<user> shows them again with a theme picker and embed URLs. - Cards are stored on disk with atomic symlink publishing and deleted after -retention (default 24h); -cooldown limits token-less regeneration. - Token-less jobs use a public-only server token with private and org scope forced off; a submitter's own token is used for that job only and never stored or logged. The form links to GitHub's new-token page with the needed scopes pre-ticked. - The CLI and web share one fetch helper; CLI output is unchanged. - compose.yml deploys to Coolify with a data volume and /healthz check.
143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
package github
|
|
|
|
// profileQuery pulls everything needed for the profile, stats and languages
|
|
// cards in one round trip. Fork/private filtering is done client-side so one
|
|
// query handles all combinations of -include-forks / -include-private.
|
|
// $affiliations decides whether org-owned repos are in scope at all; repos the
|
|
// user only has read/write access to are dropped client-side via
|
|
// viewerPermission.
|
|
const profileQuery = `
|
|
query($login: String!, $after: String, $affiliations: [RepositoryAffiliation]) {
|
|
user(login: $login) {
|
|
id
|
|
login
|
|
name
|
|
bio
|
|
avatarUrl
|
|
company
|
|
location
|
|
websiteUrl
|
|
createdAt
|
|
followers { totalCount }
|
|
following { totalCount }
|
|
pullRequests { totalCount }
|
|
issues { totalCount }
|
|
repositoriesContributedTo(
|
|
first: 1
|
|
contributionTypes: [COMMIT, PULL_REQUEST, ISSUE, PULL_REQUEST_REVIEW]
|
|
) { totalCount }
|
|
contributionsCollection {
|
|
contributionYears
|
|
totalCommitContributions
|
|
totalIssueContributions
|
|
totalPullRequestContributions
|
|
totalPullRequestReviewContributions
|
|
totalRepositoryContributions
|
|
restrictedContributionsCount
|
|
contributionCalendar {
|
|
totalContributions
|
|
weeks {
|
|
contributionDays {
|
|
contributionCount
|
|
date
|
|
}
|
|
}
|
|
}
|
|
}
|
|
repositories(
|
|
first: 100
|
|
after: $after
|
|
ownerAffiliations: $affiliations
|
|
orderBy: { field: STARGAZERS, direction: DESC }
|
|
) {
|
|
totalCount
|
|
pageInfo { hasNextPage endCursor }
|
|
nodes {
|
|
name
|
|
owner { login }
|
|
viewerPermission
|
|
isPrivate
|
|
isFork
|
|
stargazerCount
|
|
forkCount
|
|
primaryLanguage { name color }
|
|
languages(first: 20, orderBy: { field: SIZE, direction: DESC }) {
|
|
edges {
|
|
size
|
|
node { name color }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
// commitHistoryQuery fetches commit timestamps in the default branch of one
|
|
// repo, filtered to commits authored by the target user. Used to build the
|
|
// productive-time heatmap. Owner is a variable because seed repos may belong
|
|
// to other users (e.g. forks the user has contributed to).
|
|
const commitHistoryQuery = `
|
|
query($owner: String!, $repo: String!, $userId: ID!, $after: String) {
|
|
repository(owner: $owner, name: $repo) {
|
|
defaultBranchRef {
|
|
target {
|
|
... on Commit {
|
|
history(first: 100, after: $after, author: { id: $userId }) {
|
|
pageInfo { hasNextPage endCursor }
|
|
nodes { committedDate }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
// contributionYearQuery fetches one year's contribution calendar plus the
|
|
// repos the user actually committed in that year. The calendar feeds the
|
|
// all-time area chart; commitContributionsByRepository feeds the seed list
|
|
// that drives commit-history probing.
|
|
const contributionYearQuery = `
|
|
query($login: String!, $from: DateTime!, $to: DateTime!) {
|
|
user(login: $login) {
|
|
contributionsCollection(from: $from, to: $to) {
|
|
totalCommitContributions
|
|
contributionCalendar {
|
|
weeks {
|
|
contributionDays {
|
|
contributionCount
|
|
date
|
|
}
|
|
}
|
|
}
|
|
commitContributionsByRepository(maxRepositories: 100) {
|
|
contributions { totalCount }
|
|
repository {
|
|
name
|
|
owner { login }
|
|
isPrivate
|
|
isFork
|
|
primaryLanguage { name color }
|
|
languages(first: 20, orderBy: { field: SIZE, direction: DESC }) {
|
|
edges {
|
|
size
|
|
node { name color }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
// viewerQuery names the account behind the token and probes whether the
|
|
// token can read any private repository, so the web server can refuse to
|
|
// publish data a token sees beyond the public view.
|
|
const viewerQuery = `query {
|
|
viewer {
|
|
login
|
|
repositories(privacy: PRIVATE, ownerAffiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER], first: 1) {
|
|
totalCount
|
|
}
|
|
}
|
|
}`
|