Openstatus
www.openstatus.dev
1import { withSentryConfig } from "@sentry/nextjs";
2import type { NextConfig } from "next";
3
4// REMINDER: avoid Clickjacking attacks by setting the X-Frame-Options header
5const securityHeaders = [
6 {
7 key: "X-Frame-Options",
8 value: "SAMEORIGIN",
9 },
10];
11
12/** @type {import('next').NextConfig} */
13const nextConfig: NextConfig = {
14 reactStrictMode: true,
15 transpilePackages: ["@openstatus/ui", "@openstatus/api"],
16 outputFileTracingIncludes: {
17 "/": [
18 "./node_modules/.pnpm/@google-cloud/tasks/build/esm/src/**/*.json",
19 "./node_modules/@google-cloud/tasks/build/esm/src/**/*.js",
20 ],
21 },
22 experimental: {
23 turbopackScopeHoisting: false,
24 // serverMinification:false,
25 },
26 serverExternalPackages: ["@google-cloud/tasks"],
27 expireTime: 180, // 3 minutes
28 logging: {
29 fetches: {
30 fullUrl: true,
31 },
32 },
33 images: {
34 remotePatterns: [
35 {
36 protocol: "https",
37 hostname: "**.public.blob.vercel-storage.com",
38 },
39 {
40 protocol: "https",
41 hostname: "screenshot.openstat.us",
42 },
43 {
44 protocol: "https",
45 hostname: "www.openstatus.dev",
46 },
47 ],
48 },
49 async headers() {
50 return [{ source: "/(.*)", headers: securityHeaders }];
51 },
52 async redirects() {
53 return [
54 {
55 source: "/legal/terms",
56 destination: "/terms",
57 permanent: true,
58 },
59 {
60 source: "/legal/privacy",
61 destination: "/privacy",
62 permanent: true,
63 },
64 {
65 source: "/features/monitoring",
66 destination: "/uptime-monitoring",
67 permanent: true,
68 },
69 {
70 source: "/features/status-page",
71 destination: "/status-page",
72 permanent: true,
73 },
74 {
75 source: "/app/:path*",
76 destination: "https://app.openstatus.dev/",
77 permanent: true,
78 },
79 ];
80 },
81 async rewrites() {
82 return {
83 beforeFiles: [
84 {
85 source: "/status-page/themes/:path*",
86 destination: "https://www.stpg.dev/:path*",
87 },
88 {
89 source: "/:path*",
90 has: [
91 {
92 type: "host",
93 value: "themes.openstatus.dev",
94 },
95 ],
96 destination: "https://www.stpg.dev/:path*",
97 },
98 // New design: proxy app routes to external host with slug prefix
99 {
100 source: "/:path*",
101 has: [
102 { type: "cookie", key: "sp_mode", value: "new" },
103 {
104 type: "host",
105 value: "(?<slug>[^.]+)\\.(openstatus\\.dev|localhost)",
106 },
107 ],
108 destination: "https://:slug.stpg.dev/:path*",
109 },
110 // Handle custom domains (e.g., status.mxkaske.dev)
111 {
112 source:
113 "/:path((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
114 has: [
115 { type: "cookie", key: "sp_mode", value: "new" },
116 {
117 type: "host",
118 value: "^(?!.*\\.openstatus\\.dev$)(?!openstatus\\.dev$)$",
119 },
120 ],
121 destination: "https://www.stpg.dev/:path*",
122 },
123 // enfore routes to avoid infinite redirects - https://github.com/vercel/vercel/issues/6126#issuecomment-823523122
124 // testing with https://validator.w3.org/feed/check.cgi
125 {
126 source: "/feed/rss",
127 has: [
128 { type: "cookie", key: "sp_mode", value: "new" },
129 {
130 type: "host",
131 value: "^(?!.*\\.openstatus\\.dev$)(?!openstatus\\.dev$)$",
132 },
133 ],
134 destination: "https://www.stpg.dev/:domain/feed/rss",
135 },
136 {
137 source: "/feed/atom",
138 has: [
139 { type: "cookie", key: "sp_mode", value: "new" },
140 {
141 type: "host",
142 value: "^(?!.*\\.openstatus\\.dev$)(?!openstatus\\.dev$)$",
143 },
144 ],
145 destination: "https://www.stpg.dev/:domain/feed/atom",
146 },
147 {
148 source: "/feed/rss",
149 has: [
150 { type: "cookie", key: "sp_mode", value: "new" },
151 {
152 type: "host",
153 value: "^(?<domain>.+)$",
154 },
155 ],
156 destination: "https://www.stpg.dev/:domain/feed/rss",
157 },
158 {
159 source: "/feed/atom",
160 has: [
161 { type: "cookie", key: "sp_mode", value: "new" },
162 {
163 type: "host",
164 value: "^(?<domain>.+)$",
165 },
166 ],
167 destination: "https://www.stpg.dev/:domain/feed/atom",
168 },
169 {
170 source:
171 "/:path((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|badge|feed|events|monitors|protected|verify).*)",
172 has: [
173 { type: "cookie", key: "sp_mode", value: "new" },
174 {
175 type: "host",
176 value: "^(?<domain>.+)$",
177 },
178 ],
179 destination: "https://www.stpg.dev/:domain*",
180 },
181 {
182 source:
183 "/:path((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
184 has: [
185 { type: "cookie", key: "sp_mode", value: "new" },
186 {
187 type: "host",
188 value: "^(?<domain>.+)$",
189 },
190 ],
191 destination: "https://www.stpg.dev/:domain/:path*",
192 },
193 // Handle API routes for custom domains
194 {
195 source: "/api/:path*",
196 has: [
197 { type: "cookie", key: "sp_mode", value: "new" },
198 {
199 type: "host",
200 value:
201 "^(?!.*\\.openstatus\\.dev$)(?!openstatus\\.dev$)(?<domain>.+)$",
202 },
203 ],
204 destination: "https://www.stpg.dev/api/:path*",
205 },
206 // Handle static assets for custom domains
207 {
208 source: "/_next/:path*",
209 has: [
210 { type: "cookie", key: "sp_mode", value: "new" },
211 {
212 type: "host",
213 value:
214 "^(?!.*\\.openstatus\\.dev$)(?!openstatus\\.dev$)(?<domain>.+)$",
215 },
216 ],
217 destination: "https://www.stpg.dev/_next/:path*",
218 },
219 ],
220 };
221 },
222};
223
224// For detailed options, refer to the official documentation:
225// - Webpack plugin options: https://github.com/getsentry/sentry-webpack-plugin#options
226// - Next.js Sentry setup guide: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
227const sentryConfig = {
228 // Prevent log output unless running in a CI environment (helps reduce noise in logs)
229 silent: !process.env.CI,
230 org: "openstatus",
231 project: "openstatus",
232 authToken: process.env.SENTRY_AUTH_TOKEN,
233
234 // Upload a larger set of source maps for improved stack trace accuracy (increases build time)
235 widenClientFileUpload: true,
236
237 // If set to true, transpiles Sentry SDK to be compatible with IE11 (increases bundle size)
238 transpileClientSDK: false,
239
240 // Tree-shake Sentry logger statements to reduce bundle size
241 disableLogger: true,
242};
243
244export default withSentryConfig(nextConfig, sentryConfig);