Skip to content

Commit 1bfd643

Browse files
authored
Merge pull request #4 from spoo-me/feat/client-header
feat: send X-Spoo-Client header on all spoo.me API requests
2 parents 0533ec0 + b176698 commit 1bfd643

6 files changed

Lines changed: 18 additions & 9 deletions

File tree

src/api/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
VerifyEmailRequest,
1414
VerifyEmailResponse,
1515
} from "@/api/types";
16-
import { AUTH_ENDPOINTS } from "@/lib/constants";
16+
import { AUTH_ENDPOINTS, CLIENT_HEADER, CLIENT_HEADER_VALUE } from "@/lib/constants";
1717
import { accessTokenStorage, refreshTokenStorage } from "@/lib/storage";
1818
import {
1919
loginResponseSchema,
@@ -99,7 +99,7 @@ export async function refreshAccessToken(): Promise<boolean> {
9999
try {
100100
const res = await fetch(AUTH_ENDPOINTS.deviceRefresh, {
101101
method: "POST",
102-
headers: { "Content-Type": "application/json" },
102+
headers: { "Content-Type": "application/json", [CLIENT_HEADER]: CLIENT_HEADER_VALUE },
103103
body: JSON.stringify({ refresh_token: refreshToken }),
104104
signal: AbortSignal.timeout(10_000),
105105
});

src/api/client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ApiErrorResponse } from "@/api/types";
2-
import { AUTH_ENDPOINTS } from "@/lib/constants";
2+
import { AUTH_ENDPOINTS, CLIENT_HEADER, CLIENT_HEADER_VALUE } from "@/lib/constants";
33
import { ApiError, isOffline, NetworkError } from "@/lib/errors";
44
import {
55
accessTokenStorage,
@@ -43,7 +43,7 @@ async function refreshAccessToken(): Promise<string | null> {
4343
try {
4444
const res = await fetch(AUTH_ENDPOINTS.deviceRefresh, {
4545
method: "POST",
46-
headers: { "Content-Type": "application/json" },
46+
headers: { "Content-Type": "application/json", [CLIENT_HEADER]: CLIENT_HEADER_VALUE },
4747
body: JSON.stringify({ refresh_token: refreshToken }),
4848
});
4949

@@ -88,6 +88,9 @@ export async function request<T>(
8888
}
8989
}
9090

91+
// Client attribution header (all request() URLs point at the spoo.me backend)
92+
headers[CLIENT_HEADER] = CLIENT_HEADER_VALUE;
93+
9194
// Auth header
9295
if (!noAuth) {
9396
const authHeader = await getAuthHeader();

src/api/export.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ExportQuery } from "@/api/types";
2-
import { API_V1 } from "@/lib/constants";
2+
import { API_V1, CLIENT_HEADER, CLIENT_HEADER_VALUE } from "@/lib/constants";
33
import { isOffline, NetworkError } from "@/lib/errors";
44
import { accessTokenStorage, apiKeyStorage, authModeStorage } from "@/lib/storage";
55

@@ -17,7 +17,7 @@ export async function exportStats(query: ExportQuery): Promise<Blob> {
1717
}
1818
}
1919

20-
const headers: Record<string, string> = {};
20+
const headers: Record<string, string> = { [CLIENT_HEADER]: CLIENT_HEADER_VALUE };
2121
const mode = await authModeStorage.getValue();
2222
if (mode === "apikey") {
2323
const key = await apiKeyStorage.getValue();

src/api/stats.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { request } from "@/api/client";
22
import type { StatsQuery, StatsResponse } from "@/api/types";
3-
import { API_BASE_URL, API_V1 } from "@/lib/constants";
3+
import { API_BASE_URL, API_V1, CLIENT_HEADER, CLIENT_HEADER_VALUE } from "@/lib/constants";
44
import { ApiError } from "@/lib/errors";
55
import { statsResponseSchema } from "@/schemas/api";
66

@@ -50,7 +50,7 @@ export interface V0StatsResponse {
5050
*/
5151
export async function getStatsV0(shortCode: string, password?: string): Promise<V0StatsResponse> {
5252
const url = `${API_BASE_URL}/stats/${shortCode}`;
53-
const headers: Record<string, string> = {};
53+
const headers: Record<string, string> = { [CLIENT_HEADER]: CLIENT_HEADER_VALUE };
5454

5555
const body = password ? new URLSearchParams({ password }) : undefined;
5656
if (body) {

src/entrypoints/background/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
ACCESS_TOKEN_TTL_MS,
66
API_BASE_URL,
77
AUTH_ENDPOINTS,
8+
CLIENT_HEADER,
9+
CLIENT_HEADER_VALUE,
810
HISTORY_MAX_ITEMS,
911
QR_BRAND,
1012
TOKEN_REFRESH_BUFFER_MS,
@@ -139,7 +141,7 @@ async function handleTokenRefresh(): Promise<void> {
139141
async function exchangeDeviceCode(code: string): Promise<void> {
140142
const res = await fetch(AUTH_ENDPOINTS.deviceToken, {
141143
method: "POST",
142-
headers: { "Content-Type": "application/json" },
144+
headers: { "Content-Type": "application/json", [CLIENT_HEADER]: CLIENT_HEADER_VALUE },
143145
body: JSON.stringify({ code }),
144146
});
145147

src/lib/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export const QR_API_BASE_URL = import.meta.env.VITE_QR_API_BASE_URL || "https://
44
export const API_V1 = `${API_BASE_URL}/api/v1`;
55
export const QR_API_V1 = `${QR_API_BASE_URL}/api/v1`;
66

7+
/** Identifies this client to the spoo.me API. Never sent to third-party hosts. */
8+
export const CLIENT_HEADER = "X-Spoo-Client";
9+
export const CLIENT_HEADER_VALUE = `snap/${browser.runtime.getManifest().version}`;
10+
711
export const AUTH_ENDPOINTS = {
812
login: `${API_BASE_URL}/auth/login`,
913
register: `${API_BASE_URL}/auth/register`,

0 commit comments

Comments
 (0)