@greatapps/common 1.1.588 → 1.1.589

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.
@@ -0,0 +1,26 @@
1
+ import "server-only";
2
+ import { cookies } from "next/headers";
3
+ import { ApiError } from "../../../infra/api/types";
4
+ const AUTH_COOKIE_NAME = "greatapps";
5
+ async function requireTokenNotExpired() {
6
+ if (process.env.DUMMY_AUTH_TOKEN) return;
7
+ const cookieStore = await cookies();
8
+ const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;
9
+ if (!token) {
10
+ throw new ApiError("Sess\xE3o inv\xE1lida ou expirada", "SESSION_INVALID", 401);
11
+ }
12
+ try {
13
+ const payload = JSON.parse(atob(token.split(".")[1]));
14
+ const now = Math.floor(Date.now() / 1e3);
15
+ if (!payload.exp || payload.exp <= now) {
16
+ throw new ApiError("Sess\xE3o inv\xE1lida ou expirada", "SESSION_INVALID", 401);
17
+ }
18
+ } catch (err) {
19
+ if (err instanceof ApiError) throw err;
20
+ throw new ApiError("Sess\xE3o inv\xE1lida ou expirada", "SESSION_INVALID", 401);
21
+ }
22
+ }
23
+ export {
24
+ requireTokenNotExpired
25
+ };
26
+ //# sourceMappingURL=require-token-not-expired.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/modules/auth/utils/require-token-not-expired.ts"],"sourcesContent":["import 'server-only';\n\nimport { cookies } from 'next/headers';\nimport { ApiError } from '../../../infra/api/types';\n\nconst AUTH_COOKIE_NAME = 'greatapps';\n\n/**\n * Verifica se o JWT do cookie `greatapps` não expirou checando o campo `exp`\n * no payload, sem bater no backend.\n *\n * - Se o token não existe → SESSION_INVALID 401\n * - Se o token é malformado → SESSION_INVALID 401\n * - Se `exp` não existe ou já passou → SESSION_INVALID 401\n *\n * Isso substitui o `requireValidSession` (que fazia POST /auth/keep) no\n * `safeMutationAction`, eliminando 1 round-trip ao backend por mutation.\n */\nexport async function requireTokenNotExpired(): Promise<void> {\n if (process.env.DUMMY_AUTH_TOKEN) return;\n\n const cookieStore = await cookies();\n const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;\n\n if (!token) {\n throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);\n }\n\n try {\n const payload = JSON.parse(atob(token.split('.')[1]));\n const now = Math.floor(Date.now() / 1000);\n\n if (!payload.exp || payload.exp <= now) {\n throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);\n }\n } catch (err) {\n if (err instanceof ApiError) throw err;\n throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);\n }\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAEzB,MAAM,mBAAmB;AAazB,eAAsB,yBAAwC;AAC5D,MAAI,QAAQ,IAAI,iBAAkB;AAElC,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,QAAQ,YAAY,IAAI,gBAAgB,GAAG;AAEjD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,SAAS,qCAA+B,mBAAmB,GAAG;AAAA,EAC1E;AAEA,MAAI;AACF,UAAM,UAAU,KAAK,MAAM,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AACpD,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAExC,QAAI,CAAC,QAAQ,OAAO,QAAQ,OAAO,KAAK;AACtC,YAAM,IAAI,SAAS,qCAA+B,mBAAmB,GAAG;AAAA,IAC1E;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,eAAe,SAAU,OAAM;AACnC,UAAM,IAAI,SAAS,qCAA+B,mBAAmB,GAAG;AAAA,EAC1E;AACF;","names":[]}
package/dist/server.mjs CHANGED
@@ -61,6 +61,7 @@ import {
61
61
  import { getClientInfoFromRequest } from "./infra/utils/client-info";
62
62
  import { getUserContext } from "./modules/auth/utils/get-user-context";
63
63
  import { requireValidSession } from "./modules/auth/utils/require-valid-session";
64
+ import { requireTokenNotExpired } from "./modules/auth/utils/require-token-not-expired";
64
65
  import { buildWlOverride, buildWlOverrideFromJwt } from "./modules/auth/utils/build-wl-override";
65
66
  import { safeServerAction } from "./utils/safeServerAction";
66
67
  import { safeMutationAction } from "./utils/safeMutationAction";
@@ -120,6 +121,7 @@ export {
120
121
  removeProjectUserAction,
121
122
  requestEmailChangeAction,
122
123
  requestPhoneChangeAction,
124
+ requireTokenNotExpired,
123
125
  requireValidSession,
124
126
  resolvePlanExtrasPrices,
125
127
  revalidateWhitelabelAction,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
@@ -1,9 +1,9 @@
1
1
  import "server-only";
2
- import { requireValidSession } from "../modules/auth/utils/require-valid-session";
2
+ import { requireTokenNotExpired } from "../modules/auth/utils/require-token-not-expired";
3
3
  import { safeServerAction } from "./safeServerAction";
4
4
  async function safeMutationAction(fn) {
5
5
  return safeServerAction(async () => {
6
- await requireValidSession();
6
+ await requireTokenNotExpired();
7
7
  return fn();
8
8
  });
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/safeMutationAction.ts"],"sourcesContent":["import 'server-only';\n\nimport type { ActionResult } from '../infra/api/types';\nimport { requireValidSession } from '../modules/auth/utils/require-valid-session';\nimport { safeServerAction } from './safeServerAction';\n\n/**\n * Wrapper para server actions que mutam estado (delete/update/create/publish/\n * billing/etc.). Faz validação de sessão contra o backend (`POST /auth/keep`)\n * antes de executar a ação e roteia erros via `safeServerAction`.\n *\n * A validação é deduplicada por request múltiplas actions sensíveis que\n * rodem dentro de uma mesma request pagam 1 round-trip ao backend, não N.\n *\n * Motivação (resumo): o backend confia no par `(whitelabel-token, id_account\n * no path)` nos endpoints de CRUD; o Next.js deriva `id_account` de\n * `atob(cookie)` sem verificar assinatura. Chamar `requireValidSession`\n * aqui garante que o cookie corresponde a uma sessão real no banco antes\n * de qualquer mutação.\n *\n * Para reads não-sensíveis, continue usando `safeServerAction` direto.\n */\nexport async function safeMutationAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n return safeServerAction(async () => {\n await requireValidSession();\n return fn();\n });\n}\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAkBjC,eAAsB,mBACpB,IAC0B;AAC1B,SAAO,iBAAiB,YAAY;AAClC,UAAM,oBAAoB;AAC1B,WAAO,GAAG;AAAA,EACZ,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/safeMutationAction.ts"],"sourcesContent":["import 'server-only';\n\nimport type { ActionResult } from '../infra/api/types';\nimport { requireTokenNotExpired } from '../modules/auth/utils/require-token-not-expired';\nimport { safeServerAction } from './safeServerAction';\n\n/**\n * Wrapper para server actions que mutam estado (delete/update/create/publish/\n * billing/etc.). Verifica se o JWT do cookie não expirou antes de executar\n * a ação e roteia erros via `safeServerAction`.\n *\n * A verificação é feita localmente decodificando o campo `exp` do JWT —\n * sem round-trip ao backend.\n *\n * Para reads não-sensíveis, continue usando `safeServerAction` direto.\n */\nexport async function safeMutationAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n return safeServerAction(async () => {\n await requireTokenNotExpired();\n return fn();\n });\n}\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,8BAA8B;AACvC,SAAS,wBAAwB;AAYjC,eAAsB,mBACpB,IAC0B;AAC1B,SAAO,iBAAiB,YAAY;AAClC,UAAM,uBAAuB;AAC7B,WAAO,GAAG;AAAA,EACZ,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.588",
3
+ "version": "1.1.589",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,40 @@
1
+ import 'server-only';
2
+
3
+ import { cookies } from 'next/headers';
4
+ import { ApiError } from '../../../infra/api/types';
5
+
6
+ const AUTH_COOKIE_NAME = 'greatapps';
7
+
8
+ /**
9
+ * Verifica se o JWT do cookie `greatapps` não expirou checando o campo `exp`
10
+ * no payload, sem bater no backend.
11
+ *
12
+ * - Se o token não existe → SESSION_INVALID 401
13
+ * - Se o token é malformado → SESSION_INVALID 401
14
+ * - Se `exp` não existe ou já passou → SESSION_INVALID 401
15
+ *
16
+ * Isso substitui o `requireValidSession` (que fazia POST /auth/keep) no
17
+ * `safeMutationAction`, eliminando 1 round-trip ao backend por mutation.
18
+ */
19
+ export async function requireTokenNotExpired(): Promise<void> {
20
+ if (process.env.DUMMY_AUTH_TOKEN) return;
21
+
22
+ const cookieStore = await cookies();
23
+ const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;
24
+
25
+ if (!token) {
26
+ throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);
27
+ }
28
+
29
+ try {
30
+ const payload = JSON.parse(atob(token.split('.')[1]));
31
+ const now = Math.floor(Date.now() / 1000);
32
+
33
+ if (!payload.exp || payload.exp <= now) {
34
+ throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);
35
+ }
36
+ } catch (err) {
37
+ if (err instanceof ApiError) throw err;
38
+ throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);
39
+ }
40
+ }
package/src/server.ts CHANGED
@@ -75,6 +75,7 @@ export {
75
75
  export { getClientInfoFromRequest } from './infra/utils/client-info';
76
76
  export { getUserContext } from './modules/auth/utils/get-user-context';
77
77
  export { requireValidSession } from './modules/auth/utils/require-valid-session';
78
+ export { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';
78
79
  export { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';
79
80
  export { safeServerAction } from './utils/safeServerAction';
80
81
  export { safeMutationAction } from './utils/safeMutationAction';
@@ -1,22 +1,16 @@
1
1
  import 'server-only';
2
2
 
3
3
  import type { ActionResult } from '../infra/api/types';
4
- import { requireValidSession } from '../modules/auth/utils/require-valid-session';
4
+ import { requireTokenNotExpired } from '../modules/auth/utils/require-token-not-expired';
5
5
  import { safeServerAction } from './safeServerAction';
6
6
 
7
7
  /**
8
8
  * Wrapper para server actions que mutam estado (delete/update/create/publish/
9
- * billing/etc.). Faz validação de sessão contra o backend (`POST /auth/keep`)
10
- * antes de executar a ação e roteia erros via `safeServerAction`.
9
+ * billing/etc.). Verifica se o JWT do cookie não expirou antes de executar
10
+ * a ação e roteia erros via `safeServerAction`.
11
11
  *
12
- * A validação é deduplicada por request múltiplas actions sensíveis que
13
- * rodem dentro de uma mesma request pagam 1 round-trip ao backend, não N.
14
- *
15
- * Motivação (resumo): o backend confia no par `(whitelabel-token, id_account
16
- * no path)` nos endpoints de CRUD; o Next.js deriva `id_account` de
17
- * `atob(cookie)` sem verificar assinatura. Chamar `requireValidSession`
18
- * aqui garante que o cookie corresponde a uma sessão real no banco antes
19
- * de qualquer mutação.
12
+ * A verificação é feita localmente decodificando o campo `exp` do JWT —
13
+ * sem round-trip ao backend.
20
14
  *
21
15
  * Para reads não-sensíveis, continue usando `safeServerAction` direto.
22
16
  */
@@ -24,7 +18,7 @@ export async function safeMutationAction<T>(
24
18
  fn: () => Promise<T>
25
19
  ): Promise<ActionResult<T>> {
26
20
  return safeServerAction(async () => {
27
- await requireValidSession();
21
+ await requireTokenNotExpired();
28
22
  return fn();
29
23
  });
30
24
  }