Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 0 additions & 1 deletion _dev/docker/mono/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ done

# `npx yarn` because `npm i -g yarn` needs sudo
npx yarn install
npx yarn gql:allowlist
NODE_OPTIONS="--max-old-space-size=7168" CHOKIDAR_USEPOLLING=true SKIP_PREFLIGHT_CHECK=true BUILD_TARGETS=stage,prod,dev npx nx run-many -t build --all --verbose --skip-nx-cache

# This will reduce packages to only production dependencies
Expand Down
4 changes: 0 additions & 4 deletions _dev/pm2/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ cd "$DIR/../.."
# Make sure there is a common docker network fxa, so containers can
# communicate with one another if needed
_dev/pm2/create-docker-net.sh fxa

# Searches for and extracts gql queries from code
yarn gql:allowlist

pm2 start _dev/pm2/infrastructure.config.js

echo "waiting for containers to start"
Expand Down
3 changes: 0 additions & 3 deletions configs/gql/allowlist/gql-playground.json

This file was deleted.

13 changes: 1 addition & 12 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,13 @@
"outputs": ["{projectRoot}/build", "{projectRoot}/dist"],
"cache": true
},
"gql-copy": {
"dependsOn": [
{
"projects": ["fxa-admin-panel"],
"target": "gql-extract"
}
],
"inputs": ["typescript", "^typescript"],
"outputs": ["{projectRoot}/src/config/gql/allowlist"],
"cache": true
},
"lint": {
"inputs": ["lint", "{workspaceRoot}/.eslintrc.json"],
"outputs": ["{projectRoot}/.eslintcache"],
"cache": true
},
"prebuild": {
"dependsOn": ["gql-copy"],
"dependsOn": [],
"inputs": [],
"outputs": [
"{projectRoot}/public/locales",
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"l10n:prime": "_scripts/l10n/prime.sh",
"l10n:bundle": "_scripts/l10n/bundle.sh",
"legal:clone": "_scripts/clone-legal-docs.sh",
"gql:allowlist": "nx run-many -t gql-extract && nx run-many -t gql-copy",
"check:mysql": "_scripts/check-mysql.sh",
"check:url": "_scripts/check-url.sh",
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gql:allowlist script is removed here, but packages/fxa-admin-panel/pm2.config.js still runs yarn gql:allowlist (app name admin-gql-allowlist). This will break yarn start/PM2 workflows unless that PM2 entry is removed/updated or an alternative script is kept.

Suggested change
"check:url": "_scripts/check-url.sh",
"check:url": "_scripts/check-url.sh",
"gql:allowlist": "echo 'Skipping gql allowlist generation; script deprecated.'",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets just remove it

"prepare": "husky",
Expand Down Expand Up @@ -276,7 +275,6 @@
"mocha-multi": "^1.1.7",
"nx": "21.2.4",
"nx-cloud": "19.1.0",
"persistgraphql": "^0.3.11",
"postcss": "8.5.0",
"react-test-renderer": "^18.3.1",
"reflect-metadata": "^0.2.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/functional-tests/scripts/start-services.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ NODE_OPTIONS="--max-old-space-size=7168" NODE_ENV=test npx nx run-many \
--verbose \
-p \
123done \
fxa-admin-panel \
fxa-admin-server \
fxa-auth-server \
fxa-content-server \
fxa-payments-server \
Expand Down
70 changes: 70 additions & 0 deletions packages/functional-tests/tests/admin/adminPanel.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { expect, test } from '../../lib/fixtures/standard';

const ADMIN_PANEL_URL = process.env.ADMIN_PANEL_URL ?? 'http://localhost:8091';
const ADMIN_SERVER_URL = process.env.ADMIN_SERVER_URL ?? 'http://localhost:8095';

// Admin panel tests only run locally (stage/prod require SSO)
test.skip(({ target }) => target.name !== 'local');

test.describe('Admin Panel', () => {
test('admin panel loads and renders navigation', async ({ page }) => {
await page.goto(ADMIN_PANEL_URL);
await expect(
page.getByRole('link', { name: /account search/i })
).toBeVisible();
});

test('admin panel heartbeat is healthy', async () => {
const res = await fetch(`${ADMIN_PANEL_URL}/__lbheartbeat__`);
expect(res.status).toBe(200);
});

test('admin server heartbeat is healthy', async () => {
const res = await fetch(`${ADMIN_SERVER_URL}/__lbheartbeat__`);
expect(res.status).toBe(200);
});

test('account search by email returns account data via API', async ({
target,
testAccountTracker,
}) => {
const credentials = testAccountTracker.generateAccountDetails();
await target.createAccount(credentials.email, credentials.password);

const res = await fetch(
`${ADMIN_SERVER_URL}/api/account/by-email?email=${encodeURIComponent(credentials.email)}`,
{
headers: {
'oidc-claim-id-token-email': 'test-admin@mozilla.com',
'remote-groups': 'vpn_fxa_admin_panel_prod',
},
}
);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.email).toBe(credentials.email);
});

test('account search from UI shows results', async ({
target,
page,
testAccountTracker,
}) => {
const credentials = testAccountTracker.generateAccountDetails();
await target.createAccount(credentials.email, credentials.password);

await page.goto(`${ADMIN_PANEL_URL}/account-search`);

const searchInput = page.getByTestId('email-input');
await searchInput.fill(credentials.email);
await page.getByTestId('search-button').click();

await expect(page.getByText(credentials.email)).toBeVisible({
timeout: 10000,
});
});
});
1 change: 0 additions & 1 deletion packages/fxa-admin-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"jest": "27.5.1",
"jest-watch-typeahead": "0.6.5",
"nx": "21.2.4",
"persistgraphql": "^0.3.11",
"pm2": "^6.0.14",
"postcss-import": "^16.1.0",
"prettier": "^3.5.3",
Expand Down
8 changes: 0 additions & 8 deletions packages/fxa-admin-panel/pm2.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,5 @@ module.exports = {
ignore_watch: ['src/styles/tailwind.out.css'],
time: true,
},
{
name: 'admin-gql-allowlist',
autorestart: false,
script: 'yarn gql:allowlist',
watch: ['src/**/*.ts'],
cwd: __dirname,
filter_env: ['npm_'],
},
],
};
3 changes: 1 addition & 2 deletions packages/fxa-admin-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"description": "FxA Admin Server",
"scripts": {
"prebuild": "yarn clean",
"gql-copy": "mkdir -p src/config/gql/allowlist/ && cp ../../configs/gql/allowlist/*.json src/config/gql/allowlist/.",
"build": "nest build && yarn copy-config && yarn gql-copy && yarn copy-email-assets && yarn copy-email-l10n-assets",
"build": "nest build && yarn copy-config && yarn copy-email-assets && yarn copy-email-l10n-assets",
"copy-config": "cp ./src/config/*.json ./dist/packages/fxa-admin-server/src/config",
"copy-email-assets": "copyfiles --up 1 '../../libs/accounts/email-renderer/**/*.{mjml,ftl,txt,css}' dist/libs/ ",
"copy-email-l10n-assets": "copyfiles --up 1 '../../libs/accounts/email-renderer/public/locales' dist/libs/accounts/email-render/public/locales ",
Expand Down
17 changes: 0 additions & 17 deletions packages/fxa-admin-server/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,6 @@ convict.addFormats(require('convict-format-with-moment'));
convict.addFormats(require('convict-format-with-validator'));

const conf = convict({
gql: {
allowlist: {
doc: 'A list of json files holding allowed gql queries',
env: 'GQL_ALLOWLIST',
default: [
'src/config/gql/allowlist/fxa-admin-panel.json',
'src/config/gql/allowlist/gql-playground.json',
],
format: Array,
},
enabled: {
doc: 'Toggles whether or not gql queries are checked against the allowlist.',
env: 'GQL_ALLOWLIST_ENABLED',
default: true,
format: Boolean,
},
},
authHeader: {
default: USER_EMAIL_HEADER,
doc: 'Authentication header that should be logged for the user',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Field, ObjectType } from '@nestjs/graphql';

export enum AccountDeleteStatus {
Success = 'Success',
Failure = 'Failure',
NoAccount = 'No account found',
}

@ObjectType()
export class AccountDeleteResponse {
/** Name of task held in the task queue. This can be used to get task's status later. */
@Field({ nullable: false })
public taskName!: string;

/** A valid account email or UID */
@Field({ nullable: false })
public locator!: string;

/** A short status message. */
@Field({ nullable: false })
status!: AccountDeleteStatus;
}

@ObjectType()
export class AccountDeleteTaskStatus {
/** Name of task held in the task queue.. */
@Field({ nullable: false })
public taskName!: string;

/** A short status message. */
@Field({ nullable: false })
status!: string;
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Field, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class AccountEvent {
@Field({ nullable: true })
public name!: string;

@Field({ nullable: true })
public createdAt!: number;

@Field({ nullable: true })
eventType!: string;

// Email event based properties
@Field({ nullable: true })
template!: string;

// Metrics properties
@Field({ nullable: true })
flowId!: string;

@Field({ nullable: true })
service!: string;
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Field, ObjectType } from '@nestjs/graphql';

export enum AccountResetStatus {
Success = 'Success',
Failure = 'Failure',
NoAccount = 'No account found',
}

@ObjectType()
export class AccountResetResponse {
@Field({ nullable: false })
locator!: string;

@Field({ nullable: false })
status!: string;
}
23 changes: 0 additions & 23 deletions packages/fxa-admin-server/src/rest/model/account.model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Field, ID, ObjectType } from '@nestjs/graphql';

import { AttachedClient } from './attached-clients.model';
import { EmailBounce } from './email-bounces.model';
Expand All @@ -16,68 +15,46 @@ import { Cart } from './cart.model';
import { BackupCodes } from './backup-code.model';
import { RecoveryPhone } from './recovery-phone.model';

@ObjectType()
export class Account {
@Field((type) => ID)
public uid!: string;

@Field()
public email!: string;

@Field()
public emailVerified!: boolean;

@Field({ nullable: true })
public clientSalt?: string;

@Field()
public createdAt!: number;

@Field({ nullable: true })
public disabledAt?: number;

@Field({ nullable: true })
public locale?: string;

@Field({ nullable: true })
public lockedAt?: number;

@Field({ nullable: true })
public verifierSetAt?: number;

@Field((type) => [Email], { nullable: true })
public emails!: Email[];

@Field((type) => [EmailBounce], { nullable: true })
public emailBounces!: EmailBounce[];

@Field((type) => [Totp], { nullable: true })
public totp!: Totp[];

@Field((type) => [RecoveryKeys], { nullable: true })
public recoveryKeys!: RecoveryKeys[];

@Field((type) => [SecurityEvents], { nullable: true })
public securityEvents!: SecurityEvents[];

@Field((type) => [AttachedClient], { nullable: true })
public attachedClients!: AttachedClient[];

@Field((type) => [MozSubscription], { nullable: true })
public subscriptions!: MozSubscription[];

@Field((type) => [LinkedAccount], { nullable: true })
public linkedAccounts!: LinkedAccount[];

@Field((type) => [AccountEvent], { nullable: true })
public accountEvents!: AccountEvent[];

@Field((type) => [Cart], { nullable: true })
public carts!: Cart[];

@Field((type) => [BackupCodes], { nullable: true })
public backupCodes!: BackupCodes[];

@Field((type) => [RecoveryPhone], { nullable: true })
public recoveryPhone!: RecoveryPhone[];
}
Loading
Loading