pgkiln 0.70.0 → 0.72.0
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/db/migrations/137_create_page_types.sql +904 -0
- package/db/migrations/138_app_utilities.sql +153 -0
- package/db/migrations/139_opentelemetry.sql +36 -0
- package/docs/apex-feature-parity.md +9 -7
- package/docs/guide/01-installation.md +108 -0
- package/docs/guide/03-builder.md +116 -4
- package/docs/guide/04-pages-and-regions.md +3 -1
- package/docs/guide/05-items.md +1 -1
- package/docs/guide/06-processing.md +18 -0
- package/docs/guide/09-reference.md +18 -1
- package/docs/guide/12-development.md +35 -1
- package/docs/guide/16-files.md +1 -1
- package/examples/hr/hr_73_configuration_options.sql +17 -0
- package/examples/hr/hr_74_app_utilities.sql +22 -0
- package/package.json +1 -1
- package/public/app.css +9 -0
- package/public/app.js +50 -0
- package/public/builder.css +48 -0
- package/src/ai/service.ts +14 -0
- package/src/app.ts +20 -0
- package/src/builder/advisor.ts +2 -2
- package/src/builder/app-updates.ts +3 -0
- package/src/builder/components.ts +3 -1
- package/src/builder/create-page.ts +259 -0
- package/src/builder/dependencies.ts +311 -0
- package/src/builder/features.ts +313 -0
- package/src/builder/home.ts +3 -0
- package/src/builder/i18n/de.ts +6 -0
- package/src/builder/i18n/en.ts +6 -0
- package/src/builder/i18n/es.ts +6 -0
- package/src/builder/i18n/fr.ts +6 -0
- package/src/builder/i18n/it.ts +6 -0
- package/src/builder/i18n/ja.ts +6 -0
- package/src/builder/i18n/ko.ts +6 -0
- package/src/builder/i18n/nl.ts +6 -0
- package/src/builder/i18n/pt.ts +6 -0
- package/src/builder/i18n/zh-cn.ts +6 -0
- package/src/builder/i18n/zh-tw.ts +6 -0
- package/src/builder/login-page.ts +17 -14
- package/src/builder/newapp.ts +41 -23
- package/src/builder/routes.ts +13 -2
- package/src/builder/telemetry.ts +206 -0
- package/src/builder/ui-defaults.ts +161 -0
- package/src/builder/ui.ts +2 -1
- package/src/builder/upgrade.ts +154 -0
- package/src/builder/utilities.ts +181 -0
- package/src/builder/wizards.ts +211 -26
- package/src/builder/workspaces.ts +2 -1
- package/src/cli/replace.ts +9 -1
- package/src/history.ts +155 -0
- package/src/instance-checks.ts +11 -4
- package/src/layout.ts +2 -1
- package/src/mail/mail.ts +14 -0
- package/src/runtime/ai-agents.ts +9 -0
- package/src/runtime/builtin-components.ts +23 -2
- package/src/runtime/items.ts +4 -2
- package/src/runtime/routes.ts +3 -0
- package/src/session.ts +26 -0
- package/src/telemetry/browser.ts +91 -0
- package/src/telemetry/config.ts +342 -0
- package/src/telemetry/export.ts +484 -0
- package/src/telemetry/http.ts +126 -0
- package/src/telemetry/trace.ts +456 -0
- package/src/webclient.ts +25 -0
- package/src/workflow.ts +14 -7
package/src/builder/i18n/de.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const de: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST-API',
|
|
286
286
|
'app.tab.settings': 'Einstellungen',
|
|
287
287
|
'app.tab.advisor': 'Advisor',
|
|
288
|
+
'app.tab.utilities': 'Dienstprogramme',
|
|
288
289
|
'app.lock_refused': 'Änderungen werden abgelehnt, bis die Sperre aufgehoben ist.',
|
|
289
290
|
'app.search_pages': 'Seiten durchsuchen',
|
|
290
291
|
'app.search_placeholder': 'Seitenname oder -nummer',
|
|
@@ -318,6 +319,9 @@ export const de: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Tabelle oder View (Mastertabelle für Master-Detail)',
|
|
319
320
|
'app.no_access': '{table} (kein Zugriff für {role})',
|
|
320
321
|
'app.ai_pages': 'Seiten mit KI erstellen',
|
|
322
|
+
'app.all_page_types': "Alle Seitentypen, Funktionen und Kopien",
|
|
323
|
+
'app.task.add_feature': "Funktionsseite hinzufügen",
|
|
324
|
+
'app.task.copy_page': "Seite als Kopie erstellen",
|
|
321
325
|
'app.ai_pages_note': '(beschreiben Sie sie mit eigenen Worten)',
|
|
322
326
|
'app.blank': 'Leere Seite erstellen',
|
|
323
327
|
'app.page_number': 'Seitennummer',
|
|
@@ -1682,6 +1686,8 @@ export const de: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'Mail-Dienst',
|
|
1683
1687
|
'wsp.network': 'Netzwerkzugriff',
|
|
1684
1688
|
'wsp.queue': 'Mail-Warteschlange',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Traces und Logs der Anwendungen dieses Workspace: {link} (wie die Instanz, aus, oder zusätzlich an einen eigenen Collector des Workspace).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Isoliert: Seine Anwendungen und sein SQL laufen auf eigenen Datenbank-Logins.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Gemeinsam.',
|
|
1687
1693
|
'wsp.admins_change': 'Administratoren ändern das.',
|
package/src/builder/i18n/en.ts
CHANGED
|
@@ -288,6 +288,7 @@ export const en = {
|
|
|
288
288
|
'app.tab.api': 'REST API',
|
|
289
289
|
'app.tab.settings': 'Settings',
|
|
290
290
|
'app.tab.advisor': 'Advisor',
|
|
291
|
+
'app.tab.utilities': 'Utilities',
|
|
291
292
|
'app.lock_refused': 'Changes are refused until it is unlocked.',
|
|
292
293
|
'app.search_pages': 'Search pages',
|
|
293
294
|
'app.search_placeholder': 'Page name or number',
|
|
@@ -321,6 +322,9 @@ export const en = {
|
|
|
321
322
|
'app.table_or_view': 'Table or view (master table for master detail)',
|
|
322
323
|
'app.no_access': '{table} (no access for {role})',
|
|
323
324
|
'app.ai_pages': 'Create pages with AI',
|
|
325
|
+
'app.all_page_types': "All page types, features and copies",
|
|
326
|
+
'app.task.add_feature': "Add feature page",
|
|
327
|
+
'app.task.copy_page': "Create page as copy",
|
|
324
328
|
'app.ai_pages_note': '(describe them in your own words)',
|
|
325
329
|
'app.blank': 'Create blank page',
|
|
326
330
|
'app.page_number': 'Page number',
|
|
@@ -1686,6 +1690,8 @@ export const en = {
|
|
|
1686
1690
|
'wsp.mail_service': 'mail service',
|
|
1687
1691
|
'wsp.network': 'network access',
|
|
1688
1692
|
'wsp.queue': 'mail queue',
|
|
1693
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1694
|
+
'wsp.telemetry_text': 'Traces and logs of this workspace\'s applications: {link} (as the instance, off, or also to a collector of the workspace\'s own).',
|
|
1689
1695
|
'wsp.iso_short_on': 'Isolated: its applications and SQL run on database logins of its own.',
|
|
1690
1696
|
'wsp.iso_short_off': 'Shared.',
|
|
1691
1697
|
'wsp.admins_change': 'Administrators change it.',
|
package/src/builder/i18n/es.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const es: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'API REST',
|
|
286
286
|
'app.tab.settings': 'Configuración',
|
|
287
287
|
'app.tab.advisor': 'Asesor',
|
|
288
|
+
'app.tab.utilities': 'Utilidades',
|
|
288
289
|
'app.lock_refused': 'Los cambios se rechazan hasta que se desbloquee.',
|
|
289
290
|
'app.search_pages': 'Buscar páginas',
|
|
290
291
|
'app.search_placeholder': 'Nombre o número de página',
|
|
@@ -318,6 +319,9 @@ export const es: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Tabla o vista (tabla maestra para maestro-detalle)',
|
|
319
320
|
'app.no_access': '{table} (sin acceso para {role})',
|
|
320
321
|
'app.ai_pages': 'Crear páginas con IA',
|
|
322
|
+
'app.all_page_types': "Todos los tipos de página, funciones y copias",
|
|
323
|
+
'app.task.add_feature': "Agregar página de función",
|
|
324
|
+
'app.task.copy_page': "Crear página como copia",
|
|
321
325
|
'app.ai_pages_note': '(descríbalas con sus propias palabras)',
|
|
322
326
|
'app.blank': 'Crear página en blanco',
|
|
323
327
|
'app.page_number': 'Número de página',
|
|
@@ -1682,6 +1686,8 @@ export const es: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'servicio de correo',
|
|
1683
1687
|
'wsp.network': 'acceso a la red',
|
|
1684
1688
|
'wsp.queue': 'cola de correo',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Trazas y registros de las aplicaciones de este espacio de trabajo: {link} (como la instancia, desactivado, o también a un recolector propio del espacio de trabajo).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Aislado: sus aplicaciones y su SQL se ejecutan con inicios de sesión de base de datos propios.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Compartido.',
|
|
1687
1693
|
'wsp.admins_change': 'Los administradores lo cambian.',
|
package/src/builder/i18n/fr.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const fr: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'API REST',
|
|
286
286
|
'app.tab.settings': 'Paramètres',
|
|
287
287
|
'app.tab.advisor': 'Conseiller',
|
|
288
|
+
'app.tab.utilities': 'Utilitaires',
|
|
288
289
|
'app.lock_refused': "Les modifications sont refusées jusqu'à son déverrouillage.",
|
|
289
290
|
'app.search_pages': 'Rechercher des pages',
|
|
290
291
|
'app.search_placeholder': 'Nom ou numéro de page',
|
|
@@ -318,6 +319,9 @@ export const fr: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Table ou vue (table maître pour maître-détail)',
|
|
319
320
|
'app.no_access': '{table} (aucun accès pour {role})',
|
|
320
321
|
'app.ai_pages': "Créer des pages avec l'IA",
|
|
322
|
+
'app.all_page_types': "Tous les types de page, fonctionnalités et copies",
|
|
323
|
+
'app.task.add_feature': "Ajouter une page de fonctionnalité",
|
|
324
|
+
'app.task.copy_page': "Créer une page par copie",
|
|
321
325
|
'app.ai_pages_note': '(décrivez-les avec vos propres mots)',
|
|
322
326
|
'app.blank': 'Créer une page vierge',
|
|
323
327
|
'app.page_number': 'Numéro de page',
|
|
@@ -1682,6 +1686,8 @@ export const fr: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'service de messagerie',
|
|
1683
1687
|
'wsp.network': 'accès réseau',
|
|
1684
1688
|
'wsp.queue': 'file des e-mails',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Traces et journaux des applications de cet espace de travail : {link} (comme l\'instance, désactivé, ou aussi vers un collecteur propre à l\'espace de travail).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Isolé : ses applications et son SQL s’exécutent sur des connexions de base de données propres.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Partagé.',
|
|
1687
1693
|
'wsp.admins_change': 'Les administrateurs le modifient.',
|
package/src/builder/i18n/it.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const it: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'API REST',
|
|
286
286
|
'app.tab.settings': 'Impostazioni',
|
|
287
287
|
'app.tab.advisor': 'Advisor',
|
|
288
|
+
'app.tab.utilities': 'Utilità',
|
|
288
289
|
'app.lock_refused': 'Le modifiche vengono rifiutate finché non viene sbloccata.',
|
|
289
290
|
'app.search_pages': 'Cerca pagine',
|
|
290
291
|
'app.search_placeholder': 'Nome o numero della pagina',
|
|
@@ -318,6 +319,9 @@ export const it: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Tabella o vista (tabella master per master-dettaglio)',
|
|
319
320
|
'app.no_access': '{table} (nessun accesso per {role})',
|
|
320
321
|
'app.ai_pages': 'Crea pagine con l’AI',
|
|
322
|
+
'app.all_page_types': "Tutti i tipi di pagina, funzionalità e copie",
|
|
323
|
+
'app.task.add_feature': "Aggiungi pagina di funzionalità",
|
|
324
|
+
'app.task.copy_page': "Crea pagina come copia",
|
|
321
325
|
'app.ai_pages_note': '(descriverle con parole proprie)',
|
|
322
326
|
'app.blank': 'Crea pagina vuota',
|
|
323
327
|
'app.page_number': 'Numero di pagina',
|
|
@@ -1682,6 +1686,8 @@ export const it: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'servizio di posta',
|
|
1683
1687
|
'wsp.network': 'accesso di rete',
|
|
1684
1688
|
'wsp.queue': 'coda di posta',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Tracce e log delle applicazioni di questo workspace: {link} (come l\'istanza, disattivato, o anche verso un collector proprio del workspace).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Isolato: le sue applicazioni e il suo SQL vengono eseguiti su login del database propri.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Condiviso.',
|
|
1687
1693
|
'wsp.admins_change': 'Lo modificano gli amministratori.',
|
package/src/builder/i18n/ja.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const ja: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST API',
|
|
286
286
|
'app.tab.settings': '設定',
|
|
287
287
|
'app.tab.advisor': 'アドバイザ',
|
|
288
|
+
'app.tab.utilities': 'ユーティリティ',
|
|
288
289
|
'app.lock_refused': 'ロックが解除されるまで、変更は拒否されます。',
|
|
289
290
|
'app.search_pages': 'ページの検索',
|
|
290
291
|
'app.search_placeholder': 'ページ名または番号',
|
|
@@ -318,6 +319,9 @@ export const ja: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': '表またはビュー(マスター詳細の場合はマスター表)',
|
|
319
320
|
'app.no_access': '{table}({role} にはアクセス権なし)',
|
|
320
321
|
'app.ai_pages': 'AI でページを作成',
|
|
322
|
+
'app.all_page_types': "すべてのページタイプ、機能、コピー",
|
|
323
|
+
'app.task.add_feature': "機能ページを追加",
|
|
324
|
+
'app.task.copy_page': "コピーとしてページを作成",
|
|
321
325
|
'app.ai_pages_note': '(自分の言葉で説明してください)',
|
|
322
326
|
'app.blank': '空白ページの作成',
|
|
323
327
|
'app.page_number': 'ページ番号',
|
|
@@ -1682,6 +1686,8 @@ export const ja: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'メール・サービス',
|
|
1683
1687
|
'wsp.network': 'ネットワーク・アクセス',
|
|
1684
1688
|
'wsp.queue': 'メール・キュー',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'このワークスペースのアプリケーションのトレースとログ:{link}(インスタンスと同じ、オフ、またはワークスペース独自のコレクターにも送信)。',
|
|
1685
1691
|
'wsp.iso_short_on': '分離: アプリケーションと SQL は独自のデータベース・ログインで実行されます。',
|
|
1686
1692
|
'wsp.iso_short_off': '共有。',
|
|
1687
1693
|
'wsp.admins_change': '管理者が変更します。',
|
package/src/builder/i18n/ko.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const ko: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST API',
|
|
286
286
|
'app.tab.settings': '설정',
|
|
287
287
|
'app.tab.advisor': '어드바이저',
|
|
288
|
+
'app.tab.utilities': '유틸리티',
|
|
288
289
|
'app.lock_refused': '잠금이 해제될 때까지 변경이 거부됩니다.',
|
|
289
290
|
'app.search_pages': '페이지 검색',
|
|
290
291
|
'app.search_placeholder': '페이지 이름 또는 번호',
|
|
@@ -318,6 +319,9 @@ export const ko: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': '테이블 또는 뷰(마스터 세부의 경우 마스터 테이블)',
|
|
319
320
|
'app.no_access': '{table}({role}에 대한 액세스 권한 없음)',
|
|
320
321
|
'app.ai_pages': 'AI로 페이지 생성',
|
|
322
|
+
'app.all_page_types': "모든 페이지 유형, 기능 및 복사",
|
|
323
|
+
'app.task.add_feature': "기능 페이지 추가",
|
|
324
|
+
'app.task.copy_page': "복사하여 페이지 생성",
|
|
321
325
|
'app.ai_pages_note': '(원하는 내용을 직접 설명하세요)',
|
|
322
326
|
'app.blank': '빈 페이지 생성',
|
|
323
327
|
'app.page_number': '페이지 번호',
|
|
@@ -1682,6 +1686,8 @@ export const ko: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': '메일 서비스',
|
|
1683
1687
|
'wsp.network': '네트워크 액세스',
|
|
1684
1688
|
'wsp.queue': '메일 대기열',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': '이 작업 영역 애플리케이션의 트레이스와 로그: {link} (인스턴스와 동일, 끔, 또는 작업 영역 자체 수집기로도 전송).',
|
|
1685
1691
|
'wsp.iso_short_on': '격리됨: 애플리케이션과 SQL이 자체 데이터베이스 로그인으로 실행됩니다.',
|
|
1686
1692
|
'wsp.iso_short_off': '공유.',
|
|
1687
1693
|
'wsp.admins_change': '관리자가 변경합니다.',
|
package/src/builder/i18n/nl.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const nl: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST-API',
|
|
286
286
|
'app.tab.settings': 'Instellingen',
|
|
287
287
|
'app.tab.advisor': 'Adviseur',
|
|
288
|
+
'app.tab.utilities': 'Hulpmiddelen',
|
|
288
289
|
'app.lock_refused': 'Wijzigingen worden geweigerd tot de vergrendeling is opgeheven.',
|
|
289
290
|
'app.search_pages': "Pagina's zoeken",
|
|
290
291
|
'app.search_placeholder': 'Paginanaam of -nummer',
|
|
@@ -318,6 +319,9 @@ export const nl: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Tabel of view (mastertabel voor master-detail)',
|
|
319
320
|
'app.no_access': '{table} (geen toegang voor {role})',
|
|
320
321
|
'app.ai_pages': "Pagina's maken met AI",
|
|
322
|
+
'app.all_page_types': "Alle paginatypen, functies en kopieën",
|
|
323
|
+
'app.task.add_feature': "Functiepagina toevoegen",
|
|
324
|
+
'app.task.copy_page': "Pagina als kopie maken",
|
|
321
325
|
'app.ai_pages_note': '(beschrijf ze in je eigen woorden)',
|
|
322
326
|
'app.blank': 'Lege pagina maken',
|
|
323
327
|
'app.page_number': 'Paginanummer',
|
|
@@ -1682,6 +1686,8 @@ export const nl: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'maildienst',
|
|
1683
1687
|
'wsp.network': 'netwerktoegang',
|
|
1684
1688
|
'wsp.queue': 'mailwachtrij',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Traces en logs van de applicaties van deze werkruimte: {link} (zoals de instantie, uit, of ook naar een eigen collector van de werkruimte).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Geïsoleerd: de applicaties en SQL draaien op eigen databaselogins.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Gedeeld.',
|
|
1687
1693
|
'wsp.admins_change': 'Beheerders wijzigen dit.',
|
package/src/builder/i18n/pt.ts
CHANGED
|
@@ -285,6 +285,7 @@ export const pt: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'API REST',
|
|
286
286
|
'app.tab.settings': 'Configurações',
|
|
287
287
|
'app.tab.advisor': 'Consultor',
|
|
288
|
+
'app.tab.utilities': 'Utilitários',
|
|
288
289
|
'app.lock_refused': 'As alterações são recusadas até que seja desbloqueado.',
|
|
289
290
|
'app.search_pages': 'Pesquisar páginas',
|
|
290
291
|
'app.search_placeholder': 'Nome ou número da página',
|
|
@@ -318,6 +319,9 @@ export const pt: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': 'Tabela ou view (tabela mestre para mestre-detalhe)',
|
|
319
320
|
'app.no_access': '{table} (sem acesso para {role})',
|
|
320
321
|
'app.ai_pages': 'Criar páginas com IA',
|
|
322
|
+
'app.all_page_types': "Todos os tipos de página, recursos e cópias",
|
|
323
|
+
'app.task.add_feature': "Adicionar página de recurso",
|
|
324
|
+
'app.task.copy_page': "Criar página como cópia",
|
|
321
325
|
'app.ai_pages_note': '(descreva-as com suas próprias palavras)',
|
|
322
326
|
'app.blank': 'Criar página em branco',
|
|
323
327
|
'app.page_number': 'Número da página',
|
|
@@ -1682,6 +1686,8 @@ export const pt: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': 'serviço de e-mail',
|
|
1683
1687
|
'wsp.network': 'acesso à rede',
|
|
1684
1688
|
'wsp.queue': 'fila de e-mails',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': 'Traces e logs das aplicações deste espaço de trabalho: {link} (como a instância, desligado, ou também para um coletor próprio do espaço de trabalho).',
|
|
1685
1691
|
'wsp.iso_short_on': 'Isolado: suas aplicações e seu SQL são executados em logins de banco de dados próprios.',
|
|
1686
1692
|
'wsp.iso_short_off': 'Compartilhado.',
|
|
1687
1693
|
'wsp.admins_change': 'Os administradores o alteram.',
|
|
@@ -285,6 +285,7 @@ export const zhCN: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST API',
|
|
286
286
|
'app.tab.settings': '设置',
|
|
287
287
|
'app.tab.advisor': '顾问',
|
|
288
|
+
'app.tab.utilities': '实用工具',
|
|
288
289
|
'app.lock_refused': '解锁前将拒绝所有更改。',
|
|
289
290
|
'app.search_pages': '搜索页面',
|
|
290
291
|
'app.search_placeholder': '页面名称或编号',
|
|
@@ -318,6 +319,9 @@ export const zhCN: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': '表或视图(主从页面的主表)',
|
|
319
320
|
'app.no_access': '{table}({role} 无访问权限)',
|
|
320
321
|
'app.ai_pages': '使用 AI 创建页面',
|
|
322
|
+
'app.all_page_types': "所有页面类型、功能和副本",
|
|
323
|
+
'app.task.add_feature': "添加功能页",
|
|
324
|
+
'app.task.copy_page': "以副本方式创建页面",
|
|
321
325
|
'app.ai_pages_note': '(用您自己的话描述它们)',
|
|
322
326
|
'app.blank': '创建空白页面',
|
|
323
327
|
'app.page_number': '页码',
|
|
@@ -1682,6 +1686,8 @@ export const zhCN: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': '邮件服务',
|
|
1683
1687
|
'wsp.network': '网络访问',
|
|
1684
1688
|
'wsp.queue': '邮件队列',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': '此工作区应用程序的跟踪和日志:{link}(与实例相同、关闭,或同时发送到工作区自己的收集器)。',
|
|
1685
1691
|
'wsp.iso_short_on': '已隔离:其应用程序和 SQL 在专属的数据库登录上运行。',
|
|
1686
1692
|
'wsp.iso_short_off': '共享。',
|
|
1687
1693
|
'wsp.admins_change': '由管理员更改。',
|
|
@@ -285,6 +285,7 @@ export const zhTW: BuilderMessages = {
|
|
|
285
285
|
'app.tab.api': 'REST API',
|
|
286
286
|
'app.tab.settings': '設定',
|
|
287
287
|
'app.tab.advisor': '顧問',
|
|
288
|
+
'app.tab.utilities': '公用程式',
|
|
288
289
|
'app.lock_refused': '在解除鎖定前,所有變更都會被拒絕。',
|
|
289
290
|
'app.search_pages': '搜尋頁面',
|
|
290
291
|
'app.search_placeholder': '頁面名稱或編號',
|
|
@@ -318,6 +319,9 @@ export const zhTW: BuilderMessages = {
|
|
|
318
319
|
'app.table_or_view': '表格或檢視(主從式的主表格)',
|
|
319
320
|
'app.no_access': '{table}({role} 無存取權)',
|
|
320
321
|
'app.ai_pages': '以 AI 建立頁面',
|
|
322
|
+
'app.all_page_types': "所有頁面類型、功能與副本",
|
|
323
|
+
'app.task.add_feature': "新增功能頁",
|
|
324
|
+
'app.task.copy_page': "以副本建立頁面",
|
|
321
325
|
'app.ai_pages_note': '(用您自己的話描述)',
|
|
322
326
|
'app.blank': '建立空白頁面',
|
|
323
327
|
'app.page_number': '頁面編號',
|
|
@@ -1682,6 +1686,8 @@ export const zhTW: BuilderMessages = {
|
|
|
1682
1686
|
'wsp.mail_service': '郵件服務',
|
|
1683
1687
|
'wsp.network': '網路存取',
|
|
1684
1688
|
'wsp.queue': '郵件佇列',
|
|
1689
|
+
'wsp.telemetry': 'OpenTelemetry',
|
|
1690
|
+
'wsp.telemetry_text': '此工作區應用程式的追蹤與日誌:{link}(與執行個體相同、關閉,或同時傳送到工作區自己的收集器)。',
|
|
1685
1691
|
'wsp.iso_short_on': '已隔離:其應用程式與 SQL 以專屬的資料庫登入執行。',
|
|
1686
1692
|
'wsp.iso_short_off': '共用。',
|
|
1687
1693
|
'wsp.admins_change': '由管理員變更。',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FastifyInstance } from 'fastify';
|
|
2
|
-
import { owner } from '../db.ts';
|
|
2
|
+
import { owner, type Client } from '../db.ts';
|
|
3
3
|
import { APP_MFA, EMAIL_CODE_MINUTES, EMAIL_CODES_PER_HOUR, type AppMfa } from '../account-mfa.ts';
|
|
4
4
|
import { html, raw } from '../html.ts';
|
|
5
5
|
import { secretKeyConfigured } from '../secrets.ts';
|
|
@@ -78,6 +78,21 @@ export async function saveLoginSettings(appId: string, b: Record<string, string
|
|
|
78
78
|
[appId, page, b.password_reset === 'true', mfa, roles, b.mfa_email === 'true']);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** Page 9999 (or the next free number), public, minimal, with a login region, made the login page. (137) Also Add feature → Login page. */
|
|
82
|
+
export async function createLoginPage(c: Client, a: { id: number; name: string }) {
|
|
83
|
+
const n = (await c.query<{ n: number }>(
|
|
84
|
+
`select case when not exists (select 1 from meta.page where app_id = $1 and page_no = 9999) then 9999
|
|
85
|
+
else (select max(page_no) + 1 from meta.page where app_id = $1) end as n`, [a.id],
|
|
86
|
+
)).rows[0].n;
|
|
87
|
+
const id = (await c.query<{ id: number }>(
|
|
88
|
+
`insert into meta.page (app_id, page_no, name, title, requires_auth, page_template) values ($1, $2, 'Login', $3, false, 'minimal') returning id`,
|
|
89
|
+
[a.id, n, a.name],
|
|
90
|
+
)).rows[0].id;
|
|
91
|
+
await c.query(`insert into meta.region (page_id, seq, title, type, template) values ($1, 10, $2, 'login', 'standard')`, [id, a.name]);
|
|
92
|
+
await c.query('update meta.app set login_page = $2, updated_at = now() where id = $1', [a.id, n]);
|
|
93
|
+
return { page: id, no: n };
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
export async function loginPageRoutes(app: FastifyInstance) {
|
|
82
97
|
// Create a login page: page 9999 (or the next free number), public, minimal, with a login region
|
|
83
98
|
app.post(`${BASE}/apps/:id/login-page`, async (req: Req, reply) => {
|
|
@@ -86,19 +101,7 @@ export async function loginPageRoutes(app: FastifyInstance) {
|
|
|
86
101
|
const a = await owner.one<{ id: number; name: string }>('select id, name from meta.app where id = $1', [req.params.id]);
|
|
87
102
|
if (!a) return reply.code(404).send('Not found');
|
|
88
103
|
try {
|
|
89
|
-
const { page, no } = await owner.tx(
|
|
90
|
-
const n = (await c.query<{ n: number }>(
|
|
91
|
-
`select case when not exists (select 1 from meta.page where app_id = $1 and page_no = 9999) then 9999
|
|
92
|
-
else (select max(page_no) + 1 from meta.page where app_id = $1) end as n`, [a.id],
|
|
93
|
-
)).rows[0].n;
|
|
94
|
-
const id = (await c.query<{ id: number }>(
|
|
95
|
-
`insert into meta.page (app_id, page_no, name, title, requires_auth, page_template) values ($1, $2, 'Login', $3, false, 'minimal') returning id`,
|
|
96
|
-
[a.id, n, a.name],
|
|
97
|
-
)).rows[0].id;
|
|
98
|
-
await c.query(`insert into meta.region (page_id, seq, title, type, template) values ($1, 10, $2, 'login', 'standard')`, [id, a.name]);
|
|
99
|
-
await c.query('update meta.app set login_page = $2, updated_at = now() where id = $1', [a.id, n]);
|
|
100
|
-
return { page: id, no: n };
|
|
101
|
-
});
|
|
104
|
+
const { page, no } = await owner.tx((c) => createLoginPage(c, a));
|
|
102
105
|
flash(s, `Page ${no} "Login" created and made the login page. Add regions around the sign-in form in the Page Designer.`);
|
|
103
106
|
return back(reply, s, `${BASE}/pages/${page}`);
|
|
104
107
|
} catch (e) {
|
package/src/builder/newapp.ts
CHANGED
|
@@ -71,34 +71,52 @@ export const featuresFrom = (b: Record<string, unknown>, posted: boolean): Featu
|
|
|
71
71
|
/** (sprint 73) The Activity page of a generated application. */
|
|
72
72
|
export const ACTIVITY_PAGE = 9010;
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
/** (sprint 73; exported in 137 for Add feature) The authorization schemes CONTRIBUTOR and READER (and ADMIN when missing). */
|
|
75
|
+
export async function addAccessSchemes(c: Client, appId: number) {
|
|
76
|
+
await ensureAdminScheme(c, appId);
|
|
77
|
+
await c.query(
|
|
78
|
+
`insert into meta.authz_scheme (app_id, name, type, value, error_message) values
|
|
79
|
+
($1, 'CONTRIBUTOR', 'sql', 'meta.has_role(''admin'') or meta.has_role(''contributor'')', 'Only contributors can do this.'),
|
|
80
|
+
($1, 'READER', 'sql', 'meta.has_role(''admin'') or meta.has_role(''contributor'') or meta.has_role(''reader'')', 'Only readers can open this page.')
|
|
81
|
+
on conflict do nothing`,
|
|
82
|
+
[appId],
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** (137) The authorization scheme ADMIN (role admin) that administrators' pages use, unless the application has one. */
|
|
87
|
+
export async function ensureAdminScheme(c: Client, appId: number) {
|
|
88
|
+
await c.query(
|
|
89
|
+
`insert into meta.authz_scheme (app_id, name, type, value, error_message) values ($1, 'ADMIN', 'role', 'admin', 'Only administrators can access this page.')
|
|
90
|
+
on conflict do nothing`,
|
|
91
|
+
[appId],
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** (sprint 73; exported in 137 for Add feature) The Activity page for administrators and its navigation entry; nothing when the page number is taken. */
|
|
96
|
+
export async function addActivityPage(c: Client, appId: number) {
|
|
97
|
+
if ((await c.query('select 1 from meta.page where app_id = $1 and page_no = $2', [appId, ACTIVITY_PAGE])).rowCount) return;
|
|
98
|
+
await ensureAdminScheme(c, appId);
|
|
99
|
+
const home = (await c.query('select home_page from meta.app where id = $1', [appId])).rows[0].home_page;
|
|
100
|
+
const page = (await c.query(`insert into meta.page (app_id, page_no, name, title, parent_page, authz) values ($1, $2, 'Activity', 'Activity', $3, 'ADMIN') returning id`, [appId, ACTIVITY_PAGE, home])).rows[0].id;
|
|
101
|
+
await c.query(
|
|
102
|
+
`insert into meta.region (page_id, seq, title, type, source, config) values
|
|
103
|
+
($1, 10, 'Page views per day', 'chart', $2, '{"kind": "column"}'),
|
|
104
|
+
($1, 20, 'Latest activity', 'report', $3, '{"headings": {"at": "Time", "username": "User", "event": "Event", "page_no": "Page", "elapsed_ms": "Time taken (ms)"}}')`,
|
|
105
|
+
[page,
|
|
106
|
+
`select to_char(date_trunc('day', at), 'YYYY-MM-DD') as "Day", count(*) as "Page views"
|
|
93
107
|
from meta.app_activity(30)
|
|
94
108
|
where event = 'page_view'
|
|
95
109
|
group by 1
|
|
96
110
|
order by 1`,
|
|
97
|
-
|
|
111
|
+
`select at, username, event, page_no, elapsed_ms
|
|
98
112
|
from meta.app_activity(30)`],
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
);
|
|
114
|
+
await c.query(`insert into meta.nav_entry (app_id, seq, label, icon, target_page, authz) values ($1, 9010, 'Activity', 'activity', $2, 'ADMIN')`, [appId, ACTIVITY_PAGE]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function applyFeatures(c: Client, appId: number, f: Partial<Features>, notes: string[]) {
|
|
118
|
+
if (f.access) await addAccessSchemes(c, appId);
|
|
119
|
+
if (f.activity) await addActivityPage(c, appId);
|
|
102
120
|
if (f.feedback) await c.query('update meta.app set feedback = true where id = $1', [appId]);
|
|
103
121
|
if (f.theme === false) await c.query(`update meta.app set theme = theme || '{"user_choice": false}' where id = $1`, [appId]);
|
|
104
122
|
if (f.pwa || f.push) await c.query('update meta.app set pwa = true where id = $1', [appId]);
|
package/src/builder/routes.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { advise } from './advisor.ts';
|
|
2
|
+
import { appChecksum } from '../history.ts';
|
|
2
3
|
import { appLocks, isAdmin, lockPanel, lockText } from './locks.ts';
|
|
3
4
|
import { parseRoleList, validRoleName } from '../dbauth.ts';
|
|
4
5
|
import { DEFAULT_HEADER, headerProxiesConfigured } from '../headerauth.ts';
|
|
@@ -23,6 +24,8 @@ import { securityRoutes } from './security.ts';
|
|
|
23
24
|
import { instanceRoutes } from './instance.ts';
|
|
24
25
|
import { saveTimeZoneSettings, timeZoneSettings } from './globalization.ts';
|
|
25
26
|
import { WIZARD_KINDS, wizardRoutes, wizardTables } from './wizards.ts';
|
|
27
|
+
import { createPageRoutes } from './create-page.ts';
|
|
28
|
+
import { featureRoutes } from './features.ts';
|
|
26
29
|
import { appFromFileRoutes } from './appfromfile.ts';
|
|
27
30
|
import { clearDevbar } from '../runtime/devbar.ts';
|
|
28
31
|
import { EMAIL } from './find-workspace.ts';
|
|
@@ -564,6 +567,9 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
564
567
|
await securityRoutes(app);
|
|
565
568
|
await builderSsoRoutes(app);
|
|
566
569
|
await wizardRoutes(app);
|
|
570
|
+
// (137) Create page: the grid of page types, features and copy (create-page.ts, features.ts)
|
|
571
|
+
await createPageRoutes(app);
|
|
572
|
+
await featureRoutes(app);
|
|
567
573
|
await appFromFileRoutes(app);
|
|
568
574
|
|
|
569
575
|
// (sprint 73) POST /apps, creating an application, is in appwizard.ts
|
|
@@ -653,7 +659,7 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
653
659
|
<button class="btn">${icon('search')}<span class="sr-only">${tx('common.search')}</span></button></form>
|
|
654
660
|
<div class="seg view-toggle" role="group" aria-label="${tx('ab.view')}">
|
|
655
661
|
<a href="${keep({})}"${icons ? '' : raw(' aria-current="true"')}>${icon('list')} ${tx('ab.view_report')}</a><a href="${keep({ view: 'icons' })}"${icons ? raw(' aria-current="true"') : ''}>${icon('grid')} ${tx('app.view_icons')}</a></div>
|
|
656
|
-
<a class="btn btn-hot" href="
|
|
662
|
+
<a class="btn btn-hot" href="${BASE}/apps/${a.id}/create-page">${icon('plus')} ${tx('app.create_page')}</a>
|
|
657
663
|
</div>
|
|
658
664
|
${region(tx('app.tab.pages'), html`${grouped
|
|
659
665
|
? html`<form method="get" class="search u-mwnone u-mb1"><div class="field"><label class="label" for="f_group">${tx('app.page_group')}</label>
|
|
@@ -681,6 +687,7 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
681
687
|
</div>
|
|
682
688
|
<div class="buttons"><button class="btn btn-hot">${tx('common.next')}</button></div>
|
|
683
689
|
</form>
|
|
690
|
+
<p><a href="${BASE}/apps/${a.id}/create-page">${tx('app.all_page_types')} ▸</a></p>
|
|
684
691
|
<p><a href="${BASE}/apps/${a.id}/ai-pages">${tx('app.ai_pages')} ▸</a> <span class="muted">${tx('app.ai_pages_note')}</span></p>`)}
|
|
685
692
|
${region(tx('app.blank'), html`
|
|
686
693
|
<form method="post" action="${BASE}/apps/${a.id}/pages">${csrf(s)}
|
|
@@ -697,7 +704,9 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
697
704
|
<aside class="app-tasks region region-standard" aria-labelledby="app-tasks-h"><header class="region-header"><h2 id="app-tasks-h">${tx('common.tasks')}</h2></header>
|
|
698
705
|
<ul class="sc-links">
|
|
699
706
|
<li><a href="/a/${a.alias}" target="_blank" rel="noopener">${icon('play')} ${tx('app.task.run')}</a></li>
|
|
700
|
-
<li><a href="
|
|
707
|
+
<li><a href="${BASE}/apps/${a.id}/create-page">${icon('plus')} ${tx('app.create_page')}</a></li>
|
|
708
|
+
<li><a href="${BASE}/apps/${a.id}/create-page?tab=feature">${icon('puzzle')} ${tx('app.task.add_feature')}</a></li>
|
|
709
|
+
<li><a href="${BASE}/apps/${a.id}/create-page?tab=copy">${icon('copy')} ${tx('app.task.copy_page')}</a></li>
|
|
701
710
|
<li><a href="${BASE}/apps/${a.id}/ai-pages">${icon('lightbulb')} ${tx('app.ai_pages')}</a></li>
|
|
702
711
|
<li><a href="${BASE}/apps/${a.id}/shared">${icon('layers')} ${tx('app.tab.shared')}</a></li>
|
|
703
712
|
<li><a href="${BASE}/apps/${a.id}/shared?type=page_group">${icon('folder')} ${tx('app.task.page_groups')}</a></li>
|
|
@@ -743,6 +752,8 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
743
752
|
const a = await appOr404(req.params.id);
|
|
744
753
|
if (!a) return reply.code(404).send('Not found');
|
|
745
754
|
const r = await owner.one('select meta.export_app($1) as doc', [a.alias]);
|
|
755
|
+
// (sprint 138) the application checksum (src/history.ts), as on Utilities
|
|
756
|
+
reply.header('x-pgkiln-checksum', appChecksum(r.doc));
|
|
746
757
|
// (sprint 106) the change log marks the change sets made before an application's latest export
|
|
747
758
|
await logActivity({ appId: a.id, username: s.username, event: 'app_export', ip: clientIp(req), detail: String(req.query?.format ?? 'json') });
|
|
748
759
|
// ?format=dir|text: one file per component, as `pgkiln export --format dir|text` writes it (docs/guide/18-cli.md)
|