core: Format. Fix. WIP.
This commit is contained in:
@ -10,6 +10,9 @@ insert_final_newline = true
|
||||
[*.html]
|
||||
indent_size = 2
|
||||
|
||||
[schemas/*.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
@ -3,11 +3,14 @@
|
||||
## Static Files
|
||||
**/LICENSE
|
||||
|
||||
authentik/stages/**/*
|
||||
|
||||
## Build asset directories
|
||||
/build
|
||||
coverage
|
||||
dist
|
||||
out
|
||||
.docusaurus
|
||||
website/docs/developer-docs/api/**/*
|
||||
|
||||
## Environment
|
||||
*.env
|
||||
@ -20,9 +23,25 @@ out
|
||||
|
||||
## Node
|
||||
node_modules
|
||||
coverage
|
||||
|
||||
## Configs
|
||||
*.log
|
||||
*.yaml
|
||||
*.yml
|
||||
|
||||
# Templates
|
||||
# TODO: Rename affected files to *.template.* or similar.
|
||||
*.html
|
||||
*.mdx
|
||||
*.md
|
||||
|
||||
## Import order matters
|
||||
poly.ts
|
||||
src/locale-codes.ts
|
||||
src/locales/
|
||||
|
||||
# Storybook
|
||||
storybook-static/
|
||||
.storybook/css-import-maps*
|
||||
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -17,6 +17,6 @@
|
||||
"ms-python.vscode-pylance",
|
||||
"redhat.vscode-yaml",
|
||||
"Tobermory.es6-string-html",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
||||
57
.vscode/settings.json
vendored
57
.vscode/settings.json
vendored
@ -16,7 +16,7 @@
|
||||
],
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"yaml.schemas": {
|
||||
"./blueprints/schema.json": "blueprints/**/*.yaml"
|
||||
@ -30,7 +30,56 @@
|
||||
}
|
||||
],
|
||||
"go.testFlags": ["-count=1"],
|
||||
"github-actions.workflows.pinned.workflows": [
|
||||
".github/workflows/ci-main.yml"
|
||||
]
|
||||
"github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"],
|
||||
|
||||
"eslint.useFlatConfig": true,
|
||||
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.cjs": "*.d.cts",
|
||||
"package.json": "package-lock.json, yarn.lock, .yarnrc, .yarnrc.yml, .yarn, .nvmrc, .node-version",
|
||||
"tsconfig.json": "tsconfig.*.json, jsconfig.json"
|
||||
},
|
||||
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.code-search": true,
|
||||
"**/dist": true,
|
||||
"**/out": true,
|
||||
"**/package-lock.json": true
|
||||
},
|
||||
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[shellscript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.removeUnusedImports": "explicit"
|
||||
},
|
||||
// We use Prettier for formatting, but specifying these settings
|
||||
// will ensure that VS Code's IntelliSense doesn't autocomplete unformatted code.
|
||||
"javascript.format.semicolons": "insert",
|
||||
"typescript.format.semicolons": "insert",
|
||||
"javascript.preferences.quoteStyle": "double",
|
||||
"typescript.preferences.quoteStyle": "double"
|
||||
}
|
||||
|
||||
40
.vscode/tasks.json
vendored
40
.vscode/tasks.json
vendored
@ -4,12 +4,7 @@
|
||||
{
|
||||
"label": "authentik/core: make",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"lint-fix",
|
||||
"lint"
|
||||
],
|
||||
"args": ["run", "make", "lint-fix", "lint"],
|
||||
"presentation": {
|
||||
"panel": "new"
|
||||
},
|
||||
@ -18,11 +13,7 @@
|
||||
{
|
||||
"label": "authentik/core: run",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"ak",
|
||||
"server"
|
||||
],
|
||||
"args": ["run", "ak", "server"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -32,17 +23,13 @@
|
||||
{
|
||||
"label": "authentik/web: make",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"web"
|
||||
],
|
||||
"args": ["web"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/web: watch",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"web-watch"
|
||||
],
|
||||
"args": ["web-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -52,26 +39,19 @@
|
||||
{
|
||||
"label": "authentik: install",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"install",
|
||||
"-j4"
|
||||
],
|
||||
"args": ["install", "-j4"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/website: make",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"website"
|
||||
],
|
||||
"args": ["website"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/website: watch",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"website-watch"
|
||||
],
|
||||
"args": ["website-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -81,11 +61,7 @@
|
||||
{
|
||||
"label": "authentik/api: generate",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"gen"
|
||||
],
|
||||
"args": ["run", "make", "gen"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
|
||||
12
Makefile
12
Makefile
@ -133,16 +133,14 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
|
||||
--rm -v ${PWD}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g typescript-fetch \
|
||||
-o /local/${GEN_API_TS} \
|
||||
-c /local/scripts/api-ts-config.yaml \
|
||||
--input-spec /local/schema.yml \
|
||||
--generator-name typescript-fetch \
|
||||
--output /local/${GEN_API_TS} \
|
||||
--config /local/scripts/api-ts-config.yaml \
|
||||
--additional-properties=npmVersion=${NPM_VERSION} \
|
||||
--git-repo-id authentik \
|
||||
--git-user-id goauthentik
|
||||
mkdir -p web/node_modules/@goauthentik/api
|
||||
cd ./${GEN_API_TS} && npm i
|
||||
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
npm install
|
||||
|
||||
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
docker run \
|
||||
|
||||
@ -323,12 +323,7 @@
|
||||
"multiValued": false,
|
||||
"description": "Indicates whether or not an attribute is modifiable.",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"readOnly",
|
||||
"readWrite",
|
||||
"immutable",
|
||||
"writeOnly"
|
||||
],
|
||||
"canonicalValues": ["readOnly", "readWrite", "immutable", "writeOnly"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
@ -340,12 +335,7 @@
|
||||
"multiValued": false,
|
||||
"description": "Indicates when an attribute is returned in a response (e.g., to a query).",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"always",
|
||||
"never",
|
||||
"default",
|
||||
"request"
|
||||
],
|
||||
"canonicalValues": ["always", "never", "default", "request"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
@ -369,12 +359,7 @@
|
||||
"multiValued": true,
|
||||
"description": "Used only with an attribute of type 'reference'. Specifies a SCIM resourceType that a reference attribute MAY refer to, e.g., 'User'.",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"resource",
|
||||
"external",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"canonicalValues": ["resource", "external", "uri", "url"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
10
eslint.config.mjs
Normal file
10
eslint.config.mjs
Normal file
@ -0,0 +1,10 @@
|
||||
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* ESLint configuration for authentik's monorepo.
|
||||
*/
|
||||
const ESLintConfig = createESLintPackageConfig();
|
||||
|
||||
export default ESLintConfig;
|
||||
@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@goauthentik/lifecycle-aws",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1005.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
}
|
||||
|
||||
43195
package-lock.json
generated
43195
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -1,5 +1,38 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"name": "@goauthentik/universe",
|
||||
"version": "2025.2.2",
|
||||
"private": true
|
||||
"description": "Monorepo for authentik.",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"compile": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b",
|
||||
"compile:clean": "node ./sdk/out/scripts/clean.js",
|
||||
"lint": "run-s lint:prettier:check lint:eslint:check",
|
||||
"lint:eslint:check": "eslint .",
|
||||
"lint:eslint:fix": "eslint --fix .",
|
||||
"lint:fix": "run-s lint:prettier:fix lint:eslint:fix",
|
||||
"lint:prettier": "eslint .",
|
||||
"lint:prettier:check": "prettier --cache --check -u .",
|
||||
"lint:prettier:fix": "prettier --cache --write -u ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
||||
"@typescript-eslint/parser": "^8.28.0",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-lit": "^2.0.0",
|
||||
"eslint-plugin-wc": "^3.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0",
|
||||
"zx": "^8.4.1"
|
||||
},
|
||||
"workspaces": [
|
||||
"gen-ts-api",
|
||||
"web",
|
||||
"website",
|
||||
"packages/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config"
|
||||
}
|
||||
|
||||
11
packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts
vendored
Normal file
11
packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file TypeScript type definitions for eslint-plugin-react-hooks
|
||||
*/
|
||||
declare module "eslint-plugin-react-hooks" {
|
||||
import { ESLint } from "eslint";
|
||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
||||
type PluginInstance = ESLint.Plugin;
|
||||
const Plugin: PluginInstance;
|
||||
|
||||
export default Plugin;
|
||||
}
|
||||
11
packages/eslint-config/@types/eslint-plugin-react.d.ts
vendored
Normal file
11
packages/eslint-config/@types/eslint-plugin-react.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file TypeScript type definitions for eslint-plugin-react
|
||||
*/
|
||||
declare module "eslint-plugin-react" {
|
||||
import { ESLint } from "eslint";
|
||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
||||
type PluginInstance = ESLint.Plugin;
|
||||
const Plugin: PluginInstance;
|
||||
|
||||
export default Plugin;
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import eslint from "@eslint/js";
|
||||
import { javaScriptConfig } from "@goauthentik/eslint-config/javascript-config";
|
||||
import { reactConfig } from "@goauthentik/eslint-config/react-config";
|
||||
import { typescriptConfig } from "@goauthentik/eslint-config/typescript-config";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import * as litconf from "eslint-plugin-lit";
|
||||
import * as wcconf from "eslint-plugin-wc";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
// @ts-check
|
||||
@ -10,7 +11,6 @@ import tseslint from "typescript-eslint";
|
||||
/**
|
||||
* @typedef ESLintPackageConfigOptions Options for creating package ESLint configuration.
|
||||
* @property {string[]} [ignorePatterns] Override ignore patterns for ESLint.
|
||||
* @property {import("typescript-eslint").ConfigWithExtends} [overrides] Additional ESLint rules
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -19,9 +19,17 @@ import tseslint from "typescript-eslint";
|
||||
export const DefaultIgnorePatterns = [
|
||||
// ---
|
||||
"**/*.md",
|
||||
"**/.yarn",
|
||||
"**/out",
|
||||
"**/dist",
|
||||
"**/.wireit",
|
||||
"website/build/**",
|
||||
"website/.docusaurus/**",
|
||||
"**/node_modules",
|
||||
"**/coverage",
|
||||
"**/storybook-static",
|
||||
"**/locale-codes.ts",
|
||||
"**/src/locales",
|
||||
"**/gen-ts-api",
|
||||
];
|
||||
|
||||
/**
|
||||
@ -31,24 +39,34 @@ export const DefaultIgnorePatterns = [
|
||||
*
|
||||
* @returns The ESLint configuration object.
|
||||
*/
|
||||
export function createESLintPackageConfig({
|
||||
ignorePatterns = DefaultIgnorePatterns,
|
||||
overrides = {},
|
||||
} = {}) {
|
||||
export function createESLintPackageConfig({ ignorePatterns = DefaultIgnorePatterns } = {}) {
|
||||
return tseslint.config(
|
||||
{
|
||||
ignores: ignorePatterns,
|
||||
},
|
||||
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
eslint.configs.recommended,
|
||||
javaScriptConfig,
|
||||
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
|
||||
...reactConfig,
|
||||
...tseslint.configs.recommended,
|
||||
|
||||
...typescriptConfig,
|
||||
|
||||
overrides,
|
||||
...reactConfig,
|
||||
|
||||
{
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
files: [
|
||||
// ---
|
||||
"**/scripts/**/*",
|
||||
"**/test/**/*",
|
||||
"**/tests/**/*",
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
143
packages/eslint-config/javascript-config.js
Normal file
143
packages/eslint-config/javascript-config.js
Normal file
@ -0,0 +1,143 @@
|
||||
// @ts-check
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const MAX_DEPTH = 4;
|
||||
const MAX_NESTED_CALLBACKS = 4;
|
||||
const MAX_PARAMS = 5;
|
||||
|
||||
/**
|
||||
* ESLint configuration for JavaScript authentik projects.
|
||||
*/
|
||||
export const javaScriptConfig = tseslint.config({
|
||||
rules: {
|
||||
// TODO: Clean up before enabling.
|
||||
"accessor-pairs": "off",
|
||||
"array-callback-return": "error",
|
||||
"block-scoped-var": "error",
|
||||
"consistent-return": ["error", { treatUndefinedAsUnspecified: false }],
|
||||
"consistent-this": ["error", "that"],
|
||||
"curly": "off",
|
||||
"dot-notation": [
|
||||
"error",
|
||||
{
|
||||
allowKeywords: true,
|
||||
},
|
||||
],
|
||||
"eqeqeq": "error",
|
||||
"func-names": ["error", "as-needed"],
|
||||
"guard-for-in": "error",
|
||||
"max-depth": ["error", MAX_DEPTH],
|
||||
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
|
||||
"max-params": ["error", MAX_PARAMS],
|
||||
// TODO: Clean up before enabling.
|
||||
// "new-cap": "error",
|
||||
"no-alert": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-bitwise": [
|
||||
"error",
|
||||
{
|
||||
allow: ["~"],
|
||||
int32Hint: true,
|
||||
},
|
||||
],
|
||||
"no-caller": "error",
|
||||
"no-case-declarations": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-constant-condition": "error",
|
||||
"no-control-regex": "error",
|
||||
"no-debugger": "error",
|
||||
"no-delete-var": "error",
|
||||
"no-div-regex": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty": "error",
|
||||
"no-empty-character-class": "error",
|
||||
"no-empty-function": ["error", { allow: ["constructors"] }],
|
||||
"no-labels": "error",
|
||||
"no-eq-null": "error",
|
||||
"no-eval": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-implicit-globals": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-iterator": "error",
|
||||
"no-label-var": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-multi-str": "error",
|
||||
// TODO: Clean up before enabling.
|
||||
"no-negated-condition": "off",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-octal": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": ["error", { props: false }],
|
||||
"no-proto": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-restricted-syntax": ["error", "WithStatement"],
|
||||
"no-script-url": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
// TODO: Clean up before enabling.
|
||||
// "no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "off", // Handled by Prettier.
|
||||
"no-undef": "off",
|
||||
"no-undef-init": "off",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-use-before-define": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-var": "error",
|
||||
"no-void": "error",
|
||||
"no-with": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"radix": "error",
|
||||
"require-yield": "error",
|
||||
"strict": ["error", "global"],
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "error",
|
||||
"vars-on-top": "error",
|
||||
"yoda": ["error", "never"],
|
||||
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
// SonarJS is not yet compatible with ESLint 9. Commenting these out
|
||||
// until it is.
|
||||
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
||||
// "sonarjs/no-duplicate-string": "off",
|
||||
// "sonarjs/no-nested-template-literals": "off",
|
||||
},
|
||||
});
|
||||
|
||||
export default javaScriptConfig;
|
||||
@ -14,13 +14,15 @@
|
||||
"import": "./react-config.js",
|
||||
"types": "./out/react-config.d.ts"
|
||||
},
|
||||
"./javascript-config": {
|
||||
"import": "./javascript-config.js",
|
||||
"types": "./out/javascript-config.d.ts"
|
||||
},
|
||||
"./typescript-config": {
|
||||
"import": "./typescript-config.js",
|
||||
"types": "./out/typescript-config.d.ts"
|
||||
}
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"dependencies": {
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
@ -30,13 +32,12 @@
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "1.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/eslint__js": "^9.14.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.27.0"
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.27.0"
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
@ -44,6 +45,8 @@
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
18
packages/eslint-config/react-config.js
vendored
18
packages/eslint-config/react-config.js
vendored
@ -1,12 +1,11 @@
|
||||
import reactPlugin from "eslint-plugin-react"
|
||||
import hooksPlugin from "eslint-plugin-react-hooks"
|
||||
import tseslint from "typescript-eslint"
|
||||
import reactPlugin from "eslint-plugin-react";
|
||||
import hooksPlugin from "eslint-plugin-react-hooks";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
/**
|
||||
* ESLint configuration for React authentik projects.
|
||||
*/
|
||||
export const reactConfig = tseslint.config(
|
||||
{
|
||||
export const reactConfig = tseslint.config({
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
@ -14,9 +13,7 @@ export const reactConfig = tseslint.config(
|
||||
},
|
||||
|
||||
plugins: {
|
||||
// @ts-ignore Fixup plugin rules.
|
||||
react: reactPlugin,
|
||||
// @ts-ignore Fixup plugin
|
||||
"react": reactPlugin,
|
||||
"react-hooks": hooksPlugin,
|
||||
},
|
||||
|
||||
@ -32,7 +29,6 @@ export const reactConfig = tseslint.config(
|
||||
"react/prop-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
},
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
export default reactConfig
|
||||
export default reactConfig;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
|
||||
@ -7,54 +7,28 @@ import tseslint from "typescript-eslint";
|
||||
export const typescriptConfig = tseslint.config({
|
||||
rules: {
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"warn",
|
||||
"error",
|
||||
{
|
||||
"ts-ignore": "allow-with-description",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-extra-semi": "off",
|
||||
"@typescript-eslint/no-misused-new": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"warn",
|
||||
{
|
||||
ignoreFunctionTypeParameterNameValueShadow: true,
|
||||
ignoreTypeValueShadow: true,
|
||||
"ts-expect-error": "allow-with-description",
|
||||
"ts-ignore": true,
|
||||
"ts-nocheck": "allow-with-description",
|
||||
"ts-check": false,
|
||||
"minimumDescriptionLength": 5,
|
||||
},
|
||||
],
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"no-invalid-this": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
// Ignore all variables, since Prettier takes care of this.
|
||||
varsIgnorePattern: "^\\w",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
|
||||
"eqeqeq": ["error", "always", { null: "ignore" }],
|
||||
"no-shadow": "off",
|
||||
"no-extra-semi": "off",
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
"object-shorthand": [
|
||||
"warn",
|
||||
"always",
|
||||
{
|
||||
avoidQuotes: true,
|
||||
ignoreConstructors: true,
|
||||
avoidExplicitReturnArrows: false,
|
||||
},
|
||||
],
|
||||
"prefer-const": "warn",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 Authentik Security, Inc.
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
5
packages/monorepo/README.md
Normal file
5
packages/monorepo/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# `@goauthentik/monorepo`
|
||||
|
||||
This package contains utility scripts common to all TypeScript and JavaScript packages in the
|
||||
`@goauthentik` monorepo.
|
||||
|
||||
17
packages/monorepo/constants.js
Normal file
17
packages/monorepo/constants.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file Constants for JavaScript and TypeScript files.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The current Node.js environment, defaulting to "development" when not set.
|
||||
*
|
||||
* Note, this should only be used during the build process.
|
||||
*
|
||||
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
|
||||
* ensure that module tree-shaking works correctly.
|
||||
*
|
||||
*/
|
||||
export const NodeEnvironment = /** @type {'development' | 'production'} */ (
|
||||
process.env.NODE_ENV || "development"
|
||||
);
|
||||
4
packages/monorepo/index.js
Normal file
4
packages/monorepo/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./paths.js";
|
||||
export * from "./constants.js";
|
||||
export * from "./version.js";
|
||||
export * from "./scripting.js";
|
||||
19
packages/monorepo/package.json
Normal file
19
packages/monorepo/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@goauthentik/monorepo",
|
||||
"version": "1.0.0",
|
||||
"description": "Utilities for the authentik monorepo.",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
}
|
||||
}
|
||||
30
packages/monorepo/paths.js
Normal file
30
packages/monorepo/paths.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* @typedef {'~authentik'} MonoRepoRoot
|
||||
*/
|
||||
|
||||
/**
|
||||
* The root of the authentik monorepo.
|
||||
*/
|
||||
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", ".."));
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
/**
|
||||
* Resolve a package name to its location in the monorepo to the single node_modules directory.
|
||||
* @param {string} packageName
|
||||
* @returns {string} The resolved path to the package.
|
||||
* @throws {Error} If the package cannot be resolved.
|
||||
*/
|
||||
export function resolvePackage(packageName) {
|
||||
const packageJSONPath = require.resolve(join(packageName, "package.json"), {
|
||||
paths: [MonoRepoRoot],
|
||||
});
|
||||
|
||||
return dirname(packageJSONPath);
|
||||
}
|
||||
40
packages/monorepo/scripting.js
Normal file
40
packages/monorepo/scripting.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { createRequire } from "module";
|
||||
import * as path from "path";
|
||||
import * as process from "process";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/**
|
||||
* Predicate to determine if a module was run directly, i.e. not imported.
|
||||
*
|
||||
* @param {ImportMeta} meta The `import.meta` object of the module.
|
||||
*
|
||||
* @return {boolean} Whether the module was run directly.
|
||||
*/
|
||||
export function isMain(meta) {
|
||||
// Are we not in a module context?
|
||||
if (!meta) return false;
|
||||
|
||||
const relativeScriptPath = process.argv[1];
|
||||
|
||||
if (!relativeScriptPath) return false;
|
||||
|
||||
const require = createRequire(meta.url);
|
||||
const absoluteScriptPath = require.resolve(relativeScriptPath);
|
||||
|
||||
const modulePath = fileURLToPath(meta.url);
|
||||
|
||||
const scriptExtension = path.extname(absoluteScriptPath);
|
||||
|
||||
if (scriptExtension) {
|
||||
return modulePath === absoluteScriptPath;
|
||||
}
|
||||
|
||||
const moduleExtension = path.extname(modulePath);
|
||||
|
||||
if (moduleExtension) {
|
||||
return absoluteScriptPath === modulePath.slice(0, -moduleExtension.length);
|
||||
}
|
||||
|
||||
// If both are without extension, compare them directly.
|
||||
return modulePath === absoluteScriptPath;
|
||||
}
|
||||
0
packages/monorepo/scripts.js
Normal file
0
packages/monorepo/scripts.js
Normal file
9
packages/monorepo/tsconfig.json
Normal file
9
packages/monorepo/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
45
packages/monorepo/version.js
Normal file
45
packages/monorepo/version.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
import PackageJSON from "../../package.json" with { type: "json" };
|
||||
import { MonoRepoRoot } from "./paths.js";
|
||||
|
||||
/**
|
||||
* The current version of authentik in SemVer format.
|
||||
*
|
||||
*/
|
||||
export const AuthentikVersion = /**@type {`${number}.${number}.${number}`} */ (PackageJSON.version);
|
||||
|
||||
/**
|
||||
* Reads the last commit hash from the current git repository.
|
||||
*/
|
||||
export function readGitBuildHash() {
|
||||
try {
|
||||
const commit = execSync("git rev-parse HEAD", {
|
||||
encoding: "utf8",
|
||||
cwd: MonoRepoRoot,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return commit;
|
||||
} catch (_error) {
|
||||
console.debug("Git commit could not be read.");
|
||||
}
|
||||
|
||||
return process.env.GIT_BUILD_HASH || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the build identifier for the current environment.
|
||||
*
|
||||
* This must match the behavior defined in authentik's server-side `get_full_version` function.
|
||||
*
|
||||
* @see {@link "authentik\_\_init\_\_.py"}
|
||||
*/
|
||||
export function readBuildIdentifier() {
|
||||
const { GIT_BUILD_HASH = "d72def036820985a909266e8167ccb8087c7ce32" } = process.env;
|
||||
|
||||
if (!GIT_BUILD_HASH) return AuthentikVersion;
|
||||
|
||||
return [AuthentikVersion, GIT_BUILD_HASH].join("+");
|
||||
}
|
||||
@ -1,7 +1,17 @@
|
||||
/**
|
||||
* @file Prettier configuration for authentik.
|
||||
*
|
||||
* @import { Config as PrettierConfig } from "prettier";
|
||||
* @import { PluginConfig as SortPluginConfig } from "@trivago/prettier-plugin-sort-imports";
|
||||
*
|
||||
* @typedef {object} PackageJSONPluginConfig
|
||||
* @property {string[]} [packageSortOrder] Custom ordering array.
|
||||
*/
|
||||
|
||||
/**
|
||||
* authentik Prettier configuration.
|
||||
*
|
||||
* @type {import("prettier").Config}
|
||||
* @type {PrettierConfig & SortPluginConfig & PackageJSONPluginConfig}
|
||||
* @internal
|
||||
*/
|
||||
export const AuthentikPrettierConfig = {
|
||||
@ -27,6 +37,12 @@ export const AuthentikPrettierConfig = {
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
overrides: [
|
||||
{
|
||||
files: "schemas/**/*.json",
|
||||
options: {
|
||||
tabWidth: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "tsconfig.json",
|
||||
options: {
|
||||
@ -41,9 +57,22 @@ export const AuthentikPrettierConfig = {
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"license",
|
||||
"private",
|
||||
"author",
|
||||
"authors",
|
||||
"scripts",
|
||||
"devDependencies",
|
||||
"main",
|
||||
"type",
|
||||
"exports",
|
||||
"imports",
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
"wireit",
|
||||
"resolutions",
|
||||
"engines",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { format } from "prettier";
|
||||
|
||||
import { AuthentikPrettierConfig } from "./config.js";
|
||||
|
||||
/**
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
@ -13,9 +13,9 @@
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"peerDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
{
|
||||
"name": "@goauthentik/web-sfe",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "wireit",
|
||||
"lint:lockfile": "wireit",
|
||||
"prettier": "prettier --write ./src ./tsconfig.json ./rollup.config.js ./package.json",
|
||||
"watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2024.6.0-1719577139",
|
||||
"base64-js": "^1.5.1",
|
||||
"bootstrap": "^4.6.1",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.4.0",
|
||||
"@swc/core": "^1.7.28",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"lockfile-lint": "^4.14.0",
|
||||
"rollup": "^4.23.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"wireit": "^0.14.9"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core": "^1.7.28",
|
||||
"@swc/core-darwin-arm64": "^1.6.13",
|
||||
"@swc/core-darwin-x64": "^1.6.13",
|
||||
"@swc/core-linux-arm-gnueabihf": "^1.6.13",
|
||||
"@swc/core-linux-arm64-gnu": "^1.6.13",
|
||||
"@swc/core-linux-arm64-musl": "^1.6.13",
|
||||
"@swc/core-linux-x64-gnu": "^1.6.13",
|
||||
"@swc/core-linux-x64-musl": "^1.6.13",
|
||||
"@swc/core-win32-arm64-msvc": "^1.6.13",
|
||||
"@swc/core-win32-ia32-msvc": "^1.6.13",
|
||||
"@swc/core-win32-x64-msvc": "^1.6.13"
|
||||
},
|
||||
"wireit": {
|
||||
"build:sfe": {
|
||||
"command": "rollup -c rollup.config.js --bundleConfigAsCjs",
|
||||
"files": [
|
||||
"../../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/index.ts"
|
||||
],
|
||||
"output": [
|
||||
"./dist/sfe/*"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"command": "mkdir -p ../../dist/sfe && cp -r dist/sfe/* ../../dist/sfe",
|
||||
"dependencies": [
|
||||
"build:sfe"
|
||||
]
|
||||
},
|
||||
"lint:lockfile": {
|
||||
"command": "lockfile-lint --path package.json --type npm --allowed-hosts npm --validate-https"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import swc from "@rollup/plugin-swc";
|
||||
import copy from "rollup-plugin-copy";
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: {
|
||||
dir: "./dist/sfe",
|
||||
format: "cjs",
|
||||
},
|
||||
context: "window",
|
||||
plugins: [
|
||||
copy({
|
||||
targets: [
|
||||
{
|
||||
src: "../../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
dest: "./dist/sfe",
|
||||
},
|
||||
],
|
||||
}),
|
||||
resolve({ browser: true }),
|
||||
commonjs(),
|
||||
swc({
|
||||
swc: {
|
||||
jsc: {
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
// Requires v1.2.50 or upper and requires target to be es2016 or upper.
|
||||
keepClassNames: false,
|
||||
},
|
||||
minify: false,
|
||||
env: {
|
||||
targets: {
|
||||
edge: "17",
|
||||
ie: "11",
|
||||
},
|
||||
mode: "entry",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
@ -28,10 +28,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the type of resource, e.g., 'User' or 'Group'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"User",
|
||||
"Group"
|
||||
],
|
||||
"enum": ["User", "Group"],
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
@ -39,7 +36,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"displayName"
|
||||
]
|
||||
"required": ["displayName"]
|
||||
}
|
||||
|
||||
@ -51,10 +51,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schema",
|
||||
"required"
|
||||
],
|
||||
"required": ["schema", "required"],
|
||||
"additionalProperties": true
|
||||
}
|
||||
],
|
||||
@ -62,11 +59,6 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"endpoint",
|
||||
"schema",
|
||||
"schemaExtensions"
|
||||
],
|
||||
"required": ["name", "endpoint", "schema", "schemaExtensions"],
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@ -21,9 +21,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"bulk": {
|
||||
@ -36,9 +34,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"filter": {
|
||||
@ -56,9 +52,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"changePassword": {
|
||||
@ -71,9 +65,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"sort": {
|
||||
@ -86,9 +78,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"authenticationSchemes": {
|
||||
@ -119,21 +109,11 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"required": ["name", "description"],
|
||||
"readOnly": true
|
||||
},
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"patch",
|
||||
"bulk",
|
||||
"filter",
|
||||
"changePassword",
|
||||
"sort",
|
||||
"authenticationSchemes"
|
||||
]
|
||||
"required": ["patch", "bulk", "filter", "changePassword", "sort", "authenticationSchemes"]
|
||||
}
|
||||
|
||||
@ -99,11 +99,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work' or 'home'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "other"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -129,14 +125,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work', 'home', 'mobile'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"mobile",
|
||||
"fax",
|
||||
"pager",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "mobile", "fax", "pager", "other"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred phone number or primary phone number. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -162,16 +151,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'aim', 'gtalk', 'xmpp'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"aim",
|
||||
"gtalk",
|
||||
"icq",
|
||||
"xmpp",
|
||||
"msn",
|
||||
"skype",
|
||||
"qq",
|
||||
"yahoo"
|
||||
]
|
||||
"enum": ["aim", "gtalk", "icq", "xmpp", "msn", "skype", "qq", "yahoo"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred messenger or primary messenger. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -198,10 +178,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, i.e., 'photo' or 'thumbnail'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"photo",
|
||||
"thumbnail"
|
||||
]
|
||||
"enum": ["photo", "thumbnail"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred photo or thumbnail. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -243,11 +220,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work' or 'home'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "other"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -277,10 +250,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'direct' or 'indirect'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"direct",
|
||||
"indirect"
|
||||
],
|
||||
"enum": ["direct", "indirect"],
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
@ -366,7 +336,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"userName"
|
||||
]
|
||||
"required": ["userName"]
|
||||
}
|
||||
|
||||
@ -19,9 +19,8 @@
|
||||
"files": [],
|
||||
"references": [
|
||||
// Note that references are in the order we want them to be built.
|
||||
{ "path": "./packages/tsconfig" },
|
||||
{ "path": "./packages/prettier-config" },
|
||||
{ "path": "./packages/eslint-config" },
|
||||
{ "path": "./packages/sfe" }
|
||||
{ "path": "./web" }
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
# Import order matters
|
||||
poly.ts
|
||||
src/locale-codes.ts
|
||||
src/locales/
|
||||
storybook-static/
|
||||
# Prettier breaks the tsconfig file
|
||||
tsconfig.json
|
||||
.storybook/css-import-maps*
|
||||
package.json
|
||||
packages/**/package.json
|
||||
@ -1,23 +0,0 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "consistent",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"],
|
||||
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"importOrderParserPlugins": ["typescript", "jsx", "classProperties", "decorators-legacy"]
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import type { StorybookConfig } from "@storybook/web-components-vite";
|
||||
import { cwd } from "process";
|
||||
import { cwd } from "node:process";
|
||||
import modify from "rollup-plugin-modify";
|
||||
import postcssLit from "rollup-plugin-postcss-lit";
|
||||
import { mergeConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export const isProdBuild = process.env.NODE_ENV === "production";
|
||||
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || "";
|
||||
|
||||
const importInlinePatterns = [
|
||||
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
|
||||
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
|
||||
@ -53,8 +56,14 @@ const config: StorybookConfig = {
|
||||
autodocs: "tag",
|
||||
},
|
||||
async viteFinal(config) {
|
||||
return {
|
||||
...config,
|
||||
return mergeConfig(config, {
|
||||
define: {
|
||||
"process.env.NODE_ENV": JSON.stringify(NODE_ENV),
|
||||
"process.env.CWD": JSON.stringify(cwd()),
|
||||
"process.env.AK_API_BASE_PATH": JSON.stringify(AK_API_BASE_PATH),
|
||||
"process.env.WATCHER_URL": "",
|
||||
},
|
||||
|
||||
plugins: [
|
||||
modify({
|
||||
find: importInlineRegexp,
|
||||
@ -62,19 +71,10 @@ const config: StorybookConfig = {
|
||||
return `${match}?inline`;
|
||||
},
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development",
|
||||
),
|
||||
"process.env.CWD": JSON.stringify(cwd()),
|
||||
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
||||
"preventAssignment": true,
|
||||
}),
|
||||
...config.plugins,
|
||||
postcssLit(),
|
||||
tsconfigPaths(),
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
// You would not believe how much this change has frustrated users: ["if an ignores key is used
|
||||
// without any other keys in the configuration object, then the patterns act as global
|
||||
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
|
||||
{
|
||||
ignores: [
|
||||
"dist/",
|
||||
// don't lint the cache
|
||||
".wireit/",
|
||||
// let packages have their own configurations
|
||||
"packages/",
|
||||
// don't ever lint node_modules
|
||||
"node_modules/",
|
||||
".storybook/*",
|
||||
// don't lint build output (make sure it's set to your correct build folder name)
|
||||
// don't lint nyc coverage output
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
},
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"lit/attribute-names": "off",
|
||||
// "lit/attribute-names": "error",
|
||||
"lit/no-private-properties": "error",
|
||||
// "lit/prefer-nothing": "warn",
|
||||
"lit/no-template-bind": "error",
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
files: ["scripts/**/*.mjs", "*.ts", "*.mjs"],
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
// We WANT our scripts to output to the console!
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
272
web/package.json
272
web/package.json
@ -1,138 +1,9 @@
|
||||
{
|
||||
"name": "@goauthentik/web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.2-1742585853",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@lit/task": "^1.0.1",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.0.2",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@spotlightjs/spotlight": "^2.4.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.38.1",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"lit": "^3.2.0",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.4.1",
|
||||
"rapidoc": "^9.3.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-directive": "^4.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^5.0.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"ts-pattern": "^5.4.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@rollup/plugin-replace": "^6.0.1",
|
||||
"@storybook/addon-essentials": "^8.3.4",
|
||||
"@storybook/addon-links": "^8.3.4",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/blocks": "^8.3.4",
|
||||
"@storybook/builder-vite": "^8.3.4",
|
||||
"@storybook/manager-api": "^8.3.4",
|
||||
"@storybook/web-components": "^8.3.4",
|
||||
"@storybook/web-components-vite": "^8.3.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.2",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.13",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^8.8.0",
|
||||
"@wdio/browser-runner": "9.4",
|
||||
"@wdio/cli": "9.4",
|
||||
"@wdio/spec-reporter": "^9.1.2",
|
||||
"chromedriver": "^131.0.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-wc": "^2.1.1",
|
||||
"find-free-ports": "^3.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"globals": "^15.10.0",
|
||||
"knip": "^5.30.6",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"pseudolocale": "^2.1.0",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.3.4",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"syncpack": "^13.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.8.0",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.24.0",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.23.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.23.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "wireit",
|
||||
"build-locales": "wireit",
|
||||
"build-locales:build": "wireit",
|
||||
"build-proxy": "wireit",
|
||||
"build:sfe": "wireit",
|
||||
"esbuild:watch": "node scripts/build-web.mjs --watch",
|
||||
@ -160,7 +31,138 @@
|
||||
"tsc": "wireit",
|
||||
"watch": "run-s build-locales esbuild:watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/monorepo": "1.0.0",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@rollup/plugin-replace": "^6.0.1",
|
||||
"@storybook/addon-essentials": "^8.3.4",
|
||||
"@storybook/addon-links": "^8.3.4",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/blocks": "^8.3.4",
|
||||
"@storybook/builder-vite": "^8.3.4",
|
||||
"@storybook/manager-api": "^8.3.4",
|
||||
"@storybook/web-components": "^8.3.4",
|
||||
"@storybook/web-components-vite": "^8.3.4",
|
||||
"@swc/helpers": "^0.5.15",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.2",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.13",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@wdio/browser-runner": "9.12.1",
|
||||
"@wdio/cli": "9.12.1",
|
||||
"@wdio/spec-reporter": "^9.11.0",
|
||||
"chromedriver": "^134.0.5",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-es5": "^2.1.1",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"find-free-ports": "^3.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"globals": "^15.10.0",
|
||||
"knip": "^5.30.6",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pseudolocale": "^2.1.0",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.3.4",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"syncpack": "^13.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.9",
|
||||
"zx": "^8.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.2-1742585853",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@lit/task": "^1.0.1",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.0.2",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@spotlightjs/spotlight": "^2.4.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"bootstrap": "^4.6.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.38.1",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.4",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"jquery": "^3.7.1",
|
||||
"lit": "^3.2.0",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.4.1",
|
||||
"rapidoc": "^9.3.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-directive": "^4.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^5.0.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"ts-pattern": "^5.4.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"weakmap-polyfill": "^2.0.4",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./paths": "./paths.js",
|
||||
"./scripts/*": "./scripts/*.mjs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.24.0",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.23.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
"#comment": [
|
||||
@ -217,12 +219,6 @@
|
||||
"build-locales"
|
||||
]
|
||||
},
|
||||
"build-locales:build": {
|
||||
"command": "lit-localize build"
|
||||
},
|
||||
"build-locales:repair": {
|
||||
"command": "prettier --write ./src/locale-codes.ts"
|
||||
},
|
||||
"build-locales": {
|
||||
"command": "node scripts/build-locales.mjs",
|
||||
"files": [
|
||||
@ -376,9 +372,5 @@
|
||||
"lint:types"
|
||||
]
|
||||
}
|
||||
},
|
||||
"workspaces": [
|
||||
".",
|
||||
"./packages/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* @file Simple Flow Executor entry point.
|
||||
*/
|
||||
import { fromByteArray } from "base64-js";
|
||||
import "formdata-polyfill";
|
||||
import $ from "jquery";
|
||||
@ -33,82 +36,6 @@ function ak(): GlobalAuthentik {
|
||||
).authentik;
|
||||
}
|
||||
|
||||
class SimpleFlowExecutor {
|
||||
challenge?: ChallengeTypes;
|
||||
flowSlug: string;
|
||||
container: HTMLDivElement;
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this.flowSlug = window.location.pathname.split("/")[3];
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
get apiURL() {
|
||||
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
||||
}
|
||||
|
||||
start() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: this.apiURL,
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submit(data: { [key: string]: unknown } | FormData) {
|
||||
$("button[type=submit]").addClass("disabled")
|
||||
.html(`<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
|
||||
<span role="status">Loading...</span>`);
|
||||
let finalData: { [key: string]: unknown } = {};
|
||||
if (data instanceof FormData) {
|
||||
finalData = {};
|
||||
data.forEach((value, key) => {
|
||||
finalData[key] = value;
|
||||
});
|
||||
} else {
|
||||
finalData = data;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: this.apiURL,
|
||||
data: JSON.stringify(finalData),
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
});
|
||||
}
|
||||
|
||||
renderChallenge() {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-identification":
|
||||
new IdentificationStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-password":
|
||||
new PasswordStage(this, this.challenge).render();
|
||||
return;
|
||||
case "xak-flow-redirect":
|
||||
new RedirectStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-autosubmit":
|
||||
new AutosubmitStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-authenticator-validate":
|
||||
new AuthenticatorValidateStage(this, this.challenge).render();
|
||||
return;
|
||||
default:
|
||||
this.container.innerText =
|
||||
"Unsupported stage: " + this.challenge?.component;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FlowInfoChallenge {
|
||||
flowInfo?: ContextualFlowInfo;
|
||||
responseErrors?: {
|
||||
@ -271,13 +198,10 @@ export interface AuthAssertion {
|
||||
}
|
||||
|
||||
class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
declare deviceChallenge?: DeviceChallenge;
|
||||
|
||||
b64enc(buf: Uint8Array): string {
|
||||
return fromByteArray(buf)
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]/g, "");
|
||||
}
|
||||
|
||||
b64RawEnc(buf: Uint8Array): string {
|
||||
@ -285,9 +209,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
}
|
||||
|
||||
u8arr(input: string): Uint8Array {
|
||||
return Uint8Array.from(
|
||||
atob(input.replace(/_/g, "/").replace(/-/g, "+")),
|
||||
(c) => c.charCodeAt(0),
|
||||
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
|
||||
c.charCodeAt(0),
|
||||
);
|
||||
}
|
||||
|
||||
@ -295,13 +218,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
if ("credentials" in navigator) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
window.location.protocol === "http:" &&
|
||||
window.location.hostname !== "localhost"
|
||||
) {
|
||||
console.warn(
|
||||
"WebAuthn requires this page to be accessed via HTTPS.",
|
||||
);
|
||||
if (window.location.protocol === "http:" && window.location.hostname !== "localhost") {
|
||||
console.warn("WebAuthn requires this page to be accessed via HTTPS.");
|
||||
return false;
|
||||
}
|
||||
console.warn("WebAuthn not supported by browser.");
|
||||
@ -322,9 +240,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
// string, then a byte array, re-encode it and wrap that in an array.
|
||||
const stringId = decodeURIComponent(window.atob(userId));
|
||||
user.id = this.u8arr(this.b64enc(this.u8arr(stringId)));
|
||||
const challenge = this.u8arr(
|
||||
credentialCreateOptions.challenge.toString(),
|
||||
);
|
||||
const challenge = this.u8arr(credentialCreateOptions.challenge.toString());
|
||||
|
||||
return Object.assign({}, credentialCreateOptions, {
|
||||
challenge,
|
||||
@ -337,28 +253,19 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
* for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
transformNewAssertionForServer(
|
||||
newAssertion: PublicKeyCredential,
|
||||
): Assertion {
|
||||
transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
|
||||
const attObj = new Uint8Array(
|
||||
(
|
||||
newAssertion.response as AuthenticatorAttestationResponse
|
||||
).attestationObject,
|
||||
);
|
||||
const clientDataJSON = new Uint8Array(
|
||||
newAssertion.response.clientDataJSON,
|
||||
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
|
||||
);
|
||||
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
|
||||
const registrationClientExtensions =
|
||||
newAssertion.getClientExtensionResults();
|
||||
const registrationClientExtensions = newAssertion.getClientExtensionResults();
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: this.b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
registrationClientExtensions: JSON.stringify(
|
||||
registrationClientExtensions,
|
||||
),
|
||||
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
|
||||
response: {
|
||||
clientDataJSON: this.b64enc(clientDataJSON),
|
||||
attestationObject: this.b64enc(attObj),
|
||||
@ -369,16 +276,14 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
transformCredentialRequestOptions(
|
||||
credentialRequestOptions: PublicKeyCredentialRequestOptions,
|
||||
): PublicKeyCredentialRequestOptions {
|
||||
const challenge = this.u8arr(
|
||||
credentialRequestOptions.challenge.toString(),
|
||||
);
|
||||
const challenge = this.u8arr(credentialRequestOptions.challenge.toString());
|
||||
|
||||
const allowCredentials = (
|
||||
credentialRequestOptions.allowCredentials || []
|
||||
).map((credentialDescriptor) => {
|
||||
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
|
||||
(credentialDescriptor) => {
|
||||
const id = this.u8arr(credentialDescriptor.id.toString());
|
||||
return Object.assign({}, credentialDescriptor, { id });
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return Object.assign({}, credentialRequestOptions, {
|
||||
challenge,
|
||||
@ -390,25 +295,19 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
* Encodes the binary data in the assertion into strings for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
transformAssertionForServer(
|
||||
newAssertion: PublicKeyCredential,
|
||||
): AuthAssertion {
|
||||
const response =
|
||||
newAssertion.response as AuthenticatorAssertionResponse;
|
||||
transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
|
||||
const response = newAssertion.response as AuthenticatorAssertionResponse;
|
||||
const authData = new Uint8Array(response.authenticatorData);
|
||||
const clientDataJSON = new Uint8Array(response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
const sig = new Uint8Array(response.signature);
|
||||
const assertionClientExtensions =
|
||||
newAssertion.getClientExtensionResults();
|
||||
const assertionClientExtensions = newAssertion.getClientExtensionResults();
|
||||
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: this.b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
assertionClientExtensions: JSON.stringify(
|
||||
assertionClientExtensions,
|
||||
),
|
||||
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
|
||||
|
||||
response: {
|
||||
clientDataJSON: this.b64RawEnc(clientDataJSON),
|
||||
@ -421,8 +320,10 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
|
||||
render() {
|
||||
if (!this.deviceChallenge) {
|
||||
return this.renderChallengePicker();
|
||||
this.renderChallengePicker();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.deviceChallenge.deviceClass) {
|
||||
case "static":
|
||||
case "totp":
|
||||
@ -437,10 +338,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
}
|
||||
|
||||
renderChallengePicker() {
|
||||
const challenges = this.challenge.deviceChallenges.filter(
|
||||
(challenge) =>
|
||||
challenge.deviceClass === "webauthn" &&
|
||||
!this.checkWebAuthnSupport()
|
||||
const challenges = this.challenge.deviceChallenges.filter((challenge) =>
|
||||
challenge.deviceClass === "webauthn" && !this.checkWebAuthnSupport()
|
||||
? undefined
|
||||
: challenge,
|
||||
);
|
||||
@ -480,12 +379,13 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
.join("")}
|
||||
</form>`);
|
||||
this.challenge.deviceChallenges.forEach((challenge) => {
|
||||
$(
|
||||
`#picker-form button#${challenge.deviceClass}-${challenge.deviceUid}`,
|
||||
).on("click", () => {
|
||||
$(`#picker-form button#${challenge.deviceClass}-${challenge.deviceUid}`).on(
|
||||
"click",
|
||||
() => {
|
||||
this.deviceChallenge = challenge;
|
||||
this.render();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -523,8 +423,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
navigator.credentials
|
||||
.get({
|
||||
publicKey: this.transformCredentialRequestOptions(
|
||||
this.deviceChallenge
|
||||
?.challenge as PublicKeyCredentialRequestOptions,
|
||||
this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions,
|
||||
),
|
||||
})
|
||||
.then((assertion) => {
|
||||
@ -534,8 +433,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
try {
|
||||
// we now have an authentication assertion! encode the byte arrays contained
|
||||
// in the assertion data as strings for posting to the server
|
||||
const transformedAssertionForServer =
|
||||
this.transformAssertionForServer(
|
||||
const transformedAssertionForServer = this.transformAssertionForServer(
|
||||
assertion as PublicKeyCredential,
|
||||
);
|
||||
|
||||
@ -544,9 +442,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
webauthn: transformedAssertionForServer,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`Error when validating assertion on server: ${err}`,
|
||||
);
|
||||
throw new Error(`Error when validating assertion on server: ${err}`);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -557,7 +453,87 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
}
|
||||
}
|
||||
|
||||
const sfe = new SimpleFlowExecutor(
|
||||
$("#flow-sfe-container")[0] as HTMLDivElement,
|
||||
);
|
||||
class SimpleFlowExecutor {
|
||||
challenge?: ChallengeTypes;
|
||||
flowSlug: string;
|
||||
container: HTMLDivElement;
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this.flowSlug = window.location.pathname.split("/")[3] || "";
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
get apiURL() {
|
||||
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
||||
}
|
||||
|
||||
start() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: this.apiURL,
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submit(payload: { [key: string]: unknown } | FormData) {
|
||||
$("button[type=submit]").addClass("disabled")
|
||||
.html(`<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
|
||||
<span role="status">Loading...</span>`);
|
||||
let finalData: { [key: string]: unknown } = {};
|
||||
if (payload instanceof FormData) {
|
||||
finalData = {};
|
||||
payload.forEach((value, key) => {
|
||||
finalData[key] = value;
|
||||
});
|
||||
} else {
|
||||
finalData = payload;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: this.apiURL,
|
||||
data: JSON.stringify(finalData),
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
});
|
||||
}
|
||||
|
||||
renderChallenge() {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-identification":
|
||||
new IdentificationStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-password":
|
||||
new PasswordStage(this, this.challenge).render();
|
||||
return;
|
||||
case "xak-flow-redirect":
|
||||
new RedirectStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-autosubmit":
|
||||
new AutosubmitStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-authenticator-validate":
|
||||
new AuthenticatorValidateStage(this, this.challenge).render();
|
||||
return;
|
||||
default:
|
||||
this.container.innerText = `Unsupported stage: ${this.challenge?.component}`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [flowContainer] = $<HTMLDivElement>("#flow-sfe-container");
|
||||
|
||||
if (!flowContainer) {
|
||||
throw new Error("No flow container element found");
|
||||
}
|
||||
|
||||
const sfe = new SimpleFlowExecutor(flowContainer);
|
||||
|
||||
sfe.start();
|
||||
@ -1,8 +1,8 @@
|
||||
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"types": ["jquery"],
|
||||
"emitDeclarationOnly": true,
|
||||
"baseUrl": ".",
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "ES2015", "ES2017"]
|
||||
}
|
||||
22
web/paths.js
Normal file
22
web/paths.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* @typedef {'@goauthentik/web'} WebPackageIdentifier
|
||||
*/
|
||||
|
||||
/**
|
||||
* The root of the web package.
|
||||
*/
|
||||
export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(__dirname));
|
||||
|
||||
/**
|
||||
* Path to the web package's distribution directory.
|
||||
*
|
||||
* This is where the built files are located after running the build process.
|
||||
*/
|
||||
export const DistDirectory = /** @type {`${WebPackageIdentifier}/dist`} */ (
|
||||
resolve(__dirname, "dist")
|
||||
);
|
||||
@ -1,72 +1,103 @@
|
||||
import { spawnSync } from "child_process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import process from "process";
|
||||
/**
|
||||
* @file Lit Localize build script.
|
||||
*
|
||||
* @import { Config } from "@lit/localize-tools/lib/types/config.js"
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import process from "node:process";
|
||||
import { $ } from "zx";
|
||||
|
||||
const localizeRules = await import("../lit-localize.json", {
|
||||
with: {
|
||||
type: "json",
|
||||
},
|
||||
})
|
||||
.then((module) => {
|
||||
return /** @type {Config} */ (module.default);
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
console.error("Failed to load lit-localize.json", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Determines if all the Xliff translation source files are present and if the Typescript source
|
||||
* files generated from those sources are up-to-date. If they are not, it runs the locale building
|
||||
* script, intercepting the long spew of "this string is not translated" and replacing it with a
|
||||
* summary of how many strings are missing with respect to the source locale.
|
||||
* Attempt to stat a file, returning null if it doesn't exist.
|
||||
*/
|
||||
function tryStat(filePath) {
|
||||
return fs.stat(filePath).catch(() => null);
|
||||
}
|
||||
|
||||
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8"));
|
||||
/**
|
||||
* Check if a generated file is up-to-date with its XLIFF source.
|
||||
*
|
||||
* @param {string} languageCode The locale to check.
|
||||
*/
|
||||
async function generatedFileIsUpToDateWithXliffSource(languageCode) {
|
||||
const xlfFilePath = path.join("./xliff", `${languageCode}.xlf`);
|
||||
const xlfStat = await tryStat(xlfFilePath);
|
||||
|
||||
function generatedFileIsUpToDateWithXliffSource(loc) {
|
||||
const xliff = path.join("./xliff", `${loc}.xlf`);
|
||||
const gened = path.join("./src/locales", `${loc}.ts`);
|
||||
if (!xlfStat) {
|
||||
console.error(`lit-localize expected '${languageCode}.xlf', but XLF file is not present`);
|
||||
|
||||
// Returns false if: the expected XLF file doesn't exist, The expected
|
||||
// generated file doesn't exist, or the XLF file is newer (has a higher date)
|
||||
// than the generated file. The missing XLF file is important enough it
|
||||
// generates a unique error message and halts the build.
|
||||
|
||||
try {
|
||||
var xlfStat = fs.statSync(xliff);
|
||||
} catch (_error) {
|
||||
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// If the generated file doesn't exist, of course it's not up to date.
|
||||
try {
|
||||
var genedStat = fs.statSync(gened);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
const generatedTSFilePath = path.join("./src/locales", `${languageCode}.ts`);
|
||||
|
||||
const generatedTSFilePathStat = await tryStat(generatedTSFilePath);
|
||||
|
||||
// Does the generated file exist?
|
||||
if (!generatedTSFilePathStat) {
|
||||
return {
|
||||
languageCode,
|
||||
exists: false,
|
||||
expired: null,
|
||||
};
|
||||
}
|
||||
|
||||
// if the generated file is the same age or newer (date is greater) than the xliff file, it's
|
||||
// presumed to have been generated by that file and is up-to-date.
|
||||
return genedStat.mtimeMs >= xlfStat.mtimeMs;
|
||||
return {
|
||||
languageCode,
|
||||
exists: true,
|
||||
// Is the generated file older than the XLIFF file?
|
||||
expired: generatedTSFilePathStat.mtimeMs < xlfStat.mtimeMs,
|
||||
};
|
||||
}
|
||||
|
||||
// For all the expected files, find out if any aren't up-to-date.
|
||||
const upToDate = localizeRules.targetLocales.reduce(
|
||||
(acc, loc) => acc && generatedFileIsUpToDateWithXliffSource(loc),
|
||||
true,
|
||||
const results = await Promise.all(
|
||||
localizeRules.targetLocales.map(generatedFileIsUpToDateWithXliffSource),
|
||||
);
|
||||
|
||||
if (!upToDate) {
|
||||
const status = spawnSync("npm", ["run", "build-locales:build"], { encoding: "utf8" });
|
||||
const pendingBuild = results.some((result) => !result.exists || result.expired);
|
||||
|
||||
if (!pendingBuild) {
|
||||
console.log("Local is up-to-date!");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const status = await $({ stdio: ["ignore", "pipe", "pipe"] })`npx lit-localize build`;
|
||||
|
||||
/**
|
||||
* @type {Map<string, number>}
|
||||
*/
|
||||
const counts = new Map();
|
||||
|
||||
// Count all the missing message warnings
|
||||
const counts = status.stderr.split("\n").reduce((acc, line) => {
|
||||
for (const line of status.stderr.split("\n")) {
|
||||
const match = /^([\w-]+) message/.exec(line);
|
||||
if (!match) {
|
||||
return acc;
|
||||
}
|
||||
acc.set(match[1], (acc.get(match[1]) || 0) + 1);
|
||||
return acc;
|
||||
}, new Map());
|
||||
if (!match) continue;
|
||||
|
||||
const locales = Array.from(counts.keys());
|
||||
locales.sort();
|
||||
|
||||
const report = locales
|
||||
.map((locale) => `Locale '${locale}' has ${counts.get(locale)} missing translations`)
|
||||
.join("\n");
|
||||
|
||||
console.log(`Translation tables rebuilt.\n${report}\n`);
|
||||
const count = counts.get(match[1]) || 0;
|
||||
counts.set(match[1], count + 1);
|
||||
}
|
||||
|
||||
console.log("Locale ./src is up-to-date");
|
||||
const locales = Array.from(counts.keys()).sort();
|
||||
|
||||
for (const locale of locales) {
|
||||
console.log(`Locale '${locale}' has ${counts.get(locale)} missing translations`);
|
||||
}
|
||||
|
||||
await $`npx prettier --write src/locale-codes.ts`;
|
||||
|
||||
console.log("\nTranslation tables rebuilt.\n");
|
||||
|
||||
79
web/scripts/build-sfe.mjs
Normal file
79
web/scripts/build-sfe.mjs
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @file Build script for the simplified flow executor (SFE).
|
||||
*/
|
||||
import { DistDirectory, PackageRoot } from "@goauthentik/web/paths";
|
||||
import esbuild from "esbuild";
|
||||
import copy from "esbuild-plugin-copy";
|
||||
import { es5Plugin } from "esbuild-plugin-es5";
|
||||
import { createRequire } from "node:module";
|
||||
import * as path from "node:path";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
async function buildSFE() {
|
||||
const sourceDirectory = path.join(PackageRoot, "packages", "sfe");
|
||||
const outDirectory = path.join(DistDirectory, "sfe");
|
||||
|
||||
const bootstrapCSSPath = require.resolve(
|
||||
path.join("bootstrap", "dist", "css", "bootstrap.min.css"),
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {esbuild.BuildOptions}
|
||||
*/
|
||||
const config = {
|
||||
tsconfig: path.join(sourceDirectory, "tsconfig.json"),
|
||||
entryPoints: [path.join(sourceDirectory, "index.ts")],
|
||||
minify: false,
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
|
||||
legalComments: "external",
|
||||
platform: "browser",
|
||||
format: "iife",
|
||||
alias: {
|
||||
"@swc/helpers": path.dirname(require.resolve("@swc/helpers/package.json")),
|
||||
},
|
||||
banner: {
|
||||
js: [
|
||||
// ---
|
||||
"// Simplified Flow Executor (SFE)",
|
||||
"// @ts-nocheck",
|
||||
"",
|
||||
].join("\n"),
|
||||
},
|
||||
plugins: [
|
||||
copy({
|
||||
assets: [
|
||||
{
|
||||
from: bootstrapCSSPath,
|
||||
to: outDirectory,
|
||||
},
|
||||
],
|
||||
}),
|
||||
es5Plugin({
|
||||
swc: {
|
||||
jsc: {
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
keepClassNames: false,
|
||||
},
|
||||
minify: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
target: ["es5"],
|
||||
outdir: outDirectory,
|
||||
};
|
||||
|
||||
esbuild.build(config);
|
||||
}
|
||||
|
||||
buildSFE()
|
||||
.then(() => {
|
||||
console.log("Build complete");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Build failed", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@ -1,14 +1,14 @@
|
||||
import { execFileSync } from "child_process";
|
||||
import { deepmerge } from "deepmerge-ts";
|
||||
import esbuild from "esbuild";
|
||||
import { polyfillNode } from "esbuild-plugin-polyfill-node";
|
||||
import findFreePorts from "find-free-ports";
|
||||
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
|
||||
import { globSync } from "glob";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { cwd } from "node:process";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import * as path from "path";
|
||||
import { cwd } from "process";
|
||||
import process from "process";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
|
||||
import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs";
|
||||
@ -117,6 +117,7 @@ const BASE_ESBUILD_OPTIONS = {
|
||||
write: true,
|
||||
sourcemap: true,
|
||||
minify: NODE_ENV === "production",
|
||||
legalComments: "external",
|
||||
splitting: true,
|
||||
treeShaking: true,
|
||||
external: ["*.woff", "*.woff2"],
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { execSync } from "child_process";
|
||||
import path from "path";
|
||||
import { execSync } from "node:child_process";
|
||||
import * as path from "node:path";
|
||||
|
||||
const projectRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).replace(
|
||||
"\n",
|
||||
|
||||
@ -40,22 +40,13 @@ const name = "mdx-plugin";
|
||||
* @returns {Plugin} Plugin.
|
||||
*/
|
||||
export function mdxPlugin({ root }) {
|
||||
return { name, setup };
|
||||
|
||||
/**
|
||||
* @param {PluginBuild} build
|
||||
* Build.
|
||||
* @returns {undefined}
|
||||
* Nothing.
|
||||
*/
|
||||
function setup(build) {
|
||||
build.onLoad({ filter: /\.mdx?$/ }, onload);
|
||||
|
||||
/**
|
||||
* @param {LoadData} data
|
||||
* Data.
|
||||
* @returns {Promise<OnLoadResult>}
|
||||
* Result.
|
||||
* @param {LoadData} data Data.
|
||||
* @returns {Promise<OnLoadResult>} Result.
|
||||
*/
|
||||
async function onload(data) {
|
||||
const content = String(
|
||||
@ -77,5 +68,9 @@ export function mdxPlugin({ root }) {
|
||||
loader: "file",
|
||||
};
|
||||
}
|
||||
|
||||
build.onLoad({ filter: /\.mdx?$/ }, onload);
|
||||
}
|
||||
|
||||
return { name, setup };
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import * as http from "http";
|
||||
import path from "path";
|
||||
import * as path from "node:path";
|
||||
|
||||
/**
|
||||
* Serializes a custom event to a text stream.
|
||||
@ -13,7 +13,7 @@ export function serializeCustomEventToStream(event) {
|
||||
|
||||
const eventContent = [`event: ${event.type}`, `data: ${JSON.stringify(data)}`];
|
||||
|
||||
return eventContent.join("\n") + "\n\n";
|
||||
return `${eventContent.join("\n")}\n\n`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,6 +70,13 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
|
||||
dispatcher.addEventListener("esbuild:error", listener);
|
||||
dispatcher.addEventListener("esbuild:end", listener);
|
||||
|
||||
const keepAliveInterval = setInterval(() => {
|
||||
console.timeStamp("🏓 Keep-alive");
|
||||
|
||||
res.write("event: keep-alive\n\n");
|
||||
res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive")));
|
||||
}, 15_000);
|
||||
|
||||
req.on("close", () => {
|
||||
console.log("🔌 Client disconnected");
|
||||
|
||||
@ -79,13 +86,6 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
|
||||
dispatcher.removeEventListener("esbuild:error", listener);
|
||||
dispatcher.removeEventListener("esbuild:end", listener);
|
||||
});
|
||||
|
||||
const keepAliveInterval = setInterval(() => {
|
||||
console.timeStamp("🏓 Keep-alive");
|
||||
|
||||
res.write("event: keep-alive\n\n");
|
||||
res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive")));
|
||||
}, 15_000);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { execFileSync } from "child_process";
|
||||
import { ESLint } from "eslint";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import process from "process";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
function changedFiles() {
|
||||
const gitStatus = execFileSync("git", ["diff", "--name-only", "HEAD"], { encoding: "utf8" });
|
||||
const gitUntracked = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
const changed = gitStatus
|
||||
.split("\n")
|
||||
.filter((line) => line.trim().substring(0, 4) === "web/")
|
||||
.filter((line) => /\.(m|c)?(t|j)s$/.test(line))
|
||||
.map((line) => line.substring(4))
|
||||
.filter((line) => fs.existsSync(line));
|
||||
|
||||
const untracked = gitUntracked
|
||||
.split("\n")
|
||||
.filter((line) => /\.(m|c)?(t|j)s$/.test(line))
|
||||
.filter((line) => fs.existsSync(line));
|
||||
|
||||
const sourceFiles = [...changed, ...untracked].filter((line) => /^src\//.test(line));
|
||||
const scriptFiles = [...changed, ...untracked].filter(
|
||||
(line) => /^scripts\//.test(line) || !/^src\//.test(line),
|
||||
);
|
||||
|
||||
return [...sourceFiles, ...scriptFiles];
|
||||
}
|
||||
|
||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||
const projectRoot = path.join(__dirname, "..");
|
||||
process.chdir(projectRoot);
|
||||
|
||||
const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]);
|
||||
|
||||
const [configFile, files] = hasFlag(["-n", "--nightmare"])
|
||||
? [path.join(__dirname, "eslint.nightmare.mjs"), changedFiles()]
|
||||
: hasFlag(["-p", "--precommit"])
|
||||
? [path.join(__dirname, "eslint.precommit.mjs"), changedFiles()]
|
||||
: [path.join(projectRoot, "eslint.config.mjs"), ["."]];
|
||||
|
||||
const eslint = new ESLint({
|
||||
overrideConfigFile: configFile,
|
||||
warnIgnored: false,
|
||||
});
|
||||
|
||||
const results = await eslint.lintFiles(files);
|
||||
const formatter = await eslint.loadFormatter("stylish");
|
||||
const resultText = formatter.format(results);
|
||||
const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
|
||||
console.log(resultText);
|
||||
process.exit(errors > 1 ? 1 : 0);
|
||||
@ -1,217 +0,0 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const MAX_DEPTH = 4;
|
||||
const MAX_NESTED_CALLBACKS = 4;
|
||||
const MAX_PARAMS = 5;
|
||||
|
||||
// Waiting for SonarJS to be compatible
|
||||
// const MAX_COGNITIVE_COMPLEXITY = 9;
|
||||
|
||||
const rules = {
|
||||
"accessor-pairs": "error",
|
||||
"array-callback-return": "error",
|
||||
"block-scoped-var": "error",
|
||||
"consistent-return": "error",
|
||||
"consistent-this": ["error", "that"],
|
||||
"curly": ["error", "all"],
|
||||
"dot-notation": [
|
||||
"error",
|
||||
{
|
||||
allowKeywords: true,
|
||||
},
|
||||
],
|
||||
"eqeqeq": "error",
|
||||
"func-names": "error",
|
||||
"guard-for-in": "error",
|
||||
"max-depth": ["error", MAX_DEPTH],
|
||||
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
|
||||
"max-params": ["error", MAX_PARAMS],
|
||||
"new-cap": "error",
|
||||
"no-alert": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-case-declarations": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-constant-condition": "error",
|
||||
"no-control-regex": "error",
|
||||
"no-debugger": "error",
|
||||
"no-delete-var": "error",
|
||||
"no-div-regex": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty": "error",
|
||||
"no-empty-character-class": "error",
|
||||
"no-empty-function": "error",
|
||||
"no-labels": "error",
|
||||
"no-eq-null": "error",
|
||||
"no-eval": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-implicit-globals": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-iterator": "error",
|
||||
"no-invalid-this": "error",
|
||||
"no-label-var": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-magic-numbers": ["error", { ignore: [0, 1, -1] }],
|
||||
"no-multi-str": "error",
|
||||
"no-negated-condition": "error",
|
||||
"no-nested-ternary": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-octal": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": "error",
|
||||
"no-proto": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-restricted-syntax": ["error", "WithStatement"],
|
||||
"no-script-url": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
"no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-undef": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-use-before-define": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-var": "error",
|
||||
"no-void": "error",
|
||||
"no-with": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"radix": "error",
|
||||
"require-yield": "error",
|
||||
"strict": ["error", "global"],
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "error",
|
||||
"vars-on-top": "error",
|
||||
"yoda": ["error", "never"],
|
||||
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
// SonarJS is not yet compatible with ESLint 9. Commenting these out
|
||||
// until it is.
|
||||
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
||||
// "sonarjs/no-duplicate-string": "off",
|
||||
// "sonarjs/no-nested-template-literals": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default [
|
||||
// You would not believe how much this change has frustrated users: ["if an ignores key is used
|
||||
// without any other keys in the configuration object, then the patterns act as global
|
||||
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
|
||||
{
|
||||
ignores: [
|
||||
"dist/",
|
||||
".wireit/",
|
||||
"packages/",
|
||||
// don't ever lint node_modules
|
||||
"node_modules/",
|
||||
".storybook/*",
|
||||
// don't lint build output (make sure it's set to your correct build folder name)
|
||||
// don't lint nyc coverage output
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
"src/**/*.test.ts",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
...tseslint.configs.recommended,
|
||||
// sonar.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
files: ["src/**"],
|
||||
rules,
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
},
|
||||
},
|
||||
files: ["scripts/*.mjs", "*.ts", "*.mjs"],
|
||||
rules,
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
...globals.jest,
|
||||
},
|
||||
},
|
||||
files: ["src/**/*.test.ts"],
|
||||
rules,
|
||||
},
|
||||
];
|
||||
@ -1,87 +0,0 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
// You would not believe how much this change has frustrated users: ["if an ignores key is used
|
||||
// without any other keys in the configuration object, then the patterns act as global
|
||||
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
|
||||
{
|
||||
ignores: [
|
||||
"dist/",
|
||||
".wireit/",
|
||||
"packages/",
|
||||
// don't ever lint node_modules
|
||||
"node_modules/",
|
||||
".storybook/*",
|
||||
// don't lint build output (make sure it's set to your correct build folder name)
|
||||
// don't lint nyc coverage output
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
...tseslint.configs.recommended,
|
||||
// sonar.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
},
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
// SonarJS is not yet compatible with ESLint 9. Commenting these out
|
||||
// until it is.
|
||||
// "sonarjs/cognitive-complexity": ["off", 9],
|
||||
// "sonarjs/no-duplicate-string": "off",
|
||||
// "sonarjs/no-nested-template-literals": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
},
|
||||
},
|
||||
files: ["scripts/*.mjs", "*.ts", "*.mjs"],
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -1,22 +1,47 @@
|
||||
import { readFileSync } from "fs";
|
||||
import path from "path";
|
||||
/**
|
||||
* @import { Config } from "@lit/localize-tools/lib/types/config.js"
|
||||
* @import { TransformOutputConfig } from "@lit/localize-tools/lib/types/modes.js"
|
||||
* @import { Locale } from "@lit/localize-tools/lib/types/locale.js"
|
||||
* @import { ProgramMessage } from "@lit/localize-tools/lib/messages.js"
|
||||
* @import { Message } from "@lit/localize-tools/lib/messages.js"
|
||||
*
|
||||
* @typedef {Config & { output: TransformOutputConfig; }} TransformerConfig
|
||||
*/
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import pseudolocale from "pseudolocale";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
|
||||
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
|
||||
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
|
||||
|
||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||
const pseudoLocale = "pseudo-LOCALE";
|
||||
const pseudoLocale = /** @type {Locale} */ ("pseudo-LOCALE");
|
||||
|
||||
const targetLocales = [pseudoLocale];
|
||||
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
|
||||
|
||||
const baseConfig = await import("../lit-localize.json", {
|
||||
with: {
|
||||
type: "json",
|
||||
},
|
||||
})
|
||||
.then((module) => {
|
||||
return /** @type {Config} */ (module.default);
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
console.error("Failed to load lit-localize.json", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
|
||||
// which Localizer we use (transformer or runtime), because all of the functionality we care about
|
||||
// is in their common parent class, but I had to pick one. Everything else here is just pure
|
||||
// exploitation of the lit/localize-tools internals.
|
||||
|
||||
/**
|
||||
* @type {TransformerConfig}
|
||||
*/
|
||||
const config = {
|
||||
...baseConfig,
|
||||
baseDir: path.join(__dirname, ".."),
|
||||
@ -25,18 +50,26 @@ const config = {
|
||||
...baseConfig.output,
|
||||
mode: "transform",
|
||||
},
|
||||
resolve: (path) => path,
|
||||
resolve: (filePath) => filePath,
|
||||
};
|
||||
|
||||
const pseudoMessagify = (message) => ({
|
||||
name: message.name,
|
||||
contents: message.contents.map((content) =>
|
||||
typeof content === "string" ? pseudolocale(content, { prepend: "", append: "" }) : content,
|
||||
/**
|
||||
* @param {ProgramMessage} message
|
||||
* @returns {Message}
|
||||
*/
|
||||
function pseudoMessagify({ name, contents }) {
|
||||
return {
|
||||
name,
|
||||
contents: contents.map((content) =>
|
||||
typeof content === "string"
|
||||
? pseudolocale(content, { prepend: "", append: "" })
|
||||
: content,
|
||||
),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const localizer = new TransformLitLocalizer(config);
|
||||
const messages = localizer.extractSourceMessages().messages;
|
||||
const { messages } = localizer.extractSourceMessages();
|
||||
const translations = messages.map(pseudoMessagify);
|
||||
const sorted = sortProgramMessages([...messages]);
|
||||
const formatter = makeFormatter(config);
|
||||
|
||||
@ -19,7 +19,7 @@ import { AdminApi, CapabilitiesEnum, LicenseSummaryStatusEnum } from "@goauthent
|
||||
@customElement("ak-about-modal")
|
||||
export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton)) {
|
||||
static get styles() {
|
||||
return super.styles.concat(
|
||||
return ModalButton.styles.concat(
|
||||
PFAbout,
|
||||
css`
|
||||
.pf-c-about-modal-box__hero {
|
||||
@ -59,7 +59,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
||||
|
||||
renderModal() {
|
||||
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
|
||||
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
|
||||
if (this.licenseSummary.status !== LicenseSummaryStatusEnum.Unlicensed) {
|
||||
product += ` ${msg("Enterprise")}`;
|
||||
}
|
||||
return html`<div
|
||||
|
||||
@ -125,6 +125,7 @@ export class AdminInterface extends AuthenticatedInterface {
|
||||
if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
|
||||
const { ESBuildObserver } = await import("@goauthentik/common/client");
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new ESBuildObserver(process.env.WATCHER_URL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
return;
|
||||
}
|
||||
const outpost = outposts.results[0];
|
||||
outpost.config["authentik_host"] = window.location.origin;
|
||||
outpost.config.authentik_host = window.location.origin;
|
||||
await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
|
||||
uuid: outpost.pk,
|
||||
outpostRequest: outpost,
|
||||
|
||||
@ -28,17 +28,19 @@ export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
|
||||
icon: "fa fa-times-circle pf-m-danger",
|
||||
message: html`${msg("No workers connected. Background tasks will not run.")}`,
|
||||
});
|
||||
} else if (value.filter((w) => !w.versionMatching).length > 0) {
|
||||
}
|
||||
|
||||
if (value.filter((w) => !w.versionMatching).length > 0) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-times-circle pf-m-danger",
|
||||
message: html`${msg("Worker with incorrect version connected.")}`,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderValue() {
|
||||
return html`${this.value?.length}`;
|
||||
|
||||
@ -127,7 +127,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
msg("LDAP Source"),
|
||||
),
|
||||
];
|
||||
this.centerText = statuses.reduce((total, el) => (total += el.total), 0).toString();
|
||||
this.centerText = statuses.reduce((total, el) => total + el.total, 0).toString();
|
||||
return statuses;
|
||||
}
|
||||
|
||||
|
||||
@ -6,26 +6,26 @@ import { html } from "lit";
|
||||
import "../AdminSettingsFooterLinks.js";
|
||||
|
||||
describe("ak-admin-settings-footer-link", () => {
|
||||
afterEach(async () => {
|
||||
await browser.execute(async () => {
|
||||
await document.body.querySelector("ak-admin-settings-footer-link")?.remove();
|
||||
if (document.body["_$litPart$"]) {
|
||||
// @ts-expect-error expression of type '"_$litPart$"' is added by Lit
|
||||
await delete document.body["_$litPart$"];
|
||||
afterEach(() => {
|
||||
return browser.execute(() => {
|
||||
document.body.querySelector("ak-admin-settings-footer-link")?.remove();
|
||||
|
||||
if ("_$litPart$" in document.body) {
|
||||
delete document.body._$litPart$;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should render an empty control", async () => {
|
||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||
const link = await $("ak-admin-settings-footer-link");
|
||||
const link = $("ak-admin-settings-footer-link");
|
||||
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
||||
await expect(await link.getProperty("toJson")).toEqual({ name: "", href: "" });
|
||||
});
|
||||
|
||||
it("should not be valid if just a name is filled in", async () => {
|
||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||
const link = await $("ak-admin-settings-footer-link");
|
||||
const link = $("ak-admin-settings-footer-link");
|
||||
await link.$('input[name="name"]').setValue("foo");
|
||||
await expect(await link.getProperty("isValid")).toStrictEqual(false);
|
||||
await expect(await link.getProperty("toJson")).toEqual({ name: "foo", href: "" });
|
||||
@ -33,7 +33,7 @@ describe("ak-admin-settings-footer-link", () => {
|
||||
|
||||
it("should be valid if just a URL is filled in", async () => {
|
||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||
const link = await $("ak-admin-settings-footer-link");
|
||||
const link = $("ak-admin-settings-footer-link");
|
||||
await link.$('input[name="href"]').setValue("https://foo.com");
|
||||
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
||||
await expect(await link.getProperty("toJson")).toEqual({
|
||||
@ -44,7 +44,7 @@ describe("ak-admin-settings-footer-link", () => {
|
||||
|
||||
it("should be valid if both are filled in", async () => {
|
||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||
const link = await $("ak-admin-settings-footer-link");
|
||||
const link = $("ak-admin-settings-footer-link");
|
||||
await link.$('input[name="name"]').setValue("foo");
|
||||
await link.$('input[name="href"]').setValue("https://foo.com");
|
||||
await expect(await link.getProperty("isValid")).toStrictEqual(true);
|
||||
@ -56,7 +56,7 @@ describe("ak-admin-settings-footer-link", () => {
|
||||
|
||||
it("should not be valid if the URL is not valid", async () => {
|
||||
render(html`<ak-admin-settings-footer-link name="link"></ak-admin-settings-footer-link>`);
|
||||
const link = await $("ak-admin-settings-footer-link");
|
||||
const link = $("ak-admin-settings-footer-link");
|
||||
await link.$('input[name="name"]').setValue("foo");
|
||||
await link.$('input[name="href"]').setValue("never://foo.com");
|
||||
await expect(await link.getProperty("toJson")).toEqual({
|
||||
|
||||
@ -79,7 +79,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
});
|
||||
}
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["metaIcon"];
|
||||
const icon = this.getFormFiles().metaIcon;
|
||||
if (icon || this.clearIcon) {
|
||||
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
|
||||
slug: app.slug,
|
||||
@ -117,7 +117,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
if (!(ev instanceof InputEvent) || !ev.target) {
|
||||
return;
|
||||
}
|
||||
this.clearIcon = !!(ev.target as HTMLInputElement).checked;
|
||||
this.clearIcon = Boolean((ev.target as HTMLInputElement).checked);
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
|
||||
@ -71,7 +71,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFCard, applicationListStyle);
|
||||
return TablePage.styles.concat(PFCard, applicationListStyle);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
|
||||
@ -6,7 +6,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult } from "lit";
|
||||
@ -31,9 +31,8 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance?.pbmUuid) {
|
||||
return msg("Successfully updated entitlement.");
|
||||
} else {
|
||||
return msg("Successfully created entitlement.");
|
||||
}
|
||||
return msg("Successfully created entitlement.");
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -49,12 +48,11 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
pbmUuid: this.instance.pbmUuid || "",
|
||||
applicationEntitlementRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsCreate({
|
||||
applicationEntitlementRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -52,7 +52,6 @@ function renderRadiusOverview(rawProvider: OneOfProvider) {
|
||||
}
|
||||
|
||||
function renderRACOverview(rawProvider: OneOfProvider) {
|
||||
// @ts-expect-error TS6133
|
||||
const _provider = rawProvider as RACProvider;
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
errors = new Map<string, string>();
|
||||
|
||||
@query("form#applicationform")
|
||||
form!: HTMLFormElement;
|
||||
declare form: HTMLFormElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -61,18 +61,18 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
this.errors = new Map();
|
||||
const values = trimMany(this.formValues ?? {}, ["metaLaunchUrl", "name", "slug"]);
|
||||
|
||||
if (values["name"] === "") {
|
||||
if (values.name === "") {
|
||||
this.errors.set("name", msg("An application name is required"));
|
||||
}
|
||||
if (
|
||||
!(
|
||||
isStr(values["metaLaunchUrl"]) &&
|
||||
(values["metaLaunchUrl"] === "" || URL.canParse(values["metaLaunchUrl"]))
|
||||
isStr(values.metaLaunchUrl) &&
|
||||
(values.metaLaunchUrl === "" || URL.canParse(values.metaLaunchUrl))
|
||||
)
|
||||
) {
|
||||
this.errors.set("metaLaunchUrl", msg("Not a valid URL"));
|
||||
}
|
||||
if (!(isStr(values["slug"]) && values["slug"] !== "" && isSlug(values["slug"]))) {
|
||||
if (!(isStr(values.slug) && values.slug !== "" && isSlug(values.slug))) {
|
||||
this.errors.set("slug", msg("Not a valid slug"));
|
||||
}
|
||||
return this.errors.size === 0;
|
||||
|
||||
@ -45,7 +45,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
hide = true;
|
||||
|
||||
@query("form#bindingform")
|
||||
form!: HTMLFormElement;
|
||||
declare form: HTMLFormElement;
|
||||
|
||||
@query(".policy-search-select")
|
||||
searchSelect!: SearchSelectBase<Policy> | SearchSelectBase<Group> | SearchSelectBase<User>;
|
||||
|
||||
@ -22,7 +22,7 @@ export class ApplicationWizardProviderSamlForm extends ApplicationWizardProvider
|
||||
const setHasSigningKp = (ev: InputEvent) => {
|
||||
const target = ev.target as AkCryptoCertificateSearch;
|
||||
if (!target) return;
|
||||
this.hasSigningKp = !!target.selectedKeypair;
|
||||
this.hasSigningKp = Boolean(target.selectedKeypair);
|
||||
};
|
||||
|
||||
return html` <ak-wizard-title>${this.label}</ak-wizard-title>
|
||||
|
||||
@ -8,7 +8,7 @@ import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
@ -59,12 +59,11 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
instanceUuid: this.instance.pk,
|
||||
blueprintInstanceRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsCreate({
|
||||
blueprintInstanceRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -9,7 +9,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
@ -43,12 +43,11 @@ export class BrandForm extends ModelForm<Brand, string> {
|
||||
brandUuid: this.instance.brandUuid,
|
||||
brandRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new CoreApi(DEFAULT_CONFIG).coreBrandsCreate({
|
||||
brandRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Domain")} required name="domain">
|
||||
|
||||
@ -107,7 +107,7 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
selected(item: CertificateKeyPair, items: CertificateKeyPair[]) {
|
||||
return (
|
||||
(this.singleton && !this.certificate && items.length === 1) ||
|
||||
(!!this.certificate && this.certificate === item.pk)
|
||||
(Boolean(this.certificate) && this.certificate === item.pk)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ const metadata: Meta<AkCryptoCertificateSearch> = {
|
||||
argTypes: {
|
||||
// Typescript is unaware that arguments for components are treated as properties, and
|
||||
// properties are typically renamed to lower case, even if the variable is not.
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error TODO: Explain.
|
||||
nokey: {
|
||||
control: "boolean",
|
||||
description:
|
||||
@ -75,7 +75,7 @@ export const CryptoCertificateSearch = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const showMessage = (ev: CustomEvent<any>) => {
|
||||
const detail = ev.detail;
|
||||
delete detail["target"];
|
||||
delete detail.target;
|
||||
document.getElementById("message-pad")!.innerText = `Event: ${JSON.stringify(
|
||||
detail,
|
||||
null,
|
||||
|
||||
@ -30,12 +30,11 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
|
||||
kpUuid: this.instance.pk || "",
|
||||
patchedCertificateKeyPairRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsCreate({
|
||||
certificateKeyPairRequest: data as unknown as CertificateKeyPairRequest,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
|
||||
|
||||
@ -51,12 +51,11 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
pbmUuid: this.instance.pk || "",
|
||||
notificationRuleRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesCreate({
|
||||
notificationRuleRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -47,12 +47,11 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
uuid: this.instance.pk || "",
|
||||
notificationTransportRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsCreate({
|
||||
notificationTransportRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onModeChange(mode: string | undefined): void {
|
||||
if (
|
||||
|
||||
@ -58,7 +58,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
}
|
||||
|
||||
if (this.can(CapabilitiesEnum.CanSaveMedia)) {
|
||||
const icon = this.getFormFiles()["background"];
|
||||
const icon = this.getFormFiles().background;
|
||||
if (icon || this.clearBackground) {
|
||||
await new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({
|
||||
slug: flow.slug,
|
||||
|
||||
@ -27,7 +27,7 @@ export class FlowImportForm extends Form<Flow> {
|
||||
}
|
||||
|
||||
async send(): Promise<FlowImportResult> {
|
||||
const file = this.getFormFiles()["flow"];
|
||||
const file = this.getFormFiles().flow;
|
||||
if (!file) {
|
||||
throw new SentryIgnoredError("No form data");
|
||||
}
|
||||
|
||||
@ -39,9 +39,8 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance?.pk) {
|
||||
return msg("Successfully updated binding.");
|
||||
} else {
|
||||
return msg("Successfully created binding.");
|
||||
}
|
||||
return msg("Successfully created binding.");
|
||||
}
|
||||
|
||||
send(data: FlowStageBinding): Promise<unknown> {
|
||||
@ -50,7 +49,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
fsbUuid: this.instance.pk,
|
||||
patchedFlowStageBindingRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
if (this.targetPk) {
|
||||
data.target = this.targetPk;
|
||||
}
|
||||
@ -58,7 +57,6 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
flowStageBindingRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getOrder(): Promise<number> {
|
||||
if (this.instance?.pk) {
|
||||
|
||||
@ -10,7 +10,7 @@ import "@goauthentik/elements/chips/ChipGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -55,13 +55,12 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
groupUuid: this.instance.pk,
|
||||
patchedGroupRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
data.users = [];
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
|
||||
groupRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -125,7 +125,8 @@ export class RelatedGroupList extends Table<Group> {
|
||||
buttonLabel=${msg("Remove")}
|
||||
.objects=${this.selectedElements}
|
||||
.delete=${(item: Group) => {
|
||||
if (!this.targetUser) return;
|
||||
if (!this.targetUser) return null;
|
||||
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsRemoveUserCreate({
|
||||
groupUuid: item.pk,
|
||||
userAccountRequest: {
|
||||
|
||||
@ -46,8 +46,12 @@ import {
|
||||
User,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
interface AddUsersToGroupFormData {
|
||||
users: number[];
|
||||
}
|
||||
|
||||
@customElement("ak-user-related-add")
|
||||
export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
export class RelatedUserAdd extends Form<AddUsersToGroupFormData> {
|
||||
@property({ attribute: false })
|
||||
group?: Group;
|
||||
|
||||
@ -58,7 +62,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
return msg("Successfully added user(s).");
|
||||
}
|
||||
|
||||
async send(data: { users: number[] }): Promise<{ users: number[] }> {
|
||||
async send(data: AddUsersToGroupFormData): Promise<AddUsersToGroupFormData> {
|
||||
await Promise.all(
|
||||
data.users.map((user) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsAddUserCreate({
|
||||
@ -69,6 +73,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -133,7 +138,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
me?: SessionUser;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFDescriptionList, PFAlert, PFBanner);
|
||||
return Table.styles.concat(PFDescriptionList, PFAlert, PFBanner);
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
|
||||
@ -65,7 +65,7 @@ export class OutpostDeploymentModal extends ModalButton {
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="true" />
|
||||
</div>
|
||||
${this.outpost?.type == OutpostTypeEnum.Proxy
|
||||
${this.outpost?.type === OutpostTypeEnum.Proxy
|
||||
? html`
|
||||
<h3>
|
||||
${msg(
|
||||
|
||||
@ -10,7 +10,7 @@ import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
@ -129,12 +129,11 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
uuid: this.instance.pk || "",
|
||||
outpostRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesCreate({
|
||||
outpostRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
const typeOptions = [
|
||||
|
||||
@ -32,12 +32,11 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
|
||||
uuid: this.instance.pk || "",
|
||||
dockerServiceConnectionRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerCreate({
|
||||
dockerServiceConnectionRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -4,7 +4,7 @@ import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
@ -36,12 +36,11 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
|
||||
uuid: this.instance.pk || "",
|
||||
kubernetesServiceConnectionRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesCreate({
|
||||
kubernetesServiceConnectionRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
|
||||
@ -72,9 +72,8 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
return msg(str`Group ${item.groupObj?.name}`);
|
||||
} else if (item.user) {
|
||||
return msg(str`User ${item.userObj?.name}`);
|
||||
} else {
|
||||
return msg("-");
|
||||
}
|
||||
return msg("-");
|
||||
}
|
||||
|
||||
getPolicyUserGroupRow(item: PolicyBinding): TemplateResult {
|
||||
@ -123,9 +122,8 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
${msg("Edit User")}
|
||||
</button>
|
||||
</ak-forms-modal>`;
|
||||
} else {
|
||||
return html``;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
|
||||
@ -72,9 +72,8 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance?.pk) {
|
||||
return msg("Successfully updated binding.");
|
||||
} else {
|
||||
return msg("Successfully created binding.");
|
||||
}
|
||||
return msg("Successfully created binding.");
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -111,12 +110,11 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
policyBindingUuid: this.instance.pk,
|
||||
policyBindingRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesBindingsCreate({
|
||||
policyBindingRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getOrder(): Promise<number> {
|
||||
if (this.instance?.pk) {
|
||||
|
||||
@ -7,7 +7,7 @@ import "@goauthentik/elements/events/LogViewer";
|
||||
import { Form } from "@goauthentik/elements/forms/Form";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import YAML from "yaml";
|
||||
import * as YAML from "yaml";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
|
||||
@ -25,12 +25,12 @@ export class DummyPolicyForm extends BasePolicyForm<DummyPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
dummyPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesDummyCreate({
|
||||
dummyPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
|
||||
@ -37,12 +37,11 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
eventMatcherPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesEventMatcherCreate({
|
||||
eventMatcherPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
|
||||
@ -25,12 +25,11 @@ export class PasswordExpiryPolicyForm extends BasePolicyForm<PasswordExpiryPolic
|
||||
policyUuid: this.instance.pk || "",
|
||||
passwordExpiryPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordExpiryCreate({
|
||||
passwordExpiryPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
|
||||
@ -28,12 +28,11 @@ export class ExpressionPolicyForm extends BasePolicyForm<ExpressionPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
expressionPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionCreate({
|
||||
expressionPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
|
||||
@ -39,12 +39,11 @@ export class GeoIPPolicyForm extends BasePolicyForm<GeoIPPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
geoIPPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesGeoipCreate({
|
||||
geoIPPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<span>
|
||||
|
||||
@ -38,12 +38,11 @@ export class PasswordPolicyForm extends BasePolicyForm<PasswordPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
passwordPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesPasswordCreate({
|
||||
passwordPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderStaticRules(): TemplateResult {
|
||||
return html` <ak-form-group>
|
||||
|
||||
@ -25,12 +25,11 @@ export class ReputationPolicyForm extends BasePolicyForm<ReputationPolicy> {
|
||||
policyUuid: this.instance.pk || "",
|
||||
reputationPolicyRequest: data,
|
||||
});
|
||||
} else {
|
||||
}
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesReputationCreate({
|
||||
reputationPolicyRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user