mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-11-14 08:17:26 +01:00
merge: reconcile paid hosting with latest main
This commit is contained in:
commit
ded6f905fd
180 files changed
+21709
-3614
No files matched your search
@@ -5,7 +5,7 @@
|
||||
<h1 class="paper-page-title">Errors</h1>
|
||||
<div class="errors-actions">
|
||||
<button class="btn btn-sm" type="button" @click="exportCsv()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-export"></use></svg> Export CSV</button>
|
||||
<button class="btn btn-sm btn-danger" type="button" @click="clearAll()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Clear all</button>
|
||||
<button class="btn btn-sm btn-danger-soft" type="button" @click="clearAll()"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-trash"></use></svg> Clear all</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
</div>
|
||||
<div class="col-sev">
|
||||
<span class="sev-dot" :class="'sev-' + row?._bucket"></span>
|
||||
<span class="sev-label">{{ fmt?.uppercase(row?._bucket) }}</span>
|
||||
<span class="sev-label">{{ fmt?.title(row?._bucket) }}</span>
|
||||
</div>
|
||||
<div class="col-mod"><span class="pill pill-module">{{ row?.module }}</span></div>
|
||||
<div class="col-msg">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container paper-page admin-page overview-page">
|
||||
<div class="paper-crumbs">Admin · System Health</div>
|
||||
<div class="paper-crumbs">Admin / <span class="here">Overview</span></div>
|
||||
|
||||
<div class="ov-header">
|
||||
<h1 class="paper-page-title">Overview</h1>
|
||||
@@ -105,11 +105,11 @@
|
||||
<div class="ov-chart-card">
|
||||
<div class="ov-chart-head">
|
||||
<span class="ov-chart-title">Users · 30d</span>
|
||||
<span class="ov-chart-legend"><span class="ov-dot-legend ov-dot-user-fill"></span>total users</span>
|
||||
<span class="ov-chart-legend"><span class="ov-dot-legend ov-dot-user-fill"></span>total users · {{ fmt?.number(historyRange('nbUsers').min) }}–{{ fmt?.number(historyRange('nbUsers').max) }}</span>
|
||||
</div>
|
||||
<div class="ov-spark-bars" v-if="data?.history?.length">
|
||||
<div class="ov-spark-col has-tip" v-for="(d, index) in data?.history" :key="index" :data-tip="(historyLabel(d)) + ": " + (fmt?.number(d?.nbUsers)) + " users"" :aria-label="(historyLabel(d)) + ": " + (fmt?.number(d?.nbUsers)) + " users"">
|
||||
<span class="ov-spark-fill ov-spark-fill-user" :style="{height: historyBarH(d, 'nbUsers') + 'px'}"></span>
|
||||
<span class="ov-spark-fill ov-spark-fill-user" :style="{height: historyRangeH(d, 'nbUsers') + 'px'}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ov-spark-x" v-if="data?.history?.length">
|
||||
@@ -308,7 +308,7 @@
|
||||
<thead><tr><th scope="col">Route</th><th scope="col" class="num">Requests</th><th scope="col" class="num">Average</th><th scope="col" class="num">First byte p95</th><th scope="col" class="num">p95</th><th scope="col" class="num">p99</th><th scope="col" class="num">Interrupted</th><th scope="col" class="num">5xx</th><th scope="col" class="num">Slow</th></tr></thead>
|
||||
<tbody><tr v-for="row in performance.routes" :key="row.service + row.method + row.route">
|
||||
<th scope="row" class="ov-metric-name"><span>{{ row.route }}</span><small>{{ row.service }} · {{ row.method }}</small></th>
|
||||
<td class="num">{{ fmt.number(row.count) }}</td><td class="num">{{ row.avgMs }} ms</td>
|
||||
<td class="num">{{ fmt.number(row.count) }}</td><td class="num">{{ formatMs(row.avgMs) }}</td>
|
||||
<td class="num">{{ firstByteBound(row) }}</td><td class="num">{{ latencyBound(row, 'p95UpperMs') }}</td><td class="num">{{ latencyBound(row, 'p99UpperMs') }}</td>
|
||||
<td class="num" :class="{'ov-metric-warn': row.aborted > 0}">{{ fmt.number(row.aborted) }}</td><td class="num" :class="{'ov-metric-warn': row.errors > 0}">{{ fmt.number(row.errors) }}</td><td class="num" :class="{'ov-metric-warn': row.slow > 0}">{{ fmt.number(row.slow) }}</td>
|
||||
</tr></tbody>
|
||||
@@ -340,7 +340,7 @@
|
||||
<caption class="sr-only">Work stages and cache results</caption>
|
||||
<thead><tr><th scope="col">Work stage</th><th scope="col" class="num">Samples</th><th scope="col" class="num">Average</th><th scope="col" class="num">p95</th></tr></thead>
|
||||
<tbody><tr v-for="row in performance.stages.filter(row => row.metric !== 'first_byte')" :key="row.service + row.metric">
|
||||
<th scope="row" class="ov-metric-name"><span>{{ row.metric }}</span><small>{{ row.service }}</small></th><td class="num">{{ fmt.number(row.count) }}</td><td class="num">{{ row.avgMs }} ms</td><td class="num">{{ latencyBound(row, 'p95UpperMs') }}</td>
|
||||
<th scope="row" class="ov-metric-name"><span>{{ row.metric }}</span><small>{{ row.service }}</small></th><td class="num">{{ fmt.number(row.count) }}</td><td class="num">{{ formatMs(row.avgMs) }}</td><td class="num">{{ latencyBound(row, 'p95UpperMs') }}</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<button class="btn btn-sm" :class="{active: range == '7d'}" @click="setRange('7d')">7d</button>
|
||||
</div>
|
||||
<button class="btn btn-sm" @click="pauseAll()">Pause all</button>
|
||||
<button class="btn btn-sm btn-dark" @click="drainSelected()">Drain {{ selectedQueue }}</button>
|
||||
<button class="btn btn-sm btn-danger-soft" @click="drainSelected()">Drain {{ selectedQueue }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<!-- Detail: throughput chart + stats panel -->
|
||||
<div class="q-detail" v-if="selectedStats">
|
||||
<div class="q-throughput">
|
||||
<div class="q-section-label">{{ selectedQueue }}·throughput <span class="q-section-right"><span class="q-legend-completed">●</span> completed <span class="q-legend-failed">●</span> failed <span class="q-legend-exec">- -</span> avg time · {{ fmt?.uppercase(range) }}</span></div>
|
||||
<div class="q-section-label">{{ selectedQueue }} · throughput <span class="q-section-right"><span class="q-legend-completed">●</span> completed <span class="q-legend-failed">●</span> failed <span class="q-legend-exec">- -</span> avg time · {{ range }}</span></div>
|
||||
<div class="q-chart-wrap">
|
||||
<canvas id="q-throughput-chart" height="180"></canvas>
|
||||
<div id="q-chart-tooltip" class="q-chart-tooltip" style="display:none;"></div>
|
||||
@@ -52,30 +52,30 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-stats-panel">
|
||||
<div class="q-section-label">{{ selectedQueue }}·stats</div>
|
||||
<div class="q-section-label">{{ selectedQueue }} · stats</div>
|
||||
<div class="q-stats-grid">
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">WAITING</div>
|
||||
<div class="q-stat-label">Waiting</div>
|
||||
<div class="q-stat-value" v-text="selectedStats?.counts?.waiting || 0"></div>
|
||||
</div>
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">ACTIVE</div>
|
||||
<div class="q-stat-label">Active</div>
|
||||
<div class="q-stat-value" v-text="selectedStats?.counts?.active || 0"></div>
|
||||
</div>
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">COMPLETED (24H)</div>
|
||||
<div class="q-stat-label">Completed (24h)</div>
|
||||
<div class="q-stat-value" v-text="fmt?.number(selectedStats?.completed24h)"></div>
|
||||
</div>
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">FAILED (24H)</div>
|
||||
<div class="q-stat-label">Failed (24h)</div>
|
||||
<div class="q-stat-value" v-text="selectedStats?.failed24h || 0"></div>
|
||||
</div>
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">DELAYED</div>
|
||||
<div class="q-stat-label">Delayed</div>
|
||||
<div class="q-stat-value" v-text="selectedStats?.counts?.delayed || 0"></div>
|
||||
</div>
|
||||
<div class="q-stat">
|
||||
<div class="q-stat-label">WORKERS</div>
|
||||
<div class="q-stat-label">Workers</div>
|
||||
<div class="q-stat-value" v-text="selectedStats?.workers || 0"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@
|
||||
<!-- Jobs table -->
|
||||
<div class="q-jobs">
|
||||
<div class="q-jobs-header">
|
||||
<div class="q-section-label">ALL JOBS · {{ fmt?.uppercase(selectedQueue) }}</div>
|
||||
<div class="q-section-label">All jobs · {{ selectedQueue }}</div>
|
||||
<div class="q-state-filters">
|
||||
<label class="q-state-toggle" v-for="(s, index) in allStates">
|
||||
<input type="checkbox" v-field="{ state: viewState, set: value => { stateFilter[s] = value }, value: stateFilter[s], form: null, options: {} }">
|
||||
@@ -115,12 +115,12 @@
|
||||
<table class="q-table" v-if="filteredJobs().length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>STATE</th>
|
||||
<th>JOB ID</th>
|
||||
<th>PAYLOAD</th>
|
||||
<th>ATTEMPTS</th>
|
||||
<th>DURATION</th>
|
||||
<th>PROGRESS</th>
|
||||
<th>State</th>
|
||||
<th>Job id</th>
|
||||
<th>Payload</th>
|
||||
<th>Attempts</th>
|
||||
<th>Duration</th>
|
||||
<th>Progress</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -156,52 +156,52 @@
|
||||
<div class="q-job-detail">
|
||||
<div class="q-job-detail-grid">
|
||||
<div class="q-job-detail-item">
|
||||
<span class="q-job-detail-label">JOB ID</span>
|
||||
<span class="q-job-detail-label">Job id</span>
|
||||
<span class="q-job-detail-value"><a target="_blank" v-text="job?.id" :href="safeUrl("/r/" + (job?.id))"></a></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item">
|
||||
<span class="q-job-detail-label">STATE</span>
|
||||
<span class="q-job-detail-label">State</span>
|
||||
<span class="q-job-detail-value"><span v-text="job?._state" :class=""q-state-badge q-state-" + (job?._state)"></span></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.data?.repoId">
|
||||
<span class="q-job-detail-label">REPO ID</span>
|
||||
<span class="q-job-detail-label">Repo ID</span>
|
||||
<span class="q-job-detail-value" v-text="job?.data?.repoId"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.timestamp">
|
||||
<span class="q-job-detail-label">CREATED</span>
|
||||
<span class="q-job-detail-label">Created</span>
|
||||
<span class="q-job-detail-value" v-text="humanTime(job?.timestamp)"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?._state == 'delayed' && job?.delayUntil">
|
||||
<span class="q-job-detail-label">RETRY AT</span>
|
||||
<span class="q-job-detail-label">Retry at</span>
|
||||
<span class="q-job-detail-value">{{ humanTime(job?.delayUntil) }} ({{ delayCountdown(job?.delayUntil) }})</span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.processedOn">
|
||||
<span class="q-job-detail-label">PROCESSED</span>
|
||||
<span class="q-job-detail-label">Processed</span>
|
||||
<span class="q-job-detail-value" v-text="humanTime(job?.processedOn)"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.finishedOn">
|
||||
<span class="q-job-detail-label">FINISHED</span>
|
||||
<span class="q-job-detail-label">Finished</span>
|
||||
<span class="q-job-detail-value" v-text="humanTime(job?.finishedOn)"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.attemptsMade">
|
||||
<span class="q-job-detail-label">ATTEMPTS</span>
|
||||
<span class="q-job-detail-label">Attempts</span>
|
||||
<span class="q-job-detail-value" v-text="job?.attemptsMade"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="job?.progress && job?.progress?.status">
|
||||
<span class="q-job-detail-label">STATUS</span>
|
||||
<span class="q-job-detail-label">Status</span>
|
||||
<span class="q-job-detail-value" v-text="job?.progress?.status"></span>
|
||||
</div>
|
||||
<div class="q-job-detail-item" v-if="jobProgressPct(job) !== null">
|
||||
<span class="q-job-detail-label">PROGRESS</span>
|
||||
<span class="q-job-detail-label">Progress</span>
|
||||
<span class="q-job-detail-value" v-text="jobProgressPct(job) + '%'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-job-detail-error" v-if="job?.failedReason">
|
||||
<span class="q-job-detail-label">ERROR</span>
|
||||
<span class="q-job-detail-label">Error</span>
|
||||
<div class="q-error-reason" v-text="job?.failedReason"></div>
|
||||
</div>
|
||||
<div v-if="job?.stacktrace?.length">
|
||||
<span class="q-job-detail-label">STACKTRACE</span>
|
||||
<span class="q-job-detail-label">Stacktrace</span>
|
||||
<pre class="q-error-stack" v-for="(stack, index) in job?.stacktrace" :key="index"><code v-text="stack"></code></pre>
|
||||
</div>
|
||||
<div class="q-job-detail-actions">
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" target="_blank" v-text="repo?.repoId" :href="safeUrl("/r/" + (repo?.repoId))"></a>
|
||||
<div class="anon-sub">
|
||||
<a v-text="repo?.source?.repositoryName" :href="safeUrl("https://github.com/" + (repo?.source?.repositoryName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
<a v-text="repo?.source?.repositoryName" :href="safeUrl("https://github.com/" + (repo?.source?.repositoryName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} {{ repo?.options?.terms?.length === 1 ? 'term' : 'terms' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="container paper-page admin-page">
|
||||
<div class="paper-crumbs"><a href="/admin/users">Users</a> / <span class="here">{{ userInfo?.username || 'Profile' }}</span></div>
|
||||
<div class="paper-crumbs"><a href="/admin/">Admin</a> / <a href="/admin/users">Users</a> / <span class="here">{{ userInfo?.username || 'Profile' }}</span></div>
|
||||
<h1 class="paper-page-title">{{ userInfo?.username || 'User' }}</h1>
|
||||
|
||||
<nav class="admin-nav">
|
||||
@@ -40,7 +40,8 @@
|
||||
<div class="detail-value">{{ userInfo?.emails?.[0]?.email }}</div>
|
||||
|
||||
<div class="detail-label">Access token</div>
|
||||
<div class="detail-value" style="font-family: var(--font-mono); font-size: 0.85rem;">{{ userInfo?.accessTokens?.github }}</div>
|
||||
<div class="detail-value" style="font-family: var(--font-mono); font-size: 0.85rem;" v-if="userInfo?.accessTokens?.github">{{ userInfo?.accessTokens?.github }}</div>
|
||||
<div class="detail-value text-muted" v-if="!userInfo?.accessTokens?.github">None stored</div>
|
||||
|
||||
<div class="detail-label">GitHub</div>
|
||||
<div class="detail-value">
|
||||
@@ -98,7 +99,7 @@
|
||||
|
||||
<form class="d-flex" style="gap: 8px; margin-bottom: 12px;" v-form="viewState" @submit.prevent="submitForm($event, () => { createToken() })">
|
||||
<input type="text" class="form-control" placeholder="Token name (e.g. dev-laptop)" required v-field="{ state: viewState, set: value => { tokenForm.name = value }, value: tokenForm?.name, form: null, options: {} }">
|
||||
<button type="submit" class="btn btn-primary"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-plus"></use></svg> Generate</button>
|
||||
<button type="submit" class="btn btn-ink"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-plus"></use></svg> Generate</button>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-warning" role="alert" v-if="tokenForm?.plaintext">
|
||||
@@ -180,7 +181,7 @@
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" target="_blank" v-text="repo?.repoId" :href="safeUrl("/r/" + (repo?.repoId))"></a>
|
||||
<div class="anon-sub">
|
||||
<a v-text="repo?.source?.fullName" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} terms</span>
|
||||
<a v-text="repo?.source?.fullName" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/")"></a><span v-if="repo?.options?.update"> · <a v-text="repo?.source?.branch" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.branch))"></a></span><span v-if="!repo?.options?.update"> · @<a v-text="repo?.source?.commit?.substring(0, 8)" :href="safeUrl("https://github.com/" + (repo?.source?.fullName) + "/tree/" + (repo?.source?.commit))"></a></span><span v-if="repo?.conference"> · <svg class="ui-icon" aria-hidden="true"><use href="#project-icon-conference"></use></svg> {{ repo?.conference }}</span><span> · {{ fmt?.humanFileSize(repo?.size?.storage) }}</span><span> · {{ fmt?.number(repo?.options?.terms?.length) }} {{ repo?.options?.terms?.length === 1 ? 'term' : 'terms' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
<small class="form-text" style="color: var(--ink-muted);">
|
||||
Repository, pull request (…/pull/42) and gist (gist.github.com/…) URLs are all accepted.
|
||||
</small>
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="!sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="!sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected">
|
||||
<div class="repo-access-main">
|
||||
<div class="repo-access-controls">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg></button>
|
||||
<a class="connection-link" href="/connections" v-if="isUpdate || detectedType === 'gist' || githubConnection === 'oauth'">{{ isUpdate ? 'Change connection' : 'Manage connections' }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-access-note"><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Anonymized content is public, even from private repositories.</span></div>
|
||||
<div class="repo-access-note"><span v-if="error && detectedType !== 'gist' && githubConnection === 'github-app'">Signing in does not grant access to private repositories. Use Manage access to select this repository, then return here. Organization access may require administrator approval.</span><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Anonymized content is public, even from private repositories.</span></div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,14 +105,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected && githubConnections.oauthConnected">
|
||||
<aside class="repo-access-details" aria-label="Previous repository configuration" v-if="!isUpdate && previousConfigurations.length">
|
||||
<p role="status">You have already anonymized this repository. Use the existing configuration to keep its anonymous link and avoid creating another repository.</p>
|
||||
<div class="form-group" v-if="previousConfigurations.length > 1">
|
||||
<label class="paper-field-label" for="previousConfiguration">Previous anonymization</label>
|
||||
<select id="previousConfiguration" class="form-control" v-model="previousConfigurationId">
|
||||
<option v-for="previous in previousConfigurations" :key="previous.repoId" :value="previous.repoId">{{ previous.repoId }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p v-else>{{ previousConfigurationId }}</p>
|
||||
<p>You can review and update its settings before saving.</p>
|
||||
<div class="repo-access-controls">
|
||||
<a class="btn btn-ink" :href="existingConfigurationUrl()">Use existing configuration</a>
|
||||
<button type="button" class="btn btn-link" @click="previousConfigurations = []">Create a separate anonymization</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<aside class="repo-access-details" aria-label="GitHub access" v-if="sourceUrl && githubConnections?.appEnabled && githubConnections.appConnected">
|
||||
<div class="repo-access-main">
|
||||
<div class="repo-access-controls">
|
||||
<button type="button" class="btn btn-outline-ink" v-if="detectedType !== 'gist' && githubConnection !== 'oauth'" @click="grantGitHubAccess()">Manage access <svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-external-link"></use></svg></button>
|
||||
<a class="connection-link" href="/connections" v-if="isUpdate || detectedType === 'gist' || githubConnection === 'oauth'">{{ isUpdate ? 'Change connection' : 'Manage connections' }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-access-note"><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Anonymized content is public, even from private repositories.</span></div>
|
||||
<div class="repo-access-note"><span v-if="error && detectedType !== 'gist' && githubConnection === 'github-app'">Signing in does not grant access to private repositories. Use Manage access to select this repository, then return here. Organization access may require administrator approval.</span><span><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-info"></use></svg> Anonymized content is public, even from private repositories.</span></div>
|
||||
</aside>
|
||||
|
||||
<aside class="repo-access-details" v-if="isUpdate && detectedType === 'repo' && role === 'owner' && accessRepairAllowed && githubConnections?.appEnabled && githubConnection === 'github-app' && sourceUrl === 'https://github.com/' + _originalFullName">
|
||||
<p>If this repository became private or its App installation changed, repair its access here. This keeps the same anonymous URL and published commit.</p>
|
||||
<button type="button" class="btn btn-outline-ink" :disabled="repairingGitHubAccess" @click="repairGitHubAccess()">Repair GitHub access</button>
|
||||
<button type="button" class="btn btn-outline-ink" @click="grantGitHubAccess()">Manage access</button>
|
||||
<p v-if="accessRepairMessage" role="status">{{ accessRepairMessage }}</p>
|
||||
</aside>
|
||||
|
||||
<aside class="repo-access-details" v-if="isUpdate && detectedType === 'repo' && role === 'owner' && sourceUrl === 'https://github.com/' + _originalFullName">
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<form class="form needs-validation" name="claimForm" novalidate v-form="viewState" @submit.prevent="submitForm($event, () => { claim() })">
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="repoUrl">GitHub repository URL</label>
|
||||
<input type="text" class="form-control" name="repoUrl" id="repoUrl" required v-field="{ state: viewState, set: value => { repoUrl = value }, value: repoUrl, form: "claimForm", options: {} }" :class="{'is-invalid': claimForm?.repoUrl?.invalid && (claimForm?.repoUrl?.touched || claimForm?.submitted)}">
|
||||
<input type="text" class="form-control" name="repoUrl" id="repoUrl" placeholder="https://github.com/owner/repository" required v-field="{ state: viewState, set: value => { repoUrl = value }, value: repoUrl, form: "claimForm", options: {} }" :class="{'is-invalid': claimForm?.repoUrl?.invalid && (claimForm?.repoUrl?.touched || claimForm?.submitted)}">
|
||||
<div class="invalid-feedback" v-show="claimForm?.repoUrl?.errors?.not_found">
|
||||
No anonymization matches this repository and ID. Check both values and that you can access the repository on GitHub.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="repoId">Anonymized repository ID</label>
|
||||
<input type="text" class="form-control" name="repoId" id="repoId" required v-field="{ state: viewState, set: value => { repoId = value }, value: repoId, form: "claimForm", options: {} }" :class="{'is-invalid': claimForm?.repoId?.invalid && (claimForm?.repoId?.touched || claimForm?.submitted)}">
|
||||
<input type="text" class="form-control" name="repoId" id="repoId" placeholder="e.g. my-paper-7F2A" aria-describedby="idHelp" required v-field="{ state: viewState, set: value => { repoId = value }, value: repoId, form: "claimForm", options: {} }" :class="{'is-invalid': claimForm?.repoId?.invalid && (claimForm?.repoId?.touched || claimForm?.submitted)}">
|
||||
<small id="idHelp" class="form-text text-muted">The ID is the last part of the anonymized URL: <code>anonymous.4open.science/r/<ID></code>.</small>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
<p role="status" v-if="!connections && !connectionError">Loading your connections…</p>
|
||||
<button class="btn btn-outline-ink" v-if="!connections && connectionError" :disabled="busy" @click="loadConnections()">Try again</button>
|
||||
<div v-if="connections">
|
||||
<section class="connection-resources" v-if="connections.emailNotificationsEnabled">
|
||||
<h2>Email alerts</h2>
|
||||
<p>We will email you if your GitHub connection stops working or a repository becomes inaccessible. Your address is never shown on anonymous pages.</p>
|
||||
<form @submit.prevent="saveNotificationEmail()">
|
||||
<label for="notification-email">Email address</label>
|
||||
<input id="notification-email" class="form-control" type="email" autocomplete="email" maxlength="254" required v-model="notificationEmail" :disabled="busy" />
|
||||
<button class="btn btn-ink mt-2" type="submit" :disabled="busy">Save email address</button>
|
||||
<p v-if="emailSaved" role="status">Email address saved.</p>
|
||||
</form>
|
||||
</section>
|
||||
<div class="connections-grid">
|
||||
<section v-if="connections.appEnabled">
|
||||
<div class="connection-top"><i class="fab fa-github" aria-hidden="true"></i><span class="connection-badge">Recommended</span></div>
|
||||
|
||||
@@ -20,8 +20,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="leftCol-project-header">
|
||||
<span class="project-name" v-text="repoId"></span>
|
||||
<span class="project-name" v-text="repoId" :title="repoId"></span>
|
||||
<span class="project-file-count">{{ fileCounts?.[''] || files?.length }} files</span>
|
||||
<span class="project-expiry" v-if="options?.expirationDate" :title="options.expirationDate">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-clock"></use></svg>
|
||||
Available until {{ fmt?.date(options.expirationDate) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="leftCol-body">
|
||||
<div class="paper-inline-warning" role="alert" v-if="options?.truncatedFolders?.length > 0">
|
||||
@@ -53,7 +57,7 @@
|
||||
<div class="leftCol-backdrop" v-show="files?.length && !sidebarCollapsed" @click="sidebarCollapsed = true"></div>
|
||||
|
||||
<div class="explorer-main">
|
||||
<div class="status-bar">
|
||||
<div class="status-bar" v-show="type != 'error' || files?.length">
|
||||
<ol class="breadcrumb paths" aria-label="Path">
|
||||
<li class="breadcrumb-item" v-for="(p, index) in paths" v-text="p"></li>
|
||||
</ol>
|
||||
@@ -67,9 +71,9 @@
|
||||
<svg class="ui-icon" aria-hidden="true"><use :href="showSource ? '#project-icon-eye' : '#ui-icon-code'"></use></svg><span class="d-none d-md-inline">
|
||||
{{ showSource ? "Rendered" : "Source" }}</span>
|
||||
</button>
|
||||
<a target="_self" class="btn btn-sm" aria-label="View raw current file" title="View the raw content of the current file" v-show="content != null" :href="safeUrl((url))"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-file"></use></svg><span class="d-none d-md-inline"> Raw</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download current file" title="Download the current file" v-show="content != null" :href="safeUrl((url) + "&download=true")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-download"></use></svg><span class="d-none d-md-inline"> Download</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Download full repository as ZIP" title="Download the full repository as a ZIP archive" v-if="options?.download" :href="safeUrl("/api/repo/" + (repoId) + "/zip")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-archive"></use></svg><span class="d-none d-md-inline"> Full repo ZIP</span></a>
|
||||
<a target="_self" class="btn btn-sm btn-icon-only" aria-label="View raw current file" title="View raw file" v-show="content != null && type != 'error' && type != 'rate_limited'" :href="safeUrl((url))"><svg class="ui-icon" aria-hidden="true"><use href="#project-icon-file"></use></svg></a>
|
||||
<a target="_self" class="btn btn-sm btn-icon-only" aria-label="Download current file" title="Download this file" v-show="content != null && type != 'error' && type != 'rate_limited'" :href="safeUrl((url) + "&download=true")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-download"></use></svg></a>
|
||||
<a target="_self" class="btn btn-sm btn-ink" aria-label="Download repository as ZIP" title="Download the whole repository as a ZIP archive" v-if="options?.download" :href="safeUrl("/api/repo/" + (repoId) + "/zip")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-archive"></use></svg><span class="d-none d-md-inline"> Download repository</span></a>
|
||||
<a target="_self" class="btn btn-sm" aria-label="Website" v-if="options?.hasWebsite" :href="safeUrl("/w/" + (repoId) + "/")"><svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-globe"></use></svg><span class="d-none d-md-inline"> Website</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<li class="nav-item" v-if="user || !isReviewerRoute">
|
||||
<a class="nav-link nav-icon" target="_blank" href="https://github.com/tdurieux/anonymous_github/" title="Anonymous GitHub source code" aria-label="Anonymous GitHub source code on GitHub" rel="noopener" data-offset="30"><i class="fab fa-github" aria-hidden="true"></i></a>
|
||||
</li>
|
||||
<!-- "Report a bug" is about this service. Reviewers who land on an
|
||||
@@ -67,7 +67,7 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" v-if="!user">
|
||||
<li class="nav-item" v-if="!user && !isReviewerRoute">
|
||||
<a class="nav-link btn-signin" target="_self" href="/signin" data-offset="30"><i class="fab fa-github mr-1"></i>
|
||||
Sign in
|
||||
</a>
|
||||
@@ -118,3 +118,18 @@
|
||||
</div>
|
||||
</nav>
|
||||
<div class="navbar shadow generalMessage" v-if="generalMessage" v-html="sanitize(generalMessage)"></div>
|
||||
|
||||
<dialog id="email-prompt" class="email-prompt" v-if="emailPromptOpen" aria-labelledby="email-prompt-title" aria-describedby="email-prompt-reason" @cancel.prevent="respondToEmailPrompt('later')">
|
||||
<form @submit.prevent="respondToEmailPrompt('save')">
|
||||
<h2 id="email-prompt-title">Stay informed about access problems</h2>
|
||||
<p id="email-prompt-reason">Get email alerts when your GitHub token or repository access fails.</p>
|
||||
<label for="email-prompt-address">Email address</label>
|
||||
<input id="email-prompt-address" class="form-control" type="email" autocomplete="email" maxlength="254" required v-model="emailPromptAddress" :disabled="emailPromptBusy" />
|
||||
<p class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="emailPromptError">{{ emailPromptError }}</p>
|
||||
<div class="email-prompt-actions">
|
||||
<button type="button" class="btn btn-outline-ink" :disabled="emailPromptBusy" @click="respondToEmailPrompt('later')">Remind me later</button>
|
||||
<button type="button" class="btn btn-outline-ink" :disabled="emailPromptBusy" @click="respondToEmailPrompt('never')">Never</button>
|
||||
<button type="submit" class="btn btn-ink" :disabled="emailPromptBusy">{{ emailPromptBusy ? 'Saving…' : 'Save' }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
@@ -108,7 +108,8 @@
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" v-field="{ state: viewState, set: value => { options.options.notebook = value }, value: options?.options?.notebook, form: "conference", options: {} }">
|
||||
<label class="form-check-label" for="notebook">Display Notebooks</label>
|
||||
<label class="form-check-label" for="notebook">Display notebooks</label>
|
||||
<small class="form-text text-muted">Render Jupyter notebooks instead of raw JSON.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div v-code-editor="{ content: content, options: aceOption }" v-if="type == 'text'"></div>
|
||||
<div class="file-content markdown-body" v-if="type == 'html'" v-html="sanitize(content)"></div>
|
||||
<div class="file-content markdown-body" v-if="type == 'html'" v-html="highlightRedactions(sanitize(content))"></div>
|
||||
<html-doc v-if="type == 'html-doc' && !showSource && content != null" :content="content" :base-url="(fileBaseUrl)" :allow-scripts="allowScripts"></html-doc>
|
||||
<div v-code-editor="{ content: content, options: aceOption }" v-if="(type == 'code' || (type == 'html-doc' && showSource)) && content != null"></div>
|
||||
<img class="image-content" v-if="type == 'image'" :src="safeUrl((url))">
|
||||
@@ -19,7 +19,22 @@
|
||||
<p class="paper-error-msg">This repository will be available in <strong>{{ rateLimitCountdown }}</strong>. The page will reload automatically.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-error container d-flex h-100" v-if="type == 'error'"><h1 class="paper-empty-title m-auto" v-text="fmt.translate("ERRORS." + (content))">Error</h1></div>
|
||||
<div class="file-error container d-flex h-100" v-if="type == 'error'">
|
||||
<section class="viewer-error paper-error-card file-error-card m-auto" role="alert" aria-labelledby="viewer-error-title">
|
||||
<h1 id="viewer-error-title" class="paper-empty-title">{{ fmt.translate(isSourceAccessError() ? 'VIEWER_ERRORS.access_title' : 'VIEWER_ERRORS.title') }}</h1>
|
||||
<p>{{ fmt.translate('ERRORS.' + content) }}</p>
|
||||
<template v-if="isSourceAccessError()">
|
||||
<p>{{ fmt.translate('VIEWER_ERRORS.reader_help') }}</p>
|
||||
<p>{{ fmt.translate('VIEWER_ERRORS.owner_help') }}</p>
|
||||
</template>
|
||||
<div class="paper-error-actions">
|
||||
<a v-if="!isSourceAccessError()" href="/" class="btn btn-sm">Back to home</a>
|
||||
<a v-if="!isSourceAccessError()" href="/faq" class="btn btn-sm">Read the FAQ</a>
|
||||
<button type="button" class="btn btn-primary" @click="retryContent()">{{ fmt.translate('VIEWER_ERRORS.retry') }}</button>
|
||||
<a v-if="isSourceAccessError()" class="btn btn-outline-ink" href="/dashboard">{{ fmt.translate('VIEWER_ERRORS.dashboard') }}</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="file-error container d-flex h-100" v-if="type == 'loading' && !error"><h1 class="paper-empty-title m-auto">Loading…</h1></div>
|
||||
<div class="file-error container d-flex h-100" v-if="type == 'empty'"><h1 class="paper-empty-title m-auto">Empty <em>repository</em>.</h1></div>
|
||||
<div class="file-error container d-flex h-100" v-if="content == null && type != 'empty'"><h1 class="paper-empty-title m-auto">Empty <em>file</em>.</h1></div>
|
||||
|
||||
@@ -76,6 +76,17 @@ jane.smith@example.org" v-field="{ state: viewState, set: value => { terms =
|
||||
<a class="btn btn-outline-ink" href="/api/user/logout" target="_self">Sign out</a>
|
||||
</div>
|
||||
|
||||
<form class="paper-account-email" @submit.prevent="saveAccountEmail()">
|
||||
<label class="paper-field-label" for="account-email">Email address</label>
|
||||
<p id="account-email-help" class="paper-section-copy">Used for account alerts. Your address is never shown on anonymous pages.</p>
|
||||
<input id="account-email" class="form-control" type="email" autocomplete="email" maxlength="254" required v-model="accountEmail" aria-describedby="account-email-help" :disabled="emailLoading || emailSaving || !emailCSRFReady" @input="emailSaved = false" />
|
||||
<p role="status" v-if="emailLoading">Loading your email address…</p>
|
||||
<p class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="emailError">{{ emailError }}</p>
|
||||
<button class="btn btn-outline-ink mt-2" type="button" v-if="!emailCSRFReady && !emailLoading" @click="loadAccountEmail()">Try again</button>
|
||||
<button class="btn btn-ink mt-2" type="submit" :disabled="emailLoading || emailSaving || !emailCSRFReady">{{ emailSaving ? 'Saving…' : 'Save email address' }}</button>
|
||||
<p role="status" v-if="emailSaved">Email address saved.</p>
|
||||
</form>
|
||||
|
||||
<a class="paper-settings-connection-link" href="/connections">
|
||||
<i class="fab fa-github" aria-hidden="true"></i>
|
||||
<span>GitHub connections<small>Manage repository access and legacy OAuth.</small></span>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<section class="container py-4" style="max-width: 48rem; overflow-wrap: anywhere">
|
||||
<a href="/">Anonymous GitHub</a>
|
||||
<h1 class="mt-3">Approve artifact access</h1>
|
||||
<p>This records your approval as the repository owner or an authorized coauthor.</p>
|
||||
<p v-if="!user?.username">Sign in first, then reopen the artifact link from your submission. <a href="/signin">Sign in</a></p>
|
||||
<p v-if="error" role="alert">{{ error }}</p>
|
||||
<p v-if="saved" role="status">Consent recorded. <span v-if="handoffEnabled">Continue to review to finish linking.</span><span v-else>Artifact linking is not complete. Return to your submission to check its status.</span></p>
|
||||
<button v-if="saved && handoffEnabled" class="btn btn-primary" type="button" @click="continueReview" :disabled="busy">Continue to review</button>
|
||||
<form v-if="user?.username && !saved" @submit.prevent="loadPreview">
|
||||
<label for="review-repository">Anonymous repository ID</label>
|
||||
<input id="review-repository" class="form-control" v-model="repositoryId" maxlength="128" autocomplete="off" :disabled="busy || uncertain" aria-describedby="review-repository-help">
|
||||
<p id="review-repository-help">Use the ID after /r/ in the anonymous repository URL.</p>
|
||||
<button type="submit" class="btn btn-primary" :disabled="busy || uncertain">Preview access and retention</button>
|
||||
</form>
|
||||
<section v-if="preview && !saved" class="mt-4" aria-labelledby="review-policy-title">
|
||||
<h2 id="review-policy-title">{{ preview.repositoryName }}</h2>
|
||||
<p>Access: {{ preview.policy.access === 'anonymous-link' ? 'Anyone with the anonymous link' : 'Restricted review access' }}.</p>
|
||||
<p>Retain until {{ preview.policy.retainUntil }}. Policy version {{ preview.policy.version }}.</p>
|
||||
<p v-if="expired" role="alert">This preview expired. Open a new artifact link from your submission.</p>
|
||||
<div><label><input type="checkbox" v-model="acceptAccess" :disabled="busy || uncertain || expired"> I approve the access policy above.</label></div>
|
||||
<div><label><input type="checkbox" v-model="acceptRetention" :disabled="busy || uncertain || expired"> I approve retention until the date above.</label></div>
|
||||
<button class="btn btn-primary" type="button" @click="confirmConsent" :disabled="busy || expired || !acceptAccess || !acceptRetention">{{ uncertain ? 'Retry confirmation' : 'Confirm consent' }}</button>
|
||||
</section>
|
||||
<p v-if="busy" role="status">Waiting for the server…</p>
|
||||
</section>
|
||||
+15
-15
@@ -57,25 +57,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-result" v-if="repo?.status === 'ready'">
|
||||
<div class="detail-label">Repository</div>
|
||||
<div class="detail-value">
|
||||
<a target="_self" :href="safeUrl("/r/" + (repoId) + "/")">{{ reviewUrl }}</a>
|
||||
<div class="share-link" v-if="repo?.status == 'ready'">
|
||||
<p class="paper-section-copy">Reviewers can browse the repository at this address without seeing who you are.</p>
|
||||
<div class="share-link-row">
|
||||
<input id="share-url" class="form-control share-link-input" readonly aria-label="Anonymous link" :value="shareUrl" @focus="$event.target.select()">
|
||||
<button type="button" class="btn btn-ink" @click="copyShareUrl()">
|
||||
<svg class="ui-icon" aria-hidden="true"><use :href="copied ? '#ui-icon-check' : '#project-icon-copy'"></use></svg>
|
||||
<span aria-live="polite" v-text="copied ? 'Copied' : 'Copy link'"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="detail-label" v-if="repo?.options?.page">GitHub Page</div>
|
||||
<div class="detail-value" v-if="repo?.options?.page">
|
||||
<a target="_self" :href="safeUrl("/w/" + (repoId) + "/")">/w/{{ repoId }}/</a>
|
||||
<div class="share-link-actions">
|
||||
<a class="btn" target="_self" :href="safeUrl("/r/" + (repoId) + "/")">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#project-icon-eye"></use></svg> Open repository
|
||||
</a>
|
||||
<a class="btn" target="_self" v-if="repo?.options?.page" :href="safeUrl("/w/" + (repoId) + "/")">
|
||||
<svg class="ui-icon" aria-hidden="true"><use href="#ui-icon-globe"></use></svg> Open GitHub page
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-actions" v-if="repo?.status == 'ready'">
|
||||
<a class="btn btn-ink" target="_self" :href="safeUrl("/r/" + (repoId) + "/")">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#project-icon-eye"></use></svg> Go to anonymized repository
|
||||
</a>
|
||||
<a class="btn" target="_self" v-if="repo?.options?.page" :href="safeUrl("/w/" + (repoId) + "/")">
|
||||
<svg class="ui-icon mr-1" aria-hidden="true"><use href="#ui-icon-globe"></use></svg> Go to anonymized GitHub page
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="task-summary" aria-label="Repository summary">
|
||||
|
||||
Reference in new issue
Block a user