Compare commits
22 Commits
fe20cd2db2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a9f8183309 | |||
|
1381635467
|
|||
|
d831f89ad8
|
|||
|
579c73b128
|
|||
|
f2b9fd4c11
|
|||
| 64c297a905 | |||
|
39c02779a9
|
|||
|
daf180c210
|
|||
|
ba2e1e4655
|
|||
|
615fc2a24f
|
|||
|
6e106657ea
|
|||
|
4e913599cb
|
|||
|
043c379647
|
|||
|
269545596a
|
|||
|
a25e9f6214
|
|||
|
9bd7003589
|
|||
|
f9a22a11e1
|
|||
|
344723abde
|
|||
|
806dd67110
|
|||
|
c1d3af7ab0
|
|||
|
d3b0c13dad
|
|||
|
82c7eed868
|
60
.gitea/workflows/apply-master.yaml
Normal file
60
.gitea/workflows/apply-master.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Terraform validate and apply
|
||||
run-name: ${{ gitea.actor }} applying terraform
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
TF_VAR_aws_region: ${{ vars.TF_VAR_aws_region }}
|
||||
TF_VAR_site_domain: ${{ vars.TF_VAR_site_domain }}
|
||||
TF_VAR_site_domains: ${{ vars.TF_VAR_site_domains }}
|
||||
TF_VAR_project_name: ${{ vars.TF_VAR_project_name }}
|
||||
TF_VAR_environment: ${{ vars.TF_VAR_environment }}
|
||||
TF_VAR_tuffas_applier_role_arn: ${{ vars.TF_VAR_tuffas_applier_role_arn }}
|
||||
TF_VAR_tfstate_backend_role_arn: ${{ vars.TF_VAR_tfstate_backend_role_arn }}
|
||||
TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache
|
||||
jobs:
|
||||
terraform:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- run: ls -alh
|
||||
name: List files
|
||||
- name: Set up terraform
|
||||
uses: hashicorp/setup-terraform@v3
|
||||
- name: Set up and configure Terraform plugin cache
|
||||
run: |
|
||||
mkdir --parents $TF_PLUGIN_CACHE_DIR
|
||||
- name: Cache terraform
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
key: ${{ runner.os }}-terraform-${{hashFiles('**.terraform.lock.hcl') }}
|
||||
restore-keys: ${{ runner.os }}-terraform-
|
||||
- name: Init
|
||||
id: init
|
||||
run: terraform init
|
||||
- name: Check formatting
|
||||
id: fmt
|
||||
run: terraform fmt -check
|
||||
- name: Validate
|
||||
id: validate
|
||||
run: terraform validate
|
||||
- name: Plan
|
||||
id: plan
|
||||
run: terraform plan -no-color -input=false
|
||||
- name: Apply
|
||||
id: apply
|
||||
run: terraform apply -auto-approve -no-color -input=false
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,7 +8,11 @@ result
|
||||
# Local .terraform directories
|
||||
.terraform/
|
||||
|
||||
# cache directories
|
||||
.terraform.d/
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
terraform.tfvars
|
||||
|
||||
20
.terraform.lock.hcl
generated
20
.terraform.lock.hcl
generated
@@ -1,6 +1,26 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/cloudflare/cloudflare" {
|
||||
version = "2.19.2"
|
||||
constraints = "~> 2.19.2"
|
||||
hashes = [
|
||||
"h1:gcgDf0Ltyopd5j30oCcnjceCyRpJmSBhTTwldOFnJEc=",
|
||||
"zh:35a4d37c7601b537e156a032730e2987f137017e38c9a1a383f75cfeccb1975e",
|
||||
"zh:3bdb1544aef7469813a699ba8d322248c96ffa05573c2bb990e1297aa95473d0",
|
||||
"zh:41a322d3eeeb0dde185ea7a9cafe952c445a683a6a372089f8d003d8d2f4b722",
|
||||
"zh:447ec6386879ff56cd3a97fc5d20b428451a445f8846a0127f5788de9e213b3c",
|
||||
"zh:4a1fa7c6c9e28916009fe3c7a9f7f944e8b4e307ab3d97a34d81ba66769160f6",
|
||||
"zh:5a2cb0e8ddc725c78ba09a817105136f564c7f4fe0173633d82bc3f8005dc15a",
|
||||
"zh:83c0edc0ddd6ad8e3c140dcecafccad69edd199d2526cc9be10d857316f3859e",
|
||||
"zh:a5a1917943a9e8486dc3d0eb315bc899944fe67888e38b35999b6a79907ec762",
|
||||
"zh:a5cfcd8ec0fd3d0c80de8c519ee07b1e899b8f86d5f6f5800bc959190df9eb93",
|
||||
"zh:be3a37ef3f0991989a4e51e5fe16d9cf71571cb1ecb7a41b31d91c2ae2a3313d",
|
||||
"zh:ef1155fd12e3528f686b6a59fc732e35265f8d08450bc27baf8ccebbcd4cff0c",
|
||||
"zh:f3a2293a7ccb14fa16472c7948498d5a19cb5f26e3aeb1b59756c7f9045c277b",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "5.100.0"
|
||||
constraints = "~> 5.92"
|
||||
|
||||
32
README.md
32
README.md
@@ -1,8 +1,34 @@
|
||||
# Testaform
|
||||
# tuffas
|
||||
|
||||
Testing terraform out
|
||||
hosting hruday.me via terraform
|
||||
|
||||
---
|
||||
|
||||
|
||||
Add `dotenv` to .envrc after other nix stuff, and store keys in .env, which is fine for a testing project.
|
||||
~~Add `dotenv` to .envrc after other nix stuff, and store keys in .env, which is fine for a testing project.~~
|
||||
|
||||
Don't add dotenv.
|
||||
Workflow is to just use the `dev` branch or anything else, then only actually deploy via PR to `master`.
|
||||
PR to master is a great deployment strategy, no notes.
|
||||
|
||||
Currently manages hruday.me and deepakmallubhotla.com, creating buckets which match the domain names.
|
||||
The content of the sites are managed externally, in their own repos which deploy by uploading to the S3 bucket created here.
|
||||
|
||||
## adding a domain
|
||||
|
||||
Not an ideal process, so we should improve.
|
||||
|
||||
1. Acquire domain name, manually atm.
|
||||
2. let Cloudflare manage DNS by setting nameservers (following the wizard in cf works with no DNS records required before we get here!) etc., also manual
|
||||
3. Add domain name to relevant Gitea variable, should be easy.
|
||||
4. Bucket will be created, empty. If you want an easy start you can manually upload to the bucket.
|
||||
5. Deploy with whatever method you want, can include a build process or anything else. Follow hruday.me as a guide maybe
|
||||
|
||||
|
||||
|
||||
## Todos
|
||||
- [x] better secrets management
|
||||
- [x] ci
|
||||
- [x] test ci permissions with a real terraform apply (not in ci)
|
||||
- [ ] can we make a lower-weight runner? ubuntu-latest is heavy and still requires ~1m for providers
|
||||
- [ ] For new domain should provide a default set of content in the bucket? or does that cost more for the extra creates, for a local project we may not care
|
||||
|
||||
1
justfile
1
justfile
@@ -22,4 +22,3 @@ fmt:
|
||||
set -euxo pipefail
|
||||
nix fmt
|
||||
# shouldn't actually be that hard to put into nix fmt but lazyyy
|
||||
terraform fmt
|
||||
|
||||
19
main.tf
Normal file
19
main.tf
Normal file
@@ -0,0 +1,19 @@
|
||||
provider "aws" {
|
||||
region = var.aws_region
|
||||
assume_role {
|
||||
role_arn = var.tuffas_applier_role_arn
|
||||
}
|
||||
}
|
||||
|
||||
provider "cloudflare" {
|
||||
}
|
||||
|
||||
module "static_website" {
|
||||
source = "./modules/static-website"
|
||||
|
||||
# toset to dedupe
|
||||
for_each = toset(var.site_domains)
|
||||
site_domain = each.key
|
||||
project_name = var.project_name
|
||||
environment = var.environment
|
||||
}
|
||||
141
modules/static-website/main.tf
Normal file
141
modules/static-website/main.tf
Normal file
@@ -0,0 +1,141 @@
|
||||
locals {
|
||||
common_tags = {
|
||||
Project = var.project_name
|
||||
Environment = var.environment
|
||||
ManagedBy = "terraform"
|
||||
Domain = var.site_domain
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket" "site" {
|
||||
bucket = var.site_domain
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_public_access_block" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
block_public_acls = false
|
||||
block_public_policy = false
|
||||
ignore_public_acls = false
|
||||
restrict_public_buckets = false
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_website_configuration" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
index_document {
|
||||
suffix = "index.html"
|
||||
}
|
||||
|
||||
error_document {
|
||||
key = "error.html"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_ownership_controls" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
rule {
|
||||
object_ownership = "BucketOwnerPreferred"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_acl" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
acl = "public-read"
|
||||
depends_on = [
|
||||
aws_s3_bucket_ownership_controls.site,
|
||||
aws_s3_bucket_public_access_block.site
|
||||
]
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Sid = "PublicReadGetObject"
|
||||
Effect = "Allow"
|
||||
Principal = "*"
|
||||
Action = "s3:GetObject"
|
||||
Resource = "${aws_s3_bucket.site.arn}/*"
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
depends_on = [
|
||||
aws_s3_bucket_public_access_block.site
|
||||
]
|
||||
}
|
||||
|
||||
data "cloudflare_zones" "domain" {
|
||||
filter {
|
||||
name = var.site_domain
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_record" "site_cname" {
|
||||
zone_id = data.cloudflare_zones.domain.zones[0].id
|
||||
name = var.site_domain
|
||||
value = aws_s3_bucket_website_configuration.site.website_endpoint
|
||||
type = "CNAME"
|
||||
ttl = 1
|
||||
proxied = true
|
||||
}
|
||||
|
||||
resource "cloudflare_record" "www" {
|
||||
zone_id = data.cloudflare_zones.domain.zones[0].id
|
||||
name = "www"
|
||||
value = var.site_domain
|
||||
type = "CNAME"
|
||||
ttl = 1
|
||||
proxied = true
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_versioning" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_lifecycle_configuration" "site" {
|
||||
bucket = aws_s3_bucket.site.id
|
||||
|
||||
rule {
|
||||
id = "cleanup_old_versions"
|
||||
status = "Enabled"
|
||||
|
||||
noncurrent_version_expiration {
|
||||
noncurrent_days = 90
|
||||
}
|
||||
filter {
|
||||
prefix = ""
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
id = "cleanup_incomplete_uploads"
|
||||
status = "Enabled"
|
||||
|
||||
abort_incomplete_multipart_upload {
|
||||
days_after_initiation = 7
|
||||
}
|
||||
filter {
|
||||
prefix = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_page_rule" "https" {
|
||||
zone_id = data.cloudflare_zones.domain.zones[0].id
|
||||
target = "*.${var.site_domain}/*"
|
||||
actions {
|
||||
always_use_https = true
|
||||
}
|
||||
}
|
||||
14
modules/static-website/outputs.tf
Normal file
14
modules/static-website/outputs.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
output "website_bucket_name" {
|
||||
description = "Name (id) of the bucket"
|
||||
value = aws_s3_bucket.site.id
|
||||
}
|
||||
|
||||
output "bucket_endpoint" {
|
||||
description = "Bucket endpoint"
|
||||
value = aws_s3_bucket_website_configuration.site.website_endpoint
|
||||
}
|
||||
|
||||
output "domain_name" {
|
||||
description = "Website endpoint"
|
||||
value = var.site_domain
|
||||
}
|
||||
20
modules/static-website/variables.tf
Normal file
20
modules/static-website/variables.tf
Normal file
@@ -0,0 +1,20 @@
|
||||
variable "site_domain" {
|
||||
type = string
|
||||
description = "The domain name of the site"
|
||||
}
|
||||
|
||||
variable "project_name" {
|
||||
type = string
|
||||
description = "Name of the project for resource tagging"
|
||||
default = "tuffas"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
type = string
|
||||
description = "Environment name (e.g., dev, staging, prod)"
|
||||
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "prod"], var.environment)
|
||||
error_message = "Environment must be one of: dev, staging, prod."
|
||||
}
|
||||
}
|
||||
14
modules/static-website/versions.tf
Normal file
14
modules/static-website/versions.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.92"
|
||||
}
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "~> 2.19.2"
|
||||
}
|
||||
}
|
||||
|
||||
required_version = ">= 1.2"
|
||||
}
|
||||
60
moved.tf
Normal file
60
moved.tf
Normal file
@@ -0,0 +1,60 @@
|
||||
moved {
|
||||
from = aws_s3_bucket.site
|
||||
to = module.static_website.aws_s3_bucket.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_acl.site
|
||||
to = module.static_website.aws_s3_bucket_acl.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_lifecycle_configuration.site
|
||||
to = module.static_website.aws_s3_bucket_lifecycle_configuration.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_ownership_controls.site
|
||||
to = module.static_website.aws_s3_bucket_ownership_controls.site
|
||||
}
|
||||
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_policy.site
|
||||
to = module.static_website.aws_s3_bucket_policy.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_public_access_block.site
|
||||
to = module.static_website.aws_s3_bucket_public_access_block.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_versioning.site
|
||||
to = module.static_website.aws_s3_bucket_versioning.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = aws_s3_bucket_website_configuration.site
|
||||
to = module.static_website.aws_s3_bucket_website_configuration.site
|
||||
}
|
||||
|
||||
moved {
|
||||
from = cloudflare_record.www
|
||||
to = module.static_website.cloudflare_record.www
|
||||
}
|
||||
|
||||
moved {
|
||||
from = cloudflare_record.site_cname
|
||||
to = module.static_website.cloudflare_record.site_cname
|
||||
}
|
||||
|
||||
moved {
|
||||
from = cloudflare_page_rule.https
|
||||
to = module.static_website.cloudflare_page_rule.https
|
||||
}
|
||||
|
||||
moved {
|
||||
from = module.static_website
|
||||
to = module.static_website["hruday.me"]
|
||||
}
|
||||
14
outputs.tf
Normal file
14
outputs.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
output "website_bucket_names" {
|
||||
description = "Names (ids) of the buckets by domain"
|
||||
value = { for k, v in module.static_website : k => v.website_bucket_name }
|
||||
}
|
||||
|
||||
output "bucket_endpoints" {
|
||||
description = "Bucket endpoints by domain"
|
||||
value = { for k, v in module.static_website : k => v.bucket_endpoint }
|
||||
}
|
||||
|
||||
output "domain_names" {
|
||||
description = "Website endpoints by domain"
|
||||
value = { for k, v in module.static_website : k => v.domain_name }
|
||||
}
|
||||
12
terraform.tf
12
terraform.tf
@@ -4,14 +4,18 @@ terraform {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.92"
|
||||
}
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "~> 2.19.2"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {
|
||||
region = "us-east-2"
|
||||
bucket = "hrudayme-test-tfstate"
|
||||
key = "tuffas-tf"
|
||||
region = "us-east-2"
|
||||
bucket = "hrudayme-test-tfstate"
|
||||
key = "tuffas-tf"
|
||||
use_lockfile = true
|
||||
assume_role = { role_arn = "arn:aws:iam::677425296084:role/tfstate_backend_role" }
|
||||
assume_role = { role_arn = "arn:aws:iam::677425296084:role/tfstate_backend_role" }
|
||||
}
|
||||
|
||||
required_version = ">= 1.2"
|
||||
|
||||
17
terraform.tfvars.example
Normal file
17
terraform.tfvars.example
Normal file
@@ -0,0 +1,17 @@
|
||||
# Example Terraform variables file
|
||||
# Copy this file to terraform.tfvars and update with your actual values
|
||||
|
||||
# AWS region where resources will be created
|
||||
aws_region = "us-east-2"
|
||||
|
||||
# Your domain name (must be managed by Cloudflare)
|
||||
site_domain = "example.com"
|
||||
|
||||
# Project name for resource tagging
|
||||
project_name = "tuffas"
|
||||
|
||||
# Environment (dev, staging, prod)
|
||||
environment = "prod"
|
||||
|
||||
# IAM role ARNs (update with your actual role ARNs)
|
||||
tuffas_applier_role_arn = "arn:aws:iam::YOUR-ACCOUNT-ID:role/ROLE_NAME"
|
||||
@@ -17,5 +17,6 @@
|
||||
programs.shfmt.enable = true;
|
||||
programs.yamlfmt.enable = true;
|
||||
programs.just.enable = true;
|
||||
programs.terraform.enable = true;
|
||||
|
||||
}
|
||||
|
||||
31
variables.tf
Normal file
31
variables.tf
Normal file
@@ -0,0 +1,31 @@
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
description = "The AWS region of these sites"
|
||||
}
|
||||
|
||||
variable "site_domains" {
|
||||
type = list(string)
|
||||
description = "The domain name of these sites, which will be mapped over"
|
||||
}
|
||||
|
||||
variable "tuffas_applier_role_arn" {
|
||||
type = string
|
||||
description = "IAM role ARN for Terraform to assume when applying changes"
|
||||
}
|
||||
|
||||
variable "project_name" {
|
||||
type = string
|
||||
description = "Name of the project for resource tagging"
|
||||
default = "tuffas"
|
||||
}
|
||||
|
||||
# future proofing
|
||||
variable "environment" {
|
||||
type = string
|
||||
description = "Environment name (e.g., dev, staging, prod)"
|
||||
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "prod"], var.environment)
|
||||
error_message = "Environment must be one of: dev, staging, prod."
|
||||
}
|
||||
}
|
||||
3
website/README.md
Normal file
3
website/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Custom Start page
|
||||
|
||||
Based on https://github.com/jeroenpardon/sui
|
||||
558
website/assets/css/styles.css
Normal file
558
website/assets/css/styles.css
Normal file
@@ -0,0 +1,558 @@
|
||||
html{
|
||||
box-sizing: border-box;
|
||||
moz-box-sizing: border-box;
|
||||
webkit-box-sizing: border-box;
|
||||
webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
html,
|
||||
body{
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text-pri);
|
||||
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: auto;
|
||||
letter-spacing: -.012em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
webkit-font-smoothing: antialiased;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after{
|
||||
box-sizing: inherit;
|
||||
moz-box-sizing: inherit;
|
||||
webkit-box-sizing: inherit;
|
||||
}
|
||||
|
||||
:root{
|
||||
module-spacing: 3vh;
|
||||
}
|
||||
|
||||
|
||||
/* TEXT STYLES */
|
||||
|
||||
h1, h2{
|
||||
font-weight: 300;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2, h3, h4{
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-size: 16px;
|
||||
height: 30px;
|
||||
|
||||
}
|
||||
|
||||
h3{
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
h4{
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
a{
|
||||
color: var(--color-text-pri);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover{
|
||||
text-decoration: underline;
|
||||
webkit-text-decoration-color: var(--color-text-acc);
|
||||
webkit-text-decoration-skip: true;
|
||||
}
|
||||
|
||||
.icon{
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
|
||||
/* FORMS */
|
||||
|
||||
input{
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: thin solid var(--color-text-acc);
|
||||
color: var(--color-text-pri);
|
||||
font-size: 0.8em;
|
||||
height: 3.5em;
|
||||
transition: all 0.4s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input:focus{
|
||||
color-border: var(--color-text-pri);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input:focus{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/* TABLES */
|
||||
|
||||
table{
|
||||
border: thin solid #e4e4e4;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table td:nth-of-type(2){
|
||||
padding-right: 5em;
|
||||
}
|
||||
|
||||
table td{
|
||||
border: thin solid #e4e4e4;
|
||||
color: #333333;
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
padding: 10px 5px;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
table th{
|
||||
border: thin solid #e4e4e4;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
table a{
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
|
||||
/* ANIMATION */
|
||||
|
||||
.fade{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeseq{
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fade{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade{
|
||||
animation: fadeseq .3s forwards;
|
||||
}
|
||||
|
||||
.fade:nth-child(2){
|
||||
animation-delay: .4s;
|
||||
}
|
||||
|
||||
|
||||
/* LAYOUT */
|
||||
|
||||
#container{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 3vh;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 12vh 4vh auto;
|
||||
justify-items: stretch;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 5vh;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* SECTIONS */
|
||||
|
||||
#header{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
display: grid;
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: 64px;
|
||||
padding-bottom: var(--module-spacing);
|
||||
}
|
||||
|
||||
.apps_icon{
|
||||
height: 64px;
|
||||
margin-right: 1em;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.apps_icon span{
|
||||
font-size: 2.5em;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.apps_item{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
height: 64px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.apps_text{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.apps_text a{
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.apps_text span{
|
||||
color: var(--color-text-acc);
|
||||
font-size: 0.8em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
||||
#links_loop{
|
||||
display: grid;
|
||||
flex-wrap: nowrap;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
#links_item{
|
||||
line-height: 1.5rem;
|
||||
margin-bottom: 2em;
|
||||
webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#links_item h4{
|
||||
color: var(--color-text-acc);
|
||||
}
|
||||
|
||||
#links_item a{
|
||||
display: block;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MODAL */
|
||||
|
||||
|
||||
#modal{
|
||||
overflow-y: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: all 0.3s;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
#modal:target{
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#modal>div{
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.25);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 2em;
|
||||
margin-top: 5vh;
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#modal h1{
|
||||
color: #333333;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#modal h2{
|
||||
margin-top:1.5em;
|
||||
}
|
||||
|
||||
#modal-header{
|
||||
display:flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#modal-footer{
|
||||
display:flex;
|
||||
font-size:2em;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
#modal-footer a{
|
||||
margin-right:0.25em;
|
||||
}
|
||||
|
||||
.modal-close{
|
||||
color: #000000;
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.modal-close:hover{
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#modal_init a{
|
||||
bottom: 1vh;
|
||||
color: var(--color-text-acc);
|
||||
left: 1vw;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#modal_init a:hover{
|
||||
color: var(--color-text-pri);
|
||||
}
|
||||
|
||||
#modal-theme{
|
||||
border-bottom: 0px solid var(--color-text-acc);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#providers{
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
|
||||
/* THEMING */
|
||||
|
||||
.theme-button{
|
||||
font-size: 0.8em;
|
||||
margin: 2px;
|
||||
width:128px;
|
||||
line-height: 3em;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.theme-blackboard{
|
||||
background-color: #000000;
|
||||
border: 4px solid #5c5c5c;
|
||||
color: #FFFDEA;
|
||||
}
|
||||
|
||||
.theme-gazette{
|
||||
background-color: #F2F7FF;
|
||||
border: 4px solid #5c5c5c;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.theme-espresso{
|
||||
background-color: #21211F;
|
||||
border: 4px solid #4E4E4E;
|
||||
color: #D1B59A;
|
||||
}
|
||||
|
||||
.theme-cab{
|
||||
background-color: #FEED01;
|
||||
border: 4px solid #424242;
|
||||
color: #1F1F1F;
|
||||
}
|
||||
|
||||
.theme-cloud{
|
||||
background-color: #f1f2f0;
|
||||
border: 4px solid #35342f;
|
||||
color: #37bbe4;
|
||||
}
|
||||
|
||||
.theme-lime{
|
||||
background-color: #263238;
|
||||
border: 4px solid #AABBC3;
|
||||
color: #aeea00;
|
||||
}
|
||||
|
||||
.theme-passion{
|
||||
background-color: #f5f5f5;
|
||||
border: 4px solid #8e24aa;
|
||||
color: #12005e;
|
||||
}
|
||||
|
||||
.theme-blues{
|
||||
background-color: #2B2C56;
|
||||
border: 4px solid #6677EB;
|
||||
color: #EFF1FC;
|
||||
}
|
||||
|
||||
.theme-chalk{
|
||||
background-color: #263238;
|
||||
border: 4px solid #FF869A;
|
||||
color: #AABBC3;
|
||||
}
|
||||
|
||||
.theme-tron{
|
||||
background-color: #242B33;
|
||||
border: 4px solid #6EE2FF;
|
||||
color: #EFFBFF;
|
||||
}
|
||||
|
||||
.theme-paper{
|
||||
background-color: #F8F6F1;
|
||||
border: 4px solid #F5E1A4;
|
||||
color: #4C432E;
|
||||
}
|
||||
|
||||
|
||||
/* MEDIA QUERIES */
|
||||
|
||||
@media screen and (max-width: 1260px)
|
||||
{
|
||||
#container
|
||||
{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 80px auto;
|
||||
justify-items: stretch;
|
||||
margin-bottom: 1vh;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#links_loop {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
#modal>div{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 5vh;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 667px)
|
||||
{
|
||||
html{
|
||||
font-size: calc(16px + 6 * ((100vw - 320px) / 680));
|
||||
}
|
||||
|
||||
#container{
|
||||
align-items: stretch;
|
||||
display: grid;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 80px auto;
|
||||
justify-items: stretch;
|
||||
margin-bottom: 1vh;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4em;
|
||||
height: auto;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-size: 1em;
|
||||
height: auto;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
h3{
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#apps_loop{
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.apps_icon{
|
||||
height: 64px;
|
||||
margin-right: 0.8em;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.apps_icon span{
|
||||
font-size: 2em;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
|
||||
#links_loop{
|
||||
display: grid;
|
||||
flex-wrap: nowrap;
|
||||
grid-column-gap: 20px;
|
||||
grid-row-gap: 0px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small Screens */
|
||||
@media only screen and (max-width: 400px) {
|
||||
#app-address {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
220
website/assets/js/script.js
Normal file
220
website/assets/js/script.js
Normal file
@@ -0,0 +1,220 @@
|
||||
function date() {
|
||||
let currentDate = new Date();
|
||||
let dateOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric"
|
||||
};
|
||||
let date = currentDate.toLocaleDateString("en-GB", dateOptions);
|
||||
document.getElementById("header_date").innerHTML = date;
|
||||
}
|
||||
|
||||
function greet() {
|
||||
let currentTime = new Date();
|
||||
let greet = Math.floor(currentTime.getHours() / 6);
|
||||
switch (greet) {
|
||||
case 0:
|
||||
document.getElementById("header_greet").innerHTML = "Good night!";
|
||||
break;
|
||||
case 1:
|
||||
document.getElementById("header_greet").innerHTML = "Good morning!";
|
||||
break;
|
||||
case 2:
|
||||
document.getElementById("header_greet").innerHTML = "Good afternoon!";
|
||||
break;
|
||||
case 3:
|
||||
document.getElementById("header_greet").innerHTML = "Good evening!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const strategies = [
|
||||
"(Organic) machinery",
|
||||
"A line has two sides",
|
||||
"A very small object Its center",
|
||||
"Abandon desire",
|
||||
"Abandon normal instructions",
|
||||
"Abandon normal instruments",
|
||||
"Accept advice",
|
||||
"Accretion",
|
||||
"Adding on",
|
||||
"Allow an easement (an easement is the abandonment of a stricture)",
|
||||
"Always first steps",
|
||||
"Always give yourself credit for having more than personality",
|
||||
"Are there sections? Consider transitions",
|
||||
"Ask people to work against their better judgement",
|
||||
"Ask your body",
|
||||
"Assemble some of the elements in a group and treat the group",
|
||||
"Balance the consistency principle with the inconsistency principle",
|
||||
"Be dirty",
|
||||
"Be extravagant",
|
||||
"Be less critical",
|
||||
"Breathe more deeply",
|
||||
"Bridges -build -burn",
|
||||
"Cascades",
|
||||
"Change ambiguities to specifics",
|
||||
"Change instrument roles",
|
||||
"Change nothing and continue with immaculate consistency",
|
||||
"Change specifics to ambiguities",
|
||||
"Children -speaking -singing",
|
||||
"Cluster analysis",
|
||||
"Consider different fading systems",
|
||||
"Consider transitions",
|
||||
"Consult other sources -promising -unpromising",
|
||||
"Convert a melodic element into a rhythmic element",
|
||||
"Courage!",
|
||||
"Cut a vital connection",
|
||||
"Decorate, decorate",
|
||||
"Define an area as 'safe' and use it as an anchor",
|
||||
"Destroy -nothing -the most important thing",
|
||||
"Discard an axiom",
|
||||
"Disciplined self-indulgence",
|
||||
"Disconnect from desire",
|
||||
"Discover the recipes you are using and abandon them",
|
||||
"Display your talent",
|
||||
"Distorting time",
|
||||
"Do nothing for as long as possible",
|
||||
"Do something boring",
|
||||
"Do something sudden, destructive and unpredictable",
|
||||
"Do the last thing first",
|
||||
"Do the washing up",
|
||||
"Do the words need changing?",
|
||||
"Do we need holes?",
|
||||
"Don't avoid what is easy",
|
||||
"Don't be frightened of cliches",
|
||||
"Don't break the silence",
|
||||
"Don't stress one thing more than another",
|
||||
"Don't be afraid of things because they're easy to do",
|
||||
"Don't be frightened to display your talents",
|
||||
"Emphasize differences",
|
||||
"Emphasize repetitions",
|
||||
"Emphasize the flaws",
|
||||
"Faced with a choice, do both",
|
||||
"Feed the recording back out of the medium",
|
||||
"Fill every beat with something",
|
||||
"Find a safe part and use it as an anchor",
|
||||
"Get your neck massaged",
|
||||
"Ghost echoes",
|
||||
"Give the game away",
|
||||
"Give the name away",
|
||||
"Give way to your worst impulse",
|
||||
"Go outside. Shut the door.",
|
||||
"Go slowly all the way round the outside",
|
||||
"Go to an extreme, come part way back",
|
||||
"Honor thy error as a hidden intention",
|
||||
"How would someone else do it?",
|
||||
"How would you have done it?",
|
||||
"Humanize something free of error",
|
||||
"Idiot glee (?)",
|
||||
"Imagine the piece as a set of disconnected events",
|
||||
"In total darkness, or in a very large room, very quietly",
|
||||
"Infinitesimal gradations",
|
||||
"Intentions -nobility of -humility of -credibility of",
|
||||
"Into the impossible",
|
||||
"Is it finished?",
|
||||
"Is something missing?",
|
||||
"Is the information correct?",
|
||||
"Is the style right?",
|
||||
"It is quite possible (after all)",
|
||||
"It is simply a matter of work",
|
||||
"Just carry on",
|
||||
"Left channel, right channel, center channel",
|
||||
"Listen to the quiet voice",
|
||||
"Look at the order in which you do things",
|
||||
"Look closely at the most embarrassing details & amplify them",
|
||||
"Lost in useless territory",
|
||||
"Lowest common denominator",
|
||||
"Magnify the most difficult details",
|
||||
"Make a blank valuable by putting it in an exquisite frame",
|
||||
"Make a sudden, destructive unpredictable action; incorporate",
|
||||
"Make an exhaustive list of everything you might do \n& do the last thing on the list",
|
||||
"Make it more sensual",
|
||||
"Make what's perfect more human",
|
||||
"Mechanicalize something idiosyncratic",
|
||||
"Move towards the unimportant",
|
||||
"Mute and continue",
|
||||
"Not building a wall but making a brick",
|
||||
"Once the search has begun, something will be found",
|
||||
"Only a part, not the whole",
|
||||
"Only one element of each kind",
|
||||
"Overtly resist change",
|
||||
"Put in earplugs",
|
||||
"Question the heroic approach",
|
||||
"Reevaluation (a warm feeling)",
|
||||
"Remember those quiet evenings",
|
||||
"Remove a restriction",
|
||||
"Remove ambiguities and convert to specifics",
|
||||
"Remove specifics and convert to ambiguities",
|
||||
"Repetition is a form of change",
|
||||
"Retrace your steps",
|
||||
"Reverse",
|
||||
"Short circuit \n(example: a man eating peas with the idea that they will improve his virility \nshovels them straight into his lap)",
|
||||
"Simple subtraction",
|
||||
"Simply a matter of work",
|
||||
"Slow preparation, fast execution",
|
||||
"Spectrum analysis",
|
||||
"State the problem in words as clearly as possible",
|
||||
"Take a break",
|
||||
"Take away the elements in order of apparent non-importance",
|
||||
"Take away the important parts",
|
||||
"Tape your mouth",
|
||||
"The inconsistency principle",
|
||||
"The most important thing is the thing most easily forgotten",
|
||||
"The tape is now the music",
|
||||
"Think - inside the work -outside the work",
|
||||
"Think of the radio",
|
||||
"Tidy up",
|
||||
"Towards the insignificant",
|
||||
"Trust in the you of now",
|
||||
"Try faking it",
|
||||
"Turn it upside down",
|
||||
"Twist the spine",
|
||||
"Use 'unqualified' people",
|
||||
"Use an old idea",
|
||||
"Use an unacceptable color",
|
||||
"Use cliches",
|
||||
"Use fewer notes",
|
||||
"Use filters",
|
||||
"Use something nearby as a model",
|
||||
"Use your own ideas",
|
||||
"Voice your suspicions",
|
||||
"Water",
|
||||
"What are the sections sections of? Imagine a caterpillar moving",
|
||||
"What are you really thinking about just now?",
|
||||
"What context would look right?",
|
||||
"What is the reality of the situation?",
|
||||
"What is the simplest solution?",
|
||||
"What mistakes did you make last time?",
|
||||
"What to increase? What to reduce? What to maintain?",
|
||||
"What would your closest friend do?",
|
||||
"What wouldn't you do?",
|
||||
"When is it for?",
|
||||
"Where is the edge?",
|
||||
"Which parts can be grouped?",
|
||||
"Work at a different speed",
|
||||
"Would anyone want it?",
|
||||
"You are an engineer",
|
||||
"You can only make one dot at a time",
|
||||
"You don't have to be ashamed of using your own ideas",
|
||||
"[blank white card]"
|
||||
];
|
||||
|
||||
const pickStrategy = () => {
|
||||
let strategyNumber = Math.floor(Math.random() * strategies.length);
|
||||
return strategies[strategyNumber];
|
||||
};
|
||||
|
||||
const strategize = () => {
|
||||
let strat = pickStrategy();
|
||||
document.getElementById("header_strategy").innerHTML = strat;
|
||||
}
|
||||
|
||||
function loadFunctions() {
|
||||
date();
|
||||
greet();
|
||||
strategize();
|
||||
}
|
||||
|
||||
|
||||
29
website/index.html
Normal file
29
website/index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>deepak.science</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Default-Style" content="">
|
||||
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
|
||||
<link type="text/css" rel="stylesheet" href="./assets/css/styles.css" media="screen,projection"/>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700,900" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body onload="loadFunctions()">
|
||||
|
||||
|
||||
<main id="container" class="fade">
|
||||
|
||||
<section id="header">
|
||||
<h2 id="header_date"></h2>
|
||||
<h1 id="header_greet"></h1>
|
||||
</section>
|
||||
<section id="strategies">
|
||||
<h2 id="header_strategy"></h2>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="./assets/js/script.js" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user