feat(api): Add custom auth input component for operation pages

- Create api-auth-input.ts TypeScript component
- Store credentials in sessionStorage (secure, cleared on tab close)
- Register component in main.js
- Add auth form styling with dark theme support
- Add component wrapper to rapidoc-mini.html template
- Fix SCSS dark theme (use CSS selectors, not non-existent mixin)
claude/api-code-samples-plan-MEkQO
Jason Stirnaman 2025-12-16 16:36:38 -06:00
parent 4ee1311e37
commit 1c8a2d3fa0
4 changed files with 223 additions and 0 deletions

View File

@ -0,0 +1,128 @@
/**
* API Auth Input Component
*
* Provides credential input fields for API operations.
* Stores credentials in sessionStorage for "Try it" requests.
*/
interface ComponentOptions {
component: HTMLElement;
}
interface AuthCredentials {
bearer?: string;
basic?: { username: string; password: string };
apiKey?: string;
}
const STORAGE_KEY = 'influxdb_api_credentials';
/**
* Get stored credentials from sessionStorage
*/
function getCredentials(): AuthCredentials {
try {
const stored = sessionStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : {};
} catch {
return {};
}
}
/**
* Store credentials in sessionStorage
*/
function setCredentials(credentials: AuthCredentials): void {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(credentials));
}
/**
* Create the auth input form
*/
function createAuthForm(schemes: string[]): HTMLElement {
const form = document.createElement('div');
form.className = 'api-auth-form';
form.innerHTML = `
<h4>Authentication</h4>
<p class="auth-form-description">Enter credentials to use with "Try it" requests.</p>
${
schemes.includes('bearer')
? `
<div class="auth-field">
<label for="auth-bearer">Bearer Token</label>
<input type="password" id="auth-bearer" placeholder="Enter your API token" />
</div>
`
: ''
}
${
schemes.includes('basic')
? `
<div class="auth-field">
<label for="auth-username">Username</label>
<input type="text" id="auth-username" placeholder="Username (optional)" />
</div>
<div class="auth-field">
<label for="auth-password">Password / Token</label>
<input type="password" id="auth-password" placeholder="Enter token" />
</div>
`
: ''
}
<button type="button" class="btn btn-primary auth-save">Save Credentials</button>
`;
return form;
}
/**
* Initialize the auth input component
*/
export default function ApiAuthInput({ component }: ComponentOptions): void {
const schemesAttr = component.dataset.schemes || 'bearer';
const schemes = schemesAttr.split(',').map((s) => s.trim().toLowerCase());
const form = createAuthForm(schemes);
component.appendChild(form);
// Load existing credentials
const credentials = getCredentials();
const bearerInput = form.querySelector<HTMLInputElement>('#auth-bearer');
const usernameInput = form.querySelector<HTMLInputElement>('#auth-username');
const passwordInput = form.querySelector<HTMLInputElement>('#auth-password');
if (bearerInput && credentials.bearer) {
bearerInput.value = credentials.bearer;
}
if (usernameInput && credentials.basic?.username) {
usernameInput.value = credentials.basic.username;
}
if (passwordInput && credentials.basic?.password) {
passwordInput.value = credentials.basic.password;
}
// Save button handler
const saveBtn = form.querySelector('.auth-save');
saveBtn?.addEventListener('click', () => {
const newCredentials: AuthCredentials = {};
if (bearerInput?.value) {
newCredentials.bearer = bearerInput.value;
}
if (usernameInput?.value || passwordInput?.value) {
newCredentials.basic = {
username: usernameInput?.value || '',
password: passwordInput?.value || '',
};
}
setCredentials(newCredentials);
// Notify RapiDoc of new credentials
const rapiDoc = document.querySelector('rapi-doc');
if (rapiDoc && 'setApiKey' in rapiDoc) {
(rapiDoc as any).setApiKey(newCredentials.bearer || '');
}
alert('Credentials saved for this session');
});
}

View File

@ -46,6 +46,7 @@ import SidebarSearch from './components/sidebar-search.js';
import { SidebarToggle } from './sidebar-toggle.js';
import Theme from './theme.js';
import ThemeSwitch from './theme-switch.js';
import ApiAuthInput from './components/api-auth-input.ts';
import ApiRapiDoc from './components/api-rapidoc.ts';
import ApiToc from './components/api-toc.ts';
import RapiDocMini from './components/rapidoc-mini.ts';
@ -80,6 +81,7 @@ const componentRegistry = {
'sidebar-toggle': SidebarToggle,
theme: Theme,
'theme-switch': ThemeSwitch,
'api-auth-input': ApiAuthInput,
'api-rapidoc': ApiRapiDoc,
'api-toc': ApiToc,
'rapidoc-mini': RapiDocMini,

View File

@ -70,3 +70,90 @@
}
}
}
////////////////////////////////////////////////////////////////////////////////
// API Auth Form - Custom credential input for operation pages
////////////////////////////////////////////////////////////////////////////////
.api-auth-form {
margin-bottom: 1.5rem;
padding: 1rem;
background: $g3-castle;
border: 1px solid $g5-pepper;
border-radius: 4px;
h4 {
margin: 0 0 0.5rem 0;
}
.auth-form-description {
margin: 0 0 1rem 0;
font-size: 0.9rem;
color: $g9-mountain;
}
.auth-field {
margin-bottom: 1rem;
label {
display: block;
margin-bottom: 0.25rem;
font-weight: 600;
font-size: 0.9rem;
}
input {
width: 100%;
padding: 0.5rem;
border: 1px solid $g5-pepper;
border-radius: 3px;
font-family: inherit;
}
}
.auth-save {
margin-top: 0.5rem;
}
}
// Dark theme overrides - using CSS selectors (no mixin in this codebase)
[data-theme="dark"],
html:has(link[title="dark-theme"]:not([disabled])) {
.api-security-schemes {
border-top-color: $grey25;
.security-scheme {
background: $grey15;
border-color: $grey25;
}
.scheme-details {
dt {
color: $g15-platinum;
}
dd code {
background: $grey20;
}
}
.scheme-description {
border-top-color: $grey25;
}
}
.api-auth-form {
background: $grey15;
border-color: $grey25;
.auth-form-description {
color: $g15-platinum;
}
.auth-field input {
background: $grey20;
border-color: $grey25;
color: $g20-white;
}
}
}

View File

@ -22,6 +22,12 @@
<link rel="alternate" type="application/json" href="{{ $specPathJSON | absURL }}" title="OpenAPI Specification (JSON)" />
{{ end }}
{{/* Auth input component */}}
<div class="api-auth-input-wrapper"
data-component="api-auth-input"
data-schemes="bearer,basic">
</div>
{{/* Component container - TypeScript handles initialization */}}
<div class="api-reference-wrapper api-reference-mini"
data-component="rapidoc-mini"