@revenexx/cover 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app.config.ts +1 -1
- package/app/components/account/address/AccountAddressCard.vue +69 -88
- package/app/components/auth/AuthRegisterPanel.vue +122 -194
- package/app/formkit.config.ts +21 -0
- package/app/interfaces/auth.ts +0 -2
- package/app/interfaces/validation.ts +1 -1
- package/app/validations/formValidationConfig.ts +0 -30
- package/nuxt.config.ts +0 -3
- package/package.json +2 -2
- package/server/api/account/orders.get.ts +6 -5
- package/server/api/account/profile.get.ts +3 -3
- package/server/api/account/profile.put.ts +6 -4
- package/server/api/auth/login.post.ts +2 -5
- package/server/api/auth/recovery.post.ts +5 -13
- package/server/api/auth/recovery.put.ts +5 -14
- package/server/api/auth/register.post.ts +21 -44
- package/server/api/orders/index.post.ts +28 -24
- package/server/api/payment/methods.post.ts +5 -4
- package/server/api/shipping/rates.post.ts +6 -4
- package/server/interfaces/auth.ts +1 -1
- package/server/services/ApiAccountService.ts +44 -0
- package/server/services/ApiAuthService.ts +15 -14
- package/server/services/ApiCartService.ts +45 -27
- package/server/services/ApiMarketService.ts +3 -5
- package/server/utils/accountService.ts +4 -5
- package/server/utils/authService.ts +0 -3
- package/server/utils/liveCatalog.ts +22 -13
- package/server/utils/liveInventories.ts +4 -4
- package/server/utils/liveOrders.ts +5 -3
- package/server/utils/livePrices.ts +10 -9
- package/server/utils/revenexxSdk.ts +162 -0
- package/app/validations/companyName.ts +0 -18
- package/app/validations/maxLength.ts +0 -12
- package/app/validations/optionalCompanyName.ts +0 -23
- package/app/validations/phoneNumber.ts +0 -19
- package/app/validations/termsRequired.ts +0 -4
- package/app/validations/zipCode.ts +0 -15
- package/server/services/SdkAccountService.ts +0 -56
- package/server/services/SdkAuthService.ts +0 -83
- package/server/utils/revenexxApi.ts +0 -136
- package/server/utils/shopSdk.ts +0 -88
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { H3Event } from "h3";
|
|
2
|
-
|
|
3
|
-
import type { StoredSession } from "../../app/interfaces/auth";
|
|
4
|
-
import type { AccountUser, IAccountService } from "../interfaces/account";
|
|
5
|
-
|
|
6
|
-
function createInitials(name: string): string {
|
|
7
|
-
const parts = name
|
|
8
|
-
.trim()
|
|
9
|
-
.split(/\s+/)
|
|
10
|
-
.filter(Boolean);
|
|
11
|
-
if (parts.length === 0) {
|
|
12
|
-
return "";
|
|
13
|
-
}
|
|
14
|
-
return parts
|
|
15
|
-
.slice(0, 2)
|
|
16
|
-
.map(part => part[0]?.toUpperCase() ?? "")
|
|
17
|
-
.join("");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Live identity implementation backed by the revenexx web SDK.
|
|
22
|
-
* Resolves the current user from the request's session cookie and the
|
|
23
|
-
* platform account endpoint. Requires the NUXT_WEB_SDK_* runtime config.
|
|
24
|
-
* Register via app.config → accountService: "sdk".
|
|
25
|
-
*/
|
|
26
|
-
export class SdkAccountService implements IAccountService {
|
|
27
|
-
async getUser(event?: H3Event): Promise<AccountUser> {
|
|
28
|
-
if (!event) {
|
|
29
|
-
throw createError({ statusCode: 500, statusMessage: "SdkAccountService requires the request event" });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const rawSession = getCookie(event, SESSION_COOKIE_NAME);
|
|
33
|
-
if (!rawSession) {
|
|
34
|
-
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let session: StoredSession;
|
|
38
|
-
try {
|
|
39
|
-
session = JSON.parse(rawSession) as StoredSession;
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
43
|
-
}
|
|
44
|
-
if (!session.fallbackCookie) {
|
|
45
|
-
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const accountUser = await useAuthenticatedAccount(session.fallbackCookie).accountGet();
|
|
49
|
-
return {
|
|
50
|
-
id: accountUser.$id,
|
|
51
|
-
name: accountUser.name,
|
|
52
|
-
email: accountUser.email,
|
|
53
|
-
initials: createInitials(accountUser.name),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { H3Event } from "h3";
|
|
2
|
-
|
|
3
|
-
import type { AuthUser, StoredSession } from "../../app/interfaces/auth";
|
|
4
|
-
import type { AuthLoginResult, IAuthService } from "../interfaces/auth";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Live authentication via the revenexx web SDK. Session state is carried in
|
|
8
|
-
* the same cookie as the mock implementation (with fallbackCookie set), so
|
|
9
|
-
* the client-side flow is identical. Requires NUXT_WEB_SDK_* runtime config.
|
|
10
|
-
*/
|
|
11
|
-
export class SdkAuthService implements IAuthService {
|
|
12
|
-
async login(event: H3Event, email: string, password: string): Promise<AuthLoginResult> {
|
|
13
|
-
const { result: session, fallbackCookie } = await withCookieCapture(
|
|
14
|
-
() => useShopSdkAccount().accountCreateEmailPasswordSession({ email, password }),
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
const user = await useAuthenticatedAccount(fallbackCookie).accountGet();
|
|
18
|
-
|
|
19
|
-
const storedSession: StoredSession = {
|
|
20
|
-
id: session.$id,
|
|
21
|
-
expire: session.expire,
|
|
22
|
-
fallbackCookie,
|
|
23
|
-
};
|
|
24
|
-
setCookie(event, SESSION_COOKIE_NAME, JSON.stringify(storedSession), sessionCookieOptions(session.expire));
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
user: { $id: user.$id, name: user.name, email: user.email },
|
|
28
|
-
session: { id: session.$id, expire: session.expire },
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async me(event: H3Event): Promise<AuthUser | null> {
|
|
33
|
-
const raw = getCookie(event, SESSION_COOKIE_NAME);
|
|
34
|
-
if (!raw) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let parsed: StoredSession;
|
|
39
|
-
try {
|
|
40
|
-
parsed = JSON.parse(raw) as StoredSession;
|
|
41
|
-
}
|
|
42
|
-
catch (err) {
|
|
43
|
-
getLogService().error("Failed to parse session cookie", toErrorContext(err));
|
|
44
|
-
deleteCookie(event, SESSION_COOKIE_NAME, sessionCookieOptions());
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!parsed.fallbackCookie) {
|
|
49
|
-
deleteCookie(event, SESSION_COOKIE_NAME, sessionCookieOptions());
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const user = await useAuthenticatedAccount(parsed.fallbackCookie).accountGet();
|
|
55
|
-
return { $id: user.$id, name: user.name, email: user.email };
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
if (!isSdkUserError(err)) {
|
|
59
|
-
getLogService().error("SDK request failed: me/accountGet", sdkErrorContext(err));
|
|
60
|
-
}
|
|
61
|
-
deleteCookie(event, SESSION_COOKIE_NAME, sessionCookieOptions());
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async logout(event: H3Event): Promise<void> {
|
|
67
|
-
const raw = getCookie(event, SESSION_COOKIE_NAME);
|
|
68
|
-
if (raw) {
|
|
69
|
-
try {
|
|
70
|
-
const parsed = JSON.parse(raw) as StoredSession;
|
|
71
|
-
if (parsed.fallbackCookie) {
|
|
72
|
-
const sessionId = parsed.id || "current";
|
|
73
|
-
await useAuthenticatedAccount(parsed.fallbackCookie).accountDeleteSession({ sessionId });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
getLogService().error("SDK request failed: logout", sdkErrorContext(err));
|
|
78
|
-
// always clear the cookie regardless of SDK result
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
deleteCookie(event, SESSION_COOKIE_NAME, sessionCookieOptions());
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mini client for the public revenexx gateway (api.revenexx.com) — the
|
|
3
|
-
* stand-in for the generated web SDKs that do not exist yet. Every live
|
|
4
|
-
* "api"-keyed BFF implementation goes through this client; once the real
|
|
5
|
-
* SDKs land, only the service implementations swap, the BFF contracts stay.
|
|
6
|
-
*
|
|
7
|
-
* Auth model: tenant API key (server-side only, never reaches the client).
|
|
8
|
-
* Configure via runtime config / env:
|
|
9
|
-
* NUXT_REVENEXX_API_URL (default https://api.revenexx.com)
|
|
10
|
-
* NUXT_REVENEXX_TENANT (tenant slug, e.g. "revenexx")
|
|
11
|
-
* NUXT_REVENEXX_API_KEY (rvxk_… gateway key)
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export class RevenexxApiError extends Error {
|
|
15
|
-
readonly statusCode: number;
|
|
16
|
-
readonly type: string;
|
|
17
|
-
|
|
18
|
-
constructor(statusCode: number, type: string, message: string) {
|
|
19
|
-
super(message);
|
|
20
|
-
this.name = "RevenexxApiError";
|
|
21
|
-
this.statusCode = statusCode;
|
|
22
|
-
this.type = type;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Expected user-facing failures (wrong credentials, duplicate email, …) are
|
|
28
|
-
* forwarded to the client as-is; everything else is an infrastructure error.
|
|
29
|
-
*/
|
|
30
|
-
const API_USER_ERROR_STATUS = new Set([400, 401, 404, 409, 429]);
|
|
31
|
-
|
|
32
|
-
export function isApiUserError(err: unknown): err is RevenexxApiError {
|
|
33
|
-
return err instanceof RevenexxApiError && API_USER_ERROR_STATUS.has(err.statusCode);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The customers app's auth passthrough answers with a generic error envelope
|
|
38
|
-
* (no machine type). Map the HTTP status onto the platform error types the
|
|
39
|
-
* client-side auth store already translates, so mock/sdk/api modes produce
|
|
40
|
-
* identical UX for the standard failures.
|
|
41
|
-
*/
|
|
42
|
-
export function apiAuthErrorType(err: RevenexxApiError): string {
|
|
43
|
-
switch (err.statusCode) {
|
|
44
|
-
case 401: return "user_invalid_credentials";
|
|
45
|
-
case 404: return "user_not_found";
|
|
46
|
-
case 409: return "user_email_already_exists";
|
|
47
|
-
case 429: return "general_rate_limit_exceeded";
|
|
48
|
-
default: return err.type;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Diagnostic fields for structured logs — never includes the API key. */
|
|
53
|
-
export function apiErrorContext(err: unknown): Record<string, unknown> {
|
|
54
|
-
if (err instanceof RevenexxApiError) {
|
|
55
|
-
return { apiStatus: err.statusCode, apiType: err.type, apiMessage: err.message };
|
|
56
|
-
}
|
|
57
|
-
return { errorMessage: err instanceof Error ? err.message : String(err) };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface RevenexxApiRequest {
|
|
61
|
-
readonly method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
62
|
-
readonly query?: Record<string, string | number | boolean | undefined>;
|
|
63
|
-
readonly body?: unknown;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface RevenexxApiClient {
|
|
67
|
-
request<T>(path: string, options?: RevenexxApiRequest): Promise<T>;
|
|
68
|
-
get<T>(path: string, query?: RevenexxApiRequest["query"]): Promise<T>;
|
|
69
|
-
post<T>(path: string, body?: unknown): Promise<T>;
|
|
70
|
-
put<T>(path: string, body?: unknown): Promise<T>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let client: RevenexxApiClient | null = null;
|
|
74
|
-
|
|
75
|
-
export function useRevenexxApi(): RevenexxApiClient {
|
|
76
|
-
if (client) {
|
|
77
|
-
return client;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const config = useRuntimeConfig();
|
|
81
|
-
const baseUrl = String(config.revenexxApiUrl || "https://api.revenexx.com").replace(/\/+$/, "");
|
|
82
|
-
const tenant = String(config.revenexxTenant || "");
|
|
83
|
-
const apiKey = String(config.revenexxApiKey || "");
|
|
84
|
-
|
|
85
|
-
async function request<T>(path: string, options: RevenexxApiRequest = {}): Promise<T> {
|
|
86
|
-
if (!tenant || !apiKey) {
|
|
87
|
-
throw new RevenexxApiError(503, "api_not_configured", "revenexx API credentials are not configured (NUXT_REVENEXX_TENANT / NUXT_REVENEXX_API_KEY)");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const url = new URL(baseUrl + path);
|
|
91
|
-
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
92
|
-
if (value !== undefined) {
|
|
93
|
-
url.searchParams.set(key, String(value));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const response = await fetch(url, {
|
|
98
|
-
method: options.method ?? "GET",
|
|
99
|
-
headers: {
|
|
100
|
-
"X-Revenexx-Tenant": tenant,
|
|
101
|
-
"X-Revenexx-Api-Key": apiKey,
|
|
102
|
-
"Accept": "application/json",
|
|
103
|
-
...(options.body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
104
|
-
},
|
|
105
|
-
...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const text = await response.text();
|
|
109
|
-
let payload: unknown = null;
|
|
110
|
-
try {
|
|
111
|
-
payload = text ? JSON.parse(text) : null;
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
payload = null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (!response.ok) {
|
|
118
|
-
const envelope = (payload ?? {}) as { type?: string; message?: string; error?: string };
|
|
119
|
-
throw new RevenexxApiError(
|
|
120
|
-
response.status,
|
|
121
|
-
envelope.type ?? "api_error",
|
|
122
|
-
envelope.message ?? envelope.error ?? `revenexx API responded with ${response.status}`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return payload as T;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
client = {
|
|
130
|
-
request,
|
|
131
|
-
get: (path, query) => request(path, { query }),
|
|
132
|
-
post: (path, body) => request(path, { method: "POST", body }),
|
|
133
|
-
put: (path, body) => request(path, { method: "PUT", body }),
|
|
134
|
-
};
|
|
135
|
-
return client;
|
|
136
|
-
}
|
package/server/utils/shopSdk.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Account, Client, RevenexxException } from "@revenexx/sdk";
|
|
2
|
-
|
|
3
|
-
let baseClient: Client | null = null;
|
|
4
|
-
|
|
5
|
-
function getBaseClient(): Client {
|
|
6
|
-
if (baseClient) {
|
|
7
|
-
return baseClient;
|
|
8
|
-
}
|
|
9
|
-
const config = useRuntimeConfig();
|
|
10
|
-
baseClient = new Client();
|
|
11
|
-
baseClient
|
|
12
|
-
.setEndpoint(config.webSdkApiUrl as string)
|
|
13
|
-
.setProject(config.webSdkProject as string)
|
|
14
|
-
.setDevKey(config.webSdkDevKey as string);
|
|
15
|
-
return baseClient;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useShopSdkAccount(): Account {
|
|
19
|
-
return new Account(getBaseClient());
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function useAuthenticatedAccount(fallbackCookie: string): Account {
|
|
23
|
-
const config = useRuntimeConfig();
|
|
24
|
-
const client = new Client();
|
|
25
|
-
client
|
|
26
|
-
.setEndpoint(config.webSdkApiUrl as string)
|
|
27
|
-
.setProject(config.webSdkProject as string)
|
|
28
|
-
.setDevKey(config.webSdkDevKey as string);
|
|
29
|
-
client.headers["X-Fallback-Cookies"] = fallbackCookie;
|
|
30
|
-
return new Account(client);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const SDK_USER_ERROR_TYPES = new Set([
|
|
34
|
-
"user_invalid_credentials",
|
|
35
|
-
"user_not_found",
|
|
36
|
-
"user_blocked",
|
|
37
|
-
"general_rate_limit_exceeded",
|
|
38
|
-
"user_session_already_exists",
|
|
39
|
-
"user_email_already_exists",
|
|
40
|
-
"user_invalid_token",
|
|
41
|
-
"general_argument_invalid",
|
|
42
|
-
"password_recently_used",
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Returns true for expected user-facing errors (wrong credentials, rate limit, etc.)
|
|
47
|
-
* that should be forwarded to the client as-is without server-side logging.
|
|
48
|
-
* Everything else is treated as an infrastructure error.
|
|
49
|
-
*/
|
|
50
|
-
export function isSdkUserError(err: unknown): err is RevenexxException {
|
|
51
|
-
return err instanceof RevenexxException && SDK_USER_ERROR_TYPES.has(err.type);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Extracts diagnostic fields from an SDK error for structured log entries.
|
|
56
|
-
* Does not include secrets (API key is never logged).
|
|
57
|
-
*/
|
|
58
|
-
export function sdkErrorContext(err: unknown): Record<string, unknown> {
|
|
59
|
-
if (err instanceof RevenexxException) {
|
|
60
|
-
return { sdkCode: err.code, sdkType: err.type, sdkMessage: err.message };
|
|
61
|
-
}
|
|
62
|
-
return { errorMessage: err instanceof Error ? err.message : String(err) };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function withCookieCapture<T>(
|
|
66
|
-
fn: () => Promise<T>,
|
|
67
|
-
): Promise<{ result: T; fallbackCookie: string }> {
|
|
68
|
-
let fallbackCookie = "";
|
|
69
|
-
const origFetch = globalThis.fetch;
|
|
70
|
-
globalThis.fetch = async (
|
|
71
|
-
input: RequestInfo | URL,
|
|
72
|
-
init?: RequestInit,
|
|
73
|
-
) => {
|
|
74
|
-
const res = await origFetch(input, init);
|
|
75
|
-
const header = res.headers.get("x-fallback-cookies");
|
|
76
|
-
if (header) {
|
|
77
|
-
fallbackCookie = header;
|
|
78
|
-
}
|
|
79
|
-
return res;
|
|
80
|
-
};
|
|
81
|
-
try {
|
|
82
|
-
const result = await fn();
|
|
83
|
-
return { result, fallbackCookie };
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
globalThis.fetch = origFetch;
|
|
87
|
-
}
|
|
88
|
-
}
|