add other pati.to services, upgrade gts

This commit is contained in:
Roberto Hidalgo 2024-11-20 18:34:28 -06:00
parent 54eb1ebd31
commit 4e236e0592
87 changed files with 11314 additions and 19 deletions

8
.gitignore vendored
View File

@ -1,3 +1,7 @@
.terraform.lock.hcl
private/*
dist/*
node_modules
.terraform
*.vars
.terraform.lock.hcl
*.tfstate
.jekyll-cache/*

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# pati.to
The best domain name on the whole internet.

2
services/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.terraform
.terraform.lock.hcl

2
services/.joao.yaml Normal file
View File

@ -0,0 +1,2 @@
vault: nidito
nameTemplate: pati.to:{{ DirName }}

View File

@ -1,12 +1,8 @@
_config: !!joao
vault: nidito
name: pati.to:club
smtp:
host: smtp.mailgun.org
port: 587
username: no-reply@mail.club.pati.to
password: !!secret
do_token: !!secret
cdn:
bucket: club-patito
endpoint: sjc1.vultrobjects.com

View File

@ -1,6 +1,3 @@
_config: !!joao
vault: none
name: none
description: activitypub server
docs:
- https://docs.gotosocial.org/en/latest/
@ -9,7 +6,7 @@ packages:
self:
image: superseriousbusiness/gotosocial
source: https://github.com/superseriousbusiness/gotosocial
version: "0.15.0"
version: "0.17.3"
check: github-releases
comparison: suffix
litestream:

View File

@ -4,11 +4,6 @@ terraform {
}
required_providers {
acme = {
source = "vancluever/acme"
version = "~> 2.15.1"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.29.0"
@ -39,10 +34,6 @@ locals {
}
}
provider "acme" {
server_url = "https://acme-v02.api.letsencrypt.org/directory"
}
// DO tokens for compute resources
data "vault_generic_secret" "DO" {
path = "cfg/infra/tree/provider:digitalocean"

View File

@ -0,0 +1,6 @@
description: Comisión nacional para el reconocimiento de imbéciles al volante
packages: {}
build: echo "build tbd"
deploy:
credentials: vault://nidito/deploy/cnriv.pati.to
src: ./src

73
services/cnriv/cnriv.tf Normal file
View File

@ -0,0 +1,73 @@
terraform {
backend "consul" {
path = "nidito/state/service/cnriv.pati.to"
}
required_providers {
consul = {
source = "hashicorp/consul"
version = "~> 2.21.0"
}
vault = {
source = "hashicorp/vault"
version = "~> 4.4.0"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.29.0"
}
}
required_version = ">= 1.0.0"
}
data "vault_generic_secret" "DO" {
path = "cfg/infra/tree/provider:digitalocean"
}
provider "digitalocean" {
token = data.vault_generic_secret.DO.data.patito
}
data "vault_generic_secret" "backblaze" {
path = "cfg/infra/tree/provider:backblaze"
}
data "terraform_remote_state" "rob_mx" {
backend = "consul"
workspace = "default"
config = {
datacenter = "casa"
path = "nidito/state/rob.mx"
}
}
resource "digitalocean_record" "to_pati_club" {
domain = "pati.to"
type = "A"
ttl = 3600
name = "cnriv"
value = data.terraform_remote_state.rob_mx.outputs.bernal.ip
}
resource "consul_keys" "cdn-config" {
datacenter = "qro0"
key {
path = "cdn/cnriv.pati.to"
value = jsonencode({
cert = "pati.to"
folder = "cnriv.pati.to"
nginx_config = file("${abspath(path.root)}/nginx.conf")
})
}
}
resource "vault_kv_secret" "deploy-config" {
path = "nidito/deploy/cnriv.pati.to"
data_json = jsonencode({
type = "ssh"
host = "bernal"
domain = "cnriv.pati.to"
})
}

View File

@ -0,0 +1,3 @@
location / {
index index.html;
}

View File

@ -0,0 +1,178 @@
body {
font: 16px/1em Helvetica, Arial, sans-serif;
}
#main {
max-width: 900px;
padding: 20px;
margin: 0 auto;
}
#principal {
margin-bottom: 20px;
}
#logo {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
p {
margin: 10px 0;
font-size: 18px;
font-family: 'georgia';
line-height: 1.3em;
}
.dl {
text-align: center;
}
.dl img {
display: block;
width: 100%;
margin: 0 auto;
max-width: 600px;
}
#stickers {
background: #e25826;
}
#stickers img {
border: 10px solid #eee;
box-shadow: 0px 0px 5px rgba(0,0,0,.5);
-webkit-transform: rotate(-5deg);
}
#stencil {
background: url(../img/bg-stencil.jpg);
background-attachment: fixed;
}
.button {
display: block;
background: #999;
border-radius:5px;
text-decoration: none;
font-size: 18px;
font-weight: bold;
text-transform: uppercase;
-border: 1px solid #000;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
border-radius: 5px;
max-width: 600px;
margin: 20px auto;
color: #ddd;
cursor: pointer;
display: block;
line-height: 1.714285714;
padding: 5px;
text-align: center;
text-shadow: 0 -1px 0 #333;
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#33000000', EndColorStr='#00000000'), glow(color=black,strength=2);
background-image: linear-gradient(bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.0) 100%);
background-image: -o-linear-gradient(bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.0) 100%);
background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.0) 100%);
background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,.2) 0%, rgba(0,0,0,0.0) 100%);
background-image: -ms-linear-gradient(bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.0) 100%);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#33000000', EndColorStr='#00000000')";
-webkit-box-shadow:
inset 0 1px 0 rgba(255,255,255, .5),
inset 0 -2px 8px rgba(0,0,0, 0.1),
0 1px 3px rgba(0,0,0, 0.1);
-moz-box-shadow:
inset 0 1px 0 rgba(255,255,255, .5),
inset 0 -2px 8px rgba(0,0,0, 0.1),
0 1px 3px rgba(0,0,0, 0.1);
-ms-box-shadow:
inset 0 1px 0 rgba(255,255,255, .5),
inset 0 -2px 8px rgba(0,0,0, 0.1),
0 1px 3px rgba(0,0,0, 0.1);
box-shadow:
inset 0 1px 0 rgba(255,255,255, .5),
inset 0 -2px 8px rgba(0,0,0, 0.1),
inset 0 0 2px rgba(0,0,0,.4),
0 1px 3px rgba(0,0,0, 0.5);
-webkit-transition: all 0.2s ease-in;
}
.button:hover {
color: #fff;
background-color: #666;
}
.button:active {
background-image: linear-gradient(bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.2) 100%);
background-image: -o-linear-gradient(bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.2) 100%);
background-image: -moz-linear-gradient(bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.2) 100%);
background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.2) 100%);
background-image: -ms-linear-gradient(bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.2) 100%);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#bbbbbb')";
-webkit-box-shadow:
inset 0 1px 0 rgba(255,255,255, .2),
inset 0 -2px 8px rgba(0,0,0, 0.1),
inset 0 1px 3px rgba(0,0,0, 0.4);
-moz-box-shadow:
inset 0 1px 0 rgba(255,255,255, .2),
inset 0 -2px 8px rgba(0,0,0, 0.1),
inset 0 1px 3px rgba(0,0,0, 0.4);
-ms-box-shadow:
inset 0 1px 0 rgba(255,255,255, .2),
inset 0 -2px 8px rgba(0,0,0, 0.1),
inset 0 1px 3px rgba(0,0,0, 0.4);
box-shadow:
inset 0 1px 0 rgba(255,255,255, .2),
inset 0 -2px 8px rgba(0,0,0, 0.1),
inset 0 1px 3px rgba(0,0,0, 0.4);
}
footer {
background: #333;
color: #999;
text-align: center;
padding: 20px 0;
border-top: 1px solid #555;
}
footer a {
color: #999;
text-decoration: none;
}
footer a:hover {
color: #eee;
}
@media (max-width:600px) {
#main {
padding: 20px 0;
}
#main section {
padding: 20px;
}
#stickers img {
border: 10px solid #eee;
box-shadow: 0px 0px 5px rgba(0,0,0,.5);
width: 80%;
-webkit-transform: rotate(-5deg);
}
.button {
width: 80%;
}
}

View File

@ -0,0 +1,109 @@
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
body {
line-height:1;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section,
div,h1,h2,h3,h4,h5,h6,p,blockquote,pre {
display:block;
}
nav ul {
list-style:none;
}
blockquote, q {
quotes:none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
a {
margin:0;
padding:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
/* change colours to suit your needs */
ins {
background-color:#ff9;
color:#000;
text-decoration:none;
}
/* change colours to suit your needs */
mark {
background-color:#ff9;
color:#000;
font-style:italic;
font-weight:bold;
}
del {
text-decoration: line-through;
}
abbr[title], dfn[title] {
border-bottom:1px dotted;
cursor:help;
}
table {
border-collapse:collapse;
border-spacing:0;
}
/* change border colour to suit your needs */
hr {
display:block;
height:1px;
border:0;
border-top:1px solid #cccccc;
margin:1em 0;
padding:0;
}
input, select {
vertical-align:middle;
}
:focus {
outline: none;
}
ol, ul {
list-style: none;
}
.clear { clear: both; float: none; }
.clearfix::after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width" />
<title>Campaña Nacional para el Reconocimiento de Imbéciles al Volante</title>
<meta property="og:description" content="Súmate la campaña de reconocimiento de imbéciles al volante!" />
<meta property="og:title" content="Campaña Nacional para el Reconocimiento de Imbéciles al Volante" />
<meta property="fb:admins" content="792675081" />
<meta property="og:image" content="http://cnriv.pati.to/img/og-icon.png" />
<meta property="og:url" content="http://cnriv.pati.to" />
<meta itemprop="name" content="Campaña Nacional para el Reconocimiento de Imbéciles al Volante" />
<meta itemprop="description" content="Súmate la campaña de reconocimiento de imbéciles al volante!" />
<meta itemprop="image" content="http://cnriv.pati.to/img/og-icon.png" />
<link rel="stylesheet" href="./css/reset.css" />
<link rel="stylesheet" href="./css/main.css" />
<link rel="shortcut icon" href="./favicon.png" />
</head>
<body>
<div id="main">
<section id="principal">
<div id="logo">
<img src="img/logo.png" width="100%" alt="Campaña Nacional para el Reconocimiento de Imbéciles al Volante"/>
</div>
<p>Se nos cierran a diario, tocan el claxon como maniáticos, se estacionan ejemplificando los daños de una lobotomía e ignoran al resto de los seres que no conducen toneladas de acero por las calles de tu ciudad.</p>
<p>Es nuestro deber civil reconocerlos por sus acciones, súmate a la campaña descargando los stickers y el stencil, y comienza a reconocer la descerebrada labor del imbécil al volante que tienes en tu vida.</p>
</section>
</div>
<section id="stickers" class="dl clearfix">
<img src="img/etiqueta.png" alt="descarga las etiquetas" />
<a href="etiquetas.pdf" class="button">Descarga los stickers</a>
</section>
<section id="stencil" class="dl clearfix">
<img src="img/stencil.png" alt="descarga el stencil" />
<a href="stencil.pdf" class="button">Descarga el stencil</a>
</section>
<footer>
<a href="http://rob.mx">Cortesía de un Rob</a>
</footer>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-37922009-2', 'cnriv.pati.to');
ga('send', 'pageview');
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

107
services/fichitas/compile.rb Executable file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env ruby
require 'sass'
require 'sprockets'
require 'listen'
absPath = File.expand_path File.dirname(__FILE__)
$root = "#{absPath}/assets/src"
$dst = "#{absPath}/assets"
$sprockets = Sprockets::Environment.new($root)
$sprockets.append_path("#{$root}/scripts")
$sprockets.append_path("#{$root}/scss")
$sprockets.css_compressor = :scss
def type path
return :js if path =~ /(js|coffee)/
return :css
end
def pathFor str
folder = type(str) == :js ? 'scripts' : 'css';
return str.gsub("#{$root}/#{folder}/", '')
end
#str = '/Users/rob/Sites/fichitas/assets/src/scss/_buttons.scss'
#p pathFor str
#assets = $sprockets.find_asset(pathFor str)
#puts "#{$dst}/#{type str}/"
#p assets
#File.open("")
#assets.write_to "#{$dst}/#{type str}/"
#=begin
begin
listener = Listen.to("#{$root}", debug: true) do |mod, add, del|
del.each do |d|
File.unlink "#{$dst}/#{type del}/#{pathFor del}"
end
mod.each do |f|
folder = type f
err = nil
if folder==:js
dest = "#{$dst}/js/main.js"
begin
asset = $sprockets.find_asset("main.coffee")
rescue Exception => e
err = e
end
else
dest = "#{$dst}/css/main.css"
begin
asset = $sprockets.find_asset("main.css")
rescue Exception => e
err = e
end
end
puts dest
if err
puts err
else
File.open(dest, 'w+') do |file|
file << asset.to_s
end
end
end
add.each do |f|
folder = type f
err = nil
if folder==:js
dest = "#{$dst}/js/main.js"
begin
asset = $sprockets.find_asset("main.coffee")
rescue Exception => e
err = e
end
else
dest = "#{$dst}/css/main.css"
begin
asset = $sprockets.find_asset("main.css")
rescue Exception => e
err = e
end
end
puts dest
if err
puts err
else
File.open(dest, 'w+') do |file|
file << asset.to_s
end
end
end
end
listener.start
sleep
rescue Exception
exit
end
#=end

View File

@ -0,0 +1,9 @@
description: para el dominó
packages:
self:
version: 0.0.0
source: https://github.com/unRob/fichitas
build: echo "build tbd"
deploy:
credentials: vault://nidito/deploy/fichitas.pati.to
src: ./src

View File

@ -0,0 +1,69 @@
terraform {
backend "consul" {
path = "nidito/state/service/fichitas.pati.to"
}
required_providers {
consul = {
source = "hashicorp/consul"
version = "~> 2.21.0"
}
vault = {
source = "hashicorp/vault"
version = "~> 4.4.0"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.29.0"
}
}
required_version = ">= 1.0.0"
}
data "vault_generic_secret" "DO" {
path = "cfg/infra/tree/provider:digitalocean"
}
provider "digitalocean" {
token = data.vault_generic_secret.DO.data.patito
}
data "terraform_remote_state" "rob_mx" {
backend = "consul"
workspace = "default"
config = {
datacenter = "casa"
path = "nidito/state/rob.mx"
}
}
resource "digitalocean_record" "service" {
domain = "pati.to"
type = "A"
ttl = 3600
name = "fichitas"
value = data.terraform_remote_state.rob_mx.outputs.bernal.ip
}
resource "consul_keys" "cdn-config" {
datacenter = "qro0"
key {
path = "cdn/fichitas.pati.to"
value = jsonencode({
cert = "pati.to"
folder = "fichitas.pati.to"
nginx_config = file("${abspath(path.root)}/nginx.conf")
})
}
}
resource "vault_kv_secret" "deploy-config" {
path = "nidito/deploy/fichitas.pati.to"
data_json = jsonencode({
type = "ssh"
host = "bernal"
domain = "fichitas.pati.to"
})
}

View File

@ -0,0 +1,3 @@
location / {
index index.html;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,326 @@
html, body {
height: 100%;
}
body {
font: bold 16px/1em 'Futura Std', Futura, sans-serif;
background: #0d0d0d -webkit-linear-gradient(rgba(255,255,255,.8), rgba(255,255,255,.7));
color: #fff;
text-align: center;
text-transform: uppercase;
}
h1, h2, h3, h4 {
text-shadow: 1px 1px 0px #fff;
}
h1 {
font-size: 36px;
line-height: 2em;
color: #333;
}
h2 {
color: #666;
font-size: 24px;
}
#max-puntos {
position: absolute;
opacity: 0;
}
#container {
padding: 1%;
height: 100%;
-webkit-box-sizing: border-box;
max-width: 480px;
overflow: hidden;
margin: 0 auto;
}
.separador {
background: url(/assets/img/separador.png) center center no-repeat;
width: 170px;
height: 20px;
margin: 0 auto;
-webkit-box-orient: vertical;
}
.separador-large {
background: url(/assets/img/separador-large.png) center center no-repeat;
min-width: 300px;
max-height: 35px;
display: table;
position: relative;
max-width: 480px;
}
.separador-large p {
vertical-align: middle;
color: #333;
text-align: center;
line-height: 40px;
}
.patote {
background: url(/assets/img/patote.png) no-repeat;
width: 75px;
height: 75px;
margin: auto;
}
.action-small {
font-size: 12px;
letter-spacing: 1%;
color: #fff;
text-decoration: none;
display: block;
}
#jugadores {
color: #ccc;
font-size:24px;
line-height: 1.5em;
}
.card {
display: -webkit-box;
-webkit-box-orient: vertical;
height: 100%;
-webkit-transition: all .5s ease-in;
-webkit-backface-visibility: hidden;
}
.card-part {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 1;
-webkit-box-pack: center;
}
.flex {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 1;
-webkit-box-pack: center;
}
.top {
-webkit-box-pack: start;
}
.bottom {
-webkit-box-pack: end;
}
.card-upper {
-webkit-box-pack: end;
}
.card-bottom {
padding-top: 1%;
-webkit-box-pack: start;
}
.first-h2 {
margin-top: 2%;
}
.score {
font-size:60px;
line-height: 1em;
}
#resultado {
position:absolute;
top: 0px;
left: 0px;
width: 100%;
padding: 10px;
background: #222;
-webkit-box-sizing: border-box;
}
#resultado h1, #resultado h2 {
color: #fff;
text-shadow: -1px -1px 0px #000;
}
.flip {
-webkit-transform: rotateX(180deg);
-webkit-transition: all .5s ease-in;
}
#resultado .score {
text-align: center;
background: rgba(255,255,255,.5);
border: none;
font: inherit;
color: #fff;
font-size: 60px;
font-family: inherit;
width: 3em;
border-radius: 5px;
padding: 0;
}
::-webkit-input-placeholder {
line-height: 1.3em;
}
button {
color: #393B47;
font-family: inherit;
text-transform: uppercase;
font-weight: bold;
font-size: 0.875em;
line-height: 1.2;
text-align: center;
text-decoration: none;
text-shadow: 0 1px 0 #fff;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
-ms-border-radius: 2px;
-o-border-radius: 2px;
border-radius: 5px;
width: 100%;
max-width: 400px;
padding: 10px;
margin: 0 auto 10px;
background: #5499CE;
border-color: #2F6893;
color: #fff;
font-size: 1em;
text-shadow: 0 1px 2px #2f6893;
-webkit-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-moz-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-ms-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-o-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
cursor: pointer;
-webkit-transition: 0.25s ease all;
-moz-transition: 0.25s ease all;
-ms-transition: 0.25s ease all;
-o-transition: 0.25s ease all;
transition: 0.25s ease all;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
button:hover, .boton:hover {
border-color: #aaa;
}
button:active,
.boton:active,
button.guardar:active,
.boton.guardar:active,
button.borrar:active,
.boton.borrar:active {
-webkit-box-shadow:
inset 0 2px 5px rgba(0,0,0, 0.5),
0 1px 0 #fff;
-moz-box-shadow:
inset 0 2px 5px rgba(0,0,0, 0.5),
0 1px 0 #fff;
-ms-box-shadow:
inset 0 2px 5px rgba(0,0,0, 0.5),
0 1px 0 #fff;
-o-box-shadow:
inset 0 2px 5px rgba(0,0,0, 0.5),
0 1px 0 #fff;
box-shadow:
inset 0 2px 5px rgba(0,0,0, 0.5),
0 1px 0 #fff;
}
button,
.boton {
width: 100%;
max-width: 400px;
padding: 10px;
margin: 0 auto 10px;
background: #5499CE;
border-color: #2F6893;
color: #fff;
font-size: 1em;
text-shadow: 0 1px 2px #2f6893;
-webkit-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-moz-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-ms-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
-o-box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
box-shadow:
inset 0 1px 0 rgba(255,255,255, 0.2),
inset 0 -2px 10px rgba(0,0,0, 0.05),
0 1px 0 #fff;
}
button.guardar:hover,
.boton.guardar:hover {
background: #2379B9;
border-color: #2F6893;
}
.scores-list { text-align: left; max-width: 300px; margin: 0 auto;}
.scores-list li {
display: inline-block;
color: #aaa;
text-align: center;
min-width: 10%;
font-weight: normal;
border-right: 1px solid rgba(255,255,255,.1);
}
.hidden {
display: none;
}
input.hidden {
display: block;
position: absolute;
left: -999em;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,170 @@
/*global $,jQuery,window,console,confirm,alert*/
var storage = {
ls: window.localStorage,
callback: null,
set: function(k,v) {
var ahora = JSON.stringify(v);
this.ls[k]=ahora;
if (storage.callback) {
storage.callback({key:k,newValue:v});
}
return v;
},
get: function(k) {
var v = null;
try {
v = JSON.parse(this.ls[k]);
} catch (err) {
console.error(err);
}
return v;
},
change: function(cb) {
storage.callback = cb;
}
};
$(function(){
$('#no-webapp').hide();
var juegos_numerados = ['primer', 'segundo', 'tercer', 'cuarto', 'quinto', 'sexto', 'séptimo', 'octavo', 'noveno'];
var cb = {
juego: function(actual) {
var t = '';
if (juegos_numerados[actual]) {
t = juegos_numerados[actual]+' juego';
} else {
t = actual+1;
t = "juego "+t;
}
$('#numero-juego').text(t);
},
score: function(id, score) {
var newScore = score < 9? '0'+score : score;
$('#'+id).text(newScore);
},
table: function(id, scores) {
if (scores.length === 0) {
$('#'+id).html('');
} else {
var html = "";
scores.forEach(function(i){
html += "<li>"+i+"</li>";
});
$('#'+id).html(html);
}
}
};
var setupGame = function(){
storage.set('numero-juego', 0);
};
if (!storage.get('setup')) {
storage.set('numero-juego', null);
storage.set('player-1', null);
storage.set('player-2', null);
storage.set('scores-player-1', []);
storage.set('scores-player-2', []);
storage.set('setup', true);
}
if (storage.get('numero-juego') > 0){
cb.juego(storage.get('numero-juego'));
cb.score('player-1', storage.get('player-1'));
cb.score('player-2', storage.get('player-2'));
cb.table('scores-player-1', storage.get('scores-player-1'));
cb.table('scores-player-2', storage.get('scores-player-2'));
}
storage.change(function(evt){
var key = evt.key;
var ahora = evt.newValue;
//var antes = evt.oldValue;
switch (key) {
case 'numero-juego':
cb.juego(ahora);
if (ahora === 0) {
storage.set('player-1', 0);
storage.set('player-2', 0);
storage.set('scores-player-1', []);
storage.set('scores-player-2', []);
}
break;
case 'player-1':
case 'player-2':
cb.score(key, ahora);
if (ahora >= 100) {
var mensaje = key==='player-1' ? 'Perdimos :(' : 'Ganamos!';
mensaje += ' Jugamos otra vez?';
if (confirm(mensaje)) {
setupGame();
}
}
break;
case 'scores-player-1':
case 'scores-player-2':
cb.table(key, ahora);
break;
}
}, false);
$('#activa-resultados').click(function(evt){
$('#juego').addClass('flip');
$('#resultado').removeClass('flip');
});
$('#guardar').click(function(evt){
evt.preventDefault();
var p1 = Number($('#score-player-1').val());
var p2 = Number($('#score-player-2').val());
if (p1 === p2 && p1 === 0) {
return false;
}
var tp1 = storage.get('player-1');
var tp2 = storage.get('player-2');
var sp1 = storage.get('scores-player-1');
var sp2 = storage.get('scores-player-2');
sp1.push(p1);
sp2.push(p2);
storage.set('scores-player-1', sp1);
storage.set('scores-player-2', sp2);
storage.set('player-1', tp1+p1);
storage.set('player-2', tp2+p2);
storage.set('numero-juego', storage.get('numero-juego')+1);
$('#juego').removeClass('flip');
$('#resultado').addClass('flip');
$('#score-player-1').val('');
$('#score-player-2').val('');
});
$('#cancelar').click(function(evt){
evt.preventDefault();
$('#juego').removeClass('flip');
$('#resultado').addClass('flip');
});
$('.activa-input').click(function(evt){
evt.preventDefault();
var id = $(this).data('input');
console.log(id);
$(id).focus();
$(id).click();
});
$('#terminar').click(function(evt){
evt.preventDefault();
if (confirm('Seguro?')) {
setupGame();
}
});
});

View File

@ -0,0 +1,30 @@
Config = ()->
@ds = window.localStorage;
@callbacks = {}
Config.instance = this
this
Config.instance = null
Config::on = (key, cb)->
@callbacks[key] = @callbacks[key] || [cb]
true
Config::set = (key, value)->
@ds.setItem(key, value);
evt_cbs = @callbacks[key] || []
for cb in evt_cbs
cb(value)
value
Config::unset = (key)->
@ds.removeItem(key)
evt_cbs = @callbacks[key] || []
for cb in evt_cbs
cb(key)
key
Config::get = (key)->
@ds.getItem(key);
window.Config = Config;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,129 @@
Game = (config)->
@players = {}
$('#players').empty();
playerCount = (config.get('players.qty') || 2)
console.log playerCount
@playerCount = parseInt(playerCount)
for id in [1..@playerCount]
id = id
p = new Player(id)
p.name = config.get("players.player.#{id}.name") || @defaultName(id, @playerCount)
p.scores = JSON.parse(config.get("players.player.#{id}.scores")) || []
@players[id] = p
$('#players').append(p.dom())
@maxPoints = ()->
parseInt config.get('game.maxPoints'), 10
$('#players').attr('class', '').addClass("player-count-#{@playerCount}")
window.refreshPlayersUI()
@started = config.get('game.started') || false
@gameNo = config.get('game.number') || 0
@gameNo = parseInt(@gameNo)
Game.instance = this
this
Game.numbers = ['primer', 'segundo', 'tercer', 'cuarto', 'quinto', 'sexto', 'séptimo', 'octavo', 'noveno'];
Game.instance = null
Game::restart = ()->
Config.instance.unset('game.started')
Config.instance.set('game.number', '0')
@gameNo = 0 #porque se caga muy duro si no
for id, player of @players
Config.instance.unset("players.player.#{id}.scores")
game = new Game(Config.instance)
Game.instance = game
game
Game::player = (id)->
@players[id]
Game::addPoints = (id, points)->
if !@started
@started = true
Config.instance.set('game.started', true)
player = @player(id)
player.addPoints(points)
if player.score() >= @maxPoints()
console.log("lost", player.score(), @maxPoints())
@started = false
name = "#{player.name} "
verbo = 'perdió'
verbo = 'perdimos' if player.name.match(/nosotros/i)
if player.name.match(/ustedes/i)
verbo = 'Ganamos'
name = ""
return @ended("#{name}#{verbo}")
if @playerCount == 2
oid = if id == 1 then 2 else 1
other = @player(oid);
other.addPoints(0)
@next()
return this
Game::ended = (phrase)->
if (confirm("¡#{phrase}! ¿Volvemos a jugar?"))
return @restart()
this
Game::defaultName = (id, count)->
if (count == 2)
return "Nosotros" if id==1
return "Ustedes" if id==2
else
return "Jugador #{id}"
Game::next = ()->
@gameNo += 1
config = Config.instance
config.set('game.number', @gameNo)
for id, player of @players
config.set("players.player.#{player.id}.scores", JSON.stringify(player.scores))
return this
Game::name = ()->
if str = Game.numbers[@gameNo]
return "#{str} juego"
else
return "Juego #{@gameNo+1}"
window.Game = Game;
##########
# Player #
##########
Player = (id)->
@id = id;
@name = "Jugador #{@id}"
@scores = []
this
Player.template = Mustache.compile($('#player-tpl').get(0).innerText);
Player::addPoints = (points)->
@scores.push(points)
Player::score = ()->
s = 0;
for score in @scores
s+=score
str = s.toString()
str = "0#{str}" while (str.length < 2)
str
Player::renderScore = ()->
(nil, r)=>
r(@score())
Player::lost = (qty)->
Player::dom = ()->
Player.template(this)

View File

@ -0,0 +1,98 @@
$ ()->
scrollTO = null
config = Config.instance
isNumber = (n)->
!isNaN(parseInt(n)) && isFinite(n);
finishScroll = (el)->
return false if el.hasClass('animating')
clearTimeout scrollTO
sl = el.scrollLeft()
w = el.width()
pc = Math.round sl/w*100;
if pc >= 50
to = w
fn = 'focus'
else
to = 0
fn = 'blur'
falta = (100-pc)/100;
el.addClass('animating');
el.animate({scrollLeft: to}, (300*falta), ()->
el.removeClass('active animating')
el.find('input')[fn]();
)
scrollFn = (evt)->
el = $(this)
el.addClass('active')
clearTimeout scrollTO
pc = Math.round el.scrollLeft()/el.width()*100;
el.css('background', "rgba(242,242,242,#{pc/100})")
fs = ()->
finishScroll(el)
scrollTO = setTimeout fs, 100
true
changeName = (evt)->
el = $(this)
newName = prompt("¿Cómo se llama este jugador?", el.text())
newName = $.trim(newName)
id = el.parent().attr('id').replace('player-', '');
if newName==''
newName = Game.instance.defaultName(id, Game.instance.playerCount)
config.unset("players.player.#{id}.name");
else
config.set("players.player.#{id}.name", newName);
el.text(newName);
sumaPlayer = (evt)->
evt.preventDefault()
target = $($(this).data('modifies'))
val = target.val()
return false if !isNumber(val)
target.siblings('.suma').append("<li>#{val}</li>")
target.val('').focus();
guardaPlayer = (evt)->
evt.preventDefault()
target = $($(this).data('modifies'))
id = parseInt(target.attr('id').replace(/\D+/, ''), 10);
el = $(this).parents('.player')
val = parseInt(target.val(), 10);
fichas = el.find('.suma li')
if (fichas.length > 0)
fichas.each (i, ficha)->
val += parseInt(ficha.innerText, 10)
el.find('.suma').remove()
target.blur();
el.addClass('active animating').animate({scrollLeft: 0}, 300, ()->
setTimeout(()->
el.removeClass('active animating')
, 500)
)
target.val('');
window.addScore id, (val||0)
window.refreshPlayersUI = ()->
$('.player:not(.animating)').off 'scroll'
$('.player-name').off 'click'
$('.player:not(.animating)').on 'scroll', scrollFn
$('.player-name').on 'click', changeName
$('.player-add').on 'click', sumaPlayer
$('.player-save').on 'click', guardaPlayer
$('.suma').on 'click', 'li', ()->
$(this).remove();
$('#players').on 'click', '.score', (evt)->
evt.preventDefault();
el = $(this).parents('.player')
el.addClass('active animating').animate({scrollLeft: el.width()}, 300, ()->
el.removeClass('active animating')
)

View File

@ -0,0 +1,102 @@
#= require jquery
#= require mustache
#= require config
#= require juego
#= require juego.ui
config = new Config
$ ()->
screens =
config: $('#config')
game: $('#game')
cfg =
jugadores: $('#jugadores'),
puntos: $('#puntos')
game = new Game(config)
$('#game-name').text(game.name())
config.on 'game.number', ()->
$('#game-name').text(game.name())
window.saveConfig = (check=true)->
console.log "saving config"
if (check && (parseInt(cfg.jugadores.val(),10) != game.playerCount) && game.started)
console.log "checking"
if (!confirm('Hay un juego en curso, deseas comenzar uno nuevo?'))
cfg.jugadores.val(config.get('players.qty'));
return false;
console.log "changing player qty"
config.set 'players.qty', parseInt(cfg.jugadores.val(), 10)
console.log "config saved"
config.set 'game.maxPoints', parseInt(cfg.puntos.val(), 10)
window.addScore = (player, points)->
game = game.addPoints(player, points);
if game.started
$("#player-#{player} .score").text(game.player(player).score());
cfg.jugadores.val(config.get('players.qty')||2);
cfg.puntos.val(config.get('game.maxPoints')||100);
config.on 'players.qty', (ahora, antes)->
game = game.restart()
$('#game-name').text(game.name())
$('[role=action-button]').on 'click', (evt)->
evt.preventDefault();
el = $(this)
target = $(el.data('modifies'))
action = el.data('action')
val = target.attr('step') || target.val();
val = parseInt(val, 10)
newVal = parseInt(target.val(), 10)
max = target.attr('max') && parseInt(target.attr('max'), 10) || false
min = target.attr('min') && parseInt(target.attr('min'), 10) || false
switch action
when 'plus'
newVal += val
return false if max && newVal > max
when 'minus'
newVal -= parseInt(val, 10)
return false if min && newVal < min
when 'ok'
return false;
target.val(newVal);
$('#restart-game').on 'click', (evt)->
evt.preventDefault();
if (confirm "Seguro que deseas volver a empezar?")
window.saveConfig(false)
game = game.restart()
$('#game-name').text(game.name())
screens.config.removeClass('shown')
screens.game.addClass('shown')
$('#finish-game').on 'click', (evt)->
evt.preventDefault();
max = 0
for id, player of game.players
count = player.scores.length
max = count if max < count
for id, player of game.players
if player.scores.length < max
player.addPoints(0)
game = game.next()
$('.nav-icon').on 'click', (evt)->
evt.preventDefault();
visible = $('.screen.shown')
invisible = $('.screen:not(.shown)')
visible.removeClass('shown');
invisible.addClass('shown');
if (cb = $(this).data('action'))
window[cb]();
return false

View File

@ -0,0 +1,537 @@
/*
* mustache.js - Logic-less {{mustache}} templates with JavaScript
* http://github.com/janl/mustache.js
*/
/*global define: false*/
(function (root, factory) {
if (typeof exports === "object" && exports) {
factory(exports); // CommonJS
} else {
var mustache = {};
factory(mustache);
if (typeof define === "function" && define.amd) {
define(mustache); // AMD
} else {
root.Mustache = mustache; // <script>
}
}
}(this, function (mustache) {
var whiteRe = /\s*/;
var spaceRe = /\s+/;
var nonSpaceRe = /\S/;
var eqRe = /\s*=/;
var curlyRe = /\s*\}/;
var tagRe = /#|\^|\/|>|\{|&|=|!/;
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
// See https://github.com/janl/mustache.js/issues/189
var RegExp_test = RegExp.prototype.test;
function testRegExp(re, string) {
return RegExp_test.call(re, string);
}
function isWhitespace(string) {
return !testRegExp(nonSpaceRe, string);
}
var Object_toString = Object.prototype.toString;
var isArray = Array.isArray || function (obj) {
return Object_toString.call(obj) === '[object Array]';
};
function escapeRegExp(string) {
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
function escapeHtml(string) {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
function Scanner(string) {
this.string = string;
this.tail = string;
this.pos = 0;
}
/**
* Returns `true` if the tail is empty (end of string).
*/
Scanner.prototype.eos = function () {
return this.tail === "";
};
/**
* Tries to match the given regular expression at the current position.
* Returns the matched text if it can match, the empty string otherwise.
*/
Scanner.prototype.scan = function (re) {
var match = this.tail.match(re);
if (match && match.index === 0) {
this.tail = this.tail.substring(match[0].length);
this.pos += match[0].length;
return match[0];
}
return "";
};
/**
* Skips all text until the given regular expression can be matched. Returns
* the skipped string, which is the entire tail if no match can be made.
*/
Scanner.prototype.scanUntil = function (re) {
var match, pos = this.tail.search(re);
switch (pos) {
case -1:
match = this.tail;
this.pos += this.tail.length;
this.tail = "";
break;
case 0:
match = "";
break;
default:
match = this.tail.substring(0, pos);
this.tail = this.tail.substring(pos);
this.pos += pos;
}
return match;
};
function Context(view, parent) {
this.view = view || {};
this.parent = parent;
this._cache = {};
}
Context.make = function (view) {
return (view instanceof Context) ? view : new Context(view);
};
Context.prototype.push = function (view) {
return new Context(view, this);
};
Context.prototype.lookup = function (name) {
var value = this._cache[name];
if (!value) {
if (name == '.') {
value = this.view;
} else {
var context = this;
while (context) {
if (name.indexOf('.') > 0) {
value = context.view;
var names = name.split('.'), i = 0;
while (value && i < names.length) {
value = value[names[i++]];
}
} else {
value = context.view[name];
}
if (value != null) break;
context = context.parent;
}
}
this._cache[name] = value;
}
if (typeof value === 'function') value = value.call(this.view);
return value;
};
function Writer() {
this.clearCache();
}
Writer.prototype.clearCache = function () {
this._cache = {};
this._partialCache = {};
};
Writer.prototype.compile = function (template, tags) {
var fn = this._cache[template];
if (!fn) {
var tokens = mustache.parse(template, tags);
fn = this._cache[template] = this.compileTokens(tokens, template);
}
return fn;
};
Writer.prototype.compilePartial = function (name, template, tags) {
var fn = this.compile(template, tags);
this._partialCache[name] = fn;
return fn;
};
Writer.prototype.getPartial = function (name) {
if (!(name in this._partialCache) && this._loadPartial) {
this.compilePartial(name, this._loadPartial(name));
}
return this._partialCache[name];
};
Writer.prototype.compileTokens = function (tokens, template) {
var self = this;
return function (view, partials) {
if (partials) {
if (typeof partials === 'function') {
self._loadPartial = partials;
} else {
for (var name in partials) {
self.compilePartial(name, partials[name]);
}
}
}
return renderTokens(tokens, self, Context.make(view), template);
};
};
Writer.prototype.render = function (template, view, partials) {
return this.compile(template)(view, partials);
};
/**
* Low-level function that renders the given `tokens` using the given `writer`
* and `context`. The `template` string is only needed for templates that use
* higher-order sections to extract the portion of the original template that
* was contained in that section.
*/
function renderTokens(tokens, writer, context, template) {
var buffer = '';
var token, tokenValue, value;
for (var i = 0, len = tokens.length; i < len; ++i) {
token = tokens[i];
tokenValue = token[1];
switch (token[0]) {
case '#':
value = context.lookup(tokenValue);
if (typeof value === 'object') {
if (isArray(value)) {
for (var j = 0, jlen = value.length; j < jlen; ++j) {
buffer += renderTokens(token[4], writer, context.push(value[j]), template);
}
} else if (value) {
buffer += renderTokens(token[4], writer, context.push(value), template);
}
} else if (typeof value === 'function') {
var text = template == null ? null : template.slice(token[3], token[5]);
value = value.call(context.view, text, function (template) {
return writer.render(template, context);
});
if (value != null) buffer += value;
} else if (value) {
buffer += renderTokens(token[4], writer, context, template);
}
break;
case '^':
value = context.lookup(tokenValue);
// Use JavaScript's definition of falsy. Include empty arrays.
// See https://github.com/janl/mustache.js/issues/186
if (!value || (isArray(value) && value.length === 0)) {
buffer += renderTokens(token[4], writer, context, template);
}
break;
case '>':
value = writer.getPartial(tokenValue);
if (typeof value === 'function') buffer += value(context);
break;
case '&':
value = context.lookup(tokenValue);
if (value != null) buffer += value;
break;
case 'name':
value = context.lookup(tokenValue);
if (value != null) buffer += mustache.escape(value);
break;
case 'text':
buffer += tokenValue;
break;
}
}
return buffer;
}
/**
* Forms the given array of `tokens` into a nested tree structure where
* tokens that represent a section have two additional items: 1) an array of
* all tokens that appear in that section and 2) the index in the original
* template that represents the end of that section.
*/
function nestTokens(tokens) {
var tree = [];
var collector = tree;
var sections = [];
var token;
for (var i = 0, len = tokens.length; i < len; ++i) {
token = tokens[i];
switch (token[0]) {
case '#':
case '^':
sections.push(token);
collector.push(token);
collector = token[4] = [];
break;
case '/':
var section = sections.pop();
section[5] = token[2];
collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
break;
default:
collector.push(token);
}
}
return tree;
}
/**
* Combines the values of consecutive text tokens in the given `tokens` array
* to a single token.
*/
function squashTokens(tokens) {
var squashedTokens = [];
var token, lastToken;
for (var i = 0, len = tokens.length; i < len; ++i) {
token = tokens[i];
if (token) {
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
lastToken[1] += token[1];
lastToken[3] = token[3];
} else {
lastToken = token;
squashedTokens.push(token);
}
}
}
return squashedTokens;
}
function escapeTags(tags) {
return [
new RegExp(escapeRegExp(tags[0]) + "\\s*"),
new RegExp("\\s*" + escapeRegExp(tags[1]))
];
}
/**
* Breaks up the given `template` string into a tree of token objects. If
* `tags` is given here it must be an array with two string values: the
* opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
* course, the default is to use mustaches (i.e. Mustache.tags).
*/
function parseTemplate(template, tags) {
template = template || '';
tags = tags || mustache.tags;
if (typeof tags === 'string') tags = tags.split(spaceRe);
if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
var tagRes = escapeTags(tags);
var scanner = new Scanner(template);
var sections = []; // Stack to hold section tokens
var tokens = []; // Buffer to hold the tokens
var spaces = []; // Indices of whitespace tokens on the current line
var hasTag = false; // Is there a {{tag}} on the current line?
var nonSpace = false; // Is there a non-space char on the current line?
// Strips all whitespace tokens array for the current line
// if there was a {{#tag}} on it and otherwise only space.
function stripSpace() {
if (hasTag && !nonSpace) {
while (spaces.length) {
delete tokens[spaces.pop()];
}
} else {
spaces = [];
}
hasTag = false;
nonSpace = false;
}
var start, type, value, chr, token;
while (!scanner.eos()) {
start = scanner.pos;
// Match any text between tags.
value = scanner.scanUntil(tagRes[0]);
if (value) {
for (var i = 0, len = value.length; i < len; ++i) {
chr = value.charAt(i);
if (isWhitespace(chr)) {
spaces.push(tokens.length);
} else {
nonSpace = true;
}
tokens.push(['text', chr, start, start + 1]);
start += 1;
// Check for whitespace on the current line.
if (chr == '\n') stripSpace();
}
}
// Match the opening tag.
if (!scanner.scan(tagRes[0])) break;
hasTag = true;
// Get the tag type.
type = scanner.scan(tagRe) || 'name';
scanner.scan(whiteRe);
// Get the tag value.
if (type === '=') {
value = scanner.scanUntil(eqRe);
scanner.scan(eqRe);
scanner.scanUntil(tagRes[1]);
} else if (type === '{') {
value = scanner.scanUntil(new RegExp('\\s*' + escapeRegExp('}' + tags[1])));
scanner.scan(curlyRe);
scanner.scanUntil(tagRes[1]);
type = '&';
} else {
value = scanner.scanUntil(tagRes[1]);
}
// Match the closing tag.
if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
token = [type, value, start, scanner.pos];
tokens.push(token);
if (type === '#' || type === '^') {
sections.push(token);
} else if (type === '/') {
// Check section nesting.
if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
var openSection = sections.pop();
if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
} else if (type === 'name' || type === '{' || type === '&') {
nonSpace = true;
} else if (type === '=') {
// Set the tags for the next time around.
tags = value.split(spaceRe);
if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
tagRes = escapeTags(tags);
}
}
// Make sure there are no open sections when we're done.
var openSection = sections.pop();
if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
tokens = squashTokens(tokens);
return nestTokens(tokens);
}
mustache.name = "mustache.js";
mustache.version = "0.7.2";
mustache.tags = ["{{", "}}"];
mustache.Scanner = Scanner;
mustache.Context = Context;
mustache.Writer = Writer;
mustache.parse = parseTemplate;
// Export the escaping function so that the user may override it.
// See https://github.com/janl/mustache.js/issues/244
mustache.escape = escapeHtml;
// All Mustache.* functions use this writer.
var defaultWriter = new Writer();
/**
* Clears all cached templates and partials in the default writer.
*/
mustache.clearCache = function () {
return defaultWriter.clearCache();
};
/**
* Compiles the given `template` to a reusable function using the default
* writer.
*/
mustache.compile = function (template, tags) {
return defaultWriter.compile(template, tags);
};
/**
* Compiles the partial with the given `name` and `template` to a reusable
* function using the default writer.
*/
mustache.compilePartial = function (name, template, tags) {
return defaultWriter.compilePartial(name, template, tags);
};
/**
* Compiles the given array of tokens (the output of a parse) to a reusable
* function using the default writer.
*/
mustache.compileTokens = function (tokens, template) {
return defaultWriter.compileTokens(tokens, template);
};
/**
* Renders the `template` with the given `view` and `partials` using the
* default writer.
*/
mustache.render = function (template, view, partials) {
return defaultWriter.render(template, view, partials);
};
// This is here for backwards compatibility with 0.4.x.
mustache.to_html = function (template, view, partials, send) {
var result = mustache.render(template, view, partials);
if (typeof send === "function") {
send(result);
} else {
return result;
}
};
}));

View File

@ -0,0 +1,64 @@
@import "_variables";
.icono {
-webkit-user-select: none;
}
.button {
width: 100%;
background: lighten($grisObscuro, 10%);
text-decoration: none;
color: $grisClaro;
display: block;
padding: 0px;
font-weight: bold;
font-size: 26px;
line-height: 1.5em;
position: absolute;
bottom: 1em;
font-weight: 600;
padding-top: .1em;
&:hover, {
background: $grisObscuro
}
}
.round-icon {
font-size: 50px;
line-height: 85px;
width: 70px;
height: 70px;
border-radius: 60px;
background: #D9D9D9;
color: #A8A8A8;
display: block;
text-align: center;
&:hover {
background: $grisObscuro;
color: $grisClaro;
}
}
.nav-icon {
opacity: .5;
color: #fff;
font-size: 30px;
line-height: 70px;
background-color: transparent;
width: 60px;
height: 60px;
&:hover, &:active {
opacity: 1;
background-color: #D9D9D9;
color: #000;
}
}
#config-button {
float: right;
}

View File

@ -0,0 +1,109 @@
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
body {
line-height:1;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section,
div,h1,h2,h3,h4,h5,h6,p,blockquote,pre {
display:block;
}
nav ul {
list-style:none;
}
blockquote, q {
quotes:none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
a {
margin:0;
padding:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
/* change colours to suit your needs */
ins {
background-color:#ff9;
color:#000;
text-decoration:none;
}
/* change colours to suit your needs */
mark {
background-color:#ff9;
color:#000;
font-style:italic;
font-weight:bold;
}
del {
text-decoration: line-through;
}
abbr[title], dfn[title] {
border-bottom:1px dotted;
cursor:help;
}
table {
border-collapse:collapse;
border-spacing:0;
}
/* change border colour to suit your needs */
hr {
display:block;
height:1px;
border:0;
border-top:1px solid #cccccc;
margin:1em 0;
padding:0;
}
input, select {
vertical-align:middle;
}
:focus {
outline: none;
}
ol, ul {
list-style: none;
}
.clear { clear: both; float: none; }
.clearfix::after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}

View File

@ -0,0 +1,32 @@
//
// Symbols con Pika
//
@charset "UTF-8";
@font-face {
font-family: "SSPika";
src: url('/assets/fonts/ss-pika.eot');
src: url('/assets/fonts/ss-pika.eot?#iefix') format('embedded-opentype'),
url('/assets/fonts/ss-pika.woff') format('woff'),
url('/assets/fonts/ss-pika.ttf') format('truetype'),
url('/assets/fonts/ss-pika.svg#SSPika') format('svg');
font-weight: normal;
font-style: normal;
}
.icono {
font-family: "SSPika";
font-style: normal;
font-weight: normal;
text-decoration: none;
text-rendering: optimizeLegibility;
white-space: nowrap;
-webkit-font-feature-settings: "liga";
-moz-font-feature-settings: "liga=1";
-moz-font-feature-settings: "liga";
-ms-font-feature-settings: "liga" 1;
-o-font-feature-settings: "liga";
font-feature-settings: "liga";
-webkit-font-smoothing: antialiased;
}

View File

@ -0,0 +1,4 @@
$grisObscuro: #171717;
$grisClaro: #E6E6E6;
$grisMedio: #6E6E6E;
$bigNumber: 140px;

View File

@ -0,0 +1,57 @@
@import "_variables";
#config {
background-color: darken($grisMedio, 20%);
color: $grisClaro;
& h1 {
color: darken($grisMedio, 40%);
}
& input {
width: 50%;
background-color: transparent;
border: none;
font:inherit;
font-size: 90px;
padding: 0 5px;
font-weight: bold;
text-align: center;
color: $grisClaro;
-webkit-appearance: none;
font-weight: 800;
font-family: "Avenir Next Condensed", "Avenir Next", "Avenir";
}
& [role=action-button] {
display: inline-block;
background-color: $grisObscuro;
color: $grisClaro;
}
& [role=action-button]:hover {
color: $grisObscuro;
background-color: $grisClaro;
}
}
.config-element {
display: -webkit-flex;
-webkit-flex-direction: row;
vertical-align: middle;
-webkit-align-items: center;
justify-content: center;
& .cell {
text-align: center;
-webkit-flex: 1 1 auto;
vertical-align: middle;
}
}
#restart-game {
&:active {
background: rgb(180,0,0)
}
}

View File

@ -0,0 +1,219 @@
@import "_reset";
@import "_variables";
@import "_symbols";
@import "_buttons";
@import "config";
@mixin clearfix() {
*zoom: 1;
&:before,
&:after {
content: ' ';
display: table;
}
&:after {
clear: both;
}
}
body {
background-color: #000;
font: 16px/1em "Avenir Next", "Avenir", sans-serif;
text-transform: uppercase;
text-align: center;
color: #ededed;
}
.screen {
display: -webkit-box;
-webkit-box-orient: vertical;
height: 100%;
width: 100%;
position: absolute;
overflow: hidden;
top: 0px;
-webkit-transition: all .5s ease-in;
-webkit-backface-visibility: hidden;
-webkit-transform: perspective( 500px ) rotateY(180deg);
-webkit-transition: all .5s ease-in;
background-color: $grisObscuro;
&.shown {
-webkit-transform: rotateY(0deg);
-webkit-transition: all .5s ease-in;
}
}
a {
color: #fff;
}
h1 {
color: #E6E6E6;
font-size: 18px;
}
#game-name {
color: darken($grisMedio, 20%);
padding-top: 5px;
position: absolute;
width: 100%;
font-weight:300;
}
#players {
padding-top: 15px;
}
.player {
overflow-x: scroll;
overflow-y: hidden;
width: 100%;
margin-bottom: 20px;
@include clearfix;
&:active, &.active {
box-shadow: inset 0px 0px 6px #000;
}
h1 {
position: fixed;
width: 100%;
margin-top: -15px;
}
}
.player-name {
z-index: 100;
color: $grisMedio;
}
.score-box {
display: -webkit-box;
width: 200%;
-webkit-box-orient: horizontal;
-webkit-user-select: none;
user-select: none;
}
.score, .score-input {
font: inherit;
font-size:$bigNumber;
line-height: 1em;
font-weight: 800;
font-family: "Avenir Next Condensed", "Avenir Next", "Avenir";
height: 100%;
padding-top: 16px;
font-style: normal;
//background-color: $grisObscuro;
}
.score-input {
padding: 0;
background-color: transparent;
border:none;
text-align:center;
width: 80%;
}
::-webkit-input-placeholder {
line-height: 1.2em;
}
.score, .loss {
width: 50%;
position: relative;
}
.score-action-buttons {
position: absolute;
right: 1em;
top: 1em;
width: 25%;
text-align: center;
display: -webkit-flex;
-webkit-flex-direction: column;
vertical-align: middle;
-webkit-align-items: center;
-webkit-justify-content: center;
& .cell {
-webkit-flex: 1 1 auto;
vertical-align: middle;
}
.icono {
float: right;
}
}
.suma {
position: absolute;
bottom: 0px;
text-align: center;
color: $grisMedio;
width:100%;
font-weight: 600;
font-size: 30px;
line-height: 36px;
& li {display: inline-block;}
& li:after {
content: '+';
}
& li:last-child:after {
content: none;
}
}
.for-more-players {
display:none;
position:relative;
position: relative;
top: .35em;
left: .15em
}
.player-count-3 {
& .for-more-players, &+.for-more-players {
display: block;
}
& .for-more-players.icono {
float: left;
}
& .for-2-players {
display: none;
}
& .score {
font-size: 80px;
}
& .player-name {
font-size: 14px;
font-weight: normal;
}
& .score-action-buttons {
top: 1em;
right: .2em
}
& .score-input {
font-size: 80px;
width: 55%;
float: left;
}
& .suma {
font-size: 18px;
}
}

View File

@ -0,0 +1,126 @@
<!DOCTYPE html>
<html manifest="manifest.appcache">
<head>
<meta charset="utf-8" />
<title>Fichitas</title>
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="/assets/icons/icon.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/assets/icons/icon@2x.png" />
<link href="/assets/css/reset.css" rel="stylesheet" />
<link href="/assets/css/main.css" rel="stylesheet" />
</head>
<body>
<div id="no-webapp">
<h1>Poop</h1>
</div>
<div id="container">
<div id="nuevo-juego" class="card hidden">
<div class="card-upper card-part">
<h1>Nuevo Juego</h1>
<ul id="jugadores">
<li data-jugadores="2" data-equipos="2">2 vs 2</li>
<li data-jugadores="4">4 jugadores</li>
<li data-jugadores="4">3 jugadores</li>
<li data-jugadores="4">2 jugadores</li>
</ul>
</div>
<div class="separador card-part"></div>
<div class="card-bottom card-part">
<div class="patote"></div>
<a href="#regresar" class="action-small">regresar</a>
</div>
</div>
<div id="setup-juego" class="card hidden">
<div class="card-upper card-part">
<div class="flex top">
<h1 id="tipo-juego">2 vs 2</h1>
<h2>A <input type="number" min="0" range="1" value="100" id="max-puntos"/><span id="max-puntos-target">100</span> puntos</h2>
</div>
<div class="flex bottom">
<h1>Nosotros</h1>
</div>
</div>
<div class="separador separador-large card-part"><p>VS</p></div>
<div class="card-bottom card-part">
<div class="flex top">
<h1>Ellos</h1>
</div>
<div class="flex bottom">
<a href="#regresar" class="action-small">comenzar</a>
<a href="#comenzar" class="action-small">regresar</a>
</div>
</div>
</div>
<div id="juego" class="card">
<div class="card-upper card-part">
<div class="flex top">
<h2 id="numero-juego" class="first-h2">Primer juego</h2>
</div>
<div class="flex bottom">
<p id="player-1" class="score">00</p>
<h1>Nosotros</h1>
<ul id="scores-player-1" class="scores-list">
</ul>
</div>
</div>
<div class="separador separador-large card-part">
<p id="activa-resultados">VS</p>
</div>
<div class="card-bottom card-part">
<div class="flex top">
<ul id="scores-player-2" class="scores-list">
</ul>
<h1>Ellos</h1>
<p class="score" id="player-2">00</p>
</div>
<div class="flex bottom">
<a id="terminar" href="#terminar" class="action-small">empezar de nuevo</a>
</div>
</div>
</div>
<div id="resultado" class="card flip">
<div class="card-part card-upper">
<div class="flex top">
<h2 class="first-h2">Resultados</h2>
</div>
<div class="flex bottom">
<input type="tel" class="score" min="0" placeholder="0" id="score-player-1" />
<h1>Nosotros</h1>
</div>
</div>
<div class="separador separador-large card-part"><p>VS</p></div>
<div class="card-part card-bottom">
<div class="flex top">
<h1>Ellos</h1>
<input type="tel" class="score" min="0" placeholder="0" id="score-player-2" />
</div>
<div class="flex bottom">
<button id="guardar">Guardar</button>
<a id="cancelar" class="action-small">cancelar</a>
</div>
</div>
</div>
</div>
<script src="/assets/js/jquery.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html manifest="manifest.appcache">
<head>
<meta charset="utf-8" />
<title>Fichitas</title>
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="/assets/img/icons/icon.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/assets/img/icons/icon@2x.png" />
<link rel="apple-touch-startup-image" href="/assets/img/startup/320x460.png" media="screen and (max-device-width : 320px)">
<link rel="apple-touch-startup-image" href="/assets/img/startup/640x920.png" media="(max-device-width : 480px) and (-webkit-min-device-pixel-ratio : 2)">
<link rel="apple-touch-startup-image" href="/assets/img/startup/640x1096.png" media="(max-device-width : 548px) and (-webkit-min-device-pixel-ratio : 2)">
<link rel="apple-touch-startup-image" sizes="1024x748" href="/assets/img/startup/1024x748.png" media="screen and (min-device-width : 481px) and (max-device-width : 1024px) and (orientation : landscape)">
<link rel="apple-touch-startup-image" sizes="768x1004" href="/assets/img/startup/768x1004.png" media="screen and (min-device-width : 481px) and (max-device-width : 1024px) and (orientation : portrait)">
<link rel="apple-touch-startup-image" sizes="2048x1496" href="/assets/img/startup/2048x1496.png" media="screen and (-webkit-min-device-pixel-ratio : 2) and (min-device-width : 481px) and (max-device-width : 2048px) and (orientation : landscape)">
<link rel="apple-touch-startup-image" sizes="1536x2008" href="/assets/img/startup/1536x2008.png" media="screen and (-webkit-min-device-pixel-ratio : 2) and (min-device-width : 481px) and (max-device-width : 2048px) and (orientation : portrait)">
<link href="/assets/css/main.css" rel="stylesheet" />
</head>
<body>
<section id="game" class="screen shown">
<div class="nav">
<h1 id="game-name">Primer Juego</h1>
<a class="nav-icon icono round-icon" id="config-button" href="#config">settings</a>
</div>
<section id="players">
<script type="text/html" id="player-tpl">
<div id="player-{{id}}" class="player">
<h1 class="player-name">{{name}}</h1>
<div class="score-box">
<div class="score">{{#renderScore}}{{a}}{{/renderScore}}</div>
<div class="loss">
<a class="for-more-players player-save plus icono round-icon" data-modifies="#player-{{id}}-score">check</a>
<input type="number" id="player-{{id}}-score" class="score-input" placeholder="0" min="0" max="99" pattern="\d*" />
<div class="score-action-buttons">
<span class="cell">
<a class="player-add plus icono round-icon" data-modifies="#player-{{id}}-score">plus</a>
</span>
<span class="cell">
<a class="for-2-players player-save plus icono round-icon" data-modifies="#player-{{id}}-score">check</a>
</span>
</div>
<ul class="suma">
</ul>
</div>
</div>
</div>
</script>
</section>
<a role="navigation-button" class="for-more-players button" id="finish-game" href="#juego">Terminar partida</a>
</section>
<section id="config" class="screen 2-boxes ashown">
<a class="nav-icon icono round-icon" id="back" href="#game" data-action="saveConfig">back</a>
<div class="box">
<h1>Jugadores</h1>
<div class="config-element">
<span class="cell">
<a role="action-button" class="minus icono round-icon" data-modifies="#jugadores" data-action="minus">minus</a>
</span>
<input class="cell" type="number" id="jugadores" value="2" min="2" max="4" step="1" pattern="\d*" />
<span class="cell">
<a role="action-button" class="plus icono round-icon" data-modifies="#jugadores" data-action="plus">plus</a>
</span>
</div>
</div>
<div class="box">
<h1>Puntos</h1>
<div class="config-element">
<span class="cell">
<a role="action-button" class="minus round-icon icono" data-modifies="#puntos" data-action="minus">minus</a>
</span>
<input class="cell" type="number" id="puntos" value="100" min="2" max="950" step="50" pattern="\d*" />
<span class="cell">
<a role="action-button" class="plus round-icon icono" data-modifies="#puntos" data-action="plus">plus</a>
</span>
</div>
</div>
<a role="navigation-button" class="button" id="restart-game" href="#juego">Empezar de nuevo</a>
</section>
<script src="/assets/js/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,10 @@
CACHE MANIFEST
#v0.0.9
CACHE:
/assets/css/main.css
/assets/js/main.js
/assets/fonts/ss-pika.woff
/assets/fonts/ss-pika.svg
/assets/fonts/ss-pika.ttf
/assets/fonts/ss-pika.eot

View File

@ -0,0 +1,6 @@
description: Se me olvidaba qué día circulaba cuando tenía auto
packages: {}
build: exit
deploy:
credentials: vault://nidito/deploy/hoynocircula.pati.to
src: ./src

View File

@ -0,0 +1,73 @@
terraform {
backend "consul" {
path = "nidito/state/service/hoynocircula.pati.to"
}
required_providers {
consul = {
source = "hashicorp/consul"
version = "~> 2.21.0"
}
vault = {
source = "hashicorp/vault"
version = "~> 4.4.0"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.29.0"
}
}
required_version = ">= 1.0.0"
}
data "vault_generic_secret" "DO" {
path = "cfg/infra/tree/provider:digitalocean"
}
provider "digitalocean" {
token = data.vault_generic_secret.DO.data.patito
}
data "vault_generic_secret" "backblaze" {
path = "cfg/infra/tree/provider:backblaze"
}
data "terraform_remote_state" "rob_mx" {
backend = "consul"
workspace = "default"
config = {
datacenter = "casa"
path = "nidito/state/rob.mx"
}
}
resource "digitalocean_record" "service" {
domain = "pati.to"
type = "A"
ttl = 3600
name = "hoynocircula"
value = data.terraform_remote_state.rob_mx.outputs.bernal.ip
}
resource "consul_keys" "cdn-config" {
datacenter = "qro0"
key {
path = "cdn/hoynocircula.pati.to"
value = jsonencode({
cert = "pati.to"
folder = "hoynocircula.pati.to"
nginx_config = file("${abspath(path.root)}/nginx.conf")
})
}
}
resource "vault_kv_secret" "deploy-config" {
path = "nidito/deploy/hoynocircula.pati.to"
data_json = jsonencode({
type = "ssh"
host = "bernal"
domain = "hoynocircula.pati.to"
})
}

View File

@ -0,0 +1,3 @@
location / {
index index.html;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<path fill="#D91C5C" d="M69.111,23.025c0,0-3.731-7.188-9.167-7.987c-5.415-0.799-14.494,0.518-19.736,4.978
c-5.242,4.443-5.611,16.807-2.31,19.558c3.28,2.756,3.47,4.366,2.5,5.176c-0.984,0.782-3.219,3.814-14.164-2.15
c-10.92-5.959-13.508-8.098-15.544-2.668c-2.056,5.43-1.337,41.716,28.636,44.732c29.956,3.031,38.979-14.949,31.742-28.637
c-4.371-8.257-8.274-9.321-5.33-15.478c0,0,3.163,0.733,6.003-2.464c2.85-3.186,8.737-0.821,11.681-1.615
c2.942-0.821,6.769-1.968,5.793-5.7l0.717-0.893c0,0,0.165-1.417-0.017-2.48c-0.165-1.07-1.422-2.045-2.491-1.869
c-1.06,0.176-6.482,2.034-9.956,1.334c-3.461-0.717-6.135-3.732-7.38-3.473C70.088,23.389,68.936,23.742,69.111,23.025z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html manifest="manifest.appcache">
<head>
<meta charset="utf-8" />
<title>Hoy chance circulo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width" />
<!-- Web app -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="¿Circulo?">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- iOS icons -->
<link rel="apple-touch-icon" href="img/icons/icon-iphone.png">
<link rel="apple-touch-icon" sizes="76x76" href="img/icons/icon-ipad.png">
<link rel="apple-touch-icon" sizes="120x120" href="img/icons/icon-iphone@2x.png">
<link rel="apple-touch-icon" sizes="152x152" href="img/icons/icon-ipad@2x.png">
<!-- open graph -->
<meta property="og:description" content="Usa este app para saber cuándo y porqué circulas en la megalópolis" />
<meta property="og:title" content="Hoy (chance) circulo" />
<meta property="fb:admins" content="792675081" />
<meta property="og:image" content="http://hoynocircula.pati.to/img/og-icon.png" />
<meta property="og:url" content="http://hoynocircula.pati.to" />
<meta itemprop="name" content="¿Sabes si circula tu vehículo hoy en DF?" />
<meta itemprop="description" content="Usa este app para saber cuándo y porqué circulas en la megalópolis" />
<meta itemprop="image" content="http://hoynocircula.pati.to/img/share-icon.png" />
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@peeseeme">
<meta name="twitter:title" content="Hoy (chance) circulo">
<meta name="twitter:description" content="Usa este app para saber cuándo y porqué circulas en la megalópolis">
<meta name="twitter:creator" content="@peeseeme">
<meta name="twitter:image:src" content="http://hoynocircula.pati.to/img/share-icon.png">
<meta name="twitter:domain" content="http://hoynocircula.pati.to">
<link href='http://fonts.googleapis.com/css?family=Ubuntu:400,700,300italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="main.css" />
</head>
<body>
<h1>¿Circulo hoy en DF?</h1>
<div id="container">
<div id="card">
<main class="side front">
<section id="setup" class="hidden">
<p>¿Cuales son los dígitos de tu placa?</p>
<input id="placa" type="number" min="0" max="9999" step="1" placeholder="4242" pattern="\d*"/>
<p>¿Qué número de holograma tienes?</p>
<ul id="hologramas">
<li class="holograma">
<label>
<input type="radio" name="holograma" value="0" />
Cero
</label>
</li>
<li class="holograma">
<label>
<input type="radio" name="holograma" value="1" />
1
</label>
</li>
<li class="holograma">
<label>
<input type="radio" name="holograma" value="2" />
2
</label>
</li>
</ul>
<button id="guardar-datos">Listo</button>
</section>
<section id="result" class="hidden">
<h2 id="hoy-circulo"></h2>
<span id="warn"></span>
<p id="razon"></p>
<div class="actions">
<a id="reset" href="#setup">Cambiar datos</a>
</section>
<a href="#switch" class="flippy">ayuda</a>
</main>
<div id="info" class="side back">
<a href="#switch" class="flippy">regresar</a>
<h2>¿Cómo funciona esto?</h2>
<p>Usamos la información de <a href="http://www.sedema.df.gob.mx" title="Secretaría del Medio Ambiente">SEDEMA</a> para calcular los días en los que tu automovil no circula, en función de la placa y holograma del mismo.</p>
<p>Todo el código vive en <a href="https://github.com/unRob/cuando-circulo" title="Repo en github">github</a>.</p>
<h2>¿Guardan los datos de mi auto?</h2>
<p><strong>No</strong>, ni se transmiten ni almacenan por nuestra parte, simplemente se guardan dentro de tu computadora o teléfono, y nosotros no tenemos acceso a los mismos.</p>
</div>
</div>
</div>
<footer>
<a href="http://surrealista.mx" title="Partido Surrealista Mexicano">
<img src="img/pato.svg" title="patito" alt="mooo" />
Partido Surrealista Mexicano
</a>
</footer>
<script src="lib/programa.js"></script>
<script src="lib/auto.js"></script>
<script src="lib/localizador.js"></script>
<script src="lib/circula.js"></script>
<script src="main.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-37922009-3', 'auto');
ga('send', 'pageview');
</script>
</script>
</body>
</html>

View File

@ -0,0 +1,35 @@
(function(){
var Programa = (function(w){
if (typeof module === 'undefined') {
return window.Programa;
} else {
return require('./programa');
}
})();
var Auto = function(placa, holograma){
this.placa = placa;
this.holograma = parseInt(holograma, 10);
this.info = Programa.terminaciones[this.ultimo_digito()];
return this;
};
Auto.prototype.ultimo_digito = function(){
return parseInt(this.placa.toString().match(/(\d)\b/)[0]);
};
Auto.prototype.proxima_verificacion = function(fecha) {
var fecha = fecha || new Date();
var meses = this.info.verifica;
var esteMes = fecha.getMonth();
};
if (typeof module !== 'undefined') {
module.exports = Auto;
} else {
window.Auto = Auto;
}
})();

View File

@ -0,0 +1,130 @@
(function(){
var entre_semana = function(auto, date) {
if (auto.holograma === 0) {
return true;
}
return auto.info.descansa !== date.getDay();
};
var los_sabados = function(auto, date) {
if (auto.holograma === 0) {
return true;
}
if (auto.holograma === 2) {
return false;
}
//es 1 entonces..
var sabados = (function(dia){
dia = new Date(dia);
var mes = dia.getMonth();
var ret = [];
while (dia.getMonth() === mes) {
if (dia.getDay() === 6)
ret.push(dia.getDate());
dia = new Date(dia.setDate(dia.getDate()+1));
}
return ret;
})(date.setDate(1));
var numSabado = sabados.indexOf(date.getDay())+1;
if (numSabado === 5) {
// Sólo holograma 2 deja de circular el quinto sábado del mes
return true;
}
var par = auto.ultimo_digito() % 2 === 0;
return (numSabado%2===0) ? par : !par;
};
var sabados_descansa = function(auto, circula) {
circula = circula || false;
switch (auto.holograma) {
case 0:
return false;
case 1:
var placaPar = (auto.ultimo_digito() % 2 === 0);
var impar = 'el primer y tercer sábado';
var par = 'el segundo y cuarto sábado';
return placaPar ? par : impar;
case 2:
return "todos los sábados";
}
};
var Circula = function(auto, fecha) {
var r = {};
if (auto.holograma === 0) {
r.circula = true;
r.razon = "Tú circulas todos los días, chatoa!";
} else {
fecha = fecha || new Date();
var numDia = fecha.getDay();
var dia_descanso = Localizador(auto.info.descansa);
if (numDia === 0) {
r.circula = true;
r.razon = "Todos los vehículos circulan el día domingo. Tu vehículo descansa todos los "+dia_descanso;
ds = sabados_descansa(auto);
r.razon += ds && " y "+ds+" del mes";
} else if (numDia <= 5) {
r.circula = entre_semana(auto, fecha);
info = auto.info;
r.razon = r.circula ?
"Los vehículos engomado color "+auto.info.nombre+" circulan hoy y descansan el "+dia_descanso :
"Los vehículos con placa terminación "+auto.ultimo_digito()+" no circulan los días "+Localizador(numDia);
} else {
r.circula = los_sabados(auto, fecha);
r.razon = "Tu vehículo holograma "+auto.holograma+" ";
if (auto.holograma == 2) {
r.razon += "no circula ningún sábado del mes";
} else {
var placaPar = (auto.ultimo_digito() % 2);
var numero = '';
var impar = 'primer y tercer';
var par = 'segundo y cuarto';
if (r.circula) {
r.razon += " circula el "+
(placaPar ? impar : par)+
" sábado del mes";
} else {
r.razon += " no circula el "+
(placaPar ? par : impar)+
" sábado del mes";
}
}
}
r.razon += '.';
if (!r.circula) {
antes5 = (fecha.getHours() < 5);
despues10 = (fecha.getHours() > 21);
if (antes5 || despues10) {
r.circula = true;
r.warn = antes5 ? "No circulas de 5:00am a 10:00pm" : "Sólamente pasado de las 10:00pm";
} else {
r.warn = "Sólo circulas después de las 10:00pm";
}
}
}
return r;
};
if (typeof module !== 'undefined') {
module.exports = Circula;
Localizador = require('./localizador');
} else {
window.Circula = Circula;
}
})();

View File

@ -0,0 +1,23 @@
(function(){
var Localizador = function(i, tipo) {
tipo = tipo || 'dias';
var type = (typeof i).toLowerCase();
var opts = Localizador[tipo];
if (type === 'string' || type === 'number') {
return opts[i-1];
} else {
return i.map(function(periodo){ return opts[periodo-1]; });
}
};
Localizador.dias = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'];
Localizador.meses = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];
if (typeof module !== 'undefined') {
module.exports = Localizador;
} else {
window.Localizador = Localizador;
}
})();

View File

@ -0,0 +1,30 @@
(function(){
var Programa = {
engomado: {
verde: {nombre: 'verde', color: "#339900", verifica: [3,4,9,10], descansa: 4},
rojo: {nombre: 'rojo', color: "#ff3333", verifica: [2,3,8,9], descansa: 3},
amarillo: {nombre: 'amarillo', color: "#ffff66", verifica: [0,1,6,7], descansa: 1},
rosa: {nombre: 'rosa', color: "#ff99cc", verifica: [1,2,7,8], descansa: 2},
azul: {nombre: 'azul', color: "#66ccff", verifica: [5,6,10,11],descansa: 5}
}
};
Programa.terminaciones = {
1: Programa.engomado.verde,
2: Programa.engomado.verde,
3: Programa.engomado.rojo,
4: Programa.engomado.rojo,
5: Programa.engomado.amarillo,
6: Programa.engomado.amarillo,
7: Programa.engomado.rosa,
8: Programa.engomado.rosa,
9: Programa.engomado.azul,
0: Programa.engomado.azul
};
if (typeof module !== 'undefined') {
module.exports = Programa;
} else {
window.Programa = Programa;
}
})();

View File

@ -0,0 +1,279 @@
body, html {
margin: 0;
padding: 0;
}
body {
background: #666 url('img/bg.jpg') no-repeat center center fixed;
background-size: cover;
font: 16px/20px 'Ubuntu', 'Futura', 'Helvetica', sans-serif;
}
#container {
width: 100%;
height: 100%;
max-width: 400px;
margin: 0 auto;
margin-top: 1em;
position: relative;
-webkit-perspective: 800px;
}
#container:after {
content: ' ';
display: table;
}
#card {
position: relative;
width: 100%;
height: 100%;
-webkit-transition: -webkit-transform 1s;
-moz-transition: -moz-transform 1s;
-o-transition: -o-transform 1s;
transition: transform 1s;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
}
#card.flipped {
-webkit-transform: rotateY( 180deg );
-moz-transform: rotateY( 180deg );
-ms-transform: rotateY( 180deg );
-o-transform: rotateY( 180deg );
transform: rotateY( 180deg );
}
.side {
display: block;
width: 100%;
top: 0px;
position: absolute;
background: rgba(255,255,255,.9);
padding: 1em;
box-shadow: 0px 0px 3px rgba(0,0,0,.4);
box-sizing: border-box;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
}
.side.back {
position: relative;
height: 100%;
-webkit-transform: rotateY( 180deg );
-moz-transform: rotateY( 180deg );
-ms-transform: rotateY( 180deg );
-o-transform: rotateY( 180deg );
transform: rotateY( 180deg );
}
.flippy {
display: block;
color: #777;
position: relative;
text-align: center;
text-decoration: none;
font-size: .8em;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
}
.flippy:hover {
color: #333;
text-decoration: underline;
}
h1, h2 {
text-align: center;
margin: 0;
}
h1 {
line-height: 2em;
color: #fff;
text-shadow: 2px 2px 5px #000;
}
@media all and (max-width: 600px) {
#container {
width: 90%;
}
h1 {
font-size: 1.7em;
line-height: 1em;
margin-top: 1em;
}
}
/* Setup */
input[type=number] {
font: inherit;
display: block;
font-size: 2em;
box-sizing: border-box;
text-align: center;
line-height: 1em;
border: 1px solid #aaa;
border-radius: 4px;
margin: 0 auto;
}
#hologramas {
margin: 0;
padding: 0;
display: -webkit-flex;
display: -moz-flex;
display: -ms-flex;
display: -o-flex;
display: flex;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
-o-flex-direction: row;
flex-direction: row;
}
.holograma {
display: block;
list-style: none;
-webkit-flex: 1 1 auto;
-moz-flex: 1 1 auto;
-o-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
text-align: center;
}
.holograma label {
display: block;
margin: 0 10px;
background: #ddd;
padding: 5px;
border-radius: 5px;
font-weight: bold;
line-height: 1.5em;
}
.holograma input {
display: block;
margin: 0 auto;
}
button {
display: block;
width: 100%;
background: #1FAD80;
color: #116148;
border: 0;
border-radius: 5px;
padding: 5px;
font: inherit;
font-weight: bold;
letter-spacing: .2em;
text-transform: uppercase;
margin: 1em 0 0;
}
button:hover {
color: #fff;
background: #116148;
}
/* Result */
#hoy-circulo {
font-size: 3em;
color: #fff;
line-height: 1.5em;
width: 100%;
box-sizing: padding-box;
padding: 0 .5em;
margin-left: -.5em;
box-shadow: 0px 2px 3px rgba(0,0,0,.5);
text-transform: uppercase;
}
#hoy-circulo.yay {
background: #1FAD80;
}
#hoy-circulo.nay {
background: #9B0C0C;
}
#hoy-circulo.warn {
background: #E0A21C;
}
#warn {
text-align: center;
display: block;
color: #9B0C0C;
font-size: 1.5em;
line-height: 2em;
}
#razon {
font-style: italic;
}
#result a {
color: #999;
text-decoration: none;
font-weight: 200;
display: block;
text-align: center;
font-size: .8em;
}
#result a:hover {
color: #666;
text-decoration: underline;
}
/* Footer */
footer {
width: 100%;
max-width: 400px;
margin: 0 auto;
color: #fff;
text-shadow: 0px 0px 4px #000;
font-weight: bold;
text-align: center;
}
footer a {
text-decoration: none;
color: #fff;
}
footer img {
display: block;
max-width: 45px;
margin: 0 auto;
}
.hidden {
display: none !important;
}

View File

@ -0,0 +1,117 @@
/*jshint expr:true */
var VERSION = '0.0.3';
document.addEventListener('DOMContentLoaded', function(){
var storage = window.localStorage;
var _show = function(el) {el = el.container || el; el.classList.remove('hidden');};
var _hide = function(el) {el = el.container || el; el.classList.add('hidden');};
var DOM = {
setup: {
container: document.querySelector('#setup'),
placa: document.querySelector('#placa'),
hologramas: document.getElementsByName('holograma'),
valueFor: function(el) {
if (el === 'placa') {
return DOM.setup.placa.value.replace(/\D/g, '+');
} else if (el === 'holograma') {
return (function(els){
var num;
[].forEach.call(els, function(el){
if (el.checked) {
num = el.value;
}
});
return parseInt(num, 10);
})(DOM.setup.hologramas);
}
}
},
result: {
container: document.querySelector('#result'),
header: document.querySelector('#hoy-circulo'),
razon: document.querySelector('#razon'),
warn: document.querySelector('#warn')
}
};
var Actions = {
Reset: function(evt) {
evt && evt.preventDefault();
_show(DOM.setup);
_hide(DOM.result);
},
Setup: function(evt) {
evt && evt.preventDefault();
var placa = DOM.setup.valueFor('placa');
var holograma = DOM.setup.valueFor('holograma');
if (!placa || placa.length < 3 || placa.length > 4) {
alert("Parece que no has introducido correctamente los datos de tu placa :(");
DOM.setup.placa.focus();
return;
}
if ([0,1,2].indexOf(holograma) < 0) {
alert("Es necesario que elijas el holograma de tu vehículo");
return;
}
console.log('storing...');
storage.placa = placa;
storage.holograma = holograma;
Actions.Result();
},
Result: function(){
_show(DOM.result);
_hide(DOM.setup);
var d = new Date();
var mi_auto = new Auto(storage.placa, storage.holograma);
var clase;
var res = Circula(mi_auto, d);
clase = res.circula ? 'yay' : 'nay';
if (res.warn) {
DOM.result.warn.innerText = res.warn;
clase = 'warn';
_show(DOM.result.warn);
} else {
_hide(DOM.result.warn);
}
DOM.result.header.className = clase;
DOM.result.header.innerText = res.circula ? 'Sí' : 'No';
DOM.result.razon.innerText = res.razon;
}
};
if (storage.placa && storage.holograma) {
DOM.setup.placa.value = storage.placa;
DOM.setup.hologramas[parseInt(storage.holograma, 10)].checked = 'checked';
Actions.Result();
} else {
Actions.Reset();
}
document.querySelector('#guardar-datos').addEventListener('mouseup', Actions.Setup);
document.querySelector('#guardar-datos').addEventListener('touchend', Actions.Setup);
document.querySelector('#reset').addEventListener('mousedown', Actions.Reset);
document.querySelector('#reset').addEventListener('touchend', Actions.Reset);
var doFlip = function(evt){
evt.preventDefault();
evt.stopPropagation();
var card = document.querySelector('#card');
card.classList.toggle('flipped');
return false;
};
[].forEach.call(document.querySelectorAll('.flippy'), function(el) {
el.addEventListener('click', doFlip, false);
});
});

View File

@ -0,0 +1,23 @@
CACHE MANIFEST
# v0.0.3-sigh nightlies
# neta no se porqué uso esta mierda
#JS
lib/auto.js
lib/circula.js
lib/localizador.js
lib/programa.js
#HTML
#index.html
#CSS
main.css
http://fonts.googleapis.com/css?family=Ubuntu:400,700,300italic
#IMG
img/bg.jpg
img/pato.svg
NETWORK:
*

View File

@ -0,0 +1,30 @@
{
"name": "cuando-circulo",
"version": "0.0.3",
"description": "¿Cuándo circulo en DF?",
"main": "main.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "jasmine-node spec"
},
"devDependencies" : {
"jasmine-node": "~> 1.14.5"
},
"repository": {
"type": "git",
"url": "git://github.com:unrob/cuando-circulo.git"
},
"keywords": [
"hoy no circula",
"df",
"mexico"
],
"author": "Roberto Hidalgo",
"license": "WTFPL",
"bugs": {
"url": "https://github.com/unrob/cuando-circulo/issues"
},
"homepage": "https://github.com/unrob/cuando-circulo"
}

View File

@ -0,0 +1,52 @@
module.exports = [
{
nombre: "Local,H0",
placa: 666,
holograma: 0,
ultimo_digito: 6,
results: {
lunes: true,
viernes: true,
}
},
{
nombre: "Local,Par,H1",
placa: 420,
holograma: 1,
ultimo_digito: 0,
results: {
lunes: true,
viernes: false,
}
},
{
nombre: "Local,Par,H2",
placa: 420,
holograma: 2,
ultimo_digito: 0,
results: {
lunes: true,
viernes: false,
}
},
{
nombre: "Local,Impar,H1",
placa: 555,
holograma: 1,
ultimo_digito: 5,
results: {
lunes: false,
viernes: true,
}
},
{
nombre: "Local,Impar,H2",
placa: 555,
holograma: 2,
ultimo_digito: 5,
results: {
lunes: false,
viernes: true,
}
}
];

View File

@ -0,0 +1,15 @@
var Auto = require('../lib/auto');
var autos = require('./_autos');
describe("Últimos dígitos de las placas", function(){
it("debería mostrar los últimos dígitos de las placas", function(){
autos.forEach(function(auto){
var a = new Auto(auto.placa, auto.holograma);
//console.log(a);
expect(a.ultimo_digito()).toBe(auto.ultimo_digito);
});
});
});

View File

@ -0,0 +1,53 @@
var Auto = require('../lib/auto')
var Circula = require('../lib/circula');
var autos = require('./_autos');
describe('Con Holograma Cero', function(){
it("Debe permitir todos los dias", function(){
autos.forEach(function(auto){
if (auto.holograma == 0) {
a = new Auto(auto.placa, auto.holograma);
expect(Circula(a, 0).circula).toBe(true);
}
});
});
});
describe('Los lunes', function(){
var lunes = new Date(1898,10,21,16); // René Magritte
autos.forEach(function autoLunes(auto){
var puede = auto.results.lunes;
var accion = puede ? 'permitir' : 'negar';
it("Debe "+accion+" a "+auto.nombre+" circular", function(){
var a = new Auto(auto.placa, auto.holograma);
expect(Circula(a, lunes).circula).toBe(auto.results.lunes);
});
});
});
describe('Los viernes', function(){
var viernes = new Date(1879,02,14,5); // Albert Einstein
autos.forEach(function autoViernes(auto){
var puede = auto.results.viernes;
var accion = puede ? 'permitir' : 'negar';
var a = new Auto(auto.placa, auto.holograma);
it("Debe "+accion+" a "+auto.nombre+':'+a.info.descansa+" circular", function(){
expect(auto.results.viernes)
.toBe(Circula(a, viernes).circula);
});
});
});

View File

@ -0,0 +1,3 @@
location / {
return 301 https://github.com/unRob/representantes.pati.to;
}

View File

@ -0,0 +1,4 @@
description: Herramienta para encontrar tu representante (circa 2010)
packages: {}
build: exit
deploy: {}

View File

@ -0,0 +1,74 @@
terraform {
backend "consul" {
path = "nidito/state/service/representantes.pati.to"
}
required_providers {
consul = {
source = "hashicorp/consul"
version = "~> 2.21.0"
}
vault = {
source = "hashicorp/vault"
version = "~> 4.4.0"
}
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.29.0"
}
}
required_version = ">= 1.0.0"
}
data "vault_generic_secret" "DO" {
path = "cfg/infra/tree/provider:digitalocean"
}
provider "digitalocean" {
token = data.vault_generic_secret.DO.data.patito
}
data "vault_generic_secret" "backblaze" {
path = "cfg/infra/tree/provider:backblaze"
}
data "terraform_remote_state" "rob_mx" {
backend = "consul"
workspace = "default"
config = {
datacenter = "casa"
path = "nidito/state/rob.mx"
}
}
resource "digitalocean_record" "service" {
domain = "pati.to"
type = "A"
ttl = 3600
name = "representantes"
value = data.terraform_remote_state.rob_mx.outputs.bernal.ip
}
resource "consul_keys" "cdn-config" {
datacenter = "qro0"
key {
path = "cdn/representantes.pati.to"
value = jsonencode({
cert = "pati.to"
folder = "representantes.pati.to"
nginx_config = file("${abspath(path.root)}/nginx.conf")
})
}
}
# site disabled, redirects to github
# resource "vault_kv_secret" "deploy-config" {
# path = "nidito/deploy/representantes.pati.to"
# data_json = jsonencode({
# type = "ssh"
# host = "bernal"
# domain = "representantes.pati.to"
# })
# }

26
terraform/main.tf Normal file
View File

@ -0,0 +1,26 @@
terraform {
backend "consul" {
path = "nidito/state/pati.to"
}
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.43.0"
}
vault = {
source = "hashicorp/vault"
version = "~> 4.4.0"
}
}
required_version = ">= 1.0.0"
}
data "vault_generic_secret" "do_token" {
path = "cfg/infra/tree/provider:digitalocean"
}
provider "digitalocean" {
token = data.vault_generic_secret.do_token.data.patito
}

3
terraform/pati.to.tf Normal file
View File

@ -0,0 +1,3 @@
resource "digitalocean_domain" "pati_to" {
name = "pati.to"
}