All warn/error log calls now use field names the dashboard's decorate()
function recognizes: `code` for the error code pill, `httpStatus` for the
status badge and severity bucket, `url` for the sidebar link, and
`repoId` for the repository link.
Key changes:
- Streamer errors surface code, httpStatus, url, and nested err in Raw tab
- Nested `{ err: serializeError(e) }` replaced with spread pattern so
error fields (name, message, status) appear at the top level
- Raw Error objects in catch blocks now go through serializeError()
- Rate limit, token, and PR 404 warnings include code + httpStatus
- Dashboard stack walker traverses both `cause` and `err` chains
- Dashboard Raw tab renders repoId, filePath, upstream*, err, and cause
- trimRawArg recursively trims stacks in nested err/cause chains
- clampPayload strips heavy nested fields before falling back to
truncated placeholder, preserving flat diagnostic fields
Add admin endpoints to ban and activate users, block banned users
from all auth flows (OAuth, token login, bearer auth), and invalidate
existing sessions on next request. Includes frontend translation and
user detail page ban/activate buttons.
Floating button now initializes with theme-aware colors and updates
on toggle. Status page iframe uses a tuned CSS filter in dark mode
to blend with the warm palette.
- Use $location.search() instead of window.location.search for URL
params so cross-page links (owner, conference, search filters) work
with AngularJS client-side navigation
- Add missing removeRepository() in both repos and user detail controllers
- Fix removeCache() spurious $scope.$apply() that caused digest errors
- Add confirmation prompts and list refresh after remove/cache operations
Refresh button now always updates the commit to the latest SHA instead
of preserving the stale one in edit mode. Both create and update routes
verify the commit still exists on GitHub before persisting.
getFiles blindly appended fetched entries to $scope.files, so
re-opening a folder duplicated its children in the tree. Drop any
existing entries at the requested path before appending.
The Anonymize form's preview built the readme baseUrl as
"https://github.com/<owner>/<repo>/raw/<source.branch>/". When the
form rendered before the branch field had populated (initial load,
or while waiting on getBranches), the URL became ".../raw//" and
the browser collapsed the empty segment, fetching ".../raw/<file>"
instead of ".../raw/<branch>/<file>". Relative <img src="./X">
references then 404'd against a path with no branch — exactly the
"branch missing" pattern in #407.
Fall back to details.defaultBranch (then "main") so the base URL is
always well-formed.
Fixes#407.
The Anonymize form used the cached RepositoryModel for hasPage,
defaultBranch, etc. — so enabling GitHub Pages (or changing the
default branch) on the source after first cache wouldn't reflect in
the UI, leaving the GitHub Pages checkbox grayed out.
Pass force=1 when loading the form's repo details so the backend
re-queries the GitHub API once. The cost is a single GET /repos/...
call per form load.
Fixes#364.
Two regressions stacked from the recent tree work:
1. expandAllFolders (#496) was marking every folder open, including
folders whose children weren't fetched yet. The directive then
rendered an empty <ul> after each <a>, and the openFolder handler's
"no sibling means we need to load" check silently treated the empty
<ul> as already-loaded — so clicking the folder toggled the class
but the children never appeared.
Skip folders with empty children when pre-expanding, and harden the
click handler so an empty <ul> still triggers a fetch.
2. The $routeUpdate handler (#510 follow-up) became async and called
$scope.$apply(updateContent) at the end. Inside an already-running
digest cycle this no-ops or throws, leaving file navigation stuck.
Run updateContent() synchronously like before, and kick off any
missing parent-directory fetches in the background — getContent()
already falls back to sha "0" when the metadata isn't loaded yet.
Clicking a markdown link into a subdirectory's README threw
"Cannot read properties of undefined (reading 'sha')" and left the
viewer on Loading…. The route handler called updateContent() without
loading the new directory's file listing, so getSelectedFile() returned
undefined and getContent() then dereferenced fileInfo.sha.
Two fixes:
- getContent() falls back to sha "0" when fileInfo is undefined.
- The $routeUpdate handler walks the new path and loads any directory
listings that aren't yet in $scope.files before rendering, so the
selected file actually has its sha by the time we fetch.
Fixes#510.
The form's live README/PR preview was running its own copy of
ContentAnonimizer in the browser. The two implementations had been
drifting — recent fixes for word boundaries (#175/#249), accent
matching (#280), custom replacements (#285), and the diacritic-stripped
variants only landed on the server. Reviewers saw one anonymization;
authors composing the form saw another.
Add POST /api/anonymize-preview that takes a snippet (or a batch) plus
the user's options and runs them through the same ContentAnonimizer
the file route uses. Replace the client-side anonymizeReadme() body
with a debounced call to that endpoint. The PR view's
anonymizePrContent() runs as a synchronous template expression, so it
now reads from a {original -> anonymized} cache that's refreshed in
the background whenever the PR details, terms, or options change.
Single-flight + debounce keep the form responsive; an in-flight
request is dropped on the next change.
Entering an IP address (e.g. 192.168.1.1) or any term with regex
metacharacters made the form invalid because the "regex characters
detected" hint was wired up via $setValidity('terms', 'regex', false).
The text in the UI labels it as a warning, but the form treated it as
an error and refused to save.
Track the warning as a plain $scope flag and show it via ng-show on
that flag, so the form stays valid (#430).
The file tree opened collapsed, requiring the reviewer to click each
folder before they could see what was inside. Walk the tree on first
render and mark every folder open in $scope.opens. Folders the user
has explicitly toggled (a previous entry already exists in
$scope.opens) are left as-is, so collapsing still works.
Fixes#496.
The viewer already supported jumping to a line via #L42 in the URL but
never produced one — users had to type it manually. Wire guttermousedown
on the ACE editor to replaceState a #L<n> hash, with shift-click for a
range. Also reapply the highlight on hashchange so pasting a URL into
the address bar works without reload.
Fixes#392.
urlRel2abs() prepended an extra "." when it saw "./X", turning the
relative path into "../X" and silently moving up a directory. As a
result, raw HTML <img src="./imgs/run.png"> inside a README rendered
under /r/<repo>/<file> resolved to /r/<repo>/imgs/... instead of
/r/<repo>/<dir>/imgs/..., so the image 404'd. Markdown image syntax
went through marked-base-url and was unaffected.
Strip the leading "./" instead so the relative path concatenates
cleanly with baseUrl.
Fixes#346.
marked v12 dropped the headerIds option, so headings rendered with no
id attributes and links like [Releases](#releases-and-contributing)
silently failed to scroll. Add a heading renderer that emits a
GitHub-style slug id, with a numeric suffix for duplicates within a
document.
Fixes#390.
The Generate button silently no-op'd because ng-model="newTokenName" inside
an ng-if block wrote to a child scope, leaving $scope.newTokenName empty.
Use $scope.tokenForm.{name,plaintext} so prototypal lookup resolves to the
controller scope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Toasts used class="toast show" with ng-repeat but never initialized
Bootstrap's toast plugin, so they stayed pinned until manually closed.
Each navigation re-fired toasts (e.g. "README not found in github
pages"), stacking duplicates.
Add an $scope.addToast helper that schedules removal via $timeout, and
route all push sites through it.
Fixes#246.