first commit
This commit is contained in:
10
src/App.vue
Executable file
10
src/App.vue
Executable file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App'
|
||||
})
|
||||
</script>
|
||||
15
src/assets/quasar-logo-vertical.svg
Executable file
15
src/assets/quasar-logo-vertical.svg
Executable file
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
|
||||
<path
|
||||
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
|
||||
<path fill="#050A14"
|
||||
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
|
||||
<path fill="#050A14"
|
||||
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
|
||||
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
0
src/boot/.gitkeep
Executable file
0
src/boot/.gitkeep
Executable file
24
src/boot/axios.js
Executable file
24
src/boot/axios.js
Executable file
@@ -0,0 +1,24 @@
|
||||
import { boot } from 'quasar/wrappers'
|
||||
import axios from 'axios'
|
||||
|
||||
// Be careful when using SSR for cross-request state pollution
|
||||
// due to creating a Singleton instance here;
|
||||
// If any client changes this (global) instance, it might be a
|
||||
// good idea to move this instance creation inside of the
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api = axios.create({ baseURL: 'https://api.example.com' })
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
app.config.globalProperties.$axios = axios
|
||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||
// so you won't necessarily have to import axios in each vue file
|
||||
|
||||
app.config.globalProperties.$api = api
|
||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||
// so you can easily perform requests against your app's API
|
||||
})
|
||||
|
||||
export { api }
|
||||
7
src/boot/qmarkdown.js
Normal file
7
src/boot/qmarkdown.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { boot } from 'quasar/wrappers'
|
||||
import VuePlugin from '@quasar/quasar-ui-qmarkdown'
|
||||
import '@quasar/quasar-ui-qmarkdown/dist/index.css'
|
||||
|
||||
export default boot(({ app }) => {
|
||||
app.use(VuePlugin)
|
||||
})
|
||||
42
src/components/EssentialLink.vue
Executable file
42
src/components/EssentialLink.vue
Executable file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<q-item clickable tag="a" target="_blank" :href="link">
|
||||
<q-item-section v-if="icon" avatar>
|
||||
<q-icon :name="icon" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ title }}</q-item-label>
|
||||
<q-item-label caption>
|
||||
{{ caption }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EssentialLink',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
|
||||
caption: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
link: {
|
||||
type: String,
|
||||
default: '#'
|
||||
},
|
||||
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
1
src/css/app.css
Executable file
1
src/css/app.css
Executable file
@@ -0,0 +1 @@
|
||||
/* app global css */
|
||||
22
src/index.template.html
Executable file
22
src/index.template.html
Executable file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= productName %></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="<%= productDescription %>">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
|
||||
|
||||
<!-- <link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png"> -->
|
||||
<link rel="icon" type="image/ico" href="fav128.ico">
|
||||
</head>
|
||||
<body>
|
||||
<!-- DO NOT touch the following DIV -->
|
||||
<div id="q-app"></div>
|
||||
</body>
|
||||
</html>
|
||||
114
src/layouts/MainLayout.vue
Executable file
114
src/layouts/MainLayout.vue
Executable file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<q-layout view="lHh Lpr lFf">
|
||||
<q-header elevated>
|
||||
<q-toolbar>
|
||||
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" />
|
||||
<q-toolbar-title>
|
||||
<div class="flex">
|
||||
<span class="q-px-md cursor-pointer" @click="volver">El blog de Clonbg</span>
|
||||
</div>
|
||||
</q-toolbar-title>
|
||||
<q-input borderless v-model="busqueda" autofocus placeholder="Buscar" class="q-px-md" style="width : 15rem ; font-size : 1.2rem" v-if="($route.path=='/') && (!verBusqueda)" standout="text-black" />
|
||||
<q-btn flat round dense icon="search" v-if="($route.path=='/') && (verBusqueda)" class="q-mr-xs" @click="verBusqueda = false" />
|
||||
<q-btn flat round dense icon="cancel" v-if="($route.path=='/') && (!verBusqueda)" class="q-mr-xs" @click="(verBusqueda = true) ; (busqueda = '') ; (scrollToTop())" />
|
||||
<div>
|
||||
<img src="/del_blog/kiss.png" style="width:6rem" class="q-pa-md" />
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
<q-drawer v-model="leftDrawerOpen" show-if-above bordered>
|
||||
<q-list>
|
||||
<q-item-label header>
|
||||
Links de utilidad
|
||||
</q-item-label>
|
||||
<EssentialLink v-for="link in essentialLinks" :key="link.title" v-bind="link" />
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
<script>
|
||||
import EssentialLink from 'components/EssentialLink.vue'
|
||||
|
||||
const linksList = [{
|
||||
title: 'Blog hecho con: ',
|
||||
caption: 'quasar.dev',
|
||||
icon: 'school',
|
||||
link: 'https://quasar.dev'
|
||||
},
|
||||
{
|
||||
title: 'Mi Github',
|
||||
caption: 'github.com/clonbg',
|
||||
icon: 'code',
|
||||
link: 'https://github.com/clonbg'
|
||||
},
|
||||
{
|
||||
title: 'Mastodon',
|
||||
caption: '@clonbg@masto.es',
|
||||
icon: 'insert_comment',
|
||||
link: 'https://masto.es/@clonbg'
|
||||
},
|
||||
{
|
||||
title: 'RSS feed',
|
||||
caption: 'RSS',
|
||||
icon: 'rss_feed',
|
||||
link: `${window.location.origin}/feedClonbg_es.xml`
|
||||
}
|
||||
];
|
||||
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MainLayout',
|
||||
|
||||
components: {
|
||||
EssentialLink
|
||||
},
|
||||
|
||||
setup() {
|
||||
const leftDrawerOpen = ref(false)
|
||||
let busqueda = ref('')
|
||||
let verBusqueda = ref(true)
|
||||
let url = ref('')
|
||||
|
||||
return {
|
||||
essentialLinks: linksList,
|
||||
leftDrawerOpen,
|
||||
toggleLeftDrawer() {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
},
|
||||
busqueda,
|
||||
verBusqueda,
|
||||
url
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'busqueda': 'updateBusqueda',
|
||||
'$route.path': 'borrarBusqueda'
|
||||
},
|
||||
methods: {
|
||||
updateBusqueda() {
|
||||
this.$store.commit('user/updateBusqueda', this.busqueda)
|
||||
},
|
||||
borrarBusqueda() {
|
||||
if (this.$route.path != '/') {
|
||||
this.busqueda = ''
|
||||
this.verBusqueda = true
|
||||
}
|
||||
},
|
||||
scrollToTop() {
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
volver() {
|
||||
this.$router.push({ path: '/' });
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.url = window.location
|
||||
console.log('window: ', this.url)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
29
src/markdowns/stories/alacritty-la-terminal-mas-rapida.md
Normal file
29
src/markdowns/stories/alacritty-la-terminal-mas-rapida.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# **Alacritty**, la terminal más rápida
|
||||
|
||||
#### 17/05/2020
|
||||
|
||||
**Alacritty** es un simple emulador de terminal, no es más. Eso si, es súper rápido. Lo había escuchado en el podcast de [atareao.es](https://www.atareao.es/software/utilidades/emulador-de-terminal-mas-rapido/) y decidí hacer la prueba yo mismo, una de las cosas que más me gustan es hacer lo mismo en menos tiempo.
|
||||
|
||||
Creé un archivo en el fondo de mi sistema con el nombre _archivoprueba_ y hice una búsqueda con el comando _find_.
|
||||
|
||||
**Primero en la terminal de Xfce4:**
|
||||
|
||||

|
||||
Tarda 41 segundos
|
||||
|
||||
**Con Alacritty:**
|
||||
|
||||

|
||||
Tarda 16 segundos!!
|
||||
|
||||
Estamos hablando, en este caso de casi el triple de rápido que una terminal convencional. Me veo obligado a cambiarme a **Alacritty** como mi emulador de terminal.
|
||||
|
||||
El archivo de configuración se encuentra en:
|
||||
|
||||
~/.config/alacritty/alacritty.yml
|
||||
|
||||
[Aquí](https://github.com/clonbg/configs/blob/master/alacritty.yml) os dejo mi archivo de configuración, además he instalado la fuente _nerd-font-hack_ que es la que viene por defecto.
|
||||
|
||||
Ahora solo falta que me vaya familiarizando con _tmux_ para tener varios terminales a la vez, pero eso ya os lo cuento otro día.
|
||||
|
||||
Salu2
|
||||
32
src/markdowns/stories/aprender-django.md
Normal file
32
src/markdowns/stories/aprender-django.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Aprender **Django**
|
||||
#### 18/10/2022
|
||||
|
||||
Últimamente he estado ocupado en proyecto para una pequeña empresa.
|
||||
El *backend* era un reto para mi, estuve mirando posibles soluciones y al final me decidí por **Django**. Dejo aquí una recopilación de recursos que he encontrado por internet y con los que he conseguido tener un *backend* seguro y en condiciones:
|
||||
|
||||
- El primero, más largo y más importante. Con este video ya puedes tener el *backend* funcionando
|
||||
[](https://www.youtube.com/watch?v=Sjv-HTLmnB4 "Build And Deploy A REST API With Django REST Framework. Full Project Tutorial.")
|
||||
|
||||
- Encriptar la contraseña en un *serializer*
|
||||
[](https://www.youtube.com/watch?v=eQ8UCDDBOAY "14.- Curso Django REST | ENCRIPTAR contraseña en un SERIALIZER")
|
||||
|
||||
- Como implementar en django "olvidé mi contraseña"
|
||||
[](https://www.youtube.com/watch?v=y-4-qv9_zP8 "Django, como implementar olvide mi contraseña")
|
||||
|
||||
- [Filtrar contenido del modelo en Django](https://www.kyocode.com/2019/09/filtrar-contenido-modelo-django/)
|
||||
|
||||
- **Swagger API**, como funciona tu *API*
|
||||
[](https://www.youtube.com/watch?v=IU9YuRhhY7M "Add Swagger API Documentation to Django REST API | Open API | REST Framework | Python")
|
||||
|
||||
- Como hacer el login en **VueJs**, con **localStorage** y **JWT**
|
||||
[](https://www.youtube.com/watch?v=1AahtN4ClnE "VideoTutorial 13 Taller práctico desarrollo con VUE JS. Login con localStorage y JWT")
|
||||
|
||||
- Autenticación con *VueJs* y *JWT*
|
||||
[](https://www.youtube.com/watch?app=desktop&v=ULFAgn2ITko "VueJS JWT Authentication")
|
||||
|
||||
- **MEVN** *JWT* sin *localStorage*
|
||||
[![MEVN (2022) #01 - JWT en memoria - SIN ⛔ localStorage 💀 [refreshToken]](https://clonbg.netlify.app/aprender-django/mevn.png)](https://www.youtube.com/watch?v=53VBlv7K-BI "MEVN (2022) #01 - JWT en memoria - SIN ⛔ localStorage 💀 [refreshToken]")
|
||||
|
||||
Espero que os sirva de algo, a mi por lo menos me sirvió *mucho*
|
||||
|
||||
Salu2
|
||||
31
src/markdowns/stories/autosubsync.md
Executable file
31
src/markdowns/stories/autosubsync.md
Executable file
@@ -0,0 +1,31 @@
|
||||
# Sincronizar automáticamente subtítulos utilizando el aprendizaje automático con **autosubsync**
|
||||
|
||||
#### 15/02/2021
|
||||
|
||||
Muchas veces cuando tienes que sincronizar un subtítulo, bien por que es para otra versión del video o por el motivo que sea, tenemos que sincronizarlo a mano, lo cual es bastante costoso. Hay una aplicación de terminal que lo hace sola y en una sola línea, **autosubsync**, es *software libre* y está alojada en *[Github](https://github.com/oseiskar/autosubsync)*.
|
||||
|
||||
Para instalarla en nuestro **sistema operativo** (Linux), lo primero hay que instalar otra aplicación de la que depende, **ffmpeg**:
|
||||
|
||||
```
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
Y después instalar la aplicación propiamente dicha:
|
||||
|
||||
```
|
||||
sudo pip install autosubsync
|
||||
```
|
||||
|
||||
Si esto no os funciona puede que necesitéis instalar el administrador de paquetes de *python,* **[pip](https://pypi.org/project/pip/)**, que es el lenguaje en el que está escrita la librería.
|
||||
|
||||
Una vez en nuestro equipo, el uso de *autosubsync* es muy sencillo:
|
||||
|
||||
```
|
||||
autosubsync plan-9-from-outer-space.avi \ # El video
|
||||
plan-9-out-of-sync-subs.srt \ # El subtítulo que tenemos
|
||||
plan-9-subtitles-synced.srt # El subtítulo que obtenemos
|
||||
```
|
||||
|
||||
Con esto ya puedes tener todos tus subtítulos sincronizados
|
||||
|
||||
Salu2
|
||||
16
src/markdowns/stories/buscar-en-ficheros.md
Normal file
16
src/markdowns/stories/buscar-en-ficheros.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Buscar archivos en Linux por nombre o contenido
|
||||
#### 22/03/2020
|
||||
|
||||
Para buscar archivos en nuestro sistema vamos a usar el comando *find*. Por ejemplo para buscar un archivo que contenga feet en cualquier parte de su nombre, en todo el sistema:
|
||||
|
||||
find / -iname "*feet*"
|
||||
|
||||
Con *-iname* no diferencia entre mayúsculas y minúsculas, con *-name* si.
|
||||
|
||||
Y para buscar por el contenido de los ficheros echamos mano de *grep*. Por ejemplo si queremos buscar un archivo que contenga la frase “esos gusanos estaban buenos” en nuestro home:
|
||||
|
||||
grep -r -i "esos gusanos estaban buenos" /home/nombreUsuario
|
||||
|
||||
Visto en [redeszone](https://www.redeszone.net)
|
||||
|
||||
Salu2
|
||||
268
src/markdowns/stories/chuleta-de-markdown.md
Normal file
268
src/markdowns/stories/chuleta-de-markdown.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# Mi chuleta de markdown
|
||||
#### 17/03/2020
|
||||
|
||||
Guardo como oro en paño un archivo donde consulto cualquier duda que tengo cuando estoy escribiendo en formato markdown. La voy a dejar aquí, para poder consultarla y por si alguien la quiere usar. Venía con la aplicación [QownNotes](https://www.qownnotes.org/).
|
||||
|
||||
Por cierto para escribir markdown uso [Marker](https://github.com/fabiocolacio/Marker)
|
||||
|
||||
|
||||
|
||||
## Headers
|
||||
|
||||
```markdown
|
||||
# H1
|
||||
## H2
|
||||
### H3
|
||||
#### H4
|
||||
##### H5
|
||||
###### H6
|
||||
|
||||
Alternatively, for H1 and H2, an underline-ish style:
|
||||
|
||||
Alt-H1
|
||||
======
|
||||
|
||||
Alt-H2
|
||||
------
|
||||
```
|
||||
|
||||
# H1
|
||||
## H2
|
||||
### H3
|
||||
#### H4
|
||||
##### H5
|
||||
###### H6
|
||||
|
||||
Alternatively, for H1 and H2, an underline-ish style:
|
||||
|
||||
Alt-H1
|
||||
======
|
||||
|
||||
Alt-H2
|
||||
------
|
||||
|
||||
|
||||
## Emphasis
|
||||
|
||||
```markdown
|
||||
Emphasis, aka italics, with *asterisks* or _underscores_.
|
||||
|
||||
Strong emphasis, aka bold, with **asterisks** or __underscores__.
|
||||
|
||||
Combined emphasis with **asterisks and _underscores_**.
|
||||
```
|
||||
|
||||
Emphasis, aka italics, with *asterisks* or _underscores_.
|
||||
|
||||
Strong emphasis, aka bold, with **asterisks** or __underscores__.
|
||||
|
||||
Combined emphasis with **asterisks and _underscores_**.
|
||||
|
||||
|
||||
## Lists
|
||||
|
||||
(In this example, leading and trailing spaces are shown with with dots: ⋅)
|
||||
|
||||
```markdown
|
||||
1. First ordered list item
|
||||
2. Another item
|
||||
⋅⋅* Unordered sub-list.
|
||||
1. Actual numbers don't matter, just that it's a number
|
||||
⋅⋅1. Ordered sub-list
|
||||
4. And another item.
|
||||
|
||||
⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown).
|
||||
|
||||
⋅⋅⋅To have a line break without a paragraph, you will need to use two trailing spaces.⋅⋅
|
||||
⋅⋅⋅Note that this line is separate, but within the same paragraph.⋅⋅
|
||||
|
||||
* Unordered list can use asterisks
|
||||
- Or minuses
|
||||
+ Or pluses
|
||||
```
|
||||
|
||||
1. First ordered list item
|
||||
2. Another item
|
||||
* Unordered sub-list.
|
||||
1. Actual numbers don't matter, just that it's a number
|
||||
1. Ordered sub-list
|
||||
4. And another item.
|
||||
|
||||
You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown).
|
||||
|
||||
To have a line break without a paragraph, you will need to use two trailing spaces.
|
||||
Note that this line is separate, but within the same paragraph.
|
||||
|
||||
* Unordered list can use asterisks
|
||||
- Or minuses
|
||||
+ Or pluses
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
There are two ways to create links.
|
||||
|
||||
```markdown
|
||||
[I'm an inline-style link](https://www.google.com)
|
||||
|
||||
[I'm an inline-style link with title](https://www.google.com "Google's Homepage")
|
||||
|
||||
[You can use numbers for reference-style link definitions][1]
|
||||
|
||||
URLs and URLs in angle brackets will automatically get turned into links in the preview.
|
||||
http://www.example.com or <http://www.example.com>
|
||||
|
||||
[1]: https://www.qownnotes.org
|
||||
```
|
||||
|
||||
[I'm an inline-style link](https://www.google.com)
|
||||
|
||||
[I'm an inline-style link with title](https://www.google.com "Google's Homepage")
|
||||
|
||||
[You can use numbers for reference-style link definitions][1]
|
||||
|
||||
URLs and URLs in angle brackets will automatically get turned into links in the preview.
|
||||
http://www.example.com or <http://www.example.com>
|
||||
|
||||
[1]: https://www.qownnotes.org
|
||||
|
||||
|
||||
## Inline code and code blocks
|
||||
|
||||
```markdown
|
||||
Inline `code` has `back-ticks around` it.
|
||||
```
|
||||
|
||||
Inline `code` has `back-ticks around` it.
|
||||
|
||||
Blocks of code are either fenced by lines with three back-ticks, or are indented with four spaces.
|
||||
|
||||
```markdown
|
||||
s = "Code with space indent"
|
||||
print s
|
||||
```
|
||||
|
||||
s = "Code with space indent"
|
||||
print s
|
||||
|
||||
|
||||
## Tables
|
||||
|
||||
Tables aren't part of the core Markdown spec, but the QOwnNotes preview supports them.
|
||||
|
||||
```markdown
|
||||
Colons can be used to align columns.
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| ------------- | :-----------: | ----: |
|
||||
| col 3 is | right-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| zebra stripes | are neat | $1 |
|
||||
|
||||
There must be at least 3 dashes separating each header cell.
|
||||
The outer pipes (|) are optional, and you don't need to make the
|
||||
raw Markdown line up prettily. You can also use inline Markdown.
|
||||
|
||||
| | Markdown | Less | Pretty | |
|
||||
| | ------------- | --------------- | ---------- |------- |
|
||||
| | *Still* | `renders` | **nicely** | |
|
||||
| | 1 | 2 | 3 | |
|
||||
```
|
||||
|
||||
Colons can be used to align columns.
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| ------------- | :-----------: | ----: |
|
||||
| col 3 is | right-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| zebra stripes | are neat | $1 |
|
||||
|
||||
There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown.
|
||||
|
||||
| Markdown | Less | Pretty |
|
||||
| -------- | --------- | ---------- |
|
||||
| *Still* | `renders` | **nicely** |
|
||||
| 1 | 2 | 3 |
|
||||
|
||||
|
||||
## Blockquotes
|
||||
|
||||
```markdown
|
||||
> Blockquotes are very handy in email to emulate reply text.
|
||||
> This line is part of the same quote.
|
||||
|
||||
Quote break.
|
||||
|
||||
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
|
||||
```
|
||||
|
||||
> Blockquotes are very handy in email to emulate reply text.
|
||||
> This line is part of the same quote.
|
||||
|
||||
Quote break.
|
||||
|
||||
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
|
||||
|
||||
|
||||
## Horizontal Rule
|
||||
|
||||
```markdown
|
||||
Three or more...
|
||||
|
||||
---
|
||||
|
||||
Hyphens
|
||||
|
||||
***
|
||||
|
||||
Asterisks
|
||||
|
||||
___
|
||||
|
||||
Underscores
|
||||
```
|
||||
|
||||
Three or more...
|
||||
|
||||
---
|
||||
|
||||
Hyphens
|
||||
|
||||
***
|
||||
|
||||
Asterisks
|
||||
|
||||
___
|
||||
|
||||
Underscores
|
||||
|
||||
## Line Breaks
|
||||
|
||||
```markdown
|
||||
Here's a line for us to start with.
|
||||
|
||||
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
|
||||
|
||||
This line is also begins a separate paragraph, but...
|
||||
This line is only separated by two trailing spaces and a single newline, so it's a separate line in the *same paragraph*.
|
||||
```
|
||||
|
||||
Here's a line for us to start with.
|
||||
|
||||
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
|
||||
|
||||
This line is also begins a separate paragraph, but...
|
||||
This line is only separated by two trailing spaces and a single newline, so it's a separate line in the *same paragraph*.
|
||||
|
||||
## Comments
|
||||
|
||||
```markdown
|
||||
[comment]: # (This comment will not appear in the preview)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Credit: [markdown-here](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)
|
||||
License: [CC-BY](https://creativecommons.org/licenses/by/3.0/)
|
||||
|
||||
Salu2
|
||||
34
src/markdowns/stories/comandos-git.md
Normal file
34
src/markdowns/stories/comandos-git.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Comandos de git que yo utilizo
|
||||
#### 04/04/2020
|
||||
|
||||
No soy un gran especialista en *git*, de hecho con unos pocos comandos hago todo lo que necesito. Rara es la vez que he necesitado hacer en *fetch* o algo similar.
|
||||
|
||||
- Crear un repositorio nuevo
|
||||
|
||||
git init
|
||||
|
||||
- Add & commit
|
||||
|
||||
git add nombreArchivo
|
||||
git commit -m "mensaje"
|
||||
|
||||
- La primera vez hay que indicarle cual es su repositorio
|
||||
|
||||
git remote add origin https://github.com/nombreRepo
|
||||
git push -u origin master
|
||||
|
||||
- Enviar los cambios si no es la primera vez
|
||||
|
||||
git push origin master
|
||||
|
||||
- Actualizar y fusionar
|
||||
|
||||
git pull
|
||||
|
||||
- Si *git pull* falla porque se modificó algún archivo del repositorio y posteriormente se quiso actualizar, la solución es descartar todos esos cambios si estos no son necesarios
|
||||
|
||||
git stash save --keep-index
|
||||
git stash drop
|
||||
git pull
|
||||
|
||||
Salu2
|
||||
12
src/markdowns/stories/consumo-ssd-pi.md
Normal file
12
src/markdowns/stories/consumo-ssd-pi.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Consumo alto cuando la Raspberry Pi trabaja desde un SSD
|
||||
#### 25/02/2022
|
||||
|
||||
Desde hace un tiempo tengo la **Raspberry Pi 4 B** como servidor personal, con un montón de servicios: notas, calendario, vscode, etc...
|
||||
|
||||
Seguí un tutorial para que arrancara desde un dispositivo **SSD**, porque según dicen tiene un mejor funcionamiento, esto es cierto. Lo que noté es que el led de la tarjeta *microSD* no paraba de parpadear buscando todo el tiempo si estaba dentro. Leyendo por ahí comentaban que se podía bajar la carga del procesador hasta en un 10% eliminando esto. Encontre la solución para que solo lo haga una vez, al inicio del sistema, en [jamesachambers.com](https://jamesachambers.com/raspberry-pi-cheap-ssd-upgrade-30/). Es muy sencillo, sólo hay que editar el archivo ```/boot/config.txt``` y añadir la siguiente linea:
|
||||
```
|
||||
dtparam=sd_poll_once
|
||||
```
|
||||
Después de esto, reiniciar. Con esta línea comprueba al inicio si hay tarjeta *microSD*, si hay intenta iniciar desde ella, si no, arranca desde el *SSD*
|
||||
|
||||
Salu2
|
||||
23
src/markdowns/stories/copiar-mover-find.md
Normal file
23
src/markdowns/stories/copiar-mover-find.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Buscar archivos y copiar/mover con *find*
|
||||
#### 08/02/2020
|
||||
|
||||
Vamos a ver como copiar o mover archivos a la vez que los buscas. Este es el resultado de un problema, tuve que buscar archivos en la raiz de mi disco y después moverlos a la carpeta que yo quería. Encontré esta solución:
|
||||
|
||||
find . -type f -iname 'searchFile.*' -exec cp {} ~/Documentos \;
|
||||
|
||||
Veamos las partes y que es lo que hacen:
|
||||
- ".", el lugar desde donde va a buscar, el punto significa que va a empezar donde estés ubicado
|
||||
- "-type f", busca ficheros, podría ponerse una "d" y buscaría diectorios
|
||||
- "-iname", no es sensible a mayúsculas
|
||||
- "'searchFile.*'", el nombre del archivo que buscas
|
||||
- "-exec", que tiene que ejecutar lo que venga después
|
||||
- "cp", aquí podría ponerse el comando que quieras, *mv*, *rm*...
|
||||
- "~/Documentos \;" el lugar donde se vana mover o copiar los archivos
|
||||
|
||||
Veamos otro ejemplo:
|
||||
|
||||
find . -type f -iname '9rsgikqrj1g61.jpg' -exec cp {} ~/Documentos \;
|
||||
|
||||
Este comando busca el archivo *9rsgikqrj1g61.jpg* y una vez lo encuentra lo copia a la carpeta *Documentos*.
|
||||
|
||||
Salu2
|
||||
44
src/markdowns/stories/crear-servicio.md
Normal file
44
src/markdowns/stories/crear-servicio.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Ejecutar un script al iniciar la Raspberry Pi
|
||||
|
||||
#### 21/06/2020
|
||||
|
||||
Vamos a crear un servicio con _systemd_ que se ejecute cada vez que se inicie la **Raspberry Pi**. En mi caso tengo instalado [dietpi](https://dietpi.com/), pero cualquier derivado de **Debian** valdría, como por ejemplo [raspbian](https://www.raspbian.org/).
|
||||
|
||||
Lo primero creamos un archivo _.service_ en _/lib/systemd/system/_
|
||||
|
||||
sudo nano /lib/systemd/system/twitter.service
|
||||
|
||||
y pegamos el siguiente código dentro del archivo
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Mi Servicio para twitter
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
ExecStart=/usr/bin/python3.7 /home/dietpi/twitter-python/programa.py
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
En _ExecStart_ podemos poner cualquier comando que pudieramos necesitar.
|
||||
Otorgamos los permisos necesarios al archivo:
|
||||
|
||||
sudo chmod 644 /lib/systemd/system/twitter.service
|
||||
|
||||
Y ahora para que se ejecute al inicio:
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable twitter
|
||||
|
||||
Para saber el estado del servicio y si está funcionando bien:
|
||||
|
||||
sudo systemctl status twitter.service
|
||||
|
||||

|
||||
|
||||
Visto en [chips.mecatronium.com](https://chips.mecatronium.com/tutorial-como-correr-un-script-de-python-al-iniciar-el-raspberry-pi/)
|
||||
|
||||
Salu2
|
||||
29
src/markdowns/stories/crud_con_python.md
Normal file
29
src/markdowns/stories/crud_con_python.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Mi primer **CRUD** con *Python*
|
||||
#### 18/05/2020
|
||||
|
||||
He hecho un **CRUD (Create, Read, Update and Delete)** con *Python*, y diréis, porqué? Yo os lo contaré, este tipo de "proyectitos" son fundamentales para aprender y también para no olvidar conceptos, que por muy claros que los tengas, tarde o temprano se terminan por olvidar.
|
||||
|
||||

|
||||
|
||||
Se me ocurrió hacer un programa que, usara una base de datos con *SQLite3*, que como características tiene:
|
||||
|
||||
- Crear, consultar, actualizar y eliminar usuarios
|
||||
- Comprueba que los campos tengan mínimo 3 caracteres
|
||||
- Comprueba que sea un DNI válido
|
||||
|
||||
1) Descargar el repositorio
|
||||
|
||||
> git clone https://github.com/clonbg/crud-sqlite3-python.git
|
||||
|
||||
2) Entrar en la carpeta
|
||||
|
||||
> cd crud-sqlite3-python
|
||||
|
||||
3) Ejecutar el programa
|
||||
|
||||
> python crud.py
|
||||
|
||||
Y con eso ya está. Por supuesto podéis descargarlo, modificarlo y hacer lo que queráis con él. Si os sale algún error podéis comunicarmelo a traves de mi twitter [@clonbg1](https://twitter.com/clonbg1), juntos lo solucionaremos.
|
||||
|
||||
|
||||
Salu2
|
||||
15
src/markdowns/stories/cursos-youtube.md
Normal file
15
src/markdowns/stories/cursos-youtube.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Un par de cursos de **Youtube** sobre programación
|
||||
#### 18/010/2020
|
||||
|
||||
Hoy en día hay muchos sitios donde poder formarse, unos mejores y otros de menor calidad. Yo recomiendo **Youtube**, porque es rápido, lo tienes disponible siempre que quieras y puedes encontrar auténticas joyitas.
|
||||
|
||||

|
||||
|
||||
Os quería hablar de dos cursos que me han venido muy bien. El primero, como no, es de **VueJs**, creado por *Juan Andrés Núñez*. Son 28 vídeos para iniciarse en este framework. Desde mi punto de vista es el mejor curso que puedes hacer para "dar el salto". Incluye *Ajax con Vue-Resource*, *Firebase*, *Babel*, *Gulp* y *Webpack*. Lo podéis encontrar [aquí](https://www.youtube.com/playlist?list=PLM-Y_YQmMEqD2EWfWpSbiV3WgShRRW3FE). Os recomiendo que visiteis su web [**escuelavue.es**](https://escuelavue.es/)
|
||||
|
||||

|
||||
|
||||
El otro curso es de mi otra debilidad **Python**, este consta de 51 videos creados por *Manuel J. Dávila*. Con él podréis aprender desde lo más básico hasta lo más complicado. Más de la mitad del curso está orientado a programar con la interfaz gráfica **PyQt**. Lo podéis encontrar [aquí](https://www.youtube.com/playlist?list=PLjARR1053fYlEKgn0H1-x3UM9Zl4aYLxy). También os recomiendo que os deis un paseo por sus [*listas de reproducción*](https://www.youtube.com/c/ManuelJD%C3%A1vilaGonz/playlists), hay un excelente material.
|
||||
|
||||
|
||||
Salu2
|
||||
22
src/markdowns/stories/disown.md
Normal file
22
src/markdowns/stories/disown.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Como separar un proceso de la terminal con **disown**
|
||||
#### 26/03/2021
|
||||
|
||||
Según la [Wikipedia](https://en.wikipedia.org/wiki/Disown_(Unix)) en los shells **Unix** *ksh*, *bash*, *fish* y *zsh*, el comando incorporado **disown** se utiliza para eliminar trabajos de la tabla de trabajos, o para marcar trabajos de manera que no se les envíe una señal *SIGHUP* si el *shell* padre la recibe (por ejemplo, si el usuario cierra la sesión).
|
||||
|
||||
Esto quiere decir que puede deshacer la asociación de un proceso con la terminal.
|
||||
|
||||
Veamos como se usa, si por ejemplo abrimos *chromium* desde la terminal:
|
||||
|
||||

|
||||
|
||||
Cuando cerramos la terminal se cierra *chromium* también. Sin embargo, cuando usamos **disown** podemos cerrar la terminal que el proceso pasa a estar a cargo del nucleo del sistema:
|
||||
|
||||

|
||||
|
||||
Incluso nos muestra el *PID* del proceso. Sabiendo eso también podemos *matarlo* desde la terminal con el comando:
|
||||
|
||||
```
|
||||
kill -9 16821
|
||||
```
|
||||
|
||||
Salu2
|
||||
28
src/markdowns/stories/docker-acestream.md
Normal file
28
src/markdowns/stories/docker-acestream.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Como ver contenido **acestream** en **Linux**
|
||||
|
||||
#### 24/01/2021
|
||||
|
||||
Voy a explicar como veo yo contenido **acestream** en **Linux**. No voy a entrar en que contenidos son legítimos ver y que no, **cada uno es responsable de usarlo en consecuencia**. Al tema:
|
||||
|
||||
Yo uso un contenedor docker que puedes encontrar en [Github](https://github.com/JackLiar/docker-acestream). Lo primero que hay que hacer es clonar el repositorio:
|
||||
|
||||
git clone https://github.com/JackLiar/docker-acestream
|
||||
|
||||
Lo segundo es entrar en la carpeta que se ha creado llamada _docker-acestream_ y crear el contenedor, esto sólo lo tienes que hacer la primera vez:
|
||||
|
||||
cd docker-acestream
|
||||
./build.sh
|
||||
|
||||
Después para dejar corriendo el servidor en la terminal:
|
||||
|
||||
./run.sh
|
||||
|
||||
Por último en otra terminal hay lanzar el video:
|
||||
|
||||
./playstream.py \
|
||||
--content-id CONTENT_ID \
|
||||
--player /usr/bin/vlc \
|
||||
|
||||
**CONTENT_ID** es la parte de letras y números que va después de **acestream://**, por ejemplo de esta dirección **acestream://9c5df497e3ed5853b7411d1197ab86141b4a501c** el **CONTENT_ID** es **9c5df497e3ed5853b7411d1197ab86141b4a501c**
|
||||
|
||||
Salu2
|
||||
16
src/markdowns/stories/docker-sin-sudo.md
Executable file
16
src/markdowns/stories/docker-sin-sudo.md
Executable file
@@ -0,0 +1,16 @@
|
||||
# Usar **docker** con tu usuario sin *sudo*
|
||||
#### 26/04/2021
|
||||
|
||||
El como usar **docker** sin *sudo* es posiblemente una de las cosas que más veces he buscado en internet, nunca recuerdo como se hace. Por eso voy a ponerlo aquí para poder consultarlo tantas veces como sea necesario (hasta que lo recuerde).
|
||||
|
||||
La solución es muy sencilla, basta con añadir al grupo *docker* a tu usuario. Lo podemos hacer con tres lineas de terminal:
|
||||
|
||||
sudo groupadd docker
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
|
||||
Y ya está, ya no será necesario tener privilegios para usar **docker**.
|
||||
|
||||
Visto en [atareao.es](https://atareao.es/tutorial/traefik/instalacion-de-traefik/)
|
||||
|
||||
Salu2
|
||||
24
src/markdowns/stories/eliminar-recursiva.md
Executable file
24
src/markdowns/stories/eliminar-recursiva.md
Executable file
@@ -0,0 +1,24 @@
|
||||
# Eliminar archivos de forma recursiva
|
||||
|
||||
#### 05/03/2021
|
||||
|
||||
Vamos a probar a eliminar archivos de forma recursiva desde nuestra terminal, y así, no tener que ir buscando por carpetas dichos archivos.
|
||||
|
||||
Vamos a poner el ejemplo que tenemos un entramado de carpetas con archivos con extensión *json*. Lo primero deberiamos listarlos, sólo por seguridad, para ver que vamos a borrar:
|
||||
|
||||
```
|
||||
find ~/carpeta/prueba -name "*.json" -type f
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
Ahora solo hay que añadir *-delete* al final del comando y las borrará todas de forma recursiva:
|
||||
|
||||
```
|
||||
find ~/carpeta/prueba -name "*.json" -type f -delete
|
||||
```
|
||||
|
||||
Visto en [https://geekytheory.com](https://geekytheory.com/tip-linux-eliminar-archivos-de-forma-recursiva-por-extension)
|
||||
|
||||
Salu2
|
||||
91
src/markdowns/stories/free-dns.md
Normal file
91
src/markdowns/stories/free-dns.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Conectarnos desde fuera de la red con free-dns
|
||||
#### 03/05/2020
|
||||
|
||||
*Free-dns* es un servicio gratuito con el que nos podremos conectar a nuestro ordenador desde fuera de la red. Lo uso para conectarme a una *Rasperry Pi 4* que tengo en casa y le he instalado bastantes servicios.
|
||||
|
||||
- Paso 1
|
||||
|
||||
Crear una cuenta en [freeDNS](https://freedns.afraid.org/signup/?plan=starter)
|
||||
|
||||
- Paso 2
|
||||
|
||||
En el menú de la izquierda pinchar en *subdomains* > *Add* para crear uno nuevo
|
||||
|
||||

|
||||
|
||||
De esta manera la dirección sería [http://lecheDeVaca.mooo.com](http://lecheDeVaca.mooo.com), en *Destination* tienes que poner tu IP pública
|
||||
|
||||
- Paso 3
|
||||
|
||||
En el menú *DynamicDNS*, abajo veremos los dominios creados. Hacemos botón derecho en *Direct URL*, pinchamos en *copiar enlace*, nos tendremos que quedar con lo que hay detrás de *"?"* . Será algo como [https://freedns.afraid.org/dynamic/update.php?bmdMVTVo.....](https://freedns.afraid.org/dynamic/update.php?bmdMV....)
|
||||
|
||||
- Paso 4
|
||||
|
||||
Abrimos una terminal y creamos un script en *Cron*
|
||||
|
||||
sudo nano /etc/cron.d/free-dns.sh
|
||||
|
||||
copiamos este contenido:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
# freedns_update.sh: Update the public IP on freedns.afraid.org only if it has changed.
|
||||
## Place this script in the cron's job directory /etc/cron.d and assign the proper permissions
|
||||
## and owner
|
||||
## sudo chmod 500 /etc/cron.d/freedns_update.sh
|
||||
## sudo chown root:root /etc/cron.d/freedns_update.sh
|
||||
## Add to /etc/crontab to execute on reboot and every 5 minutes
|
||||
## Edit /etc/crontab and append these two lines:
|
||||
## @reboot root /etc/cron.d/freedns_update.sh >/dev/null
|
||||
## */5 * * * * root /etc/cron.d/freedns_update.sh >/dev/null
|
||||
|
||||
#Use your own values
|
||||
DOMAIN=raspberry.liar.info.tm
|
||||
HASHKEY=c29Q2s1Ml9df604bba2b1359ff62MTEyMT185e7=
|
||||
|
||||
UPDATE_URL="http://freedns.afraid.org/dynamic/update.php?${HASHKEY}"
|
||||
|
||||
current_ip=$(wget -q --output-document - http://checkip.dyndns.org | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
|
||||
registered_ip=$(ping -qn -c 1 $DOMAIN | head -n 1 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
|
||||
|
||||
if [ "${current_ip}" != "${registered_ip}" ]; then
|
||||
wget -q --read-timeout=0.0 --waitretry=5 --tries=400 --output-document /dev/null $UPDATE_URL
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$(date +"%b %_d %T") $(hostname) $0: IP address updated on freedns.afraid.org: new IP '${current_ip}', old IP '${registered_ip}'" >> /var/log/messages
|
||||
else
|
||||
echo "$(date +"%b %_d %T") $(hostname) $0: ERROR IP address could not be updated on freedns.afraid.org: current IP '${current_ip}', registered IP '${registered_ip}'" >> /var/log/messages
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Tenemos que cambiar:
|
||||
|
||||
DOMAIN=lecheDeVaca.mooo.com
|
||||
HASKEY=Por lo que hay despues de *"?"* en *Direct URL*
|
||||
|
||||
Damos permisos de ejecución al archivo
|
||||
|
||||
sudo chmod 555 /etc/cron.d/free-dns.sh
|
||||
|
||||
- Paso 5
|
||||
|
||||
Hay que programar la tarea, para eso:
|
||||
|
||||
sudo nano /etc/crontab
|
||||
|
||||
Al final del archivo añadimos
|
||||
|
||||
@reboot root /etc/cron.d/free-dns.sh>/dev/null
|
||||
*/5 * * * * root /etc/cron.d/free-dns.sh>/dev/null
|
||||
|
||||
Es muy importante abrir los puertos *80* y *443* en el router para que se conecte fuera la tarea y para que vuelva la información.
|
||||
|
||||
Ahora ejecutamos
|
||||
|
||||
sudo /etc/init.d/cron restart
|
||||
|
||||
Se debe actualizar nuestra *IP* al ejecutarlo
|
||||
|
||||
Visto en [fororaspberry.es](https://www.fororaspberry.es/viewtopic.php?t=8077)
|
||||
|
||||
Salu2
|
||||
24
src/markdowns/stories/git-password.md
Normal file
24
src/markdowns/stories/git-password.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Hacer que git no pida contraseña
|
||||
#### 11/04/2020
|
||||
|
||||
Se hace muy pesado, cada vez que hacemos un *push* tener que andar introduciendo las credenciales de *git*. Hay dos maneras de hacer que esto no sea necesario, una permanente y otra temporal:
|
||||
|
||||
- De manera permanente:
|
||||
|
||||
Hay que establecer un asistente de credenciales en la configuración de *git*
|
||||
|
||||
git config --global credential.helper store
|
||||
|
||||
De esta manera guardará los datos en el fichero *./git-credentials*
|
||||
|
||||
- De manera temporal:
|
||||
|
||||
Se puede usar el mismo asistente de manera temporal
|
||||
|
||||
git config --global credentials.helper 'cache --timeout=3600'
|
||||
|
||||
Después de pasados los *3600* segundos (1 hora) volverá a pedir usuario y contraseña.
|
||||
|
||||
Encontrado en [blog.openalfa.com](https://blog.openalfa.com)
|
||||
|
||||
Salu2
|
||||
26
src/markdowns/stories/gogs-docker.md
Normal file
26
src/markdowns/stories/gogs-docker.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Mi propio servidor **Git** gracias a **Docker**
|
||||
|
||||
#### 03/01/2023
|
||||
|
||||
Antes de nada, decir que tener mi propio servidor de **Git** instalado en mi equipo puede no ser una buena idea, ya que tienes que ocuparte tu mismo de los backups y mantenerlo, ya que no se encuentra en ningún otro sitio. A parte de esto todo son beneficios, sobre todo para no depender de servicios de terceros.
|
||||
|
||||
Buscando un servicio fácil de instalar y claro, si puede ser con **docker**, dí con [Gogs](https://gogs.io/)
|
||||
Como indica en la página de [Github](https://github.com/gogs/gogs/tree/main/docker) con unos sencillos pasos lo tendrás funcionando:
|
||||
|
||||
```
|
||||
# Pull image from Docker Hub.
|
||||
$ docker pull gogs/gogs
|
||||
|
||||
# Create local directory for volume.
|
||||
$ mkdir -p /var/gogs
|
||||
|
||||
# Use `docker run` for the first time.
|
||||
$ docker run --name=gogs -p 10022:22 -p 10880:3000 -v /var/gogs:/data gogs/gogs
|
||||
|
||||
# Use `docker start` if you have stopped it.
|
||||
$ docker start gogs
|
||||
```
|
||||
|
||||
Ahora ya puedes acceder desde [localhost](http://localhost:10880) y crear tu usuario y empezar a usarlo.
|
||||
|
||||
Salu2
|
||||
34
src/markdowns/stories/grabar-dd.md
Normal file
34
src/markdowns/stories/grabar-dd.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Grabar una *iso* en un Usb desde la terminal con **dd**
|
||||
#### 10/01/2022
|
||||
|
||||
Desde mi experiencia, la mejor manera de grabar una imagen *iso* en un Usb es desde la terminal, con el comando **dd**. Es muy útil para no tener que quemar un DVD o un CD.
|
||||
|
||||
El primer paso es saber el destino que ocupa el Usb, con el comando:
|
||||
|
||||
```
|
||||
sudo fdisk -l
|
||||
```
|
||||
|
||||

|
||||
|
||||
Como se puede ver la dirección de mi Usb es */dev/sda*.
|
||||
|
||||
Ahora hay dos maneras de grabar el Usb:
|
||||
|
||||
- Con la uitlería *pv*:
|
||||
|
||||
```
|
||||
sudo dd if=/ruta/al/archivo/iso |pv|sudo dd of=/dev/sda bs=1M
|
||||
```
|
||||
|
||||
- o bien:
|
||||
|
||||
```
|
||||
sudo dd bs=4M if=/ruta/al/archivo/iso of=/dev/sda status=progress && sync
|
||||
```
|
||||
|
||||
Hay que tener mucho cuidado de no equivocarse con la dirección del Usb porque es donde se va a grabar.
|
||||
|
||||
Visto en [slimbook](https://slimbook.es/tutoriales/linux/330-dd-o-como-quemar-o-grabar-iso-de-sistemas-operativos-en-un-usb-sin-programas)
|
||||
|
||||
Salu2
|
||||
22
src/markdowns/stories/huawei-sin-google.md
Normal file
22
src/markdowns/stories/huawei-sin-google.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# _Huawei P40 Lite_, una opción sin **Google**
|
||||
|
||||
#### 02/08/2020
|
||||
|
||||
Encontré en [Aliexpress](https://es.aliexpress.com/item/10000341399397.html?spm=a2g0o.productlist.0.0.4fdf27bcdj8PUZ&algo_pvid=1a888230-cb1b-4378-affc-950703921c11&algo_expid=1a888230-cb1b-4378-affc-950703921c11-20&btsid=0b0a01f815963739551712893eec56&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_) una oferta que no se podía rechazar, como diría 'El Padrino'. Se trataba del móvil de _Huawei P40 Lite_. Un aparato con unas [características](https://www.smart-gsm.com/moviles/huawei-p40-lite) más que interesantes. Hay un problema (o no según se mire), no se puede instalar los servicios de **Google**.
|
||||
Llevo ya tiempo queriendo deshacerme de _la gran G_ pero me encontraba con varios problemas.
|
||||
|
||||
Decidí comprarlo y buscar soluciones a los servicios que usaba.
|
||||
|
||||
- Para la tienda de aplicaciones me instalé [_Aptoide_](https://es.aptoide.com/). Tiene todos los programas que necesito y se actualizan rápido.
|
||||
- Para las copias de seguridad de mis fotos instalé [_Mega_](https://mega.nz/). Sube automáticamente tus fotos a su nube de manera que no las pierdas.
|
||||
- Para los mapas instalé [_Here WeGo_](https://wego.here.com). Antes usaba _Waze_ pero como son los mapas de _Google_ tampoco funciona. Esta aplicación funciona muy bien y además avisa del estado del tráfico, algo para mi, muy importante.
|
||||
- Como navegador [_Firefox_](https://www.mozilla.org/es-ES/firefox/new/). Esto para mi siempre ha sido innegociable.
|
||||
- Correo seguí usando el de _Gmail_ pero a través de la aplicación que usa el sistema.
|
||||
- Teclado, a mi me gustaba *Gboard* y después de probar varios me decidí por *Go Keyboard* ya que tiene escritura *swife*
|
||||
|
||||
Ahora viene lo más difícil, por un lado tener los documentos a mano como en el _Drive_, y por otro lado los contactos y el calendario. Esto lo solucioné con ayuda de mi servidor, la _Raspberry Pi 4_ que tengo instalada en casa y con la que puedo acceder desde fuera de mi red. Si no sabes como hacer esto puedes revisar esto [Conectarnos desde fuera de la red con free-dns](https://clonbg.netlify.app/#/free-dns)
|
||||
|
||||
- Lo primero simplemente creé una carpeta llamada 'Documentos' en mi _Raspberry_, puse ahí todo lo que me hizo falta y accedo a ella a traves de _sftp_ con la plicación [_Cx Explorador de archivos_](https://apkpure.com/es/cx-file-explorer/com.cxinventor.file.explorer)
|
||||
- Lo de los contactos y el calendario es más difícil, os lo explicaré en el próximo post ;)
|
||||
|
||||
Salu2
|
||||
82
src/markdowns/stories/literales.md
Normal file
82
src/markdowns/stories/literales.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# *Template literals* o *comillas invertidas*
|
||||
#### 13/09/2020
|
||||
|
||||
Como sabeis en *JavaScript*, para declarar un **String** hay que ponerlo entre comillas:
|
||||
|
||||
const texto = "me llamo Manuel"
|
||||
const texto = 'me llamo Manuel'
|
||||
|
||||
Desde la versión **ES6** es posible hacerlo con las comillas invertidas:
|
||||
|
||||
const texto = `me llamo Manuel`
|
||||
|
||||
¿Que ventajas tiene esto?, una de ellas es la **inserciòn de variables**. La forma habitual:
|
||||
|
||||
const nombre = 'Manuel'
|
||||
const texto = 'me llamo ' + nombre
|
||||
|
||||
Con comillas literales
|
||||
|
||||
const nombre = `Manuel`
|
||||
const texto = `me llamo ${nombre}`
|
||||
|
||||
Además acepta funciones u operaciones, veamos un ejemplo:
|
||||
|
||||
const num1 = `7`
|
||||
const num2 = `4`
|
||||
const multiplicar = `Multiplicar ${num1} y ${num2} da como resultado ${num1 * num2}`
|
||||
|
||||
Y ahora con una función que comprueba si un número es primo o no:
|
||||
|
||||
const num = `8`
|
||||
const primo = `El número ${num} ${esPrimo(num) ? 'es primo' : 'no es primo'}
|
||||
|
||||
Otra ventaja es que te permite definir cadenas de texto en varias líneas:
|
||||
|
||||
const texto = `Esta es la primera linea,
|
||||
segunda línea y
|
||||
tercera linea`
|
||||
|
||||
|
||||
Y por último los **Template tags**, no es más que una manera de ejecutar una función con *Strings*. Por ejemplo creamos esta función:
|
||||
|
||||
```
|
||||
function prueba(literales, ...expresiones) {
|
||||
for (let i = 0; i < literales.length; i++) {
|
||||
console.log(`literal ${i} es: "${literales[i]}"`);
|
||||
}
|
||||
for (let j = 0; j < expresiones.length; j++) {
|
||||
console.log(`expresión ${j} es: "${expresiones[j]}"`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Al ejecutar la función con un *String* se crean dos *Arrays*, "literales" que contiene las partes de texto y "expresiones" que contiene, como su nombre indica las expresiones.
|
||||
|
||||
Al ejecutar:
|
||||
|
||||
```
|
||||
const num1 = 5;
|
||||
const num2 = 3;
|
||||
|
||||
const resultado = prueba`La suma de ${num1} y ${num2} es igual a ${
|
||||
num1 + num2
|
||||
}`;
|
||||
console.log(resultado);
|
||||
```
|
||||
|
||||
esto da como resultado:
|
||||
|
||||
```
|
||||
literal 0 es: "La suma de "
|
||||
literal 1 es: " y "
|
||||
literal 2 es: " es igual a "
|
||||
literal 3 es: ""
|
||||
expresión 0 es: "5"
|
||||
expresión 1 es: "3"
|
||||
expresión 2 es: "8"
|
||||
```
|
||||
|
||||
Visto en [neoguias.com](https://www.neoguias.com/template-literals-javascript/)
|
||||
|
||||
Salu2
|
||||
12
src/markdowns/stories/manuales-vuejs-espanol.md
Normal file
12
src/markdowns/stories/manuales-vuejs-espanol.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Manuales de VueJS
|
||||
#### 15/03/2020
|
||||
|
||||
Tenía guardados un par de manuales de *VueJS* que en su momento me ayudaron mucho, sobretodo a entender el funcionamiento principal y a empezar, que siempre suele ser lo más difícil.
|
||||
|
||||
El primero es una introducción, conceptos generales y el segundo está enfocado al desarrollo de aplicaciones, aunque yo para eso prefiero [Quasar](https://quasar.dev/).
|
||||
|
||||
[Introducción a Vue](https://mega.nz/#!AdlzyJ7K!KnTmHCGIdjGU5o8Zeu1LDRWUU9V13SsUkgMfkZA5WI0)
|
||||
|
||||
[Desarrollo de aplicaciones con Vue](https://mega.nz/#!5c8X0BIL!msAt4lwSVnnMhAov_qc908kR_t5SIYgpcLl99So0G1k)
|
||||
|
||||
Salu2
|
||||
27
src/markdowns/stories/mkinitcpio.md
Normal file
27
src/markdowns/stories/mkinitcpio.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Possibly missing firmware for module: aic94xx, wd719x
|
||||
#### 29/03/2020
|
||||
|
||||
Soy usuario de Gnu/Linux en todos mis equipos, en todos en los que es posible. En mi sobremesa cada vez que compilaba el kernel o ejecutaba
|
||||
|
||||
mkinitcpio -p linux
|
||||
|
||||
me daba un error, bueno, más bien dos:
|
||||
|
||||
Possibly missing firmware for module: aic94xx
|
||||
Possibly missing firmware for module: wd719x
|
||||
|
||||
La compilación acababa perfectamente y tampoco había notado nada especial en el equipo. Ya sabemos como somos algunos informáticos, estuve buscando una solución por todas partes, durante meses. Hasta que la encontré en uno de los innumerables foros de Github. Se solucionaba bajando el código fuente de cada driver y compilándolo, facil.
|
||||
|
||||
git clone https://aur.archlinux.org/aic94xx-firmware.git
|
||||
cd aic94xx-firmware
|
||||
makepkg -sri
|
||||
|
||||
git clone https://aur.archlinux.org/wd719x-firmware.git
|
||||
cd wd719x-firmware
|
||||
makepkg -sri
|
||||
|
||||
|
||||
|
||||
Visto en [Github](https://gist.github.com/imrvelj/c65cd5ca7f5505a65e59204f5a3f7a6d)
|
||||
|
||||
Salu2
|
||||
16
src/markdowns/stories/modelo.md
Normal file
16
src/markdowns/stories/modelo.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Activar el *scroll* en **Tmux**
|
||||
#### 31/05/2020
|
||||
|
||||
Algo muy sencillo que me ha traído de cabeza ha sido activar el *scroll* en **tmux**. Me hacía falta porque en **Linux** es fundamental leer lo que pasa en la terminal, además cuando intentas ir hacia arriba van saliendo los últimos comandos, como cuando le das al botón de *flecha arriba* de nuestro teclado.
|
||||
|
||||

|
||||
|
||||
Solo hay que crear un archivo de configuración de *tmux* y añadirle la opción, esto se puede hacer con tan solo una linea en nuestra terminal:
|
||||
|
||||
"set -g mouse on" >> ~/.tmux.conf
|
||||
|
||||
Después de esto tienes que volver a abrir *tmux* y ya lo tienes
|
||||
|
||||
Visto en [https://superuser.com](https://superuser.com/questions/209437/how-do-i-scroll-in-tmux)
|
||||
|
||||
Salu2
|
||||
27
src/markdowns/stories/nativefier.md
Normal file
27
src/markdowns/stories/nativefier.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Crea tus propias aplicaciones web con **Nativefier**
|
||||
#### 27/09/2020
|
||||
|
||||
Esta es una maravillosa utilidad que convierte en aplicación *electrón* una dirección web. De esta manera puedes tener una aplicación de escritorio con: WhatsApp, Telegram, Twitter, etc. Aquí tienes su [*GitHub*](https://github.com/jiahaog/nativefier), está todo muy bien explicado.
|
||||
|
||||
El primer paso es instalarla:
|
||||
|
||||
sudo npm i -g nativefier
|
||||
|
||||
Después me he creado una carpeta llamada **Nativefier** para descargar ahí las aplicaciones. Entramos en ella:
|
||||
|
||||
cd Nativefier
|
||||
|
||||
Ahora tenemos que saber la dirección web que queremos convertir, por ejemplo la de [*Telegram*](https://web.telegram.org/), y la creamos:
|
||||
|
||||
nativefier https://web.telegram.org/
|
||||

|
||||
|
||||
Se ha creado una carpeta llamada *TelegramWeb-linux-x64*, con el ejecutable *TelegramWeb*. Para poder ejecutarlo en cualquier parte de nuestro sistema creamos un enlace simbólico al ejecutable hasta nuestro **$PATH**:
|
||||
|
||||
sudo ln -s ~/Nativefier/TelegramWeb-linux-x64/TelegramWeb /usr/bin/
|
||||
|
||||
lo ejecutamos y listo:
|
||||
|
||||
TelegramWeb
|
||||
|
||||
Salu2
|
||||
8
src/markdowns/stories/nuevo-blog.md
Normal file
8
src/markdowns/stories/nuevo-blog.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Nuevo *Blog* programado desde 0 con quasar.dev
|
||||
#### 22/12/2021
|
||||
|
||||
Cómo podéis ver hace tiempo que no publico y es que se me ha ocurrido rehacer el **Blog de Clonbg** desde cero. Está hecho con [quasar](https://quasar.dev/) qué es un *framework* para *Vuejs*. Tenéis todo el código publicado en mi [Github](https://github.com/clonbg/ejercicio_quasar). Una cosa a tener en cuenta en cuanto al ćodigo, es que tiene dos ramas, una es la *master*, donde puse comentarios, y la otra, *"sin_comentarios*, que es la que está terminada y desplegada en [Netlify](https://clonbg.netlify.app/#/).
|
||||
|
||||
El aspecto visual es bastante sencillito pero en lo que tiene que ver con el funcionamiento el cambio es brutal, la velocidad a la que realiza las búsquedas es fantástica!
|
||||
|
||||
Salu2
|
||||
24
src/markdowns/stories/ouroboros.md
Executable file
24
src/markdowns/stories/ouroboros.md
Executable file
@@ -0,0 +1,24 @@
|
||||
# Actualización automática de contenedores con **Ouroboros**
|
||||
#### 13/04/2021
|
||||
|
||||
La actuaización automática de contenedores te puede evitar mucho tiempo, por no hablar de los agujeros de seguridad de versiones antiguas. Además este método te mantiene informado de la manera que quieras, yo lo tengo configurado para que me mande un mensaje a *Telegram* cada vez que hace una actualización.
|
||||
El nombre de este servicio viene del griego, es el signo de una serpiente que se muerde la cola formando un círculo y que le da un significado de movimiento continuo.
|
||||
Veamos como funciona.
|
||||
|
||||
**Ouroboros** incluye [*Apprise*](https://github.com/caronc/apprise) en su código, el cual es un sistema de mensajería que incluye muchísimos servicios. Yo he elegido *Telegram* por su sencillez y comodidad.
|
||||
Lo primero es crear un @Bot de *Telegram* con el método que nos indica [danielmartingonzalez.com](https://www.danielmartingonzalez.com/es/notificaciones-de-home-assistant-en-telegram/#bots-en-telegram). Una vez tengas el *API Token* y el *Chat ID* podriamos levantar un contenedor de la siguiente forma:
|
||||
|
||||
```
|
||||
docker run -d --name=Ouroboros --hostname=ouroboros --network=host --restart=always -v /var/run/docker.sock:/var/run/docker.sock -e CLEANUP=true -e TZ='Europe/Madrid' -e INTERVAL=1200 -e NOTIFIERS="tgram://API_Token/Chat_ID/" pyouroboros/ouroboros:latest
|
||||
```
|
||||
Esto creará una instancia que se mantendrá en funcionamiento y cada 1200 segundos (20 minutos) vigilará si hay alguna actualización posible. Cuando haya actualizado un contenedor te mandará un mensaje a *Telegram* de este tipo:
|
||||
|
||||

|
||||
|
||||
Tiene un par de extras:
|
||||
- Puedes decirle que contenedores quieres que no sean monitorizados añadiendo ```-e IGNORE="contenedor1 contenedor2..."```
|
||||
- Elimina las imagenes anteriores para que no te ocupen espacio.
|
||||
|
||||
Visto en [www.danielmartingonzalez.com](https://www.danielmartingonzalez.com/es/actualizacion-automatica-de-contenedores/)
|
||||
|
||||
Salu2
|
||||
22
src/markdowns/stories/portainer.md
Normal file
22
src/markdowns/stories/portainer.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# **Portainer**, magnífico gestor para **Docker**
|
||||
#### 07/11/2020
|
||||
|
||||
Cuando pruebas *docker* una vez, te engancha por su facilidad y porque, al estar separado en *contenedores* no puede corromper tu sistema. Después de cierto tiempo tienes decenas de contenedores funcionando y es ahí cuando un gestor para *docker* se vuelve necesario. Para esto tenemos a [Portainer](https://www.portainer.io), es **open-source** y nos ayudará a eliminar, actualizar, etc...
|
||||
|
||||
Portainer es un servicio web y se instala con *docker*, como no! Con este comando lo tendrás funcionando:
|
||||
|
||||
$ docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
|
||||
|
||||
Que es lo que hace este comando?:
|
||||
|
||||
- corre el servicio como un *demonio*
|
||||
- se reinicia siempre que sea necesario
|
||||
- y crea un volumen en tu sistema *portainer_data* para que los datos persistan al actualizar el *docker*
|
||||
|
||||
Para acceder [http://localhost:9000](http://localhost:9000/)
|
||||
|
||||
Te pedirá crear usuario y contraseña, después te pregunta si lo quieres para gestionar un servidor remoto o en modo local, en mi caso esto último y ya puedes entrar y empezar a gestionar. Para más información sobre su funcionamiento [portainer.io](https://documentation.portainer.io/) pero ya os digo que es muy intuitivo.
|
||||
|
||||
Para actualizarlo..., el siguiente post hablará sobre eso.
|
||||
|
||||
Salu2
|
||||
60
src/markdowns/stories/primeros-pasos-python-qt.md
Normal file
60
src/markdowns/stories/primeros-pasos-python-qt.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Primeros pasos con PyQt y QtDesigner. Primer programa con Python
|
||||
#### 05/05/2020
|
||||
|
||||
Vamos a crear nuestro primer programa gráfico con Python y PyQt, es mucho más sencillo de lo que parece. Lo primero que tenemos que hacer es instalar las librerías. Si estás en *Arch/Linux* como estoy yo:
|
||||
|
||||
sudo pacman -S python-pyqt5 python
|
||||
|
||||
En nuestro menú nos habrá salido un programa llamado *QtDesigner*, lo abrimos
|
||||
|
||||

|
||||
|
||||
Cada vez que lo abrimos nos pregunta con que *plantilla* vamos a empezar. Le daremos *Main Window*, que es la raiz del programa. Qt funciona con un sistema de *Widgets*, todo lo que se añade es un *widget*, un botón, un label, etc.
|
||||
Para crear nuestro primer programa añadiremos un *Label* y un *Push Button* a nuestra ventana.
|
||||
|
||||

|
||||
|
||||
Podemos ver como queda la interfaz con *Control+R*
|
||||
|
||||

|
||||
|
||||
Ahora solo falta guardar la interfaz. *File > Save* y guardamos el archivo *ui*. Yo lo he llamado *post.ui*. Ahora al lado de donde hemos guardado el archivo *post.ui* creamos un archivo de *python*, yo lo he llamado *post.py* con este contenido:
|
||||
|
||||
from PyQt5 import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
app = QtWidgets.QApplication([])
|
||||
dlg = uic.loadUi('post.ui')
|
||||
|
||||
# Contenido del programa
|
||||
|
||||
dlg.show()
|
||||
app.exec()
|
||||
|
||||
y lo ejecutamos desde la línea de terminal:
|
||||
|
||||
python post.py
|
||||
|
||||
Si todo ha ido bien se nos tiene que abrir el programa. Ahora vamos a hacer que cuando hagamos click en el botón cambie el texto del *Label*. El programa quedaría así:
|
||||
|
||||
from PyQt5 import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
app = QtWidgets.QApplication([])
|
||||
dlg = uic.loadUi('post.ui')
|
||||
|
||||
# Creamos la función que cambiará el texto
|
||||
def cambio():
|
||||
dlg.label.setText('Este texto ha cambiado')
|
||||
|
||||
# Creamos la acción con su función
|
||||
dlg.pushButton.clicked.connect(cambio)
|
||||
|
||||
dlg.show()
|
||||
app.exec()
|
||||
|
||||
Y cuando clickamos en el botón:
|
||||
|
||||

|
||||
|
||||
Salu2
|
||||
86
src/markdowns/stories/pyqt5-icono.md
Normal file
86
src/markdowns/stories/pyqt5-icono.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Minimizar al *tray* un programa hecho en **PyQt5**
|
||||
#### 29/08/2021
|
||||
|
||||
Como podeis ver en el post sobre [Mi primer *CRUD* con *Python*](https://clonbg.netlify.app/#/crud_con_python), tenía hecho un programa que simplemente era un registro de usuarios implementado en *Python* con *PyQt5* y *Mysql3* como base de datos. Funcionaba bien pero había dejado para más adelante algunas funciones (ya sabéis lo que pasa con estas cosas). Ahora he escrito unos cambios y tiene icono en el *tray* del sistema y se puede cerrar, minimizar y maximizar a discrección.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Veamos como lo he hecho:
|
||||
|
||||
- Lo primero que he hecho ha sido añadir un icono y un título a la barra del programa con estas dos líneas:
|
||||
```
|
||||
dlg.setWindowTitle('Base de datos de usuarios en SQLite3') # Nombre del título
|
||||
dlg.setWindowIcon(QIcon('usuario.png')) # Icono del título
|
||||
```
|
||||
Evidentemente hay que tener un icono con el nombre *usuario.png*.
|
||||
|
||||
- Añadir, en este caso el mismo icono, al *tray* del sistema:
|
||||
```
|
||||
trayIcon = QSystemTrayIcon(QIcon('usuario.png'), parent=app)
|
||||
trayIcon.setToolTip('Base de Datos SQLite3')
|
||||
def icono():
|
||||
if dlg.checkIcono.isChecked():
|
||||
trayIcon.show()
|
||||
else:
|
||||
trayIcon.hide()
|
||||
```
|
||||
- Aquí creamos el menu del icono y le añadimos tres acciones, *maximizar*, *minimizar* y *cerrar*, después eliminamos *maximizar* ya que no le hace falta la primera vez.
|
||||
```
|
||||
menu = QMenu()
|
||||
minimizeAction = menu.addAction('Minimizar')
|
||||
minimizeAction.triggered.connect(minimiza)
|
||||
quitAction = menu.addAction('Cerrar')
|
||||
quitAction.triggered.connect(dlg.close)
|
||||
trayIcon.setContextMenu(menu) # Aquí se añaden al icono
|
||||
maximizeAction = menu.addAction('Maximizar')
|
||||
maximizeAction.triggered.connect(maximiza)
|
||||
menu.removeAction(maximizeAction) # Una vez creada se elimina del icono
|
||||
```
|
||||
|
||||
- Más tarde centramos la ventana, he necesitado hacerlo ya que cuando se maximizaba desde el menú salía la barra fuera de la pantalla.
|
||||
```
|
||||
qr = dlg.frameGeometry()
|
||||
cp = QtWidgets.QDesktopWidget().availableGeometry().center()
|
||||
qr.moveCenter(cp)
|
||||
dlg.move(qr.topLeft())
|
||||
```
|
||||
|
||||
- Las funciones que minimizan y maximizan la aplicación. El motivo de eliminar la acción de *cerrar* y añadirla más tarde es para que siempre esté al final del menú.
|
||||
```
|
||||
def minimiza():
|
||||
dlg.hide()
|
||||
menu.removeAction(quitAction)
|
||||
menu.addAction(maximizeAction)
|
||||
menu.addAction(quitAction)
|
||||
menu.removeAction(minimizeAction)
|
||||
|
||||
def maximiza():
|
||||
dlg.show()
|
||||
menu.removeAction(quitAction)
|
||||
menu.addAction(minimizeAction)
|
||||
menu.addAction(quitAction)
|
||||
menu.removeAction(maximizeAction)
|
||||
```
|
||||
- Le he añadido la opción de minimizar y maximizar al hacer doble click en el icono
|
||||
```
|
||||
def onTrayIconActivated(reason):
|
||||
if reason == trayIcon.DoubleClick:
|
||||
# print('double click')
|
||||
if dlg.isVisible():
|
||||
minimiza()
|
||||
else:
|
||||
maximiza()
|
||||
trayIcon.activated.connect(onTrayIconActivated)
|
||||
```
|
||||
|
||||
Otras funciones añadidas son:
|
||||
|
||||
- Comprobamos que el DNI no exista en la base de datos
|
||||
- Comprobamos que el nombre y apellidos solo acepten letras
|
||||
- Mensajes de error varios
|
||||
|
||||
El código completo lo puedes encontrar en mi repositorio de [**Github**](https://github.com/clonbg/crud-sqlite3-python)
|
||||
|
||||
Salu2
|
||||
193
src/markdowns/stories/radicale.md
Normal file
193
src/markdowns/stories/radicale.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Mi propio calendario y mi libreta de contactos con *Radicale*
|
||||
#### 16/08/2020
|
||||
|
||||
Os contaba en el anterior post la adquisición de mi nuevo terminal y de la "necesidad" de alejarme de **Google**. Dos herramientas muy importantes para mi vinculadas a *la gran G* eran el calendario y los contactos.
|
||||
|
||||
Lo he solucionado instalando [**Radicale**](https://radicale.org/3.0.html) en la Raspberry Pi 4 que tengo en casa como servidor.
|
||||
|
||||
Las aplicaciones del lado del cliente que uso son **DAV<sub>x<sup>5</sup></sub>** en Android, y en linux uso **Thunderbird**.
|
||||
|
||||
Para instalarlo:
|
||||
|
||||
sudo apt install radicale python3-radicale
|
||||
|
||||
La forma de autenticarse de manera segura es usando *htpasswd*, con este comando te crea el archivo *users*, el cual yo he guardado junto al de configuración en ```/etc/radicale/users```, y te pide la contraseña para el usuario:
|
||||
|
||||
htpasswd -c /etc/radicale/users usuario
|
||||
|
||||
El archivo de configuración ```/etc/radicale/config``` tiene que tener esta configuración:
|
||||
|
||||

|
||||
|
||||
Aquí os dejo el mio para que podáis echarle un vistazo:
|
||||
|
||||
```
|
||||
# -*- mode: conf -*-
|
||||
# vim:ft=cfg
|
||||
|
||||
# Config file for Radicale - A simple calendar server
|
||||
#
|
||||
# Place it into /etc/radicale/config (global)
|
||||
# or ~/.config/radicale/config (user)
|
||||
#
|
||||
# The current values are the default ones
|
||||
|
||||
|
||||
[server]
|
||||
|
||||
# CalDAV server hostnames separated by a comma
|
||||
# IPv4 syntax: address:port
|
||||
# IPv6 syntax: [address]:port
|
||||
# For example: 0.0.0.0:9999, [::]:9999
|
||||
hosts = 192.168.1.97:5232
|
||||
|
||||
# Daemon flag
|
||||
#daemon = False
|
||||
|
||||
# File storing the PID in daemon mode
|
||||
#pid =
|
||||
|
||||
# Max parallel connections
|
||||
max_connections = 5
|
||||
|
||||
# Max size of request body (bytes)
|
||||
max_content_length = 100000000
|
||||
|
||||
# Socket timeout (seconds)
|
||||
#timeout = 30
|
||||
|
||||
# SSL flag, enable HTTPS protocol
|
||||
#ssl = False
|
||||
|
||||
# SSL certificate path
|
||||
certificate = /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
|
||||
# SSL private key
|
||||
key = /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
|
||||
# CA certificate for validating clients. This can be used to secure
|
||||
# TCP traffic between Radicale and a reverse proxy
|
||||
#certificate_authority =
|
||||
|
||||
# SSL Protocol used. See python's ssl module for available values
|
||||
#protocol = PROTOCOL_TLSv1_2
|
||||
|
||||
# Available ciphers. See python's ssl module for available ciphers
|
||||
#ciphers =
|
||||
|
||||
# Reverse DNS to resolve client address in logs
|
||||
#dns_lookup = True
|
||||
|
||||
# Message displayed in the client when a password is needed
|
||||
#realm = Radicale - Password Required
|
||||
|
||||
|
||||
[encoding]
|
||||
|
||||
# Encoding for responding requests
|
||||
#request = utf-8
|
||||
|
||||
# Encoding for storing local collections
|
||||
#stock = utf-8
|
||||
|
||||
|
||||
[auth]
|
||||
|
||||
# Authentication method
|
||||
# Value: none | htpasswd | remote_user | http_x_remote_user
|
||||
type = htpasswd
|
||||
|
||||
# Htpasswd filename
|
||||
htpasswd_filename = /etc/radicale/users
|
||||
|
||||
# Htpasswd encryption method
|
||||
# Value: plain | sha1 | ssha | crypt | bcrypt | md5
|
||||
# Only bcrypt can be considered secure.
|
||||
# bcrypt and md5 require the passlib library to be installed.
|
||||
htpasswd_encryption = md5
|
||||
|
||||
# Incorrect authentication delay (seconds)
|
||||
delay = 2
|
||||
|
||||
|
||||
[rights]
|
||||
|
||||
# Rights backend
|
||||
# Value: none | authenticated | owner_only | owner_write | from_file
|
||||
type = from_file
|
||||
|
||||
# File for rights management from_file
|
||||
file = /etc/radicale/rights
|
||||
|
||||
|
||||
[storage]
|
||||
|
||||
# Storage backend
|
||||
# Value: multifilesystem
|
||||
#type = multifilesystem
|
||||
|
||||
# Folder for storing local collections, created if not present
|
||||
filesystem_folder = /home/dietpi/calendarios
|
||||
|
||||
# Lock the storage. Never start multiple instances of Radicale or edit the
|
||||
# storage externally while Radicale is running if disabled.
|
||||
#filesystem_locking = True
|
||||
|
||||
# Sync all changes to disk during requests. (This can impair performance.)
|
||||
# Disabling it increases the risk of data loss, when the system crashes or
|
||||
# power fails!
|
||||
#filesystem_fsync = True
|
||||
|
||||
# Delete sync token that are older (seconds)
|
||||
#max_sync_token_age = 2592000
|
||||
|
||||
# Close the lock file when no more clients are waiting.
|
||||
# This option is not very useful in general, but on Windows files that are
|
||||
# opened cannot be deleted.
|
||||
#filesystem_close_lock_file = False
|
||||
|
||||
# Command that is run after changes to storage
|
||||
# Example: ([ -d .git ] || git init) && ([ -e .gitignore ] || printf '.Radicale.cache\n.Radicale.lock\n.Radicale.tmp-*\n' > .gitignore) && git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
|
||||
#hook =
|
||||
|
||||
|
||||
[web]
|
||||
|
||||
# Web interface backend
|
||||
# Value: none | internal
|
||||
type = internal
|
||||
|
||||
|
||||
[logging]
|
||||
|
||||
# Logging configuration file
|
||||
# If no config is given, simple information is printed on the standard output
|
||||
# For more information about the syntax of the configuration file, see:
|
||||
# http://docs.python.org/library/logging.config.html
|
||||
#config =
|
||||
|
||||
# Set the default logging level to debug
|
||||
#debug = False
|
||||
|
||||
# Store all environment variables (including those set in the shell)
|
||||
#full_environment = False
|
||||
|
||||
# Don't include passwords in logs
|
||||
#mask_passwords = True
|
||||
|
||||
|
||||
[headers]
|
||||
|
||||
# Additional HTTP headers
|
||||
#Access-Control-Allow-Origin = *
|
||||
```
|
||||
|
||||
Líneas a tener en cuenta:
|
||||
|
||||
- **hosts**, aquí va la ip del servidor
|
||||
- **filesystem_folder**, la dirección donde se ubican los calendarios
|
||||
|
||||
Solo falta entrar en la web [localhost](http:localhost:5232), o en la dirección Ip del servidor y crear vuestro calendario y vuestra libreta de direcciones, instalar **DAV<sub>x<sup>5</sup></sub>** en vuestro móvil y empezar a usarlo. Si quieres que se actualice fuera de casa échale un ojo [aquí](https://clonbg.netlify.app/#/free-dns), además acuerdate de abrir el puerto *5232* en vuestro router.
|
||||
|
||||
|
||||
Salu2
|
||||
37
src/markdowns/stories/reinicio-programado.md
Normal file
37
src/markdowns/stories/reinicio-programado.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Programar el reinicio del ordenador de forma automática mediante **Cron**
|
||||
#### 05/01/2021
|
||||
|
||||
Últimamente he tenido bastantes problemas de conexión de la *Raspberry Pi*, cuando pasaban 2 ó 3 días sin reiniciarla (lo cual es algo habitual), a veces se bloqueaba y no podía acceder vía **ssh**, con lo que no podía reiniciarla hasta llegar a casa.
|
||||
Como hay ciertas horas, de madrugada, que la *Pi* no se utiliza, he decido que se reinicie todos los días a una determinada hora. Para esto la mejor solución es **Cron**.
|
||||
|
||||
Lo primero es abrir un terminal y escribir:
|
||||
|
||||
sudo crontab -e
|
||||
|
||||
Una vez añadido hay que añadirle una línea con la siguiente estructura:
|
||||
|
||||
min hor dom mon dow comando
|
||||
|
||||
- min: minuto en el que se va a ejecutar el comando, de 0 a 59
|
||||
- hor: hora en la que se va a ejecutar el comando, de 0 a 23
|
||||
- dom: día del mes en el que se va a ejecutar el comando, de 1 a 31. Si queremos que se ejecute todos los días hay que cambiarlo por un *
|
||||
- mon: mes en el que se va a ejecutar el comando, de 1 a 12. Si queremos que se ejecute todos los meses hay que cambiarlo por un *
|
||||
- dow: día de la semana que se va a ejecutar el comando, de 0 a 6. Si pones un 0 el domingo, y si pones un 6 el sábado. Si queremos que se ejecute todos los días hay que cambiarlo por un *
|
||||
- comando: el comando que se ejecuta, en nuestro caso `/sbin/reboot -h now`
|
||||
|
||||
Para que se reinicie todos los días a las 4:00 quedaría así:
|
||||
|
||||
|
||||
Una vez guardados los cambios, hay que reiniciar el servicio:
|
||||
|
||||
sudo service cron restart
|
||||
|
||||
Ya hemos terminado, no era tan difícil!!?!
|
||||
|
||||
Visto en [https://geekland.eu](https://geekland.eu/programar-el-apagado-del-ordenador/)
|
||||
|
||||
Salu2
|
||||
<!--stackedit_data:
|
||||
eyJoaXN0b3J5IjpbNjU5NzAzNjUsNTY0NTA2NjM1LDE3MzYzNz
|
||||
U3NTcsLTUxMDk5NzgxNSwtNDIzNjkwODldfQ==
|
||||
-->
|
||||
40
src/markdowns/stories/script-autostart.md
Executable file
40
src/markdowns/stories/script-autostart.md
Executable file
@@ -0,0 +1,40 @@
|
||||
# Script en **bash** que comprueba si hay internet y ejecuta unos programas
|
||||
#### 19/09/2021
|
||||
|
||||
Al arrancar mi sistema operativo y estar en un sitio sin conexión a internet algunos programas se ejecutaban igualmente dando lugar a fallos, evidentemente. Me decidí a crear un *script* que compruebe si hay conexión a internet y ejecutar dichos programas, además cuando no hay conexión los *mata*.
|
||||
|
||||
Este es el esquema:
|
||||
|
||||

|
||||
|
||||
y aquí está el *script*:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
sleep 20
|
||||
termina=true
|
||||
while [ true ]; do
|
||||
echo 'funcionando loop'
|
||||
if ping -c1 google.com &>/dev/null; then
|
||||
echo "tienes conexión"
|
||||
if [ $termina = true ]; then
|
||||
echo «Ejecutando»
|
||||
programa1 &
|
||||
programa2 &
|
||||
termina=false
|
||||
elif [ $termina = false ]; then
|
||||
echo "Ya no hace falta"
|
||||
fi
|
||||
else
|
||||
killall programa1 &
|
||||
killall programa2 &
|
||||
echo «no tienes conexion»
|
||||
termina=true
|
||||
fi
|
||||
sleep 600
|
||||
done
|
||||
```
|
||||
|
||||
Lo puedes modificar según tus necesidades. Yo, por ejemplo monto una unidad en red y uso [kalu](https://jjacky.com/kalu/).
|
||||
|
||||
Salu2
|
||||
77
src/markdowns/stories/script-aviso-bateria.md
Normal file
77
src/markdowns/stories/script-aviso-bateria.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# *Script* para avisar de la carga de la batería
|
||||
#### 01/06/2020
|
||||
|
||||
Este fin de semana he instalado **ArchLinux** con el escritorio **MATE**, todo con ayuda de [ArcoLinux](https://arcolinux.com/). Me he encontrado con que el *gestor de energía* no me lanzaba avisos, ni cuando la batería estaba casi llena ni cuando estaba prácticamente vacía, así que decidí buscar un método.
|
||||
|
||||
Encontré un script en [slimbook.es](https://slimbook.es/tutoriales/linux/45-script-linux-para-avisar-de-batearia-cargada) pero no funcionaba correctamente. Lo que ocurría es que el comando *notify-send* no se lanzaba, pero encontré la solución. Primero vamos a ejecutar el comando:
|
||||
|
||||
id -a
|
||||
|
||||
Nos da algo como esto de salida, tenemos que quedarnos con el número donde pone *uid* seguido de nuestro nombre de usuario, es el *id* de usuario
|
||||
|
||||

|
||||
|
||||
Este es el script:
|
||||
|
||||
```
|
||||
#!bin/bash
|
||||
#
|
||||
# -*- ENCODING: UTF-8 -*-
|
||||
# Este programa es software libre. Puede redistribuirlo y/o
|
||||
# modificarlo bajo los términos de la Licencia Pública General
|
||||
# de GNU según es publicada por la Free Software Foundation,
|
||||
# bien de la versión 2 de dicha Licencia o bien (según su
|
||||
# elección) de cualquier versión posterior.
|
||||
#
|
||||
# Si usted hace alguna modificación en esta aplicación,
|
||||
# deberá siempre mencionar al autor original de la misma.
|
||||
#
|
||||
# DesdeLinux.net CC-BY-SA 2015
|
||||
# Autor: ELAV
|
||||
BATLVL=$(cat /sys/class/power_supply/BAT1/capacity)
|
||||
if [ $BATLVL -ge 95 ]; then
|
||||
if [ ! -a /tmp/batwarn ]; then
|
||||
#Esta es la línea que hay que modificar
|
||||
sudo -u clonbg DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send --urgency=critical --expire-time=5000 --app-name=Bateria --icon=battery "Notificación de Batería" "Desconecta el cargador por favor" ;
|
||||
touch /tmp/batwarn ;
|
||||
fi
|
||||
elif [ $BATLVL -le 15 ]; then
|
||||
if [ ! -a /tmp/batwarn ]; then
|
||||
#Esta es la línea que hay que modificar
|
||||
sudo -u clonbg DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send --urgency=critical --expire-time=5000 --app-name=Bateria --icon=battery "Notificación de Batería" "Conecta el cargador por favor"
|
||||
touch /tmp/batwarn ;
|
||||
fi
|
||||
else
|
||||
if [ -a /tmp/batwarn ]; then
|
||||
rm -f /tmp/batwarn ;
|
||||
fi
|
||||
fi
|
||||
```
|
||||
También lo tenéis [aquí](https://github.com/clonbg/MisScripts/blob/master/bateria.sh) para descargar. No olvides darle permisos de ejecución:
|
||||
|
||||
sudo chmod a+x bateria.sh
|
||||
|
||||
Las dos líneas que hay que modificar son las que empiezan por:
|
||||
|
||||
sudo -u clonbg DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send....
|
||||
|
||||
donde **clonbg** es mi nombre de usuario y **1000** es mi *uid*. Cámbialo por los tuyos.
|
||||
|
||||
Ahora lo que hay que hacer es que se ejecute continuamente. He instalado *cronie*
|
||||
|
||||
yay cronie
|
||||
|
||||
He ejecutado:
|
||||
|
||||
sudo crontab -e
|
||||
|
||||
Y he añadido esta línea con la ruta de mi script:
|
||||
<pre>
|
||||
*/5 * * * * /bin/sh /home/clonbg/Git/Scripts/MisScripts/bateria.sh
|
||||
</pre>
|
||||
|
||||
Con esto hacemos que se ejecute cada cinco minutos, y...
|
||||
|
||||

|
||||
|
||||
Salu2
|
||||
47
src/markdowns/stories/script-debian-updates.md
Executable file
47
src/markdowns/stories/script-debian-updates.md
Executable file
@@ -0,0 +1,47 @@
|
||||
# Comprobar actualizaciones en **Debian**
|
||||
#### 22/05/2021
|
||||
|
||||
Cada vez que enciendo el ordenador, por costumbre, actualizo el sistema. Es una costumbre adquirida cuando he usado Arch Linux (todavía lo tengo en otros equipos), ya que este cada poquito tiempo tenía actualizaciones. Ahora he decidido pasarme a **Debian estable**, la versión 10 llamada **Buster** y buscar actualizaciones se hace innecesario ya que las versiones estables salen muy poco a poco. Por este motivo he decidido crear un *script en bash* que busque actualizaciones y me avise en el caso de que encuentre alguna.
|
||||
|
||||
Como dependencias hay que instalar dos paquetes
|
||||
|
||||
sudo apt install libnotify-bin mplayer -y
|
||||
|
||||
El script es este:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
|
||||
passwd='password'
|
||||
|
||||
echo $passwd | sudo -S /usr/bin/apt-get update -y
|
||||
|
||||
actualizaciones=$(echo $passwd | sudo -S /usr/bin/apt-get -s -q -u upgrade | grep -v '\.\.\.' | grep -v ':' | grep -v 'Inst ' | grep -v 'Conf ')
|
||||
|
||||
XDG_RUNTIME_DIR=/run/user/$(id -u) notify-send 'Se encuentran disponibles las siguientes actualizaciones: ' "$actualizaciones"
|
||||
|
||||
|
||||
if [ "$actualizaciones" != "0 actualizados, 0 nuevos se instalarán, 0 para eliminar y 0 no actualizados." ]; then
|
||||
export XDG_RUNTIME_DIR="/run/user/1000"
|
||||
mplayer -really-quiet /home/clonbg/Programas/campana.mp3
|
||||
fi
|
||||
```
|
||||
|
||||
Los cambios que tienes que hacer son:
|
||||
|
||||
- Por supuesto cambiar la contraseña *password* por tu contraseña de *sudo*
|
||||
- Cambiar la ruta del archivo *campana.mp3* que suena cuando hay actualizaciones. Descargado de [sonidosmp3gratis.com](http://sonidosmp3gratis.com/campana).
|
||||
|
||||
Por último hay que añadir la tarea a *cron*:
|
||||
|
||||
crontab -e
|
||||
|
||||
Y añadir la siguiente línea al final:
|
||||
|
||||
0 * * * * /home/clonbg/Programas/update.sh
|
||||
|
||||
Cambiando la ruta por donde tengas tú el script, se ejecutará cada hora en punto.
|
||||
|
||||
|
||||
|
||||
Salu2
|
||||
15
src/markdowns/stories/scroll-en-tmux.md
Normal file
15
src/markdowns/stories/scroll-en-tmux.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Activar el _scroll_ en **Tmux**
|
||||
|
||||
#### 31/05/2020
|
||||
|
||||
Algo muy sencillo que me ha traído de cabeza ha sido activar el _scroll_ en **tmux**. Me hacía falta porque en **Linux** es fundamental leer lo que pasa en la terminal, además cuando intentas ir hacia arriba van saliendo los últimos comandos, como cuando le das al botón de _flecha arriba_ de nuestro teclado.
|
||||
|
||||
Solo hay que crear un archivo de configuración de _tmux_ y añadirle la opción, esto se puede hacer con tan solo una linea en nuestra terminal:
|
||||
|
||||
echo "set -g mouse on" >> ~/.tmux.conf
|
||||
|
||||
Después de esto tienes que volver a abrir _tmux_ y ya lo tienes
|
||||
|
||||
Visto en [https://superuser.com](https://superuser.com/questions/209437/how-do-i-scroll-in-tmux)
|
||||
|
||||
Salu2
|
||||
16
src/markdowns/stories/sudo-sin-intervencion.md
Normal file
16
src/markdowns/stories/sudo-sin-intervencion.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Usar **sudo** sin intervención del usuario
|
||||
#### 17/12/2020
|
||||
|
||||
Ultimamente he necesitado ejecutar unos *scripts* y era un problema tener que escribir la contraseña de **sudo** cada vez que se ejecutaba. He buscado una solución en la que no haya que poner el comando en el archivo *sudoers*.
|
||||
|
||||
Se puede poner la contraseña directamente en el *script* gracias a [**stdin**](https://es.wikipedia.org/wiki/Entrada_est%C3%A1ndar)
|
||||
|
||||
La línea es asi:
|
||||
|
||||
printf 'password' | sudo -S comando
|
||||
|
||||
Esto, evidentemente tiene problemas de seguridad ya que se guarda la contraseña en texto plano dentro del *script*. Úsalo con cuidado!
|
||||
|
||||
Visto en [https://unix.stackexchange.com](https://unix.stackexchange.com/questions/19707/why-cant-sudo-redirect-stdout-to-etc-file-but-sudo-nano-or-cp-can)
|
||||
|
||||
Salu2
|
||||
25
src/markdowns/stories/tmux-sacale-mas-partido.md
Normal file
25
src/markdowns/stories/tmux-sacale-mas-partido.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# **Tmux**, sácale más partido a la terminal
|
||||
#### 21/05/2020
|
||||
|
||||
Os dije en el post dedicado a *Alacritty* que os iba a hablar de **Tmux**, pues lo prometido es deuda, vamos allá:
|
||||
|
||||
**Tmux** según pone en su descripción es un multiplexor de terminal. Esto significa que puedes tener cuantas terminales quieras dentro de una.
|
||||
|
||||
He estado trasteando un poco con él, siguiendo los atajos de teclado que indican en esta página, [tmuxcheatsheet.com](http://tmuxcheatsheet.com)
|
||||
|
||||
Puedes hacer prácticamente de todo, abrir sesiones y dejarlas funcionando en segundo plano después de cerrar la terminal es una de las más interesantes. Os recomiendo que echéis un ojo e intenteis dominarlo.
|
||||
|
||||
Yo soy más vago, me he creado dos alias en mi archivo *.zshrc*, uno para dividir *alacritty* en tres partes y otro para cerrar la sesión:
|
||||
|
||||
alias tmuxAll="tmux new-session \; split-window -h \; split-window -v \; attach"
|
||||
alias tmuxClose="tmux kill-ses -t 0"
|
||||
|
||||
De esta manera al escribir *tmuxAll* puedes trabajar con 3 terminales a la vez:
|
||||
|
||||

|
||||
|
||||
y al escribir *tmuxClose* cierra todas las terminales:
|
||||
|
||||

|
||||
|
||||
Salu2
|
||||
20
src/markdowns/stories/url-radio.md
Executable file
20
src/markdowns/stories/url-radio.md
Executable file
@@ -0,0 +1,20 @@
|
||||
# Conseguir la *url* para escuchar la radio online
|
||||
#### 19/02/2021
|
||||
|
||||
Vamos a ver como obtener al *url* de una radio web para poder escucharla en **vlc**, **mpv**, etc. Vamos a necesitar unicamente un navegador, yo voy a usar **firefox**.
|
||||
|
||||
Primeramente entramos en la web donde esté la radio que quieres extraer, yo por ejemplo voy a entrar en [radio-espana](http://www.radio-espana.es) y voy a buscar *RockFm*. No le des a escuchar todavía.
|
||||
|
||||

|
||||
|
||||
Presionamos **F12** para que se abra la barra de utilidades para desarrolladores, después en la pestaña **Red** y, ahora si, le damos al play. Abajo salen las conexiones que realiza la web, debe haber una cuyo **iniciador** sea de tipo **media**, si haces click te saldrá la dirección web a la derecha.
|
||||
|
||||

|
||||
|
||||
Si copias la dirección y la usas en el reproductor podrás escucharla sin necesidad de abrir el navegador
|
||||
|
||||

|
||||
|
||||
Visto en [geekland.eu](https://geekland.eu/obtener-la-url-para-escuchar-radio-en-streaming/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+geeklandlinux+%28geekland%29)
|
||||
|
||||
Salu2
|
||||
251
src/markdowns/stories/vuex-nuxt-example.md
Normal file
251
src/markdowns/stories/vuex-nuxt-example.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Ejemplo de Vuex con Nuxt
|
||||
#### 31/08/2020
|
||||
|
||||
Si no sabéis lo que es *Vuex* o *Nuxt* os diré que "Vuex es un complemento oficial para Vue.js que ofrece un almacén de datos centralizado para usar dentro de su aplicación", en pocas palabras es como tener *variables globales* en toda la aplicación. Y *Nuxt* "es un framework web gratuito y de código abierto basado en Vue.js, Node.js, Webpack y Babel.js".
|
||||
|
||||
He estado trabajando en un ejemplo de como funcionan estas dos librerías.
|
||||
|
||||
Primero va el archivo ```store/index.js``` que corresponde a *Vuex*, en el *state* he creado un array llamado 'datos', cada objeto del array tiene una 'id' y un 'titulo'.
|
||||
|
||||
```
|
||||
export const state = () => ({
|
||||
counter: 0,
|
||||
datos: [],
|
||||
indice: ''
|
||||
})
|
||||
|
||||
export const mutations = {
|
||||
increment(state) {
|
||||
state.counter++
|
||||
},
|
||||
SAVE_DATOS(state, datos) {
|
||||
state.datos = datos
|
||||
},
|
||||
DELETE_DATO(state, indice) {
|
||||
state.datos.splice(indice, 1)
|
||||
console.log(indice)
|
||||
},
|
||||
UPDATE_DATO(state, indice) {
|
||||
state.indice = indice
|
||||
console.log(state.indice)
|
||||
},
|
||||
GUARDA_DATO(state, payload) {
|
||||
var id = payload.id
|
||||
var titulo = payload.titulo
|
||||
if (
|
||||
confirm(
|
||||
"Estás seguro de editar a '" + state.datos[state.indice].title + "' ?"
|
||||
)
|
||||
) {
|
||||
state.datos[state.indice].ID = id
|
||||
state.datos[state.indice].title = titulo
|
||||
}
|
||||
},
|
||||
NUEVO_DATO(state, payload) {
|
||||
var id = payload.id
|
||||
var titulo = payload.titulo
|
||||
if (
|
||||
confirm(
|
||||
"Estás seguro de guardar '" + titulo + "' ?"
|
||||
)
|
||||
) {
|
||||
var dato = {
|
||||
'ID' : id,
|
||||
'title' : titulo
|
||||
}
|
||||
state.datos.push(dato)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
loadDatos({ commit }) {
|
||||
this.$axios
|
||||
.$get(
|
||||
'https://www.etnassoft.com/api/v1/get/?category=libros_programacion&criteria=most_viewed'
|
||||
)
|
||||
.then((result) => {
|
||||
commit('SAVE_DATOS', result)
|
||||
console.log(result)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
eliminaDatos({ commit }, indice) {
|
||||
commit('DELETE_DATO', indice)
|
||||
},
|
||||
editaDatos({ commit }, indice) {
|
||||
commit('UPDATE_DATO', indice)
|
||||
},
|
||||
guardaDatos({ commit }, payload) {
|
||||
commit('GUARDA_DATO', payload)
|
||||
},
|
||||
nuevoDato({commit}, payload) {
|
||||
commit('NUEVO_DATO', payload)
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Y después va el archivo ```pages/index.vue``` que corresponde a la web.
|
||||
|
||||
```
|
||||
<template>
|
||||
<div class="container p-5">
|
||||
<div class="grid grid-cols-2">
|
||||
<!-- Columna Izquierda -->
|
||||
<div>
|
||||
<form class="w-full md:w-1/2 px-3">
|
||||
<label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-last-name"
|
||||
>Id</label>
|
||||
<input
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||
:class="{ 'cursor-not-allowed' : cursor }"
|
||||
id="grid-last-name"
|
||||
type="text"
|
||||
placeholder="id"
|
||||
v-model="id"
|
||||
:disabled="inputOpen"
|
||||
/>
|
||||
<label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-last-name"
|
||||
>Título</label>
|
||||
<input
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||
:class="{ 'cursor-not-allowed' : cursor }"
|
||||
id="grid-last-name"
|
||||
type="text"
|
||||
placeholder="Título"
|
||||
v-model="titulo"
|
||||
:disabled="inputOpen"
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<button
|
||||
class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
|
||||
:class="{ 'cursor-not-allowed' : cursor }"
|
||||
@click="guarda"
|
||||
:disabled="inputOpen"
|
||||
>Guardar</button>
|
||||
<button
|
||||
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded"
|
||||
:class="{ 'cursor-not-allowed' : cursor }"
|
||||
@click="cancela"
|
||||
:disabled="inputOpen"
|
||||
>Cancelar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Columna derecha -->
|
||||
<div>
|
||||
<p>{{ counter }} no?????</p>
|
||||
<button
|
||||
@click="increment"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
>Incrementar</button>
|
||||
<button
|
||||
@click="nuevo"
|
||||
:class="{ 'cursor-not-allowed' : !cursor }"
|
||||
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||
>Crear nuevo</button>
|
||||
<ul class="grid">
|
||||
<li v-for="dato in datos" :key="dato.id" class="inline-flex m-2">
|
||||
{{ dato.ID }} - {{ dato.title }}
|
||||
<a href="#" class="pl-6" @click="edita(dato)">
|
||||
<img src="../assets/icons/edit-pencil.svg" alt="edit" class="tamanoIcono" />
|
||||
</a>
|
||||
<a href="#" class="pl-6" @click="elimina(dato)">
|
||||
<img src="../assets/icons/trash.svg" alt="trash" class="tamanoIcono" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
titulo: '',
|
||||
inputOpen: true,
|
||||
cursor: true,
|
||||
datoNuevo: false
|
||||
}
|
||||
},
|
||||
computed: mapState(['counter', 'datos']),
|
||||
methods: {
|
||||
...mapMutations(['increment']),
|
||||
elimina(dato) {
|
||||
var indice = this.datos.indexOf(dato)
|
||||
if (indice != -1) {
|
||||
if (confirm("Estás seguro de eliminar a '" + dato.title + "' ?")) {
|
||||
this.$store.dispatch('eliminaDatos', indice)
|
||||
}
|
||||
}
|
||||
},
|
||||
edita(dato) {
|
||||
this.id = dato.ID
|
||||
this.titulo = dato.title
|
||||
var indice = this.datos.indexOf(dato)
|
||||
this.$store.dispatch('editaDatos', indice)
|
||||
this.inputOpen = false
|
||||
this.cursor = false
|
||||
this.datoNuevo = false
|
||||
},
|
||||
guarda() {
|
||||
var payload = {
|
||||
id: this.id,
|
||||
titulo: this.titulo
|
||||
}
|
||||
if (this.datoNuevo == false) {
|
||||
this.$store.dispatch('guardaDatos', payload)
|
||||
} else {
|
||||
this.$store.dispatch('nuevoDato', payload)
|
||||
this.datoNuevo = false
|
||||
}
|
||||
this.inputOpen = true
|
||||
this.id = ''
|
||||
this.titulo = ''
|
||||
this.cursor = true
|
||||
},
|
||||
cancela() {
|
||||
this.inputOpen = true
|
||||
this.id = ''
|
||||
this.titulo = ''
|
||||
this.cursor = true
|
||||
},
|
||||
nuevo() {
|
||||
this.cursor = false
|
||||
this.inputOpen = false
|
||||
this.datoNuevo = true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('loadDatos')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tamanoIcono {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
```
|
||||
|
||||
Si hay clases que no conoceis os diré que he usado [Tailwindcss](https://tailwindcss.com/) para el *css*.
|
||||
|
||||
Espero que os haya gustado, yo seguro que lo consulto muchas veces.
|
||||
|
||||
Salu2
|
||||
17
src/markdowns/stories/watchtower.md
Normal file
17
src/markdowns/stories/watchtower.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Mantener actualizados los contenedores *Docker* con **Watchtower**
|
||||
#### 22/11/2020
|
||||
|
||||
Como os dije en la antrada anterior os voy a enseñar una herramienta que mantiene los contenedores *docker* actualizados. Esto se hace con otro contenedor, este de [aquí](https://github.com/containrrr/watchtower).
|
||||
|
||||
Para descargarlo y ponerlo en funcionamiento:
|
||||
|
||||
```
|
||||
docker run -d \
|
||||
--name watchtower \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
containrrr/watchtower
|
||||
```
|
||||
|
||||
A partir de ese momento se encargará de mantenerlos actualizados a la última versión, yo tengo por ejemplo un contenedor de [LinuxServer](https://www.linuxserver.io/) *linuxserver/freshrss:latest* y cada vez que sale una nueva versión se actualiza sin interacción con el usuario. Tengo otro de *mongoDb* bloqueado en la versión *4.4*, *mongo:4.4*, este evidentemente no se actualizará. Sólo tienes que mantener el contenedor de **Watchtower** funcionando, a partir de ese momento te puedes despreocupar.
|
||||
|
||||
Salu2
|
||||
32
src/markdowns/stories/zram.md
Executable file
32
src/markdowns/stories/zram.md
Executable file
@@ -0,0 +1,32 @@
|
||||
# Instalar **Zram Swap** en *Arch Linux*
|
||||
#### 23/07/2021
|
||||
|
||||
Primero hay que explicar que es esto y para que sirve. **Zram**.
|
||||
|
||||
> Según [Wikipedia](https://es.wikipedia.org/wiki/Zram) **Zram** es un módulo del núcleo Linux previamente llamado compcache. **Zram** incrementa el rendimiento evitando la paginación en disco y en su lugar utiliza un dispositivo de bloques comprimidos en la memoria **RAM** donde la paginación toma lugar hasta que sea necesaria la utilización del espacio compartido (**Swap**) en el disco duro.
|
||||
|
||||
Dejando a un lado tecnicismos es muy útil cuando tienes 4GB o menos de **RAM** en tu equipo haciendo que vaya más fluido cuando hay un consumo alto de memoría.
|
||||
|
||||
Veamos como instalarlo en *Arch Linux*:
|
||||
|
||||
sudo pacman -S systemd-swap
|
||||
|
||||
El archivo de configuración se encuentra en ``` /etc/systemd/swap.conf ``` y este es el mio:
|
||||
|
||||
```
|
||||
zram_enabled=1
|
||||
zram_size=$(($RAM_SIZE/4))
|
||||
zram_streams=$NCPU
|
||||
zram_alg=lz4
|
||||
zram_prio=200
|
||||
```
|
||||
|
||||
Esto usaría como **Zram** un cuarto de la **RAM** del equipo y crearía una unidad de **Zram** por cada nucleo del procesador lo cual es muy recomendable.
|
||||
|
||||
Lo siguiente es reiniciar el servicio:
|
||||
|
||||
sudo systemctl restart systemd-swap
|
||||
|
||||
Visto en [https://juncotic.com](https://juncotic.com/zram-swap-memoria-de-intercambio-comprimida-en-la-ram/)
|
||||
|
||||
Salu2
|
||||
26
src/pages/Error404.vue
Executable file
26
src/pages/Error404.vue
Executable file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div
|
||||
class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div>
|
||||
<div style="font-size: 30vh">404</div>
|
||||
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div>
|
||||
<q-btn
|
||||
class="q-mt-xl"
|
||||
color="white"
|
||||
text-color="blue"
|
||||
unelevated
|
||||
to="/"
|
||||
label="Go Home"
|
||||
no-caps
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Error404",
|
||||
});
|
||||
</script>
|
||||
144
src/pages/Index.vue
Executable file
144
src/pages/Index.vue
Executable file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<q-page>
|
||||
<div class="flex flex-center q-pt-xl" v-if="filtroPaginacion.length > 0">
|
||||
<div
|
||||
class="row full-width reverse-wrap margenes"
|
||||
v-for="item in filtroPaginacion"
|
||||
:key="item.id"
|
||||
:id="item.id"
|
||||
>
|
||||
<div class="col-sm-7 q-px-sm full-height">
|
||||
<router-link
|
||||
:href="item.id"
|
||||
:to="{ path: item.id, params: { markdown: item.id } }"
|
||||
>
|
||||
<div class="text-h5 q-mt-sm q-mb-xs" v-html="item.title"></div>
|
||||
</router-link>
|
||||
<div class="text-overline text-blue">{{ item.date }}</div>
|
||||
<div class="text-overline text-red">
|
||||
<span v-for="(tag, index) in item.categorias" :key="index"
|
||||
>#/{{ tag + " " }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="self-center">
|
||||
<div
|
||||
class="text-body1 text-grey-10 justify-between"
|
||||
v-html="item.description"
|
||||
></div>
|
||||
<div class="row full-width">
|
||||
<q-btn
|
||||
color="secondary"
|
||||
label="Leer más.."
|
||||
:to="{ path: item.id, params: { markdown: item.id } }"
|
||||
>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 col-12 q-px-sm">
|
||||
<img
|
||||
class="rounded-borders"
|
||||
:src="`./../../${item.id}${item.imagen}`"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-center q-pa-xl">
|
||||
<q-pagination
|
||||
v-model="current"
|
||||
:max="maxPaginas"
|
||||
input
|
||||
input-class="text-orange-10"
|
||||
@click="scrollToTop"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-center q-mt-xl q-pt-xl" v-else>
|
||||
<p class="text-h6 q-mt-xl q-pt-xl q-mx-md">
|
||||
No hay entradas relaccionadas con esta búsqueda
|
||||
</p>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { stories } from "../store/blog.json";
|
||||
|
||||
export default defineComponent({
|
||||
name: "PageIndex",
|
||||
setup() {
|
||||
return {
|
||||
entrys: stories,
|
||||
current: ref(1),
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
tamanyoFiltroBusqueda: "resetCurrent",
|
||||
},
|
||||
computed: {
|
||||
tamanyoFiltroBusqueda() {
|
||||
return this.filtroBusqueda.length;
|
||||
},
|
||||
filtroBusqueda() {
|
||||
var entradas;
|
||||
entradas = this.entrys.filter(
|
||||
(post) =>
|
||||
this.textoLimpio(post.title).includes(
|
||||
this.textoLimpio(this.$store.state.user.busqueda)
|
||||
) ||
|
||||
this.textoLimpio(post.description).includes(
|
||||
this.textoLimpio(this.$store.state.user.busqueda)
|
||||
)
|
||||
);
|
||||
// return this.products.filter(product => !product.category.indexOf(this.category))
|
||||
return entradas;
|
||||
},
|
||||
filtroPaginacion() {
|
||||
var entradas;
|
||||
entradas = this.filtroBusqueda.slice(
|
||||
this.current * 10 - 10,
|
||||
this.current * 10
|
||||
);
|
||||
//console.log(this.current, entradas);
|
||||
return entradas;
|
||||
},
|
||||
maxPaginas() {
|
||||
let num = this.filtroBusqueda.length;
|
||||
if (num % 10 == 0) {
|
||||
return num / 10;
|
||||
} else {
|
||||
return Math.floor(num / 10) + 1;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
scrollToTop() {
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
textoLimpio(texto) {
|
||||
texto = texto
|
||||
.toLowerCase()
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/<[^>]*>?/g, "");
|
||||
return texto.trim();
|
||||
},
|
||||
resetCurrent() {
|
||||
this.current = 1;
|
||||
this.scrollToTop();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.margenes {
|
||||
padding: 6vh 5vw 6vh 5vw;
|
||||
}
|
||||
</style>
|
||||
75
src/pages/Post.vue
Normal file
75
src/pages/Post.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<q-page class="flex flex-center q-pt-xl">
|
||||
<div class="row full-width reverse-wrap margenes">
|
||||
<div class="col q-px-sm full-height">
|
||||
<q-markdown :src="post" style="font-size: 120%"></q-markdown>
|
||||
<q-btn
|
||||
color="secondary"
|
||||
label="Volver"
|
||||
@click="volver"
|
||||
class="q-my-sm"
|
||||
></q-btn>
|
||||
<q-icon
|
||||
size="xl"
|
||||
name="img:icons/mastodon.png"
|
||||
class="cursor-pointer float-right q-mr-md"
|
||||
@click="twitter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
<script>
|
||||
import { openURL } from "quasar";
|
||||
import { stories } from "../store/blog.json";
|
||||
export default {
|
||||
props: ["markdown"],
|
||||
data() {
|
||||
return {
|
||||
post: "",
|
||||
web: "",
|
||||
categorias: "",
|
||||
};
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
scrollToTop() {
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
volver() {
|
||||
this.$router.push({ path: "/" });
|
||||
},
|
||||
twitter() {
|
||||
let url = `https://masto.es/publish?text=@clonbg@masto.es - ${this.web} |`;
|
||||
console.log(url);
|
||||
openURL(url);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
try {
|
||||
const texto = require(`../markdowns/stories/${this.$route.params.markdown}.md`);
|
||||
this.post = texto.default;
|
||||
this.scrollToTop();
|
||||
this.web = window.location.href.replace("#", "%23");
|
||||
let item = stories.find(
|
||||
(element) => element.id == this.$route.params.markdown
|
||||
);
|
||||
let itemtags = item.categorias;
|
||||
this.categorias = itemtags.toString();
|
||||
} catch (error) {
|
||||
this.$router.push({ path: "*" });
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.margenes {
|
||||
padding: 6vh 5vw 6vh 5vw;
|
||||
}
|
||||
</style>
|
||||
6
src/quasar.d.ts
vendored
Executable file
6
src/quasar.d.ts
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
// Forces TS to apply `@quasar/app` augmentations of `quasar` package
|
||||
// Removing this would break `quasar/wrappers` imports as those typings are declared
|
||||
// into `@quasar/app`
|
||||
// As a side effect, since `@quasar/app` reference `quasar` to augment it,
|
||||
// this declaration also apply `quasar` own augmentations (eg. adds `$q` into Vue component context)
|
||||
/// <reference types="@quasar/app" />
|
||||
48
src/router/index.js
Executable file
48
src/router/index.js
Executable file
@@ -0,0 +1,48 @@
|
||||
import { route } from "quasar/wrappers";
|
||||
import {
|
||||
createRouter,
|
||||
createMemoryHistory,
|
||||
createWebHistory,
|
||||
createWebHashHistory,
|
||||
} from "vue-router";
|
||||
import routes from "./routes";
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
*
|
||||
* The function below can be async too; either use
|
||||
* async/await or return a Promise which resolves
|
||||
* with the Router instance.
|
||||
*/
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === "history"
|
||||
? createWebHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
// scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
scrollBehavior: function (to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return { selector: savedPosition, behavior: "smooth" };
|
||||
} else if (to.hash) {
|
||||
return { selector: to.hash, behavior: "smooth" };
|
||||
} else {
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
},
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(
|
||||
process.env.MODE === "ssr" ? void 0 : process.env.VUE_ROUTER_BASE
|
||||
),
|
||||
});
|
||||
|
||||
return Router;
|
||||
});
|
||||
45
src/router/routes.js
Executable file
45
src/router/routes.js
Executable file
@@ -0,0 +1,45 @@
|
||||
const requireComponent = require.context(
|
||||
// The relative path of the components folder
|
||||
'../markdowns/stories',
|
||||
// Whether or not to look in subfolders
|
||||
false,
|
||||
// The regular expression used to match base component filenames
|
||||
/Base[A-Z]\w+\.(md)$/
|
||||
)
|
||||
requireComponent.keys().forEach((fileName) => {
|
||||
// Get the component config
|
||||
const componentConfig = requireComponent(fileName)
|
||||
// Get the PascalCase version of the component name
|
||||
const componentName = fileName
|
||||
// Remove the "./_" from the beginning
|
||||
.replace(/^\.\/_/, '')
|
||||
// Remove the file extension from the end
|
||||
.replace(/\.\w+$/, '')
|
||||
// Split up kebabs
|
||||
.split('-')
|
||||
// Upper case
|
||||
.map((kebab) => kebab.charAt(0).toUpperCase() + kebab.slice(1))
|
||||
// Concatenated
|
||||
.join('')
|
||||
|
||||
console.log(componentName)
|
||||
|
||||
// Globally register the component
|
||||
Vue.component(componentName, componentConfig.default || componentConfig)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
children: [
|
||||
{ path: '', component: () => import('pages/Index.vue') },
|
||||
{ path: ':markdown',component: () => import('pages/Post.vue') },
|
||||
{ path: '*', component: () => import('pages/Error404.vue') }
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
export default routes
|
||||
481
src/store/blog.json
Executable file
481
src/store/blog.json
Executable file
@@ -0,0 +1,481 @@
|
||||
{
|
||||
"stories": [
|
||||
{
|
||||
"id": "gogs-docker",
|
||||
"categorias": [
|
||||
"Git",
|
||||
"Programación",
|
||||
"docker"
|
||||
],
|
||||
"imagen": "/gogs.png",
|
||||
"date": "3 de Enero de 2023",
|
||||
"title": "Mi propio servidor <i>Git</i> gracias a <i>Docker</i>",
|
||||
"description": "Antes de nada, decir que tener mi propio servidor de <i>Git</i> instalado en mi equipo puede no ser una buena idea, ya que tienes que ocuparte tu mismo de los backups y mantenerlo, ya que no se encuentra en ningún otro sitio. A parte de..."
|
||||
},
|
||||
{
|
||||
"id": "aprender-django",
|
||||
"categorias": [
|
||||
"Programación",
|
||||
"Python"
|
||||
],
|
||||
"imagen": "/aprender-django.jpg",
|
||||
"date": "18 de Octubre de 2022",
|
||||
"title": "Aprender Django",
|
||||
"description": "Últimamente he estado ocupado en proyecto para una pequeña empresa. El <i>backend</i> era un reto para mi, estuve mirando posibles soluciones y al final me decidí por <strong>Django</strong>. Dejo aquí una recopilación de recursos que..."
|
||||
},
|
||||
{
|
||||
"id": "consumo-ssd-pi",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Raspberry"
|
||||
],
|
||||
"imagen": "/cpu.jpeg",
|
||||
"date": "25 de Febrero de 2022",
|
||||
"title": "Consumo alto de <i>CPU</i> cuando la <strong>Raspberry Pi</strong> trabaja desde un <strong>SSD</strong>",
|
||||
"description": "Desde hace un tiempo tengo la <strong>Raspberry Pi 4 B</strong> como servidor personal, con un montón de servicios: notas, calendario, vscode, etc... Seguí un tutorial para que arrancara desde un dispositivo <i>SSD</i>..."
|
||||
},
|
||||
{
|
||||
"id": "grabar-dd",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal",
|
||||
"Bash"
|
||||
],
|
||||
"imagen": "/dd.jpg",
|
||||
"date": "10 de Enero de 2022",
|
||||
"title": "Grabar una <i>iso</i> en un Usb desde la terminal con <strong>dd</strong>",
|
||||
"description": "Desde mi experiencia, la mejor manera de grabar una imagen <i>iso</i> en un Usb es desde la terminal, con el comando <strong>dd</strong>. Es muy útil para no tener que quemar un DVD o un CD. El primer paso es saber el destino..."
|
||||
},
|
||||
{
|
||||
"id": "nuevo-blog",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"VueJS",
|
||||
"Markdown",
|
||||
"Programación"
|
||||
],
|
||||
"imagen": "/nuevo.png",
|
||||
"date": "22 de Diciembre de 2021",
|
||||
"title": "Nuevo <i>Blog</i> programado desde 0 con <strong>quasar.dev</strong>",
|
||||
"description": "Cómo podéis ver hace tiempo que no publico y es que se me ha ocurrido rehacer el <strong>Blog de Clonbg</strong> desde cero. Está hecho con <a href='https://quasar.dev/' target='_blank'>quasar</a> qué es un <i>framework</i> para <i>Vuejs</i>. Tenéis todo el código publicado en..."
|
||||
},
|
||||
{
|
||||
"id": "script-autostart",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Scripts",
|
||||
"Bash"
|
||||
],
|
||||
"imagen": "/bash.png",
|
||||
"date": "10 de Septiembre de 2021",
|
||||
"title": "Script en <strong>bash</strong> que comprueba si hay internet y ejecuta unos programas",
|
||||
"description": "Al arrancar mi sistema operativo y estar en un sitio sin conexión a internet algunos programas se ejecutaban igualmente dando lugar a fallos, evidentemente. Me decidí a crear un <i>script</i> que..."
|
||||
},
|
||||
{
|
||||
"id": "pyqt5-icono",
|
||||
"categorias": [
|
||||
"Programación",
|
||||
"Python",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/programa.png",
|
||||
"date": "29 de Agosto de 2021",
|
||||
"title": "Minimizar al <i>tray</i> un programa hecho en <strong>PyQt5<strong>",
|
||||
"description": "Como podeis ver en el post sobre <a href='https://clonbg.netlify.app/#/crud_con_python' target='_blank'>Mi primer <i>CRUD</i> con <i>Python</i></a>, tenía hecho un programa que simplemente era un registro de usuarios implementado en <i>Python</i> con <i>PyQt5</i> y <i>Mysql3</i> como base..."
|
||||
},
|
||||
{
|
||||
"id": "zram",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Sistema"
|
||||
],
|
||||
"imagen": "/zram.png",
|
||||
"date": "23 de Julio de 2021",
|
||||
"title": "Instalar <strong>Zram Swap</strong> en <i>Arch Linux</i>",
|
||||
"description": "Primero hay que explicar que es esto y para que sirve. Según Wikipedia <strong>Zram</strong> es un módulo del núcleo <i>Linux</i> previamente llamado <i>compcache</i>. <strong>Zram</strong> incrementa el rendimiento evitando la..."
|
||||
},
|
||||
{
|
||||
"id": "script-debian-updates",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/script-debian-updates.png",
|
||||
"date": "22 de Mayo de 2021",
|
||||
"title": "Comprobar actualizaciones en <strong>Debian</strong>",
|
||||
"description": "Cada vez que enciendo el ordenador, por costumbre, actualizo el sistema. Es una costumbre adquirida cuando he usado Arch Linux (todavía lo tengo en otros equipos), ya que este cada poquito tiempo tenía actualizaciones..."
|
||||
},
|
||||
{
|
||||
"id": "docker-sin-sudo",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal",
|
||||
"Docker"
|
||||
],
|
||||
"imagen": "/docker.png",
|
||||
"date": "26 de Abril de 2021",
|
||||
"title": "Usar <strong>docker</strong> con tu usuario sin <i>sudo</i>",
|
||||
"description": "El como usar <strong>docker</strong> sin <i>sudo</i> es posiblemente una de las cosas que más veces he buscado en internet, nunca recuerdo como se hace. Por eso voy a ponerlo aquí para poder consultarlo..."
|
||||
},
|
||||
{
|
||||
"id": "ouroboros",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Docker",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/ouroboros.png",
|
||||
"date": "13 de Abril de 2021",
|
||||
"title": "Actualización automática de contenedores con <strong>Ouroboros</strong>",
|
||||
"description": "La actuaización automática de contenedores te puede evitar mucho tiempo, por no hablar de los agujeros de seguridad de versiones antiguas. Además este método te mantiene informado de la..."
|
||||
},
|
||||
{
|
||||
"id": "disown",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal"
|
||||
],
|
||||
"imagen": "/disown1.png",
|
||||
"date": "26 de Marzo de 2021",
|
||||
"title": "Como separar un proceso de la terminal con <strong>disown</strong>",
|
||||
"description": "Según la <strong>Wikipedia</strong> en los shells Unix <i>ksh</i>, <i>bash</i>, <i>fish</i> y <i>zsh</i>, el comando incorporado disown se utiliza para eliminar trabajos de la tabla de..."
|
||||
},
|
||||
{
|
||||
"id": "eliminar-recursiva",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/eliminar-recursiva.png",
|
||||
"date": "16 de Marzo de 2021",
|
||||
"title": "Eliminar archivos de forma recursiva",
|
||||
"description": "Vamos a probar a eliminar archivos de forma recursiva desde nuestra terminal, y así, no tener que ir buscando por carpetas dichos archivos..."
|
||||
},
|
||||
{
|
||||
"id": "url-radio",
|
||||
"categorias": [
|
||||
"Aplicaciones",
|
||||
"Sonido"
|
||||
],
|
||||
"imagen": "/url-radio.png",
|
||||
"date": "19 de Febrero de 2021",
|
||||
"title": "Conseguir la url para escuchar la radio online",
|
||||
"description": "Vamos a ver como obtener al url de una radio web para poder escucharla en vlc, mpv, etc. Vamos a necesitar unicamente un navegador, yo voy a..."
|
||||
},
|
||||
{
|
||||
"id": "autosubsync",
|
||||
"categorias": [
|
||||
"Terminal",
|
||||
"Python",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/autosubsync.PNG",
|
||||
"date": "15 de Febrero de 2021",
|
||||
"title": "Sincronizar automáticamente subtítulos utilizando el aprendizaje automático con <strong>autosubsync</strong>",
|
||||
"description": "Muchas veces cuando tienes que sincronizar un subtítulo, bien por que es para otra versión del video o por el motivo que sea, tenemos que..."
|
||||
},
|
||||
{
|
||||
"id": "copiar-mover-find",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/copiar-mover-find.png",
|
||||
"date": "8 de Febrero de 2021",
|
||||
"title": "Buscar archivos y copiar/mover con <i>find</i>",
|
||||
"description": "Vamos a ver como copiar o mover archivos a la vez que los buscas. Este es el resultado de un problema, tuve que buscar archivos en la raiz de..."
|
||||
},
|
||||
{
|
||||
"id": "docker-acestream",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Docker",
|
||||
"Aplicaciones",
|
||||
"Imagen"
|
||||
],
|
||||
"imagen": "/docker-acestream.jpeg",
|
||||
"date": "24 de Enero de 2021",
|
||||
"title": "Como ver contenido <strong>acestream</strong> en <strong>Linux</strong>",
|
||||
"description": "Voy a explicar como veo yo contenido <strong>acestream</strong> en <strong>Linux</strong>. No voy a entrar en que contenidos son legítimos ver y que no, cada uno es responsable..."
|
||||
},
|
||||
{
|
||||
"id": "reinicio-programado",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/crontab.png",
|
||||
"date": "5 de Enero de 2021",
|
||||
"title": "Programar el reinicio del ordenador de forma automática mediante <strong>Cron</strong>",
|
||||
"description": "Últimamente he tenido bastantes problemas de conexión de la <i>Raspberry Pi</i>, cuando pasaban 2 ó 3 días sin reiniciarla (lo cual es algo habitual)..."
|
||||
},
|
||||
{
|
||||
"id": "sudo-sin-intervencion",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/sudo-sin-intervencion.png",
|
||||
"date": "17 de Diciembre de 2020",
|
||||
"title": "Usar <strong>sudo</strong> sin intervención del usuario",
|
||||
"description": "Ultimamente he necesitado ejecutar unos <i>scripts</i> y era un problema tener que escribir la contraseña de <strong>sudo</strong> cada vez que se ejecutaba. He buscado..."
|
||||
},
|
||||
{
|
||||
"id": "watchtower",
|
||||
"categorias": [
|
||||
"Aplicaciones",
|
||||
"Docker"
|
||||
],
|
||||
"imagen": "/watchtower.png",
|
||||
"date": "22 de Noviembre de 2020",
|
||||
"title": "Mantener actualizados los contenedores <i>docker</i> con <strong>Watchtower</strong>",
|
||||
"description": "Como os dije en la antrada anterior os voy a enseñar una herramienta que mantiene los contenedores <i>docker</i> actualizados. Esto se hace con..."
|
||||
},
|
||||
{
|
||||
"id": "portainer",
|
||||
"categorias": [
|
||||
"Aplicaciones",
|
||||
"Docker"
|
||||
],
|
||||
"imagen": "/portainer.png",
|
||||
"date": "7 de Noviembre de 2020",
|
||||
"title": "<strong>Portainer</strong>, magnífico gestor para Docker",
|
||||
"description": "Cuando pruebas <i>docker</i> una vez, te engancha por su facilidad y porque, al estar separado en contenedores no puede corromper tu sistema. Después..."
|
||||
},
|
||||
{
|
||||
"id": "cursos-youtube",
|
||||
"categorias": [
|
||||
"VueJs",
|
||||
"Javascript",
|
||||
"Python",
|
||||
"Programación"
|
||||
],
|
||||
"imagen": "/cursos.jpg",
|
||||
"date": "18 de Octubre de 2020",
|
||||
"title": "Un par de cursos de <strong>Youtube</strong> sobre programación",
|
||||
"description": "Hoy en día hay muchos sitios donde poder formarse, unos mejores y otros de menor calidad. Yo recomiendo <strong>Youtube</strong>, porque es rápido, lo tienes..."
|
||||
},
|
||||
{
|
||||
"id": "nativefier",
|
||||
"categorias": [
|
||||
"Aplicaciones",
|
||||
"Linux"
|
||||
],
|
||||
"imagen": "/nativefier.png",
|
||||
"date": "27 de Septiembre de 2020",
|
||||
"title": "Crea tus propias aplicaciones web con <strong>Nativefier</strong>",
|
||||
"description": "Esta es una maravillosa utilidad que convierte en aplicación <i>electrón</i> una dirección web. De esta manera puedes tener una aplicación de escritorio..."
|
||||
},
|
||||
{
|
||||
"id": "literales",
|
||||
"categorias": [
|
||||
"VueJS",
|
||||
"Javascript",
|
||||
"Programación"
|
||||
],
|
||||
"imagen": "/literales.png",
|
||||
"date": "13 de Septiembre de 2020",
|
||||
"title": "<i>Template literals</i> o <i>comillas invertidas</i>",
|
||||
"description": "Como sabeis en JavaScript, para declarar un String hay que ponerlo entre comillas:<pre>const texto = 'me llamo Manuel'</pre>Desde la versión ES6 es..."
|
||||
},
|
||||
{
|
||||
"id": "vuex-nuxt-example",
|
||||
"categorias": [
|
||||
"VueJS",
|
||||
"Javascript",
|
||||
"Programación"
|
||||
],
|
||||
"imagen": "/vuex.png",
|
||||
"date": "31 de Agosto de 2020",
|
||||
"title": "Ejemplo de <strong>Vuex</strong> con <strong>Nuxt</strong>",
|
||||
"description": "Si no sabéis lo que es <strong>Vuex</strong> o <strong>Nuxt</strong> os diré que '<strong>Vuex</strong> es un complemento oficial para <strong>Vue.js</strong> que ofrece un almacén de datos centralizado para..."
|
||||
},
|
||||
{
|
||||
"id": "radicale",
|
||||
"categorias": [
|
||||
"Seguridad",
|
||||
"Android"
|
||||
],
|
||||
"imagen": "/radicale.png",
|
||||
"date": "16 de Agosto de 2020",
|
||||
"title": "Mi propio calendario y mi libreta de contactos con <strong>Radicale</strong>",
|
||||
"description": "Os contaba en el anterior post la adquisición de mi nuevo terminal y de la 'necesidad' de alejarme de <stron>Google</strong>. Dos herramientas muy importantes..."
|
||||
},
|
||||
{
|
||||
"id": "huawei-sin-google",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Privacidad",
|
||||
"Android"
|
||||
],
|
||||
"imagen": "/sin-google.png",
|
||||
"date": "2 de Agosto de 2020",
|
||||
"title": "<i>Huawei P40 Lite</i>, una opción sin <strong>Google</strong>",
|
||||
"description": "Encontré en <a href='https://es.aliexpress.com/item/10000341399397.html?spm=a2g0o.productlist.0.0.4fdf27bcdj8PUZ&algo_pvid=1a888230-cb1b-4378-affc-950703921c11&algo_expid=1a888230-cb1b-4378-affc-950703921c11-20&btsid=0b0a01f815963739551712893eec56&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_' target='_blank'>Aliexpress</a> una oferta que no se podía rechazar, como diría 'El Padrino'. Se trataba del móvil de <i>Huawei P40 Lite</i>. Un aparato con..."
|
||||
},
|
||||
{
|
||||
"id": "crud_con_python",
|
||||
"categorias": [
|
||||
"Git",
|
||||
"Programación",
|
||||
"Python"
|
||||
],
|
||||
"imagen": "/crud.png",
|
||||
"date": "18 de Julio de 2020",
|
||||
"title": "Mi primer CRUD con Python",
|
||||
"description": "He hecho un <strong>CRUD (Create, Read, Update and Delete)</strong> con <i>Python</i>, y diréis, porqué? Yo os lo contaré, este tipo de 'proyectitos' son fundamentales para..."
|
||||
},
|
||||
{
|
||||
"id": "crear-servicio",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Scripts"
|
||||
],
|
||||
"imagen": "/servicio.png",
|
||||
"date": "21 de Junio de 2020",
|
||||
"title": "Ejecutar un script al iniciar la Raspberry Pi",
|
||||
"description": "Vamos a crear un servicio con <i>systemd</i> que se ejecute cada vez que se inicie la <strong>Raspberry Pi</strong>. En mi caso tengo instalado <a href='https://dietpi.com/' target='_blank'>dietpi</a>, pero cualquier..."
|
||||
},
|
||||
{
|
||||
"id": "script-aviso-bateria",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Scripts",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/bateria_sh.png",
|
||||
"date": "1 de Junio de 2020",
|
||||
"title": "<i>Script</i> para avisar de la carga de la batería",
|
||||
"description": "Este fin de semana he instalado <strong>ArchLinux</strong> con el escritorio <strong>MATE</strong>, todo con ayuda de <a href='https://arcolinux.com/' target='_blank'>ArcoLinux</a>. Me he encontrado con que el <i>gestor de energía</i>..."
|
||||
},
|
||||
{
|
||||
"id": "scroll-en-tmux",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"terminal",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/scrollTmux.png",
|
||||
"date": "31 de Mayo de 2020",
|
||||
"title": "Activar el <i>scroll</i> en <strong>Tmux</strong>",
|
||||
"description": "Algo muy sencillo que me ha traído de cabeza ha sido activar el <i>scroll</i> en <strong>tmux</strong>. Me hacía falta porque en <strong>Linux</strong> es fundamental leer lo que pasa..."
|
||||
},
|
||||
{
|
||||
"id": "tmux-sacale-mas-partido",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"terminal",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/tmux.png",
|
||||
"date": "21 de Mayo de 2020",
|
||||
"title": "<strong>Tmux</strong>, sácale más partido a tu terminal",
|
||||
"description": "Os dije en el post dedicado a <i>Alacritty</i> que os iba a hablar de <strong>Tmux</strong>, pues lo prometido es deuda, vamos allá: <strong>Tmux</strong> según pone en su descripción..."
|
||||
},
|
||||
{
|
||||
"id": "alacritty-la-terminal-mas-rapida",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"terminal",
|
||||
"Aplicaciones"
|
||||
],
|
||||
"imagen": "/alacritty.png",
|
||||
"date": "17 de Mayo de 2020",
|
||||
"title": "<strong>Alacritty</strong>, la terminal más rápida",
|
||||
"description": "<strong>Alacritty</strong> es un simple emulador de terminal, no es más. Eso si, es súper rápido. Lo había escuchado en el podcast de <a href='https://www.atareao.es/software/utilidades/emulador-de-terminal-mas-rapido/'>atareao.es</a> y decidí hacer la..."
|
||||
},
|
||||
{
|
||||
"id": "primeros-pasos-python-qt",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Programación",
|
||||
"Python"
|
||||
],
|
||||
"imagen": "/qtdesigner.png",
|
||||
"date": "05 de Mayo de 2020",
|
||||
"title": "Primeros pasos con <i>PyQt</i> y <i>QtDesigner</i>. Primer programa con <i>Python</i>",
|
||||
"description": "Vamos a crear nuestro primer programa gráfico con Python y PyQt, es mucho más sencillo de lo que parece. Lo primero que tenemos que hacer es..."
|
||||
},
|
||||
{
|
||||
"id": "free-dns",
|
||||
"categorias": [
|
||||
"Redes",
|
||||
"Linux"
|
||||
],
|
||||
"imagen": "/free-dns.jpg",
|
||||
"date": "03 de Mayo de 2020",
|
||||
"title": "Conectarnos desde fuera de la red con <strong>free-dns</strong>",
|
||||
"description": "<strong>free-dns</strong> es un servicio gratuito con el que nos podremos conectar a nuestro ordenador desde fuera de la red. Lo uso para conectarme a una..."
|
||||
},
|
||||
{
|
||||
"id": "git-password",
|
||||
"categorias": [
|
||||
"Git",
|
||||
"Terminal",
|
||||
"Seguridad"
|
||||
],
|
||||
"imagen": "/git-password.png",
|
||||
"date": "11 de Abril de 2020",
|
||||
"title": "Hacer que <i>git</i> no pida contraseña",
|
||||
"description": "Se hace muy pesado, cada vez que hacemos un <i>push</i> tener que andar introduciendo las credenciales de <i>git</i>. Hay dos maneras de hacer..."
|
||||
},
|
||||
{
|
||||
"id": "comandos-git",
|
||||
"categorias": [
|
||||
"Git",
|
||||
"Terminal"
|
||||
],
|
||||
"imagen": "/comandos-git.jpg",
|
||||
"date": "4 de Abril de 2020",
|
||||
"title": "Los comandos que necesito para trabajar con <i>git</i> desde la terminal",
|
||||
"description": "No soy un gran especialista en <i>git</i>, de hecho con unos pocos comandos hago todo lo que necesito. Rara es la vez que he necesitado hacer..."
|
||||
},
|
||||
{
|
||||
"id": "mkinitcpio",
|
||||
"categorias": [
|
||||
"Errores",
|
||||
"Linux"
|
||||
],
|
||||
"imagen": "/mkinitcpio.jpg",
|
||||
"date": "26 de Marzo de 2020",
|
||||
"title": "Possibly missing firmware for module: aic94xx, wd719x",
|
||||
"description": "Soy usuario de Gnu/Linux en todos mis equipos, en todos en los que es posible. En mi sobremesa cada vez que compilaba el kernel o ejecutaba..."
|
||||
},
|
||||
{
|
||||
"id": "buscar-en-ficheros",
|
||||
"categorias": [
|
||||
"Linux",
|
||||
"Terminal"
|
||||
],
|
||||
"imagen": "/search.png",
|
||||
"date": "22 de Marzo de 2020",
|
||||
"title": "Buscar archivos en Linux por nombre o contenido",
|
||||
"description": "Para buscar archivos en nuestro sistema vamos a usar el comando <i>find</i>. Por ejemplo para buscar un archivo que contenga 'feet' en cualquier..."
|
||||
},
|
||||
{
|
||||
"id": "chuleta-de-markdown",
|
||||
"categorias": [
|
||||
"Markdown"
|
||||
],
|
||||
"imagen": "/markdown.png",
|
||||
"date": "17 de Marzo de 2020",
|
||||
"title": "Mi chuleta de Markdown",
|
||||
"description": "Guardo como oro en paño un archivo donde consulto cualquier duda que tengo cuando estoy escribiendo en formato <b>markdown</b>. La voy a dejar aquí..."
|
||||
},
|
||||
{
|
||||
"id": "manuales-vuejs-espanol",
|
||||
"categorias": [
|
||||
"Javascript",
|
||||
"VueJS",
|
||||
"Programación"
|
||||
],
|
||||
"imagen": "/vuejs.jpg",
|
||||
"date": "15 de Marzo de 2020",
|
||||
"title": "Manuales de VueJS en español",
|
||||
"description": "Tenía guardados un par de manuales de <i>VueJS</i> que en su momento me ayudaron mucho, sobretodo a entender el funcionamiento principal..."
|
||||
}
|
||||
]
|
||||
}
|
||||
21
src/store/categorias.txt
Normal file
21
src/store/categorias.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Linux
|
||||
Git
|
||||
VueJS
|
||||
Javascript
|
||||
Markdown
|
||||
Terminal
|
||||
Errores
|
||||
Seguridad
|
||||
Redes
|
||||
Programación
|
||||
Python
|
||||
Aplicaciones
|
||||
Scripts
|
||||
Privacidad
|
||||
Android
|
||||
Docker
|
||||
Sonido
|
||||
Imagen
|
||||
Sistema
|
||||
Bash
|
||||
Raspberry
|
||||
29
src/store/index.js
Executable file
29
src/store/index.js
Executable file
@@ -0,0 +1,29 @@
|
||||
import { store } from 'quasar/wrappers'
|
||||
import { createStore } from 'vuex'
|
||||
import user from './user'
|
||||
|
||||
// import example from './module-example'
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Store instantiation;
|
||||
*
|
||||
* The function below can be async too; either use
|
||||
* async/await or return a Promise which resolves
|
||||
* with the Store instance.
|
||||
*/
|
||||
|
||||
export default store(function( /* { ssrContext } */ ) {
|
||||
const Store = createStore({
|
||||
modules: {
|
||||
// example
|
||||
user
|
||||
},
|
||||
|
||||
// enable strict mode (adds overhead!)
|
||||
// for dev mode and --debug builds only
|
||||
strict: process.env.DEBUGGING
|
||||
})
|
||||
|
||||
return Store
|
||||
})
|
||||
2
src/store/module-example/actions.js
Executable file
2
src/store/module-example/actions.js
Executable file
@@ -0,0 +1,2 @@
|
||||
export function someAction (/* context */) {
|
||||
}
|
||||
2
src/store/module-example/getters.js
Executable file
2
src/store/module-example/getters.js
Executable file
@@ -0,0 +1,2 @@
|
||||
export function someGetter (/* state */) {
|
||||
}
|
||||
12
src/store/module-example/index.js
Executable file
12
src/store/module-example/index.js
Executable file
@@ -0,0 +1,12 @@
|
||||
import state from './state'
|
||||
import * as getters from './getters'
|
||||
import * as mutations from './mutations'
|
||||
import * as actions from './actions'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
getters,
|
||||
mutations,
|
||||
actions,
|
||||
state
|
||||
}
|
||||
2
src/store/module-example/mutations.js
Executable file
2
src/store/module-example/mutations.js
Executable file
@@ -0,0 +1,2 @@
|
||||
export function someMutation (/* state */) {
|
||||
}
|
||||
5
src/store/module-example/state.js
Executable file
5
src/store/module-example/state.js
Executable file
@@ -0,0 +1,5 @@
|
||||
export default function () {
|
||||
return {
|
||||
//
|
||||
}
|
||||
}
|
||||
10
src/store/store-flag.d.ts
vendored
Executable file
10
src/store/store-flag.d.ts
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
|
||||
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
|
||||
import "quasar/dist/types/feature-flag";
|
||||
|
||||
declare module "quasar/dist/types/feature-flag" {
|
||||
interface QuasarFeatureFlags {
|
||||
store: true;
|
||||
}
|
||||
}
|
||||
4
src/store/user/actions.js
Normal file
4
src/store/user/actions.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
export function someAction (context) {
|
||||
}
|
||||
*/
|
||||
4
src/store/user/getters.js
Normal file
4
src/store/user/getters.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
export function someGetter (state) {
|
||||
}
|
||||
*/
|
||||
12
src/store/user/index.js
Normal file
12
src/store/user/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import state from './state'
|
||||
import * as getters from './getters'
|
||||
import * as mutations from './mutations'
|
||||
import * as actions from './actions'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
7
src/store/user/mutations.js
Normal file
7
src/store/user/mutations.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
export function someMutation (state) {
|
||||
}
|
||||
*/
|
||||
export function updateBusqueda(state, texto) {
|
||||
state.busqueda = texto
|
||||
}
|
||||
5
src/store/user/state.js
Normal file
5
src/store/user/state.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function() {
|
||||
return {
|
||||
busqueda: ''
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user