зеркало из
1
0
Форкнуть 0

fix: load staticwebapp.config.json schema locally when timeout (#808)

This commit is contained in:
Jikun 2024-03-05 04:30:47 +08:00 коммит произвёл GitHub
Родитель f9f9b7e92f
Коммит 34864d0a78
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 636 добавлений и 5 удалений

Просмотреть файл

@ -0,0 +1,613 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"additionalProperties": false,
"default": {
"navigationFallback": {
"rewrite": "/index.html"
}
},
"definitions": {
"route": {
"type": "object",
"required": ["route"],
"properties": {
"route": {
"type": "string",
"description": "Request route pattern to match. May contain valid wildcards. See documentation: https://aka.ms/swa/config-schema"
},
"methods": {
"type": "array",
"description": "Request method(s) to match",
"items": {
"anyOf": [
{
"type": "string",
"enum": ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "CONNECT", "OPTIONS", "TRACE"]
}
]
}
},
"allowedRoles": {
"type": "array",
"description": "Roles that are allowed to access this route. If not empty, only role(s) listed are authorized to access the route. Roles are only used for authorization; they are not used to evaluate whether the route matches the request.",
"items": {
"anyOf": [
{
"type": "string",
"examples": ["anonymous", "authenticated"]
}
]
}
},
"headers": {
"type": "object",
"description": "Override any matching global headers",
"additionalProperties": true
},
"redirect": {
"type": "string",
"description": "Redirect to a relative or absolute path, or an external URI. Default status code is 302, override with 301."
},
"statusCode": {
"type": "integer",
"description": "Status code override"
},
"rewrite": {
"type": "string",
"description": "A path to rewrite the request route to"
}
},
"additionalProperties": false
},
"auth": {
"type": "object",
"required": ["identityProviders"],
"properties": {
"rolesSource": {
"type": "string",
"description": "Route to API function for assigning roles. For example, \"/api/GetRoles\". See https://aka.ms/swa-roles-function"
},
"identityProviders": {
"type": "object",
"properties": {
"azureActiveDirectory": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the azureActiveDirectory provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["openIdIssuer", "clientSecretSettingName"],
"properties": {
"openIdIssuer": {
"type": "string",
"description": "The endpoint for the OpenID configuration of the AAD tenant"
},
"clientIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the Application (client) ID for the Azure AD app registration"
},
"clientSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the client secret for the Azure AD app registration"
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"loginParameters": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"apple": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the apple provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["clientSecretSettingName"],
"properties": {
"clientIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client ID"
},
"clientSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client Secret"
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"facebook": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the facebook provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["appSecretSettingName"],
"properties": {
"appIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the App ID"
},
"appSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the App Secret"
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"github": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the gitHub provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["clientSecretSettingName"],
"properties": {
"clientIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client ID"
},
"clientSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client Secret"
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"google": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the google provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["clientSecretSettingName"],
"properties": {
"clientIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client ID"
},
"clientSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client Secret"
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"scopes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"twitter": {
"type": "object",
"required": ["registration"],
"properties": {
"enabled": {
"description": "<false> if the twitter provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["consumerSecretSettingName"],
"properties": {
"consumerKeySettingName": {
"type": "string",
"description": "The name of the application setting containing the Consumer Key"
},
"consumerSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the Consumer Secret"
}
},
"additionalProperties": false
},
"userDetailsClaim": {
"type": "string",
"description": "The name of the claim from which we should read user details"
}
},
"additionalProperties": false
},
"customOpenIdConnectProviders": {
"type": "object",
"patternProperties": {
".*": {
"required": ["registration", "login"],
"properties": {
"enabled": {
"description": "<false> if the custom OpenID Connect provider is not enabled, <true> otherwise",
"type": "boolean",
"default": true
},
"registration": {
"type": "object",
"required": ["clientCredential", "openIdConnectConfiguration"],
"properties": {
"clientIdSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client ID"
},
"clientCredential": {
"type": "object",
"required": ["clientSecretSettingName"],
"properties": {
"clientSecretSettingName": {
"type": "string",
"description": "The name of the application setting containing the Client Secret"
}
}
},
"openIdConnectConfiguration": {
"type": "object",
"properties": {
"authorizationEndpoint": {
"type": "string",
"description": "The path to the authorization endpoint"
},
"tokenEndpoint": {
"type": "string",
"description": "The path to the token endpoint"
},
"issuer": {
"type": "string",
"description": "The path to the issuer endpoint"
},
"certificationUri": {
"type": "string",
"description": "The path to the jwks uri"
},
"wellKnownOpenIdConfiguration": {
"type": "string",
"description": "The path to the well known configuration endpoint"
}
}
}
},
"additionalProperties": false
},
"login": {
"type": "object",
"description": "",
"properties": {
"nameClaimType": {
"type": "string"
},
"scopes": {
"type": "array",
"items": {
"type": "string"
}
},
"loginParameterNames": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"description": "Documentation: https://aka.ms/swa/config-schema",
"id": "https://json.schemastore.org/staticwebapp.config.json",
"properties": {
"$schema": {
"type": "string",
"default": "https://json.schemastore.org/staticwebapp.config.json",
"description": "JSON schema"
},
"routes": {
"type": "array",
"description": "Route definitions to modify routing behavior",
"default": [
{
"route": "/example",
"rewrite": "/example.html"
}
],
"items": {
"examples": [
{
"route": "/example",
"rewrite": "/example.html"
},
{
"route": "/login",
"redirect": "/.auth/login/github"
}
],
"anyOf": [
{
"allOf": [
{
"$ref": "#/definitions/route"
}
]
}
]
}
},
"navigationFallback": {
"type": "object",
"description": "A default file to return if the request does not match a resource",
"default": {
"rewrite": "/index.html"
},
"required": ["rewrite"],
"properties": {
"rewrite": {
"type": "string",
"description": "The default file to return if the request does not match a resource",
"default": "/index.html"
},
"exclude": {
"type": "array",
"description": "Paths to exclude from the fallback route. May use valid wildcards. https://aka.ms/swa/config-schema",
"examples": [["*.{jpg,gif,png}", "assets/*"]]
}
},
"additionalProperties": false
},
"responseOverrides": {
"type": "object",
"description": "Custom error pages or redirects",
"examples": [
{
"404": {
"rewrite": "/custom_404.html",
"statusCode": 200
}
}
],
"propertyNames": {
"pattern": "^\\d+$"
},
"patternProperties": {
".*": {
"oneOf": [
{
"type": "object",
"properties": {
"redirect": {
"type": "string",
"description": "Redirect to a relative or absolute path, or an external URI. Default status code is 302, override with 301."
},
"statusCode": {
"type": "integer",
"description": "Status code"
},
"rewrite": {
"type": "string",
"description": "A path to rewrite the request route to"
}
}
}
]
}
}
},
"mimeTypes": {
"type": "object",
"description": "Custom mime types configuration",
"default": {},
"examples": [
{
".config": "application/xml"
}
],
"patternProperties": {
"^\\..+$": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHeaders": {
"type": "object",
"description": "Default headers to set on all responses",
"additionalProperties": true
},
"auth": {
"$ref": "#/definitions/auth"
},
"networking": {
"type": "object",
"description": "Networking configuration",
"properties": {
"allowedIpRanges": {
"type": "array",
"description": "Restrict access to one or more IPv4 ranges. Supports CIDR notation (e.g., \"192.168.100.14/24\")",
"items": {
"type": "string"
},
"examples": [["10.0.0.0/24", "192.1.1.1/10"]]
}
},
"additionalProperties": false
},
"forwardingGateway": {
"type": "object",
"description": "Forwarding gateway configuration",
"properties": {
"allowedForwardedHosts": {
"type": "array",
"description": "The value of `X-Forwarded-Host` to allow to be used when generating redirect URLs",
"items": {
"type": "string"
},
"examples": [["example.org", "www.example.org", "staging.example.org"]]
},
"requiredHeaders": {
"type": "object",
"description": "HTTP header name/value pairs that are required for access",
"examples": [
{
"X-Azure-FDID": "10dd26ef"
}
],
"additionalProperties": true
}
},
"additionalProperties": false
},
"platform": {
"type": "object",
"description": "Platform configuration",
"properties": {
"apiRuntime": {
"type": "string",
"enum": [
"dotnet:3.1",
"dotnet:6.0",
"dotnet-isolated:6.0",
"dotnet-isolated:7.0",
"node:12",
"node:14",
"node:16",
"node:18",
"python:3.8",
"python:3.9",
"python:3.10"
],
"description": "Language runtime for the managed functions API"
}
},
"additionalProperties": false
},
"trailingSlash": {
"type": "string",
"enum": ["always", "never", "auto"],
"description": "Trailing slash configuration"
}
},
"title": "Azure Static Web Apps configuration file",
"type": "object"
}

Просмотреть файл

@ -226,6 +226,8 @@ export const SWA_CONFIG_FILENAME = "staticwebapp.config.json";
export const SWA_CONFIG_FILENAME_LEGACY = "routes.json";
export const CUSTOM_URL_SCHEME = "swa://";
export const OVERRIDABLE_ERROR_CODES = [400, 401, 403, 404];
export const SWA_CONFIG_SCHEME_URL = "https://json.schemastore.org/staticwebapp.config.json";
export const SWA_CONFIG_SCHEME_FALLBACK_PATH = path.join(__dirname, "..", "..", "schema", SWA_CONFIG_FILENAME);
// Constants related to Api runtime
export const DEFAULT_VERSION = {

Просмотреть файл

@ -5,7 +5,13 @@ import type http from "http";
import jsonMap from "json-source-map";
import fetch, { RequestInit } from "node-fetch";
import path from "path";
import { SWA_CONFIG_FILENAME, SWA_CONFIG_FILENAME_LEGACY, SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB } from "../constants";
import {
SWA_CONFIG_FILENAME,
SWA_CONFIG_FILENAME_LEGACY,
SWA_RUNTIME_CONFIG_MAX_SIZE_IN_KB,
SWA_CONFIG_SCHEME_URL,
SWA_CONFIG_SCHEME_FALLBACK_PATH,
} from "../constants";
import { logger } from "./logger";
import { isHttpUrl } from "./net";
const { readdir, readFile, stat } = fs.promises;
@ -165,11 +171,10 @@ function findLineAndColumnByPosition(content: string, position: number | undefin
}
async function loadSWAConfigSchema(): Promise<JSONSchemaType<SWACLIConfigFile> | null> {
const schemaUrl = "https://json.schemastore.org/staticwebapp.config.json";
try {
const res = await fetch(schemaUrl, { timeout: 10 * 1000 } as RequestInit);
const res = await fetch(SWA_CONFIG_SCHEME_URL, { timeout: 10 * 1000 } as RequestInit);
if (res.status === 200) {
logger.silly(`Schema loaded successfully from ${schemaUrl}`);
logger.silly(`Schema loaded successfully from ${SWA_CONFIG_SCHEME_URL}`);
return (await res.json()) as JSONSchemaType<SWACLIConfigFile>;
}
logger.silly(`Status: ${res.status} ${res.statusText}.`);
@ -177,7 +182,18 @@ async function loadSWAConfigSchema(): Promise<JSONSchemaType<SWACLIConfigFile> |
logger.warn((err as any).message);
}
logger.silly(`Failed to load schema from ${schemaUrl}`);
logger.warn(`Warning: Failed to load schema from ${SWA_CONFIG_SCHEME_URL}. Try to load fallback schema locally.`);
try {
const data = fs.readFileSync(SWA_CONFIG_SCHEME_FALLBACK_PATH, "utf8");
const config = JSON.parse(data);
logger.silly(`Schema loaded successfully from ${SWA_CONFIG_SCHEME_FALLBACK_PATH}`);
return config;
} catch (err) {
logger.warn((err as any).message);
logger.warn(`Warning: Failed to load staticwebapp.config.json schema from ${SWA_CONFIG_SCHEME_FALLBACK_PATH}.`);
}
return null;
}