diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml deleted file mode 100644 index 4ea72a911..000000000 --- a/.idea/copilot.data.migration.agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/copilot.data.migration.edit.xml b/.idea/copilot.data.migration.edit.xml deleted file mode 100644 index 8648f9401..000000000 --- a/.idea/copilot.data.migration.edit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml deleted file mode 100644 index a37f6ff4a..000000000 --- a/.idea/dictionaries/project.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - backchannel - - - \ No newline at end of file diff --git a/.idea/docs.duendesoftware.com.iml b/.idea/docs.duendesoftware.com.iml deleted file mode 100644 index b16af2e18..000000000 --- a/.idea/docs.duendesoftware.com.iml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Dockerfile.xml b/.idea/runConfigurations/Dockerfile.xml deleted file mode 100644 index eb746248d..000000000 --- a/.idea/runConfigurations/Dockerfile.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.opencode/agent/docs-writer.md b/.opencode/agent/docs-writer.md new file mode 100644 index 000000000..438f936fd --- /dev/null +++ b/.opencode/agent/docs-writer.md @@ -0,0 +1,21 @@ +--- +description: Writes and maintains documentation for Duende Software products, including IdentityServer and Backend for Frontend. +mode: all +tools: + write: true + edit: true + bash: true + webfetch: true + question: true + lsp: true +--- + +You are a technical documentation writer, with expert-level background in ASP.NET Core, OpenID Connect, OAuth, and general .NET development. +Create clear, comprehensive documentation. Ask clarifying questions if there is ambiguity or something isn't clear. + +Focus on: + +* Clear explanations +* Proper structure +* Code examples +* User-friendly language diff --git a/.opencode/rules/writing-guidelines.md b/.opencode/rules/writing-guidelines.md new file mode 100644 index 000000000..a0244e0ca --- /dev/null +++ b/.opencode/rules/writing-guidelines.md @@ -0,0 +1,11 @@ +* Strictly follow the authoring and style guides from `README.md` +* Do not use conversational filler or "fluff" in the generated documentation. Get straight to the point. +* Always check the file structure and existing content to ensure consistent links and hierarchy. +* Never invent features or configuration options. If you are unsure, ask the user. +* Code generation - Prioritize complete, runnable examples over snippets. Ensure standard formatting. + +When writing Markdown content Markdown: +* Use `*` for lists. Do not use `-`. +* Use `[link title](https://example.com)` for links, avoid reference-style links unless there are multiple occurences of the same link in one document. +* For internal links, always include the extension (e.g. `.md` or `.mdx`) +* Prefer `csharp` over `cs` to set the language for C# code blocks. \ No newline at end of file diff --git a/README.md b/README.md index cc13ccb59..5a641db2b 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ Static assets, like favicons, can be placed in the `astro/public/` directory. ## ✍️ Authoring +The `astro/` folder has been configured as a VS Code and WebStorm project, which you can open from that location to work on content. + Content can be authored in Markdown, in a `.md` or `.mdx` file. The Starlight documentation has some guidance on Markdown syntax, components, and more: * [Authoring Content in Markdown](https://starlight.astro.build/guides/authoring-content/) diff --git a/.devcontainer.json b/astro/.devcontainer.json similarity index 100% rename from .devcontainer.json rename to astro/.devcontainer.json diff --git a/astro/.gitignore b/astro/.gitignore new file mode 100644 index 000000000..2a1ea9920 --- /dev/null +++ b/astro/.gitignore @@ -0,0 +1,70 @@ +# Astro build output +astro/dist/ +astro/root/ +server/src/Docs.Web/wwwroot/ + +# generated types +astro/.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store +.vscode/settings.json +*.iml + +# Agents and assistants +.opencode/ +opencode.json +.idea/**/copilot.data.migration.*.xml + +# .NET +*.user +*.suo +*.userosscache +*.sln.docstates +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ +.vs/ +*.nupkg +*.snupkg +project.lock.json +project.fragment.lock.json +artifacts/ + +# Rider +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# User-specific files +*.rsuser + +# JetBrains Rider +*.sln.iml + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# Windows +Thumbs.db +ehthumbs.db + +# Azure Functions +local.settings.json diff --git a/.idea/.gitignore b/astro/.idea/.gitignore similarity index 100% rename from .idea/.gitignore rename to astro/.idea/.gitignore diff --git a/.idea/codeStyles/Project.xml b/astro/.idea/codeStyles/Project.xml similarity index 100% rename from .idea/codeStyles/Project.xml rename to astro/.idea/codeStyles/Project.xml diff --git a/.idea/compiler.xml b/astro/.idea/compiler.xml similarity index 100% rename from .idea/compiler.xml rename to astro/.idea/compiler.xml diff --git a/.idea/externalDependencies.xml b/astro/.idea/externalDependencies.xml similarity index 100% rename from .idea/externalDependencies.xml rename to astro/.idea/externalDependencies.xml diff --git a/.idea/modules.xml b/astro/.idea/modules.xml similarity index 100% rename from .idea/modules.xml rename to astro/.idea/modules.xml diff --git a/.idea/prettier.xml b/astro/.idea/prettier.xml similarity index 100% rename from .idea/prettier.xml rename to astro/.idea/prettier.xml diff --git a/.idea/scopes/Content.xml b/astro/.idea/scopes/Content.xml similarity index 100% rename from .idea/scopes/Content.xml rename to astro/.idea/scopes/Content.xml diff --git a/.idea/vcs.xml b/astro/.idea/vcs.xml similarity index 75% rename from .idea/vcs.xml rename to astro/.idea/vcs.xml index 35eb1ddfb..62bd7a01e 100644 --- a/.idea/vcs.xml +++ b/astro/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/.idea/webResources.xml b/astro/.idea/webResources.xml similarity index 100% rename from .idea/webResources.xml rename to astro/.idea/webResources.xml diff --git a/.vscode/extensions.json b/astro/.vscode/extensions.json similarity index 100% rename from .vscode/extensions.json rename to astro/.vscode/extensions.json diff --git a/.vscode/launch.json b/astro/.vscode/launch.json similarity index 100% rename from .vscode/launch.json rename to astro/.vscode/launch.json diff --git a/opencode.json b/opencode.json new file mode 100644 index 000000000..52fc3b8be --- /dev/null +++ b/opencode.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://opencode.ai/config.json", + "instructions": ["README.md", ".opencode/rules/writing-guidelines.md"], + "plugin": ["@opencode_weave/weave"], + "watcher": { + "ignore": ["node_modules/**", "dist/**", ".git/**"] + } +} diff --git a/server/.idea/.idea.Docs/.idea/.gitignore b/server/.idea/.idea.Docs/.idea/.gitignore new file mode 100644 index 000000000..a5361d753 --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +/.idea.Docs.iml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/server/.idea/.idea.Docs/.idea/.name b/server/.idea/.idea.Docs/.idea/.name new file mode 100644 index 000000000..1157f6756 --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/.name @@ -0,0 +1 @@ +Docs \ No newline at end of file diff --git a/server/.idea/.idea.Docs/.idea/encodings.xml b/server/.idea/.idea.Docs/.idea/encodings.xml new file mode 100644 index 000000000..df87cf951 --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/server/.idea/.idea.Docs/.idea/externalDependencies.xml b/server/.idea/.idea.Docs/.idea/externalDependencies.xml new file mode 100644 index 000000000..be4a9b9e6 --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/externalDependencies.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/server/.idea/.idea.Docs/.idea/indexLayout.xml b/server/.idea/.idea.Docs/.idea/indexLayout.xml new file mode 100644 index 000000000..7b08163ce --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/server/.idea/.idea.Docs/.idea/misc.xml b/server/.idea/.idea.Docs/.idea/misc.xml new file mode 100644 index 000000000..7fcdf3bad --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/misc.xml @@ -0,0 +1,4 @@ + + + {} + \ No newline at end of file diff --git a/server/.idea/.idea.Docs/.idea/vcs.xml b/server/.idea/.idea.Docs/.idea/vcs.xml new file mode 100644 index 000000000..6c0b86358 --- /dev/null +++ b/server/.idea/.idea.Docs/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/server/src/Docs.AppHost/Program.cs b/server/src/Docs.AppHost/Program.cs index cf71e5b3f..884ad2c96 100644 --- a/server/src/Docs.AppHost/Program.cs +++ b/server/src/Docs.AppHost/Program.cs @@ -1,7 +1,7 @@ var builder = DistributedApplication.CreateBuilder(args); // Astro dev server (for local development only) -_ = builder.AddJavaScriptApp("astro", "../../astro") +_ = builder.AddJavaScriptApp("astro", "../../../astro") .WithHttpEndpoint(port: 4321, env: "PORT") .WithExternalHttpEndpoints(); diff --git a/server/src/Docs.AppHost/Properties/launchSettings.json b/server/src/Docs.AppHost/Properties/launchSettings.json index d685085b2..34a4475f8 100644 --- a/server/src/Docs.AppHost/Properties/launchSettings.json +++ b/server/src/Docs.AppHost/Properties/launchSettings.json @@ -5,26 +5,13 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:17001;http://localhost:17000", + "applicationUrl": "https://localhost:17001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21001", "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22001" } - }, - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "http://localhost:17000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19001", - "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20001", - "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true" - } } } } diff --git a/server/src/Docs.Web/Program.cs b/server/src/Docs.Web/Program.cs index 0500bb610..4a6ab61be 100644 --- a/server/src/Docs.Web/Program.cs +++ b/server/src/Docs.Web/Program.cs @@ -54,11 +54,11 @@ // Add trailing slash redirect middleware (replicate nginx behavior) app.Use(async (context, next) => { - var path = context.Request.Path.Value; - - // If path doesn't end with slash and doesn't have a file extension, redirect with trailing slash - if (!string.IsNullOrEmpty(path) && - !path.EndsWith("/") && + var path = context.Request.Path.Value; + + // If path doesn't end with slash and doesn't have a file extension, redirect with trailing slash + if (!string.IsNullOrEmpty(path) && + !path.EndsWith("/") && !Path.HasExtension(path) && !path.StartsWith("/health") && !path.StartsWith("/alive")) @@ -67,8 +67,8 @@ context.Response.StatusCode = 301; context.Response.Headers.Location = $"{path}/{queryString}"; return; - } - + } + await next(); }); @@ -90,59 +90,59 @@ OnPrepareResponse = ctx => { var path = ctx.File.Name; - var requestPath = ctx.Context.Request.Path.Value ?? ""; - - // Astro assets (_astro folder) - cache for 1 year + var requestPath = ctx.Context.Request.Path.Value ?? ""; + + // Astro assets (_astro folder) - cache for 1 year if (requestPath.Contains("/_astro/")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=31536000, immutable"; return; - } - - // JavaScript and CSS - cache for 1 year + } + + // JavaScript and CSS - cache for 1 year if (path.EndsWith(".js") || path.EndsWith(".css")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=31536000, immutable"; return; - } - - // Fonts - cache for 1 year + } + + // Fonts - cache for 1 year if (path.EndsWith(".woff") || path.EndsWith(".woff2")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=31536000, immutable"; return; - } - - // SVG - cache for 1 month + } + + // SVG - cache for 1 month if (path.EndsWith(".svg")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=2592000"; return; - } - - // Images - cache for 1 week - if (path.EndsWith(".png") || path.EndsWith(".jpg") || + } + + // Images - cache for 1 week + if (path.EndsWith(".png") || path.EndsWith(".jpg") || path.EndsWith(".jpeg") || path.EndsWith(".webp") || path.EndsWith(".ico")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=604800"; return; - } - - // HTML - cache for 1 minute (to allow quick updates) + } + + // HTML - cache for 1 minute (to allow quick updates) if (path.EndsWith(".html")) { ctx.Context.Response.Headers.CacheControl = "public, max-age=60"; return; - } - - // JSON - cache for 1 year (except redirects.json which is internal) + } + + // JSON - cache for 1 year (except redirects.json which is internal) if (path.EndsWith(".json") && path != "redirects.json") { ctx.Context.Response.Headers.CacheControl = "public, max-age=31536000"; return; - } - - // Default: no caching + } + + // Default: no caching ctx.Context.Response.Headers.CacheControl = "no-cache"; } }); @@ -150,13 +150,13 @@ // Handle 404 with custom page app.Use(async (context, next) => { - await next(); - + await next(); + if (context.Response.StatusCode == 404 && !context.Response.HasStarted) { var webHostEnvironment = context.RequestServices.GetRequiredService(); - var notFoundPath = Path.Combine(webHostEnvironment.WebRootPath, "404.html"); - + var notFoundPath = Path.Combine(webHostEnvironment.WebRootPath, "404.html"); + if (File.Exists(notFoundPath)) { context.Response.ContentType = "text/html"; @@ -169,9 +169,9 @@ app.MapFallback(async context => { var webHostEnvironment = context.RequestServices.GetRequiredService(); - var path = context.Request.Path.Value?.TrimEnd('/') ?? ""; - - // Try to find index.html in the requested directory + var path = context.Request.Path.Value?.TrimEnd('/') ?? ""; + + // Try to find index.html in the requested directory var indexPath = Path.Combine(webHostEnvironment.WebRootPath, path.TrimStart('/'), "index.html"); if (File.Exists(indexPath)) { @@ -179,9 +179,9 @@ context.Response.Headers.CacheControl = "public, max-age=60"; await context.Response.SendFileAsync(indexPath); return; - } - - // Return 404 + } + + // Return 404 context.Response.StatusCode = 404; var notFoundPath = Path.Combine(webHostEnvironment.WebRootPath, "404.html"); if (File.Exists(notFoundPath)) diff --git a/server/src/Docs.Web/Properties/launchSettings.json b/server/src/Docs.Web/Properties/launchSettings.json index 900ff92f9..78eb76a9e 100644 --- a/server/src/Docs.Web/Properties/launchSettings.json +++ b/server/src/Docs.Web/Properties/launchSettings.json @@ -1,15 +1,6 @@ { "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "https": { "commandName": "Project", "dotnetRunMessages": true,