Skip to content

Commit ef9f87a

Browse files
Merge pull request #3313 from SwiftPackageIndex/style-maintenance-page
Adds styling of the maintenance page
2 parents a68efab + 48a4b2e commit ef9f87a

File tree

13 files changed

+250
-30
lines changed

13 files changed

+250
-30
lines changed

FrontEnd/main.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ $mobile-breakpoint: 740px;
3030
@import 'styles/keywords';
3131
@import 'styles/layout';
3232
@import 'styles/maintainer_info';
33+
@import 'styles/maintenance';
3334
@import 'styles/markdown';
3435
@import 'styles/matrix';
3536
@import 'styles/modal_panel';

FrontEnd/styles/maintenance.scss

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// -------------------------------------------------------------------------
16+
// Site maintenance page which replaces the home page when enabled.
17+
// -------------------------------------------------------------------------
18+
19+
body.maintenance {
20+
main {
21+
display: flex;
22+
align-items: center;
23+
min-height: 500px;
24+
text-align: center;
25+
26+
h1 {
27+
padding-top: 50px;
28+
font-size: 30px;
29+
background-position: top center;
30+
background-repeat: no-repeat;
31+
background-size: 40px;
32+
background-image: var(--image-info);
33+
}
34+
}
35+
}

Sources/App/Core/AppEnvironment.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct AppEnvironment: Sendable {
5959
var gitlabPipelineToken: @Sendable () -> String?
6060
var gitlabPipelineLimit: @Sendable () -> Int
6161
var hideStagingBanner: @Sendable () -> Bool
62-
var homepageInterstitial: @Sendable () -> String?
62+
var maintenanceMessage: @Sendable () -> String?
6363
var httpClient: @Sendable () -> Client
6464
var loadSPIManifest: @Sendable (String) -> SPIManifest.Manifest?
6565
var logger: @Sendable () -> Logger
@@ -195,8 +195,8 @@ extension AppEnvironment {
195195
Environment.get("HIDE_STAGING_BANNER").flatMap(\.asBool)
196196
?? Constants.defaultHideStagingBanner
197197
},
198-
homepageInterstitial: {
199-
Environment.get("HOMEPAGE_INTERSTITIAL").flatMap(\.trimmed)
198+
maintenanceMessage: {
199+
Environment.get("MAINTENANCE_MESSAGE").flatMap(\.trimmed)
200200
},
201201
httpClient: { httpClient },
202202
loadSPIManifest: { path in SPIManifest.Manifest.load(in: path) },
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
import Ink
17+
18+
enum MaintenanceMessageIndex {
19+
20+
struct Model {
21+
22+
var markdown: String
23+
var html: String
24+
25+
init(markdown: String) {
26+
self.markdown = markdown
27+
self.html = MarkdownParser().parse(markdown).html
28+
}
29+
30+
}
31+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
import Plot
17+
18+
extension MaintenanceMessageIndex {
19+
20+
class View: PublicPage {
21+
22+
let model: Model
23+
24+
init(path: String, model: Model) {
25+
self.model = model
26+
super.init(path: path)
27+
}
28+
29+
override func bodyClass() -> String? {
30+
"maintenance"
31+
}
32+
33+
override func content() -> Node<HTML.BodyContext> {
34+
.raw(model.html)
35+
}
36+
}
37+
}

Sources/App/routes.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import VaporToOpenAPI
2323
func routes(_ app: Application) throws {
2424
do { // home page
2525
app.get { req in
26-
if let interstitial = Current.homepageInterstitial() {
27-
return MarkdownPage(path: req.url.path, markdown: interstitial).document()
26+
if let maintenanceMessage = Current.maintenanceMessage() {
27+
let model = MaintenanceMessageIndex.Model(markdown: maintenanceMessage)
28+
return MaintenanceMessageIndex.View(path: req.url.path, model: model).document()
2829
} else {
2930
let model = try await HomeIndex.Model.query(database: req.db)
3031
return HomeIndex.View(path: req.url.path, model: model).document()

Tests/AppTests/AppEnvironmentTests.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,28 @@ class AppEnvironmentTests: XCTestCase {
2929
XCTAssertEqual(Current.fileManager.checkoutsDirectory(), "/tmp/foo")
3030
}
3131

32-
func test_homepageInterstitial() throws {
33-
defer { unsetenv("HOMEPAGE_INTERSTITIAL") }
34-
Current.homepageInterstitial = AppEnvironment.live.homepageInterstitial
32+
func test_maintenanceMessage() throws {
33+
defer { unsetenv("MAINTENANCE_MESSAGE") }
34+
Current.maintenanceMessage = AppEnvironment.live.maintenanceMessage
3535
do {
36-
unsetenv("HOMEPAGE_INTERSTITIAL")
37-
XCTAssertEqual(Current.homepageInterstitial(), nil)
36+
unsetenv("MAINTENANCE_MESSAGE")
37+
XCTAssertEqual(Current.maintenanceMessage(), nil)
3838
}
3939
do {
40-
setenv("HOMEPAGE_INTERSTITIAL", "foo", 1)
41-
XCTAssertEqual(Current.homepageInterstitial(), "foo")
40+
setenv("MAINTENANCE_MESSAGE", "foo", 1)
41+
XCTAssertEqual(Current.maintenanceMessage(), "foo")
4242
}
4343
do {
44-
setenv("HOMEPAGE_INTERSTITIAL", "", 1)
45-
XCTAssertEqual(Current.homepageInterstitial(), nil)
44+
setenv("MAINTENANCE_MESSAGE", "", 1)
45+
XCTAssertEqual(Current.maintenanceMessage(), nil)
4646
}
4747
do {
48-
setenv("HOMEPAGE_INTERSTITIAL", " ", 1)
49-
XCTAssertEqual(Current.homepageInterstitial(), nil)
48+
setenv("MAINTENANCE_MESSAGE", " ", 1)
49+
XCTAssertEqual(Current.maintenanceMessage(), nil)
5050
}
5151
do {
52-
setenv("HOMEPAGE_INTERSTITIAL", " \t\n ", 1)
53-
XCTAssertEqual(Current.homepageInterstitial(), nil)
52+
setenv("MAINTENANCE_MESSAGE", " \t\n ", 1)
53+
XCTAssertEqual(Current.maintenanceMessage(), nil)
5454
}
5555
}
5656

Tests/AppTests/Mocks/AppEnvironment+mock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ extension AppEnvironment {
6262
gitlabPipelineToken: { nil },
6363
gitlabPipelineLimit: { Constants.defaultGitlabPipelineLimit },
6464
hideStagingBanner: { false },
65-
homepageInterstitial: { nil },
65+
maintenanceMessage: { nil },
6666
httpClient: { httpClient },
6767
loadSPIManifest: { _ in nil },
6868
logger: { logger },

Tests/AppTests/RoutesTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,12 @@ final class RoutesTests: AppTestCase {
6565
}
6666
}
6767

68+
func test_maintenanceMessage() throws {
69+
Current.maintenanceMessage = { "MAINTENANCE_MESSAGE" }
70+
71+
try app.test(.GET, "/") { res in
72+
XCTAssertContains(res.body.string, "MAINTENANCE_MESSAGE")
73+
}
74+
}
75+
6876
}

Tests/AppTests/WebpageSnapshotTests.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,19 @@ class WebpageSnapshotTests: SnapshotTestCase {
4848
assertSnapshot(of: page, as: .html)
4949
}
5050

51-
func test_HomeIndexView_interstitial() throws {
52-
Current.homepageInterstitial = {
53-
"""
54-
# ⚠️ Server Maintenance ⚠️
51+
func test_MaintenanceMessageIndexView() throws {
52+
let maintenanceMessage = """
53+
# Server Maintenance
5554
5655
We are currently performing an update to our database server.
5756
5857
Service should be restored within a few minutes.
5958
"""
60-
}
6159

62-
try app.test(.GET, "/") { res in
63-
assertSnapshot(of: res.body.string, as: .html)
64-
}
60+
let model = MaintenanceMessageIndex.Model(markdown: maintenanceMessage)
61+
let page = { MaintenanceMessageIndex.View(path: "/", model: model).document() }
62+
63+
assertSnapshot(of: page, as: .html)
6564
}
6665

6766
func test_PackageShowView() throws {

0 commit comments

Comments
 (0)