Skip to content
Merged
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
24 changes: 15 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ jobs:
is_production: ${{ steps.build_ext.outputs.is_production }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

# The changelog needs the history and tags of this branch only.
# fetch-depth: 0 also downloads the build/* deploy branches (about
# 560 MB against 30 MB).
- name: Fetch branch history and tags
run: git fetch --unshallow --tags origin "$GITHUB_REF"

- name: Setup Bun
uses: oven-sh/setup-bun@v2
Expand All @@ -38,11 +42,6 @@ jobs:
with:
tool: git-cliff@2.13.1

# Do not set GITHUB_REPO. cliff.toml uses no GitHub metadata, and
# the unauthenticated metadata requests hit the API rate limit.
- name: Generate application changelog
run: git-cliff --config cliff.toml --output "${RUNNER_TEMP}/CHANGELOG.md"

- name: Extract branch name
id: branch_name
shell: bash
Expand Down Expand Up @@ -78,8 +77,15 @@ jobs:
# Do not restore an Nx cache for this build. Nx does not hash the
# gitignored src/env/version.ts, so a cached build could deploy
# the version info of an earlier commit.
- name: Build application
run: bun run build -- --verbose --configuration=${{ steps.build_ext.outputs.configuration }}
# git-cliff runs alongside the build; `wait` fails the step if it fails.
# Do not set GITHUB_REPO. cliff.toml uses no GitHub metadata, and
# the unauthenticated metadata requests hit the API rate limit.
- name: Build application and changelog
run: |
git-cliff --config cliff.toml --output "${RUNNER_TEMP}/CHANGELOG.md" &
cliff_pid=$!
bun run build -- --verbose --configuration=${{ steps.build_ext.outputs.configuration }}
wait "$cliff_pid"

- name: Add application changelog to build
run: cp "${RUNNER_TEMP}/CHANGELOG.md" dist/backoffice/browser/CHANGELOG.md
Expand Down
21 changes: 3 additions & 18 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Setup Bun
uses: oven-sh/setup-bun@v2
Expand Down Expand Up @@ -82,26 +82,11 @@ jobs:
- name: Setup version
run: bun run postinstall

- name: Get Playwright version
id: playwright
run: echo "version=$(bunx playwright --version | awk '{print $2}')" >> "$GITHUB_OUTPUT"

- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}-${{ env.CHROMIUM_ONLY == 'true' && 'chromium' || 'all' }}

# A browser cache saves no time here: a hit still installs the
# system packages, and a restore takes about as long as a download.
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: bunx playwright install --with-deps ${{ env.CHROMIUM_ONLY == 'true' && 'chromium' || '' }}

# The cache holds the browsers only. The system packages still need to be installed.
- name: Install Playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: bunx playwright install-deps ${{ env.CHROMIUM_ONLY == 'true' && 'chromium' || '' }}

- name: Run E2E tests
run: bunx nx e2e e2e ${{ env.CHROMIUM_ONLY == 'true' && '-- --project=chromium --project=mobile-chrome' || '' }}

Expand Down
3 changes: 0 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions docs/bundle-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ shares concurrent requests, has a 30-second timeout, and permits retry. Componen
must check whether they were destroyed before creating an editor after the load.

`markdown-languages.ts` lists the existing web languages and aliases explicitly.
Only `github-dark` is packaged. Shiki uses the JavaScript regex engine, so the
Oniguruma wasm is not downloaded. Add new language loaders there. Importing the
Add new language loaders there. Only `github-dark` is packaged. Shiki uses the
JavaScript regex engine, so the Oniguruma wasm is not downloaded. Importing the
Shiki web preset at runtime also brings its unused theme registry into the build.

The markdown pipe loads `markdown-render.ts`, which holds marked and Shiki, on
first use. Pages without markdown do not download them. Do not import that file
statically.

## Themes and caching

`src/material-theme.scss` retains the indigo-pink palette and includes the Material
Expand Down
3 changes: 0 additions & 3 deletions e2e/src/pages/base.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ export abstract class BasePage {
await this.page
.waitForSelector('sidebar-menu, item-sidebar', { timeout: 30000 })
.catch(() => undefined);

// Wait for any progress bars to complete
await this.page.waitForTimeout(500);
}

/**
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
"@xterm/xterm": "^5.5.0",
"angular-eslint": "22.6.0",
"autoprefixer": "^10.4.16",
"blob-util": "^2.0.2",
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"eslint": "^9.8.0",
Expand Down
13 changes: 7 additions & 6 deletions project.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@
{
"glob": "**/*",
"input": "node_modules/monaco-editor/min/",
"output": "./assets/monaco/min/"
},
{
"input": "src/environments/dev",
"output": "/assets",
"glob": "*.js"
"output": "./assets/monaco/min/",
"ignore": [
"**/ts.worker-*.js",
"**/css.worker-*.js",
"**/html.worker-*.js",
"**/nls.messages.*.js"
]
},
{
"input": "node_modules/ts-md5/dist/",
Expand Down
23 changes: 0 additions & 23 deletions src/app/admin/api-keys/api-keys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
PlaceDomain,
PlaceUser,
query,
queryUsers,
remove,
showUser,
update,
Expand Down Expand Up @@ -38,7 +37,6 @@ export class APIKeyService {
private _dialog = inject(MatDialog);
private _admin_data = inject(AdminDataService);

private _search = signal('');
private _domain = this._admin_data.selectedDomain('api-keys');
private _last_key = signal<PlaceAPIKeyDetails>(null);
private _change = signal(0);
Expand Down Expand Up @@ -84,23 +82,6 @@ export class APIKeyService {
this._available_keys.hasValue() ? this._available_keys.value() : [],
);

private readonly _users = resource({
params: () => ({
domain: this._domain(),
q: this._search(),
change: this._change(),
}),
loader: async ({ params }) => {
const { domain, q } = params;
if (!domain) return [] as PlaceUser[];
return (await queryUsers({ authority_id: domain.id, q })).data;
},
});

public readonly users = computed(() =>
this._users.hasValue() ? this._users.value() : [],
);

public setDomain(domain: PlaceDomain) {
this._admin_data.setDomain('api-keys', domain);
}
Expand All @@ -109,10 +90,6 @@ export class APIKeyService {
return this._admin_data.selectDefaultDomain('api-keys');
}

public setSearch(s: string) {
this._search.set(s);
}

public async newKey() {
const ref = this._dialog.open(APIKeyModalComponent, {
data: { domain: this._domain() },
Expand Down
43 changes: 27 additions & 16 deletions src/app/admin/resource-imports.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { addSystem, query, querySystemsWithEmails } from '@placeos/ts-client';
import { escapeHtml } from '../common/general';
import { readError } from '../common/errors';
import { escapeHtml } from '../common/general';
import { i18n } from '../common/locale.service';
import {
notifyError,
notifySuccess,
notifyWarn,
} from '../common/notifications';
import { collectPages } from '../common/support-access';
import { openConfirmModal } from '../overlays/confirm-modal.component';
import { IconComponent } from '../ui/icon.component';
import { SimpleTableComponent } from '../ui/simple-table.component';
Expand All @@ -25,6 +26,8 @@ import { forEachWithLimit } from './signage-plugins/signage-plugins.utilities';

/** Max number of systems created at once */
const IMPORT_LIMIT = 5;
/** Emails per systems lookup. 100 emails make a URL of about 3 KB. */
const EMAIL_CHUNK_SIZE = 100;

export interface ExternalResource {
id: string;
Expand Down Expand Up @@ -326,23 +329,31 @@ export class ResourceImportsComponent implements OnInit {
system_id: '',
}));
const emails = list.map((_) => _.email).filter((_) => _);
const { data } = emails.length
? await querySystemsWithEmails({ in: emails.join(',') }).catch(
async (err) => {
notifyError(
`Failed to check imported resources. Error: ${await readError(err)}`,
);
return { data: [] };
},
)
: { data: [] };
for (const resource of list) {
const system = data.find(
(_) => _.email?.toLowerCase() === resource.email.toLowerCase(),
const system_ids = new Map<string, string>();
try {
// Send the emails in chunks to keep each URL under common 8 KB limits
for (let i = 0; i < emails.length; i += EMAIL_CHUNK_SIZE) {
const systems = await collectPages(
querySystemsWithEmails({
in: emails.slice(i, i + EMAIL_CHUNK_SIZE).join(','),
}),
);
for (const system of systems) {
const email = system.email?.toLowerCase();
if (email && !system_ids.has(email))
system_ids.set(email, system.id);
}
}
} catch (err) {
notifyError(
`Failed to check imported resources. Error: ${await readError(err)}`,
);
if (system) {
}
for (const resource of list) {
const system_id = system_ids.get(resource.email.toLowerCase());
if (system_id) {
resource.imported = true;
resource.system_id = system.id;
resource.system_id = system_id;
}
}
this.resource_list.set(list);
Expand Down
33 changes: 25 additions & 8 deletions src/app/common/locale.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,19 +206,14 @@ export class LocaleService {
);
if (!existing.expiry || existing.expiry < Date.now()) {
localStorage.removeItem(`${STORE_KEY}.${locale}`);
const resp = await fetch(`${this.locale_folder}/${locale}.json`);
if (!resp.ok) {
const base_locale_values = await this._baseMappings(locale);
if (!base_locale_values) {
delete this._load_promises[locale];
return console.error(
`Failed to loaded locale file for "${locale}".`,
resp,
);
return;
}
const locale_data = await resp.json();
const locale_override_data = this.zone_id
? await showMetadata(this.zone_id, `locale_${locale}`)
: { details: {} };
const base_locale_values = removeNesting(locale_data);
const override_locale_values = removeNesting(
locale_override_data.details as Record<string, unknown>,
);
Expand All @@ -242,4 +237,26 @@ export class LocaleService {
}
delete this._load_promises[locale];
}

/**
* Mappings from the locale file. The default locale reuses the bundled
* copy that `ready` loads. Returns `null` if the file fails to load.
*/
private async _baseMappings(
locale: string,
): Promise<Record<string, string> | null> {
if (locale === this._default_locale) {
await this.ready;
return this._default_mappings;
}
const resp = await fetch(`${this.locale_folder}/${locale}.json`);
if (!resp.ok) {
console.error(
`Failed to loaded locale file for "${locale}".`,
resp,
);
return null;
}
return removeNesting(await resp.json());
}
}
3 changes: 2 additions & 1 deletion src/app/common/support-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ export function querySupportZones(
);
return Promise.all(
groups.map(async ({ group }) => {
const query = { ...options, group_id: group.id };
// Ask for large pages, as the pages load one after another
const query = { limit: 1000, ...options, group_id: group.id };
return collectPages(queryZones(query));
}),
).then((results) => {
Expand Down
Loading
Loading