Skip to content

Commit d212e09

Browse files
authored
feat: add ozfortress configs to standard-competitive images (#316)
* feat: add standalone terraform for landing page S3 + CloudFront deployment * fix: upload logo to S3 and fix relative paths in landing page * feat: add ozfortress configs to standard-competitive images
1 parent cb02c71 commit d212e09

7 files changed

Lines changed: 313 additions & 4 deletions

File tree

landing-page/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet" />
2222
<link href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css" rel="stylesheet" />
2323
<link href="style.css" rel="stylesheet" />
24-
<link rel="icon" href="../assets/logo.png" type="image/png" />
24+
<link rel="icon" href="assets/logo.png" type="image/png" />
2525
</head>
2626
<body>
2727

2828
<!-- Navigation -->
2929
<nav class="navbar navbar-expand-lg navbar-dark fixed-top" aria-label="Main navigation">
3030
<div class="container">
3131
<a class="navbar-brand" href="#">
32-
<img src="../assets/logo.png" alt="TF2-QuickServer" height="36" class="d-inline-block align-middle me-2" />
32+
<img src="assets/logo.png" alt="TF2-QuickServer" height="36" class="d-inline-block align-middle me-2" />
3333
<span class="align-middle fw-bold">TF2-QuickServer</span>
3434
</a>
3535
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav">
@@ -50,7 +50,7 @@
5050
<!-- Hero -->
5151
<section id="hero" class="hero d-flex align-items-center">
5252
<div class="container text-center">
53-
<img src="../assets/logo.png" alt="TF2-QuickServer Logo" class="hero-logo mb-4" />
53+
<img src="assets/logo.png" alt="TF2-QuickServer Logo" class="hero-logo mb-4" />
5454
<h1 class="hero-title">TF2-QuickServer</h1>
5555
<p class="hero-subtitle">Deploy Team Fortress 2 servers directly from Discord.<br />Powered by Docker &mdash; multi-cloud, global, and ready in minutes.</p>
5656
<div class="hero-cta mt-4">
@@ -298,7 +298,7 @@ <h2 class="section-title text-center">Discord Commands</h2>
298298
<div class="container">
299299
<div class="row g-4">
300300
<div class="col-md-4">
301-
<h5><img src="../assets/logo.png" alt="TF2-QuickServer" height="28" class="me-2" />TF2-QuickServer</h5>
301+
<h5><img src="assets/logo.png" alt="TF2-QuickServer" height="28" class="me-2" />TF2-QuickServer</h5>
302302
<p class="text-muted small mt-2">Deploy Team Fortress 2 servers from Discord. Multi-cloud, global, and ready in minutes.</p>
303303
</div>
304304
<div class="col-md-4">

terraform/landing-page/main.tf

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# ===========================================
2+
# LANDING PAGE - S3 + CloudFront + Route53
3+
# ===========================================
4+
# Standalone terraform configuration for hosting the TF2-QuickServer
5+
# landing page on a private S3 bucket served via CloudFront with HTTPS.
6+
#
7+
# Usage:
8+
# cd terraform/landing-page
9+
# terraform init
10+
# terraform apply -var="hosted_zone_id=Z..."
11+
# ===========================================
12+
13+
# ===========================================
14+
# RANDOM SUFFIX FOR UNIQUE BUCKET NAME
15+
# ===========================================
16+
17+
resource "random_id" "bucket_suffix" {
18+
byte_length = 6
19+
}
20+
21+
# ===========================================
22+
# S3 BUCKET (PRIVATE)
23+
# ===========================================
24+
25+
resource "aws_s3_bucket" "landing_page" {
26+
bucket = "${var.bucket_name}-${random_id.bucket_suffix.hex}"
27+
force_destroy = true
28+
}
29+
30+
resource "aws_s3_bucket_public_access_block" "landing_page" {
31+
bucket = aws_s3_bucket.landing_page.id
32+
33+
block_public_acls = true
34+
block_public_policy = true
35+
ignore_public_acls = true
36+
restrict_public_buckets = true
37+
}
38+
39+
resource "aws_s3_bucket_versioning" "landing_page" {
40+
bucket = aws_s3_bucket.landing_page.id
41+
42+
versioning_configuration {
43+
status = "Enabled"
44+
}
45+
}
46+
47+
resource "aws_s3_bucket_server_side_encryption_configuration" "landing_page" {
48+
bucket = aws_s3_bucket.landing_page.id
49+
50+
rule {
51+
apply_server_side_encryption_by_default {
52+
sse_algorithm = "AES256"
53+
}
54+
}
55+
}
56+
57+
# ===========================================
58+
# LANDING PAGE FILES (UPLOAD)
59+
# ===========================================
60+
61+
resource "aws_s3_object" "logo" {
62+
bucket = aws_s3_bucket.landing_page.id
63+
key = "assets/logo.png"
64+
source = "${path.module}/../../assets/logo.png"
65+
content_type = "image/png"
66+
cache_control = "max-age=604800"
67+
etag = filemd5("${path.module}/../../assets/logo.png")
68+
}
69+
70+
resource "aws_s3_object" "index_html" {
71+
bucket = aws_s3_bucket.landing_page.id
72+
key = "index.html"
73+
source = "${path.module}/../../landing-page/index.html"
74+
content_type = "text/html; charset=utf-8"
75+
cache_control = "max-age=3600"
76+
etag = filemd5("${path.module}/../../landing-page/index.html")
77+
}
78+
79+
resource "aws_s3_object" "script_js" {
80+
bucket = aws_s3_bucket.landing_page.id
81+
key = "script.js"
82+
source = "${path.module}/../../landing-page/script.js"
83+
content_type = "application/javascript"
84+
cache_control = "max-age=3600"
85+
etag = filemd5("${path.module}/../../landing-page/script.js")
86+
}
87+
88+
resource "aws_s3_object" "style_css" {
89+
bucket = aws_s3_bucket.landing_page.id
90+
key = "style.css"
91+
source = "${path.module}/../../landing-page/style.css"
92+
content_type = "text/css"
93+
cache_control = "max-age=3600"
94+
etag = filemd5("${path.module}/../../landing-page/style.css")
95+
}
96+
97+
# ===========================================
98+
# CLOUDFRONT ORIGIN ACCESS IDENTITY
99+
# ===========================================
100+
101+
resource "aws_cloudfront_origin_access_identity" "landing_page" {
102+
comment = "OAI for ${var.domain_name}"
103+
}
104+
105+
# ===========================================
106+
# S3 BUCKET POLICY (CLOUDFRONT OAI ONLY)
107+
# ===========================================
108+
109+
resource "aws_s3_bucket_policy" "landing_page" {
110+
bucket = aws_s3_bucket.landing_page.id
111+
112+
policy = jsonencode({
113+
Version = "2012-10-17"
114+
Statement = [
115+
{
116+
Sid = "CloudFrontOAIRead"
117+
Effect = "Allow"
118+
Principal = {
119+
AWS = aws_cloudfront_origin_access_identity.landing_page.iam_arn
120+
}
121+
Action = "s3:GetObject"
122+
Resource = "${aws_s3_bucket.landing_page.arn}/*"
123+
}
124+
]
125+
})
126+
127+
depends_on = [aws_s3_bucket_public_access_block.landing_page]
128+
}
129+
130+
# ===========================================
131+
# ACM CERTIFICATE (DNS VALIDATION)
132+
# ===========================================
133+
134+
resource "aws_acm_certificate" "landing_page" {
135+
domain_name = var.domain_name
136+
validation_method = "DNS"
137+
138+
lifecycle {
139+
create_before_destroy = true
140+
}
141+
}
142+
143+
resource "aws_route53_record" "cert_validation" {
144+
for_each = {
145+
for dvo in aws_acm_certificate.landing_page.domain_validation_options : dvo.domain_name => {
146+
name = dvo.resource_record_name
147+
record = dvo.resource_record_value
148+
type = dvo.resource_record_type
149+
}
150+
}
151+
152+
allow_overwrite = true
153+
name = each.value.name
154+
records = [each.value.record]
155+
ttl = 60
156+
type = each.value.type
157+
zone_id = var.hosted_zone_id
158+
}
159+
160+
resource "aws_acm_certificate_validation" "landing_page" {
161+
certificate_arn = aws_acm_certificate.landing_page.arn
162+
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
163+
}
164+
165+
# ===========================================
166+
# CLOUDFRONT DISTRIBUTION
167+
# ===========================================
168+
169+
data "aws_cloudfront_cache_policy" "caching_optimized" {
170+
name = "Managed-CachingOptimized"
171+
}
172+
173+
resource "aws_cloudfront_distribution" "landing_page" {
174+
enabled = true
175+
is_ipv6_enabled = true
176+
http_version = "http2and3"
177+
default_root_object = "index.html"
178+
aliases = [var.domain_name]
179+
180+
origin {
181+
domain_name = aws_s3_bucket.landing_page.bucket_regional_domain_name
182+
origin_id = "S3-LandingPage"
183+
184+
s3_origin_config {
185+
origin_access_identity = aws_cloudfront_origin_access_identity.landing_page.cloudfront_access_identity_path
186+
}
187+
}
188+
189+
default_cache_behavior {
190+
allowed_methods = ["GET", "HEAD"]
191+
cached_methods = ["GET", "HEAD"]
192+
target_origin_id = "S3-LandingPage"
193+
194+
cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id
195+
196+
viewer_protocol_policy = "redirect-to-https"
197+
compress = true
198+
}
199+
200+
price_class = "PriceClass_100"
201+
202+
restrictions {
203+
geo_restriction {
204+
restriction_type = "none"
205+
}
206+
}
207+
208+
viewer_certificate {
209+
acm_certificate_arn = aws_acm_certificate_validation.landing_page.certificate_arn
210+
ssl_support_method = "sni-only"
211+
minimum_protocol_version = "TLSv1.2_2021"
212+
}
213+
214+
depends_on = [
215+
aws_s3_object.logo,
216+
aws_s3_object.index_html,
217+
aws_s3_object.script_js,
218+
aws_s3_object.style_css,
219+
]
220+
}
221+
222+
# ===========================================
223+
# ROUTE53 DNS RECORD
224+
# ===========================================
225+
226+
resource "aws_route53_record" "landing_page" {
227+
zone_id = var.hosted_zone_id
228+
name = var.domain_name
229+
type = "A"
230+
231+
alias {
232+
name = aws_cloudfront_distribution.landing_page.domain_name
233+
zone_id = aws_cloudfront_distribution.landing_page.hosted_zone_id
234+
evaluate_target_health = false
235+
}
236+
}

terraform/landing-page/outputs.tf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
output "bucket_name" {
2+
description = "Name of the S3 bucket for landing page"
3+
value = aws_s3_bucket.landing_page.id
4+
}
5+
6+
output "bucket_arn" {
7+
description = "ARN of the S3 bucket for landing page"
8+
value = aws_s3_bucket.landing_page.arn
9+
}
10+
11+
output "cloudfront_domain_name" {
12+
description = "CloudFront distribution domain name"
13+
value = aws_cloudfront_distribution.landing_page.domain_name
14+
}
15+
16+
output "cloudfront_distribution_id" {
17+
description = "CloudFront distribution ID"
18+
value = aws_cloudfront_distribution.landing_page.id
19+
}
20+
21+
output "landing_page_url" {
22+
description = "Full landing page URL"
23+
value = "https://${var.domain_name}"
24+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 5.0"
6+
}
7+
random = {
8+
source = "hashicorp/random"
9+
version = "~> 3.0"
10+
}
11+
}
12+
}
13+
14+
provider "aws" {
15+
region = "us-east-1"
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
variable "bucket_name" {
2+
description = "Name of the S3 bucket for landing page assets"
3+
type = string
4+
default = "tf2-quickserver-landing-page"
5+
}
6+
7+
variable "domain_name" {
8+
description = "Custom domain name for the landing page (e.g., landing.quickserver.sonikro.com)"
9+
type = string
10+
default = "landing.quickserver.sonikro.com"
11+
}
12+
13+
variable "hosted_zone_id" {
14+
description = "Route53 hosted zone ID for sonikro.com"
15+
type = string
16+
sensitive = true
17+
}

variants/fat-standard-competitive-amd64/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ RUN curl -L "${FBTF_CONFIGS_URL}" -o "${FBTF_CONFIGS_FILE_NAME}" \
159159
&& unzip -q "${FBTF_CONFIGS_FILE_NAME}" \
160160
&& mv "fbtf_cfg/"* "${PLUGIN_INSTALL_DIR}/cfg/"
161161

162+
# ozfortress configs
163+
ARG OZFORTRESS_CONFIGS_VERSION=master
164+
ARG OZFORTRESS_CONFIGS_FILE_NAME=server-configs-${OZFORTRESS_CONFIGS_VERSION}.zip
165+
ARG OZFORTRESS_CONFIGS_URL=https://github.com/ozfortress/server-configs/archive/${OZFORTRESS_CONFIGS_VERSION}.zip
166+
RUN curl -L "${OZFORTRESS_CONFIGS_URL}" -o "${OZFORTRESS_CONFIGS_FILE_NAME}" \
167+
&& unzip -q "${OZFORTRESS_CONFIGS_FILE_NAME}" \
168+
&& cp "server-configs-${OZFORTRESS_CONFIGS_VERSION}/cfg/"* "${PLUGIN_INSTALL_DIR}/cfg/"
169+
162170
# SteamPawn - NativePawn compiler for Pawn scripting
163171
ARG STEAMPAWN_FILE_NAME=package.zip
164172
ARG STEAMPAWN_VERSION=1.2.0.2

variants/fat-standard-competitive-i386/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ RUN curl -L "${FBTF_CONFIGS_URL}" -o "${FBTF_CONFIGS_FILE_NAME}" \
150150
&& unzip -q "${FBTF_CONFIGS_FILE_NAME}" \
151151
&& mv "fbtf_cfg/"* "${PLUGIN_INSTALL_DIR}/cfg/"
152152

153+
# ozfortress configs
154+
ARG OZFORTRESS_CONFIGS_VERSION=master
155+
ARG OZFORTRESS_CONFIGS_FILE_NAME=server-configs-${OZFORTRESS_CONFIGS_VERSION}.zip
156+
ARG OZFORTRESS_CONFIGS_URL=https://github.com/ozfortress/server-configs/archive/${OZFORTRESS_CONFIGS_VERSION}.zip
157+
RUN curl -L "${OZFORTRESS_CONFIGS_URL}" -o "${OZFORTRESS_CONFIGS_FILE_NAME}" \
158+
&& unzip -q "${OZFORTRESS_CONFIGS_FILE_NAME}" \
159+
&& cp "server-configs-${OZFORTRESS_CONFIGS_VERSION}/cfg/"* "${PLUGIN_INSTALL_DIR}/cfg/"
160+
153161
# SteamPawn - NativePawn compiler for Pawn scripting
154162
ARG STEAMPAWN_FILE_NAME=package.zip
155163
ARG STEAMPAWN_VERSION=1.2.0.2

0 commit comments

Comments
 (0)