Merge pull request 'access settings' (#4) from access-settings into pages

Reviewed-on: https://codeberg.org/macchiato/settings/pulls/4
pages
bat 3 years ago
commit bcafd8f9b3

@ -0,0 +1,123 @@
export class ConnectionEdit extends HTMLElement {
textEn = {
page: 'Page',
access: 'Access',
read: 'read',
readWrite: 'read and write',
doesntExist: "Error: The page doesn't exist",
samePage: "Error: Cannot connect to same page",
alreadyConnected: "Error: This page is already connected",
}
textEs = {
page: 'Página',
access: 'Accesso',
read: 'leer',
readWrite: 'leer y escribir',
doesntExist: 'Error: La página no existe',
samePage: "Error: no se puede conectar a la misma página",
alreadyConnected: "Error: esta página ya está conectada",
}
constructor() {
super()
this.attachShadow({mode: 'open'})
this.language = navigator.language
const pageLabel = document.createElement('label')
pageLabel.innerText = this.text.page
this.pageInput = document.createElement('input')
const accessLabel = document.createElement('label')
accessLabel.innerText = this.text.access
this.accessSelect = document.createElement('select')
const wrap = document.createElement('div')
wrap.append(this.accessSelect)
const opts = ['read', 'readWrite'].map(value => {
const el = document.createElement('option')
el.value = value
el.innerText = this.text[value]
return el
})
this.accessSelect.append(...opts)
this.accessSelect.value = 'read'
const fields = document.createElement('div')
fields.classList.add('fields')
fields.append(
pageLabel,
this.pageInput,
accessLabel,
wrap,
)
this.shadowRoot.append(
fields,
)
}
connectedCallback() {
const style = document.createElement('style')
style.textContent = `
:host {
display: flex;
flex-direction: column;
align-items: stretch;
}
.fields {
display: grid;
grid-template-columns: fit-content(50%) 1fr;
gap: 5px 10px;
}
.error {
color: red;
}
`
this.shadowRoot.append(style)
}
get data() {
return {
networkAccess: this.netSelect.value,
}
}
set data(value) {
this.netText.innerText = JSON.stringify(value)
this.netSelect.value = value.networkAccess ?? 'local'
}
set error(error) {
this._error = error
if (error === undefined) {
if (this.errorEl) {
this.errorEl.remove()
this.errorEl = undefined
}
} else {
if (!this.errorEl) {
this.errorEl = document.createElement('p')
this.errorEl.classList.add('error')
this.shadowRoot.append(this.errorEl)
}
this.errorEl.innerText = this.text[error]
}
}
get error() {
return this._error
}
get language() {
return this._language
}
set language(language) {
this._language = language
this.text = this.langEs ? this.textEs : this.textEn
}
get langEs() {
return /^es\b/.test(this.language)
}
get lang() {
return this.language.split('-')[0]
}
}

@ -0,0 +1,174 @@
export class Connections extends HTMLElement {
icons = {
del: `
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-trash3-fill" viewBox="0 0 16 16">
<path d="M11 1.5v1h3.5a.5.5 0 0 1 0 1h-.538l-.853 10.66A2 2 0 0 1 11.115 16h-6.23a2 2 0 0 1-1.994-1.84L2.038 3.5H1.5a.5.5 0 0 1 0-1H5v-1A1.5 1.5 0 0 1 6.5 0h3A1.5 1.5 0 0 1 11 1.5Zm-5 0v1h4v-1a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5ZM4.5 5.029l.5 8.5a.5.5 0 1 0 .998-.06l-.5-8.5a.5.5 0 1 0-.998.06Zm6.53-.528a.5.5 0 0 0-.528.47l-.5 8.5a.5.5 0 0 0 .998.058l.5-8.5a.5.5 0 0 0-.47-.528ZM8 4.5a.5.5 0 0 0-.5.5v8.5a.5.5 0 0 0 1 0V5a.5.5 0 0 0-.5-.5Z"/>
</svg>
`
}
textEn = {
add: 'Add connection',
cancel: 'Cancel',
read: 'read',
readWrite: 'read and write',
}
textEs = {
add: 'Añadir conexión',
cancel: 'Cancelar',
read: 'leer',
readWrite: 'leer y escribir',
}
constructor() {
super()
this.attachShadow({mode: 'open'})
this.language = navigator.language
this.content = document.createElement('div')
const bGroup = document.createElement(
'm-forms-button-group'
)
bGroup.addPrimary(this.text.add, () => {
this.add()
})
this.shadowRoot.append(
this.content,
bGroup,
)
}
connectedCallback() {
const style = document.createElement('style')
style.textContent = `
:host {
display: flex;
flex-direction: column;
align-items: stretch;
margin-bottom: 5px;
}
m-dialog::part(footer) {
padding-top: 15px;
}
button.icon {
border: none;
background: inherit;
color: #555;
}
button.icon svg {
width: 12px;
height: 12px;
}
.connection {
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
}
.access {
font-size: 80%;
background: #ccc;
border-radius: 3px;
padding: 5px;
}
`
this.shadowRoot.append(style)
}
add() {
const dialog = document.createElement('m-dialog')
dialog.top = 180
const edit = document.createElement(
'm-settings-connection-edit'
)
dialog.bodyEl.append(edit)
const bGroup = document.createElement(
'm-forms-button-group'
)
bGroup.addPrimary(this.text.add, () => {
const path = edit.pageInput.value
const access = edit.accessSelect.value
const exists = localStorage.getItem(path)
if (!exists) {
edit.error = 'doesntExist'
return
} else if (path === this.path) {
edit.error = 'samePage'
return
} else if (this.data[path] ?? true !== true) {
edit.error = 'alreadyConnected'
return
}
this.data = {
...this.data,
[path]: access,
}
dialog.close()
})
bGroup.addCancel(this.text.cancel, () => {
dialog.close()
})
dialog.footerEl.appendChild(bGroup)
this.shadowRoot.append(dialog)
dialog.open()
}
display() {
const entries = Object.entries(this.data).filter(
([k, v]) => v !== undefined
)
const content = entries.map(([path, access]) => {
const el = document.createElement('div')
el.classList.add('connection')
const pathEl = document.createElement('span')
pathEl.innerText = path
el.append(pathEl)
const accessEl = document.createElement('span')
accessEl.innerText = this.text[access]
accessEl.classList.add('access')
const delBtn = document.createElement('button')
delBtn.classList.add('delete', 'icon')
delBtn.innerHTML = this.icons.del
delBtn.addEventListener('click', () => {
this.data = {...this.data, [path]: undefined}
})
el.append(accessEl, delBtn)
return el
})
this.content.replaceChildren(...content)
}
set type(value) {
this._type = value
}
get type() {
return this._type
}
get data() {
return this._data
}
set data(value) {
this._data = value
this.display()
}
get language() {
return this._language
}
set language(language) {
this._language = language
this.text = this.langEs ? this.textEs : this.textEn
}
get langEs() {
return /^es\b/.test(this.language)
}
get lang() {
return this.language.split('-')[0]
}
}

@ -0,0 +1,159 @@
export class NetworkSettings extends HTMLElement {
textEn = {
csp: 'Content Security Policy',
}
textEs = {
csp: 'Política de Seguridad de Contenido',
}
accessOptions = {
local: {
option: {
en: "network access off",
es: "acceso a la red desactivado",
},
text: {
en: 'direct network access off',
es: 'acceso directo a la red desactivado',
},
details: {
en: "Direct network access is off and the page can't send data. This prevents data from being exposed even if the code of the page isn't trusted. It is a good option if this page will contain private data.",
es: "El acceso directo a la red está desactivado y la página no puede enviar datos. Esto evita que los datos queden expuestos incluso si el código de la página no es de confianza. Es una buena opción si esta página contendrá datos privados.",
},
},
jsCdns: {
option: {
en: 'jsDelivr and UNPKG',
es: 'jsDelivr y UNPKG',
},
text: {
en: 'access to jsDelivr and UNPKG only',
es: 'acceso a jsDelivr y UNPKG solamente',
},
details: {
en: "The page can make requests to jsDelivr and UNPKG. This could allow data from your page to be sent to the servers if the code in the page sends it. This means that if you have private data in the page, you should either trust the code on the page not to send it to these servers, or you should trust these servers, or both.",
es: "La página puede realizar solicitudes a jsDelivr y UNPKG. Esto podría permitir que los datos de su página se envíen a los servidores si el código de la página los envía. Esto significa que si tiene datos privados en la página, debe confiar en el código de la página para no enviarlos a estos servidores, o debe confiar en estos servidores, o en ambos.",
},
},
open: {
option: {
en: 'network open',
es: 'red abierta',
},
text: {
en: 'network open (direct access to any site)',
es: 'red abierta (acceso directo a cualquier sitio)',
},
details: {
en: "Network access is open, so the page can send or receive requests to any site. You should either A) have no private data in the page, or B) trust the code completely. This makes the page similar to major code playgrounds like codesandbox.com, stackblitz.com, jsbin.com, and codepen.io.",
es: "El acceso a la red está abierto, por lo que la página puede enviar o recibir solicitudes a cualquier sitio. Debería A) no tener datos privados en la página, o B) confiar completamente en el código. Esto hace que la página sea similar a los principales juegos de código como codesandbox.com, stackblitz.com, jsbin.com y codepen.io.",
},
},
}
constructor() {
super()
this.attachShadow({mode: 'open'})
this.language = navigator.language
const netSelectField = document.createElement('div')
netSelectField.classList.add('field')
this.netSelect = document.createElement('select')
this.netSelect.addEventListener(
'change', () => this.display()
)
netSelectField.append(this.netSelect)
const netOptions = Object.entries(
this.accessOptions
).map(([value, {option}]) => {
const el = document.createElement('option')
el.value = value
el.innerText = option[this.lang]
return el
})
this.netSelect.append(...netOptions)
this.netHeading = document.createElement('h3')
this.netText = document.createElement('p')
this.cspLabel = document.createElement(
'div'
)
this.cspLabel.classList.add('csp-label')
this.cspLabel.innerText = (
this.text.csp + ' (CSP):'
)
this.netCsp = document.createElement('div')
this.netCsp.classList.add('csp')
this.shadowRoot.append(
netSelectField,
this.netHeading,
this.netText,
this.cspLabel,
this.netCsp,
)
}
connectedCallback() {
const style = document.createElement('style')
style.textContent = `
:host {
display: flex;
flex-direction: column;
align-items: stretch;
}
div.field {
display: flex;
flex-direction: row;
}
h1, h2, h3, p {
margin: 3px 0;
}
.csp-label {
font-weight: bold;
}
.csp {
font-family: monospace;
}
`
this.shadowRoot.append(style)
}
display() {
const value = this.netSelect.value
const opt = this.accessOptions[value]
const l = this.lang
this.netHeading.innerText = opt.text[l]
this.netText.innerText = opt.details[l]
this.netCsp.innerText = (
this.cspProfiles[value]
)
}
get data() {
return {
networkAccess: this.netSelect.value,
}
}
set data(value) {
this.netText.innerText = JSON.stringify(value)
this.netSelect.value = value.networkAccess ?? 'local'
this.display()
}
get language() {
return this._language
}
set language(language) {
this._language = language
this.text = this.langEs ? this.textEs : this.textEn
}
get langEs() {
return /^es\b/.test(this.language)
}
get lang() {
return this.language.split('-')[0]
}
}

@ -1,106 +1,47 @@
export class PageSettings extends HTMLElement {
textEn = {
networkAccess: 'Direct network access (CSP)',
csp: 'Content Security Policy',
outbound: 'Outbound connections',
inbound: 'Inbound connections',
netAccess: 'Direct network access (CSP)',
}
textEs = {
networkAccess: 'Acceso directo a la red (CSP)',
csp: 'Política de Seguridad de Contenido',
}
networkAccessOptions = {
local: {
option: {
en: "network access off",
es: "acceso a la red desactivado",
},
text: {
en: 'direct network access off',
es: 'acceso directo a la red desactivado',
},
details: {
en: "Direct network access is off and the page can't send data. This prevents data from being exposed even if the code of the page isn't trusted. It is a good option if this page will contain private data.",
es: "El acceso directo a la red está desactivado y la página no puede enviar datos. Esto evita que los datos queden expuestos incluso si el código de la página no es de confianza. Es una buena opción si esta página contendrá datos privados.",
},
},
unpkg: {
option: {
en: 'UNPKG',
es: 'UNPKG',
},
text: {
en: 'access to UNPKG only',
es: 'acceso a UNPKG solamente',
},
details: {
en: "The page can make requests to UNPKG. This could allow data from your page to be sent to the servers if the code in the page sends it. This means that if you have private data in the page, you should either trust the code on the page not to send it to these servers, or you should trust these servers, or both.",
es: "La página puede realizar solicitudes a UNPKG. Esto podría permitir que los datos de su página se envíen a los servidores si el código de la página los envía. Esto significa que si tiene datos privados en la página, debe confiar en el código de la página para no enviarlos a estos servidores, o debe confiar en estos servidores, o en ambos.",
},
},
open: {
option: {
en: 'network open',
es: 'red abierta',
},
text: {
en: 'network open (direct access to any site)',
es: 'red abierta (acceso directo a cualquier sitio)',
},
details: {
en: "Network access is open, so the page can send or receive requests to any site. You should either A) have no private data in the page, or B) trust the code completely. This makes the page similar to major code playgrounds like codesandbox.com, stackblitz.com, jsbin.com, and codepen.io.",
es: "El acceso a la red está abierto, por lo que la página puede enviar o recibir solicitudes a cualquier sitio. Debería A) no tener datos privados en la página, o B) confiar completamente en el código. Esto hace que la página sea similar a los principales juegos de código como codesandbox.com, stackblitz.com, jsbin.com y codepen.io.",
},
},
outbound: 'Conexiones salientes',
inbound: 'Conexiones entrantes',
netAccess: 'Acceso directo a la red (CSP)',
}
constructor() {
super()
this.attachShadow({mode: 'open'})
this.language = navigator.language
const netSelectLabel = document.createElement('div')
netSelectLabel.classList.add('label')
netSelectLabel.innerText = this.text.networkAccess
const netSelectField = (
document.createElement('div')
)
netSelectField.classList.add('field')
this.netSelect = document.createElement(
'select'
)
this.netSelect.addEventListener(
'change', () => this.display()
const outboundHeading = document.createElement('div')
outboundHeading.classList.add('heading')
outboundHeading.innerText = this.text.outbound
this.outbound = document.createElement(
'm-settings-connections'
)
netSelectField.append(this.netSelect)
const netOptions = Object.entries(
this.networkAccessOptions
).map(([value, {option}]) => {
const el = document.createElement('option')
el.value = value
el.innerText = option[this.lang]
return el
})
this.netSelect.append(...netOptions)
this.netHeading = document.createElement(
'h3'
this.outbound.type = 'outbound'
const inboundHeading = document.createElement('div')
inboundHeading.classList.add('heading')
inboundHeading.innerText = this.text.inbound
this.inbound = document.createElement(
'm-settings-connections'
)
this.netText = document.createElement('p')
this.cspLabel = document.createElement(
'div'
this.inbound.type = 'inbound'
const netSelectHeading = document.createElement('div')
netSelectHeading.classList.add('heading')
netSelectHeading.innerText = this.text.netAccess
this.network = document.createElement(
'm-settings-network-settings'
)
this.cspLabel.classList.add('csp-label')
this.cspLabel.innerText = (
this.text.csp + ' (CSP):'
)
this.netCsp = document.createElement('div')
this.netCsp.classList.add('csp')
this.shadowRoot.append(
netSelectLabel,
netSelectField,
this.netHeading,
this.netText,
this.cspLabel,
this.netCsp,
outboundHeading,
this.outbound,
inboundHeading,
this.inbound,
netSelectHeading,
this.network,
)
}
@ -114,54 +55,53 @@ export class PageSettings extends HTMLElement {
align-items: stretch;
overflow-y: auto;
}
div.label {
display: flex;
flex-direction: row;
justify-content: flex-start;
background: #ddd;
padding: 5px;
border-radius: 3px;
margin-bottom: 5px;
font-weight: bold;
* {
padding-left: 10px;
}
div.field {
div.heading {
display: flex;
flex-direction: row;
}
h1, h2, h3, p {
margin: 3px 0;
}
.csp-label {
justify-content: flex-start;
background: #f2dbd8;
padding: 3px;
margin-bottom: 10px;
margin-top: 10px;
font-weight: bold;
}
.csp {
font-family: monospace;
}
`
this.shadowRoot.append(style)
}
display() {
const value = this.netSelect.value
const opt = this.networkAccessOptions[value]
const l = this.lang
this.netHeading.innerText = opt.text[l]
this.netText.innerText = opt.details[l]
this.netCsp.innerText = (
this.cspProfiles[value]
)
}
get data() {
return {
networkAccess: this.netSelect.value,
...this.network.data,
connections: {
outbound: this.outbound.data,
inbound: this.inbound.data,
},
}
}
set data(value) {
this.netText.innerText = JSON.stringify(value)
this.netSelect.value = value.networkAccess ?? 'local'
this.display()
this.outbound.data = value.connections?.outbound || {}
this.inbound.data = value.connections?.inbound || {}
this.network.data = {
networkAccess: value.networkAccess
}
}
set path(value) {
this._path = value
this.outbound.path = value
this.inbound.path = value
}
get path() {
return this._path
}
set cspProfiles(value) {
this.network.cspProfiles = value
}
get language() {

Loading…
Cancel
Save