Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a061800
Strengthened User/AppUser RT lifecycle logic
NinjataWRLD Apr 17, 2026
7187b6c
Strengthened Register Validator
NinjataWRLD Apr 18, 2026
d2d854a
Critical Identity SSO fixes and improvements
NinjataWRLD Apr 24, 2026
ec85191
Removed `IdentityNames` metadata
NinjataWRLD Apr 24, 2026
08f4ca0
Significantly refactored Users/Roles constants
NinjataWRLD Apr 24, 2026
4311986
Added Head Designer seed and logic
NinjataWRLD Apr 24, 2026
5261dec
Forbid SSO users from resetting their password
NinjataWRLD Apr 24, 2026
eff067a
Improved file structure in Identity Infrastructure
NinjataWRLD Apr 25, 2026
bc1a69b
Improved JWT Events a tiny bit
NinjataWRLD Apr 25, 2026
a58aff5
Implemented soft delete for Accounts
NinjataWRLD Apr 25, 2026
a45bd25
Allowed First and Last name modification
NinjataWRLD Apr 27, 2026
1a63cf6
Formatted Validation Failures and configured Wolverine and FastEndpoi…
NinjataWRLD Apr 29, 2026
43f49ee
EF Core DI and Names assignment fixes
NinjataWRLD May 2, 2026
7caa80c
Greatly expanded ViewedProduct feature
NinjataWRLD May 2, 2026
da82f31
Implemented RefreshToken Fingerprint feature
NinjataWRLD May 11, 2026
5a100c8
Improved Accounts/Identity
NinjataWRLD May 11, 2026
0804d37
SDK update for Identity API
NinjataWRLD May 11, 2026
d4b4dda
Fixed Build & UnitTest errors
NinjataWRLD May 13, 2026
99496bf
Merge pull request #7 from CustomCADs/feature/identity-integration
NinjataWRLD May 13, 2026
4be4621
Empty Body Sanitization fix
NinjataWRLD May 13, 2026
033c0c9
Docker Bake typo
NinjataWRLD May 13, 2026
4bf6fb4
Tiny technical refactorings
NinjataWRLD May 13, 2026
d11955a
General SDK improvements
NinjataWRLD May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

<!--Infrastructure-->
<PackageVersion Include="AWSSDK.S3" Version="4.0.14.3" />
<PackageVersion Include="DeviceDetector.NET" Version="6.5.0" />
<PackageVersion Include="FluentEmail.Smtp" Version="3.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ target "awsecr_staging" {
tags = ["${awsecr_repostory}:staging"]
}

# Printing
# Production
target "production" {
context = "."
dockerfile = "Dockerfile.production"
Expand Down
4 changes: 3 additions & 1 deletion sdk/js/react/src/api/catalog/products/gallery/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ export const all = async (req: allResources.Request) =>
await axios.get<Result<allResources.Response>>(allResources.url(req));

export const single = async (req: singleResources.Request) =>
await axios.get<singleResources.Response>(singleResources.url(req));
await axios.get<singleResources.Response>(singleResources.url(req), {
headers: { 'X-Viewed-Product': req.viewed },
});

export const sortings = async () =>
await axios.get<sortingsResources.Response>(sortingsResources.url());
1 change: 1 addition & 0 deletions sdk/js/react/src/api/catalog/products/gallery/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CategoryDto, Counts, GALLERY_BASE_PATH } from '@/api/catalog/common';

export type Request = {
id: string;
viewed?: boolean;
};

export type Response = {
Expand Down
2 changes: 1 addition & 1 deletion sdk/js/react/src/api/common/exchange-rates/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Currency } from '@/types';
import { Currency } from '@/constants';

export type ExchangeRate = {
date: string;
Expand Down
13 changes: 13 additions & 0 deletions sdk/js/react/src/api/identity/common.ts
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export type ViewedProduct = {
id: string;
viewedAt: string;
};

export type Fingerprint = {
id: string;
device: string;
location?: string;
deleteAllowed: boolean;
issuedAt: string;
};

export const IDENTITY_BASE_PATH = '/identity';
9 changes: 9 additions & 0 deletions sdk/js/react/src/api/identity/identity/change-names.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IDENTITY_BASE_PATH } from '../common';

export type Request = {
username: string;
firstName?: string;
lastName?: string;
};

export const url = () => `${IDENTITY_BASE_PATH}/names`;
7 changes: 0 additions & 7 deletions sdk/js/react/src/api/identity/identity/change-username.ts

This file was deleted.

7 changes: 7 additions & 0 deletions sdk/js/react/src/api/identity/identity/delete-fingerprint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IDENTITY_BASE_PATH } from '../common';

export type Request = {
refreshTokenId: string;
};

export const url = () => `${IDENTITY_BASE_PATH}/fingerprint`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IDENTITY_BASE_PATH } from '../common';

export type Request = {
productId: string;
};

export const url = () => `${IDENTITY_BASE_PATH}/viewed-product`;
23 changes: 19 additions & 4 deletions sdk/js/react/src/api/identity/identity/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { axios } from '@/api/axios';
import { axios, config } from '@/api/axios';
import * as authnResources from './authn';
import * as authzResources from './authz';
import * as myAccountResources from './my-account';
Expand All @@ -7,7 +7,9 @@ import * as loginResources from './login';
import * as refreshResources from './refresh';
import * as logoutResources from './logout';
import * as deleteResources from './delete';
import * as changeUsernameResources from './change-username';
import * as deleteViewedProductResources from './delete-viewed-product';
import * as deleteFingerprintResources from './delete-fingerprint';
import * as changeNamesResources from './change-names';
import * as toggleTrackViewedProductsResources from './toggle-track-viewed-products';
import * as forgotPasswordResources from './forgot-password';
import * as resetPasswordResources from './reset-password';
Expand Down Expand Up @@ -37,14 +39,27 @@ export const refresh = async () =>

export const logout = async () => await axios.post(logoutResources.url());

export const changeUsername = async (req: changeUsernameResources.Request) =>
await axios.patch(changeUsernameResources.url(), req);
export const changeUsername = async (req: changeNamesResources.Request) =>
await axios.patch(changeNamesResources.url(), req);

export const toggleTrackViewedProducts = async () =>
await axios.patch(toggleTrackViewedProductsResources.url());

export const delete_ = async () => await axios.delete(deleteResources.url());

export const deleteViewedProduct = async (
req: deleteViewedProductResources.Request,
) =>
await axios.delete(
deleteViewedProductResources.url(),
config({ data: req }),
);

export const deleteFingerprint = async (
req: deleteFingerprintResources.Request,
) =>
await axios.delete(deleteFingerprintResources.url(), config({ data: req }));

export const forgotPassword = async (req: forgotPasswordResources.Request) =>
await axios.post(forgotPasswordResources.url(), req);

Expand Down
4 changes: 3 additions & 1 deletion sdk/js/react/src/api/identity/identity/my-account.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IDENTITY_BASE_PATH } from '../common';
import { IDENTITY_BASE_PATH, ViewedProduct, Fingerprint } from '../common';

export type Response = {
id: string;
Expand All @@ -9,6 +9,8 @@ export type Response = {
email: string;
trackViewedProducts: boolean;
createdAt: string;
viewedProducts: ViewedProduct[];
fingerprints: Fingerprint[];
};

export const url = () => `${IDENTITY_BASE_PATH}/my-account`;
3 changes: 2 additions & 1 deletion sdk/js/react/src/api/identity/identity/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type { ViewedProduct, Fingerprint } from '../common';
export type { Response as AuthnResponse } from './authn';
export type { Response as AuthzResponse } from './authz';
export type { Request as ChangeUsernameRequest } from './change-username';
export type { Request as ChangeNamesRequest } from './change-names';
export type { Request as ConfirmEmailRequest } from './confirm-email';
export type { Response as DownloadUserInfoResponse } from './download-info';
export type { Request as ForgotPasswordRequest } from './forgot-password';
Expand Down
76 changes: 35 additions & 41 deletions sdk/js/react/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
import { Currency, Rates, Symbols } from './types';

export const EXCHANGE_RATES = {
EUR: { rate: 1, symbol: '€', language: '' },
USD: { rate: 1.1646, symbol: '$', language: 'en-US' },
JPY: { rate: 173.1, symbol: '¥', language: 'ja-JP' },
BGN: { rate: 1.9558, symbol: 'лв', language: 'bg-BG' },
CZK: { rate: 24.485, symbol: 'Kč', language: 'cs-CZ' },
DKK: { rate: 7.4632, symbol: 'kr', language: 'da-DK' },
GBP: { rate: 0.8702, symbol: '£', language: 'en-GB' },
HUF: { rate: 395.58, symbol: 'Ft', language: 'hu-HU' },
PLN: { rate: 4.2653, symbol: 'zł', language: 'pl-PL' },
RON: { rate: 5.0822, symbol: 'lei', language: 'ro-RO' },
SEK: { rate: 11.003, symbol: 'kr', language: 'sv-SE' },
CHF: { rate: 0.9366, symbol: 'CHF', language: 'fr-CH' },
ISK: { rate: 143.6, symbol: 'Íkr', language: 'is-IS' },
NOK: { rate: 11.674, symbol: 'kr', language: 'no-NO' },
TRY: { rate: 47.9289, symbol: '₺', language: 'tr-TR' },
AUD: { rate: 1.7897, symbol: 'A$', language: 'en-AU' },
BRL: { rate: 6.3757, symbol: 'R$', language: 'pt-BR' },
CAD: { rate: 1.6056, symbol: 'CA$', language: 'en-CA' },
CNY: { rate: 8.3202, symbol: '¥', language: 'zh-CN' },
HKD: { rate: 9.0915, symbol: 'HK$', language: 'zh-HK' },
IDR: { rate: 19110.27, symbol: 'Rp', language: 'id-ID' },
ILS: { rate: 3.9478, symbol: '₪', language: 'he-IL' },
INR: { rate: 102.5795, symbol: '₹', language: 'hi-IN' },
KRW: { rate: 1625.01, symbol: '₩', language: 'ko-KR' },
MXN: { rate: 21.8726, symbol: '$', language: 'es-MX' },
MYR: { rate: 4.9263, symbol: 'RM', language: 'ms-MY' },
NZD: { rate: 1.9892, symbol: '$', language: 'en-NZ' },
PHP: { rate: 66.762, symbol: '₱', language: 'fil-PH' },
SGD: { rate: 1.5006, symbol: 'S$', language: 'en-SG' },
THB: { rate: 37.704, symbol: '฿', language: 'th-TH' },
ZAR: { rate: 20.6439, symbol: 'R', language: 'en-ZA' },
EUR: { rate: 1, symbol: '€', language: '', display: true },
USD: { rate: 1.1646, symbol: '$', language: 'en-US', display: true },
JPY: { rate: 173.1, symbol: '¥', language: 'ja-JP', display: true },
BGN: { rate: 1.9558, symbol: 'лв', language: 'bg-BG', display: false },
CZK: { rate: 24.485, symbol: 'Kč', language: 'cs-CZ', display: true },
DKK: { rate: 7.4632, symbol: 'kr', language: 'da-DK', display: true },
GBP: { rate: 0.8702, symbol: '£', language: 'en-GB', display: true },
HUF: { rate: 395.58, symbol: 'Ft', language: 'hu-HU', display: true },
PLN: { rate: 4.2653, symbol: 'zł', language: 'pl-PL', display: true },
RON: { rate: 5.0822, symbol: 'lei', language: 'ro-RO', display: true },
SEK: { rate: 11.003, symbol: 'kr', language: 'sv-SE', display: true },
CHF: { rate: 0.9366, symbol: 'CHF', language: 'fr-CH', display: true },
ISK: { rate: 143.6, symbol: 'Íkr', language: 'is-IS', display: true },
NOK: { rate: 11.674, symbol: 'kr', language: 'no-NO', display: true },
TRY: { rate: 47.9289, symbol: '₺', language: 'tr-TR', display: true },
AUD: { rate: 1.7897, symbol: 'A$', language: 'en-AU', display: true },
BRL: { rate: 6.3757, symbol: 'R$', language: 'pt-BR', display: true },
CAD: { rate: 1.6056, symbol: 'CA$', language: 'en-CA', display: true },
CNY: { rate: 8.3202, symbol: '¥', language: 'zh-CN', display: true },
HKD: { rate: 9.0915, symbol: 'HK$', language: 'zh-HK', display: true },
IDR: { rate: 19110.27, symbol: 'Rp', language: 'id-ID', display: true },
ILS: { rate: 3.9478, symbol: '₪', language: 'he-IL', display: true },
INR: { rate: 102.5795, symbol: '₹', language: 'hi-IN', display: true },
KRW: { rate: 1625.01, symbol: '₩', language: 'ko-KR', display: true },
MXN: { rate: 21.8726, symbol: '$', language: 'es-MX', display: true },
MYR: { rate: 4.9263, symbol: 'RM', language: 'ms-MY', display: true },
NZD: { rate: 1.9892, symbol: '$', language: 'en-NZ', display: true },
PHP: { rate: 66.762, symbol: '₱', language: 'fil-PH', display: true },
SGD: { rate: 1.5006, symbol: 'S$', language: 'en-SG', display: true },
THB: { rate: 37.704, symbol: '฿', language: 'th-TH', display: true },
ZAR: { rate: 20.6439, symbol: 'R', language: 'en-ZA', display: true },
} as const;

export const CURRENCIES = Object.keys(EXCHANGE_RATES) as Currency[];

export const RATES: Rates = Object.fromEntries(
Object.entries(EXCHANGE_RATES).map(([k, v]) => [k, v.rate]),
) as never;
export type Currency = keyof typeof EXCHANGE_RATES;

export const SYMBOLS: Symbols = Object.fromEntries(
Object.entries(EXCHANGE_RATES).map(([k, v]) => [k, v.symbol]),
) as never;
export const CURRENCIES = Object.entries(EXCHANGE_RATES)
.filter(([, v]) => v.display)
.map(([k]) => k) as Array<Currency>;
1 change: 1 addition & 0 deletions sdk/js/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * as queries from './queries';
export { useQuery } from './queries/useQuery';
export { useInfiniteQuery } from './queries/useInfiniteQuery';
export { queryCall } from './queries/queryCall';
export * as mutations from './mutations';
export { useMutation } from './mutations/useMutation';
20 changes: 16 additions & 4 deletions sdk/js/react/src/hooks/mutations/identity/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { identityApi as api } from '@/api';
import { Request as Login } from '@/api/identity/identity/login';
import { Request as ForgotPassword } from '@/api/identity/identity/forgot-password';
import { Request as ResetPassword } from '@/api/identity/identity/reset-password';
import { Request as ChangeUsername } from '@/api/identity/identity/change-username';
import { Request as ChangeNames } from '@/api/identity/identity/change-names';
import { Request as DeleteViewedProduct } from '@/api/identity/identity/delete-viewed-product';
import { Request as DeleteFingerprint } from '@/api/identity/identity/delete-fingerprint';
import { Request as Register } from '@/api/identity/identity/register';
import { Request as ConfirmEmail } from '@/api/identity/identity/confirm-email';
import { Request as RetryConfirmEmail } from '@/api/identity/identity/retry-confirm-email';
Expand All @@ -22,9 +24,9 @@ export const identity = {
mutationKey: [...BASE_KEY, 'refresh'],
mutationFn: async () => (await api.refresh()).data,
}),
changeUsername: mutationOptions({
mutationKey: [...BASE_KEY, 'change-username'],
mutationFn: async (params: ChangeUsername) =>
changeNames: mutationOptions({
mutationKey: [...BASE_KEY, 'change-names'],
mutationFn: async (params: ChangeNames) =>
(await api.changeUsername(params)).data,
}),
toggleTrackViewedProducts: mutationOptions({
Expand All @@ -35,6 +37,16 @@ export const identity = {
mutationKey: [...BASE_KEY, 'delete-my-account'],
mutationFn: async () => (await api.delete_()).data,
}),
deleteViewedProduct: mutationOptions({
mutationKey: [...BASE_KEY, 'delete-viewed-product'],
mutationFn: async (params: DeleteViewedProduct) =>
(await api.deleteViewedProduct(params)).data,
}),
deleteFingerprint: mutationOptions({
mutationKey: [...BASE_KEY, 'delete-fingerprint'],
mutationFn: async (params: DeleteFingerprint) =>
(await api.deleteFingerprint(params)).data,
}),
forgotPassword: mutationOptions({
mutationKey: [...BASE_KEY, 'forgot-password'],
mutationFn: async (params: ForgotPassword) =>
Expand Down
15 changes: 15 additions & 0 deletions sdk/js/react/src/hooks/queries/queryCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { AxiosResponse } from 'axios';
import { FetchQueryOptions } from '@tanstack/react-query';
import * as customcadsQueries from '.';

type Opts<TData, TKey extends readonly unknown[]> = FetchQueryOptions<
AxiosResponse<TData>,
Error,
AxiosResponse<TData>,
TKey
>;

export const queryCall = <TData, TKey extends readonly unknown[]>(
optionsSelector: (queries: typeof customcadsQueries) => Opts<TData, TKey>,
call: (options: Opts<TData, TKey>) => Promise<AxiosResponse<TData> | void>,
) => call(optionsSelector(customcadsQueries));
1 change: 0 additions & 1 deletion sdk/js/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './constants';
export type { Currency, Rate, Symbol } from './types';
export * from './api';
export * from './hooks';
13 changes: 0 additions & 13 deletions sdk/js/react/src/types.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/Modules/Accounts/API/Accounts/Endpoints/AccountsGroup.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
namespace CustomCADs.Modules.Accounts.API.Accounts.Endpoints;

using static APIConstants;
using static DomainConstants.Roles;
using static DomainConstants.Users;

public class AccountsGroup : Group
{
public AccountsGroup()
{
Configure(Paths.Accounts, x =>
{
x.Roles(Admin);
x.Roles(AdminRole);
x.Description(opt => opt.WithTags(Tags[Paths.Accounts]));
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/Modules/Accounts/API/Roles/Endpoints/RolesGroup.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
namespace CustomCADs.Modules.Accounts.API.Roles.Endpoints;

using static APIConstants;
using static DomainConstants.Roles;
using static DomainConstants.Users;

public class RolesGroup : Group
{
public RolesGroup()
{
Configure(Paths.Roles, x =>
{
x.Roles(Admin);
x.Roles(AdminRole);
x.Description(opt => opt.WithTags(Tags[Paths.Roles]));
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public async Task Handle(DeleteAccountCommand req, CancellationToken ct)
await uow.SaveChangesAsync(ct).ConfigureAwait(false);

await raiser.RaiseApplicationEventAsync(
@event: new AccountDeletedApplicationEvent(
account.Username
@event: new AccountDeletedApplicationEvent(
account.Id
)
).ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using CustomCADs.Modules.Accounts.Domain.Repositories;
using CustomCADs.Modules.Accounts.Domain.Repositories.Writes;
using CustomCADs.Shared.Application.UseCases.Accounts.Commands;

namespace CustomCADs.Modules.Accounts.Application.Accounts.Commands.Shared;

public class DeleteViewedProductHandler(IAccountWrites writes, IUnitOfWork uow) : ICommandHandler<DeleteViewedProductCommand>
{
public async Task Handle(DeleteViewedProductCommand req, CancellationToken ct = default)
{
await writes.UnviewProductAsync(req.CallerId, req.ProductId, ct).ConfigureAwait(false);
await uow.SaveChangesAsync(ct).ConfigureAwait(false);
}
}
Loading
Loading