fix: load staticwebapp.config.json schema locally when timeout (#808)
This commit is contained in:
Родитель
f9f9b7e92f
Коммит
34864d0a78
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче