mirror of
https://github.com/samsonjs/mit-license.git
synced 2026-04-27 15:07:42 +00:00
Refactor
Signed-off-by: Richie Bendall <richiebendall@gmail.com>
This commit is contained in:
parent
325cd76ca4
commit
bd71a57b7a
10 changed files with 223 additions and 178 deletions
|
|
@ -11,10 +11,10 @@ const getUrlParts = url => {
|
||||||
return url.slice(1).split('/')
|
return url.slice(1).split('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (request, response, next) => {
|
const loadOptions = url => {
|
||||||
const urlParts = getUrlParts(request.url)
|
const urlParts = getUrlParts(url)
|
||||||
|
|
||||||
response.locals.options = {
|
const options = {
|
||||||
format: 'html',
|
format: 'html',
|
||||||
startYear: null,
|
startYear: null,
|
||||||
endYear: currentYear
|
endYear: currentYear
|
||||||
|
|
@ -23,21 +23,23 @@ module.exports = (request, response, next) => {
|
||||||
for (const urlPart of urlParts) {
|
for (const urlPart of urlParts) {
|
||||||
if (yearRegex.test(urlPart)) {
|
if (yearRegex.test(urlPart)) {
|
||||||
if (urlPart.startsWith('@')) {
|
if (urlPart.startsWith('@')) {
|
||||||
response.locals.options.pinnedYear = Number.parseInt(urlPart.slice(1))
|
options.pinnedYear = Number.parseInt(urlPart.slice(1), 10)
|
||||||
} else {
|
} else {
|
||||||
response.locals.options.startYear = Number.parseInt(urlPart)
|
options.startYear = Number.parseInt(urlPart, 10)
|
||||||
}
|
}
|
||||||
} else if (yearRangeRegex.test(urlPart)) {
|
} else if (yearRangeRegex.test(urlPart)) {
|
||||||
const [startYear, endYear] = urlPart.match(yearRangeRegex).slice(1)
|
const [startYear, endYear] = urlPart.match(yearRangeRegex).slice(1)
|
||||||
|
|
||||||
response.locals.options.startYear = Number.parseInt(startYear)
|
options.startYear = Number.parseInt(startYear, 10)
|
||||||
response.locals.options.endYear = Number.parseInt(endYear)
|
options.endYear = Number.parseInt(endYear, 10)
|
||||||
} else if (urlPart.startsWith('license')) {
|
} else if (urlPart.startsWith('license')) {
|
||||||
response.locals.options.format = urlPart.split('.')[1].trim()
|
options.format = urlPart.split('.')[1].trim()
|
||||||
} else if (urlPart.startsWith('+')) {
|
} else if (urlPart.startsWith('+')) {
|
||||||
response.locals.options.license = urlPart.slice(1).toUpperCase()
|
options.license = urlPart.slice(1).toUpperCase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next()
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default loadOptions
|
||||||
26
lib/load-user.js
Normal file
26
lib/load-user.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {fileURLToPath} from 'node:url'
|
||||||
|
import path, {dirname} from 'node:path'
|
||||||
|
import loadJsonFile from 'load-json-file'
|
||||||
|
|
||||||
|
const directoryName = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
const loadUser = async hostname => {
|
||||||
|
const [id] = hostname.split('.')
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
copyright: '<copyright holders>' // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
...await loadJsonFile(path.join(directoryName, '..', 'users', `${id}.json`))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== 'ENOENT') {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default loadUser
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
const path = require('path')
|
|
||||||
const loadJsonFile = require('load-json-file')
|
|
||||||
|
|
||||||
module.exports = async (request, response, next) => {
|
|
||||||
const id = request.hostname.split('.')[0]
|
|
||||||
|
|
||||||
if (request.method.toUpperCase() !== 'GET') {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise load up the user json file
|
|
||||||
response.locals.user = {
|
|
||||||
copyright: '<copyright holders>'
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
response.locals.user = {
|
|
||||||
...response.locals.user,
|
|
||||||
...await loadJsonFile(path.join(__dirname, '..', 'users', `${id}.json`))
|
|
||||||
}
|
|
||||||
} catch ({ code, message }) {
|
|
||||||
if (code !== 'ENOENT') {
|
|
||||||
response
|
|
||||||
.code(500)
|
|
||||||
.send(
|
|
||||||
`An internal error occurred - open an issue on https://github.com/remy/mit-license with the following information: ${message}`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
35
package.json
35
package.json
|
|
@ -2,6 +2,7 @@
|
||||||
"name": "mit-licence",
|
"name": "mit-licence",
|
||||||
"description": "Hosted MIT License with details controlled through this repo",
|
"description": "Hosted MIT License with details controlled through this repo",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -17,42 +18,45 @@
|
||||||
"dev": "nodemon .",
|
"dev": "nodemon .",
|
||||||
"serve": "node server.js",
|
"serve": "node server.js",
|
||||||
"test": "node test.js",
|
"test": "node test.js",
|
||||||
"lint": "standard"
|
"lint": "xo"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/remy/mit-license/issues"
|
"url": "https://github.com/remy/mit-license/issues"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/rest": "^18.0.6",
|
"@octokit/rest": "^18.5.3",
|
||||||
"@sindresorhus/is": "^3.1.2",
|
"@sindresorhus/is": "^4.0.1",
|
||||||
"any-size": "^1.2.0",
|
"any-size": "^1.2.0",
|
||||||
"btoa": "^1.2.1",
|
"btoa": "^1.2.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"create-html-element": "^3.0.0",
|
"create-html-element": "^3.0.0",
|
||||||
"ejs": "^3.1.5",
|
"ejs": "^3.1.6",
|
||||||
"escape-goat": "^3.0.0",
|
"escape-goat": "^4.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-minify": "^1.0.0",
|
"express-minify": "^1.0.0",
|
||||||
"gravatar-url": "^3.1.0",
|
"gravatar-url": "^4.0.0",
|
||||||
"html-text": "^1.0.1",
|
"html-text": "^1.0.1",
|
||||||
"load-json-file": "^6.2.0",
|
"load-json-file": "^6.2.0",
|
||||||
"path-exists": "^4.0.0",
|
"path-exists": "^4.0.0",
|
||||||
"postcss-middleware": "^1.1.4",
|
"postcss-middleware": "^1.1.4",
|
||||||
"postcss-preset-env": "^6.7.0",
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
"read-pkg": "^6.0.0",
|
||||||
"serve-favicon": "^2.5.0",
|
"serve-favicon": "^2.5.0",
|
||||||
"temp-dir": "^2.0.0",
|
"temp-dir": "^2.0.0",
|
||||||
"write-json-file": "^4.3.0",
|
"write-json-file": "^4.3.0",
|
||||||
"yn": "^4.0.0"
|
"yn": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/eslint-parser": "^7.14.3",
|
||||||
|
"@babel/plugin-syntax-top-level-await": "^7.12.13",
|
||||||
"css": "^3.0.0",
|
"css": "^3.0.0",
|
||||||
"file-ext": "^1.0.0",
|
"file-ext": "^1.0.0",
|
||||||
"has-flag": "^4.0.0",
|
"has-flag": "^5.0.0",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"nodemon": "^2.0.4",
|
"nodemon": "^2.0.7",
|
||||||
"path-extra": "^4.3.0",
|
"path-extra": "^4.3.0",
|
||||||
"standard": "^14.3.4"
|
"xo": "^0.40.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"postcss-middleware/vinyl-fs/glob-stream/micromatch/braces": "^3.0.2"
|
"postcss-middleware/vinyl-fs/glob-stream/micromatch/braces": "^3.0.2"
|
||||||
|
|
@ -62,5 +66,18 @@
|
||||||
"pre-commit": "npm run lint",
|
"pre-commit": "npm run lint",
|
||||||
"pre-push": "npm test"
|
"pre-push": "npm test"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"xo": {
|
||||||
|
"space": 2,
|
||||||
|
"semicolon": false,
|
||||||
|
"parser": "@babel/eslint-parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"requireConfigFile": false,
|
||||||
|
"babelOptions": {
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-syntax-top-level-await"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
const path = require('path')
|
import {fileURLToPath} from 'node:url'
|
||||||
const { htmlEscape, htmlUnescape } = require('escape-goat')
|
import path, {dirname} from 'node:path'
|
||||||
const stripHtml = require('html-text')
|
import {htmlEscape, htmlUnescape} from 'escape-goat'
|
||||||
const is = require('@sindresorhus/is')
|
import stripHtml from 'html-text'
|
||||||
const getGravatarUrl = require('gravatar-url')
|
import is from '@sindresorhus/is'
|
||||||
const createHtmlElement = require('create-html-element')
|
import getGravatarUrl from 'gravatar-url'
|
||||||
const { renderFile } = require('ejs')
|
import createHtmlElement from 'create-html-element'
|
||||||
|
import {renderFile} from 'ejs'
|
||||||
|
|
||||||
|
import loadUser from '../lib/load-user.js'
|
||||||
|
import loadOptions from '../lib/load-options.js'
|
||||||
|
|
||||||
|
const directoryName = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
const getCopyrightName = (user, isPlainText) => {
|
const getCopyrightName = (user, isPlainText) => {
|
||||||
if (is.string(user)) {
|
if (is.string(user)) {
|
||||||
|
|
@ -53,9 +59,24 @@ const getGravatarEmail = user => {
|
||||||
|
|
||||||
const removeFalsy = array => array.filter(Boolean)
|
const removeFalsy = array => array.filter(Boolean)
|
||||||
|
|
||||||
module.exports = async (_, response) => {
|
const getRoute = async (request, response) => {
|
||||||
const { user, options } = response.locals
|
let user
|
||||||
const isPlainText = options.format !== 'html'
|
try {
|
||||||
|
user = await loadUser(request.hostname)
|
||||||
|
} catch ({message}) {
|
||||||
|
response
|
||||||
|
.status(500)
|
||||||
|
.send(`An internal error occurred - open an issue on https://github.com/remy/mit-license with the following information: ${message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = loadOptions(request.url)
|
||||||
|
const year = options.pinnedYear ?
|
||||||
|
options.pinnedYear :
|
||||||
|
removeFalsy([options.startYear, options.endYear]).join('-')
|
||||||
|
const license = (options.license || user.license || 'MIT').toUpperCase()
|
||||||
|
const format = options.format || user.format || 'html'
|
||||||
|
const isPlainText = format !== 'html'
|
||||||
|
|
||||||
let name
|
let name
|
||||||
|
|
||||||
|
|
@ -85,21 +106,15 @@ module.exports = async (_, response) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const year = options.pinnedYear
|
|
||||||
? options.pinnedYear
|
|
||||||
: removeFalsy([options.startYear, options.endYear]).join('-')
|
|
||||||
const license = (options.license || user.license || 'MIT').toUpperCase()
|
|
||||||
const format = options.format || user.format || 'html'
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await renderFile(path.join(__dirname, '..', 'licenses', `${license}.ejs`), {
|
const content = await renderFile(path.join(directoryName, '..', 'licenses', `${license}.ejs`), {
|
||||||
info: `${year} ${name}`,
|
info: `${year} ${name}`,
|
||||||
theme: user.theme || 'default',
|
theme: user.theme || 'default',
|
||||||
gravatar
|
gravatar
|
||||||
})
|
})
|
||||||
|
|
||||||
if (format === 'txt') {
|
if (format === 'txt') {
|
||||||
const { articleContent } = content.match(/<article>(?<articleContent>.*)<\/article>/ms).groups
|
const {articleContent} = content.match(/<article>(?<articleContent>.*)<\/article>/ms).groups
|
||||||
|
|
||||||
response
|
response
|
||||||
.set('Content-Type', 'text/plain; charset=UTF-8')
|
.set('Content-Type', 'text/plain; charset=UTF-8')
|
||||||
|
|
@ -111,8 +126,11 @@ module.exports = async (_, response) => {
|
||||||
response.send(content)
|
response.send(content)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.json({ ...user, ...options })
|
|
||||||
|
response.json({...user, ...options})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
response.status(500).send(error)
|
response.status(500).send(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default getRoute
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,29 @@
|
||||||
const path = require('path')
|
import path, {dirname} from 'node:path'
|
||||||
const btoa = require('btoa')
|
import toBase64 from 'btoa'
|
||||||
const { version } = require(path.join(__dirname, '..', 'package.json'))
|
import {readPackageAsync as readPackage} from 'read-pkg'
|
||||||
const size = require('any-size')
|
import size from 'any-size'
|
||||||
const { Octokit } = require('@octokit/rest')
|
import {Octokit} from '@octokit/rest'
|
||||||
const pathExists = require('path-exists')
|
import pathExists from 'path-exists'
|
||||||
const writeJsonFile = require('write-json-file')
|
import writeJsonFile from 'write-json-file'
|
||||||
|
import yn from 'yn'
|
||||||
|
import is from '@sindresorhus/is'
|
||||||
|
|
||||||
|
import {fileURLToPath} from 'node:url'
|
||||||
|
|
||||||
|
import {isDomainId} from './utils.js'
|
||||||
|
|
||||||
|
const directoryName = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
const {version} = await readPackage()
|
||||||
|
|
||||||
const github = new Octokit({
|
const github = new Octokit({
|
||||||
// GitHub personal access token
|
// GitHub personal access token
|
||||||
auth: process.env.github_token,
|
auth: process.env.github_token,
|
||||||
// User agent with version from package.json
|
// User agent with version from package.json
|
||||||
userAgent: `mit-license v${version}`
|
userAgent: `mit-license v${version}`
|
||||||
})
|
})
|
||||||
const yn = require('yn')
|
|
||||||
const is = require('@sindresorhus/is')
|
|
||||||
|
|
||||||
const { isDomainId } = require('./utils')
|
function getUserData({query, body}) {
|
||||||
|
|
||||||
function getUserData ({ query, body }) {
|
|
||||||
// If query parameters provided
|
// If query parameters provided
|
||||||
if (size(query) > 0) {
|
if (size(query) > 0) {
|
||||||
return query
|
return query
|
||||||
|
|
@ -31,24 +38,23 @@ function getUserData ({ query, body }) {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP POST API
|
const postRoute = async (request, response) => {
|
||||||
module.exports = async (request, response) => {
|
const {hostname} = request
|
||||||
const { hostname } = request
|
|
||||||
|
|
||||||
// Get different parts of hostname (example: remy.mit-license.org -> ['remy', 'mit-license', 'org'])
|
// Get different parts of hostname (example: remy.mit-license.org -> ['remy', 'mit-license', 'org'])
|
||||||
const params = hostname.split('.')
|
const parameters = hostname.split('.')
|
||||||
|
|
||||||
// This includes the copyright, year, etc.
|
// This includes the copyright, year, etc.
|
||||||
const userData = getUserData(request)
|
const userData = getUserData(request)
|
||||||
|
|
||||||
// If there isn't enough part of the hostname
|
// If there isn't enough part of the hostname
|
||||||
if (params.length < 2) {
|
if (parameters.length < 2) {
|
||||||
response.status(400).send('Please specify a subdomain in the URL.')
|
response.status(400).send('Please specify a subdomain in the URL.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the name from the URL
|
// Extract the name from the URL
|
||||||
const [id] = params
|
const [id] = parameters
|
||||||
|
|
||||||
if (!isDomainId(id)) {
|
if (!isDomainId(id)) {
|
||||||
// Return a vague error intentionally
|
// Return a vague error intentionally
|
||||||
|
|
@ -62,7 +68,7 @@ module.exports = async (request, response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user file exists in the users directory
|
// Check if the user file exists in the users directory
|
||||||
if (await pathExists(path.join(__dirname, '..', 'users', `${id}.json`))) {
|
if (await pathExists(path.join(directoryName, '..', 'users', `${id}.json`))) {
|
||||||
response
|
response
|
||||||
.status(409)
|
.status(409)
|
||||||
.send(
|
.send(
|
||||||
|
|
@ -73,7 +79,7 @@ module.exports = async (request, response) => {
|
||||||
|
|
||||||
if (userData.gravatar) {
|
if (userData.gravatar) {
|
||||||
// Parse the string version of a boolean or similar
|
// Parse the string version of a boolean or similar
|
||||||
userData.gravatar = yn(userData.gravatar, { lenient: true })
|
userData.gravatar = yn(userData.gravatar, {lenient: true})
|
||||||
if (is.undefined(userData.gravatar)) {
|
if (is.undefined(userData.gravatar)) {
|
||||||
response
|
response
|
||||||
.status(400)
|
.status(400)
|
||||||
|
|
@ -98,13 +104,13 @@ module.exports = async (request, response) => {
|
||||||
repo: 'mit-license',
|
repo: 'mit-license',
|
||||||
path: `users/${id}.json`,
|
path: `users/${id}.json`,
|
||||||
message: `Automated creation of user ${id}.`,
|
message: `Automated creation of user ${id}.`,
|
||||||
content: btoa(JSON.stringify(userData, 0, 2)),
|
content: toBase64(JSON.stringify(userData, 0, 2)),
|
||||||
committer: {
|
committer: {
|
||||||
name: 'MIT License Bot',
|
name: 'MIT License Bot',
|
||||||
email: 'remy@leftlogic.com'
|
email: 'remy@leftlogic.com'
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
writeJsonFile(path.join(__dirname, '..', 'users', `${id}.json`), userData, { indent: undefined })
|
writeJsonFile(path.join(directoryName, '..', 'users', `${id}.json`), userData, {indent: undefined})
|
||||||
])
|
])
|
||||||
|
|
||||||
response.status(201).send(`MIT license page created: https://${hostname}`)
|
response.status(201).send(`MIT license page created: https://${hostname}`)
|
||||||
|
|
@ -116,3 +122,6 @@ module.exports = async (request, response) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP POST API
|
||||||
|
export default postRoute
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
exports.isDomainId = value => /^[\w-_]+$/.test(value)
|
export const isDomainId = value => /^[\w-_]+$/.test(value)
|
||||||
|
|
|
||||||
43
server.js
43
server.js
|
|
@ -5,16 +5,25 @@ IMPORTANT: Set the `github_token` environment variable to a personal access to
|
||||||
Server port: The `PORT` environment variable can also be set to control the port the server
|
Server port: The `PORT` environment variable can also be set to control the port the server
|
||||||
should be hosted on.
|
should be hosted on.
|
||||||
*/
|
*/
|
||||||
const express = require('express')
|
import path, {dirname} from 'node:path'
|
||||||
const minify = require('express-minify')
|
import express from 'express'
|
||||||
const favicon = require('serve-favicon')
|
import minify from 'express-minify'
|
||||||
const postcssMiddleware = require('postcss-middleware')
|
import favicon from 'serve-favicon'
|
||||||
const tempDirectory = require('temp-dir')
|
import postcssMiddleware from 'postcss-middleware'
|
||||||
const path = require('path')
|
import tempDirectory from 'temp-dir'
|
||||||
|
import postcssPresetEnv from 'postcss-preset-env'
|
||||||
|
import cors from 'cors'
|
||||||
|
|
||||||
|
import {fileURLToPath} from 'node:url'
|
||||||
|
|
||||||
|
import postRoute from './routes/post.js'
|
||||||
|
import getRoute from './routes/get.js'
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
const PORT = process.env.PORT || 8080
|
const PORT = process.env.PORT || 8080
|
||||||
|
|
||||||
|
const directoryName = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
// Prepare application
|
// Prepare application
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use(
|
app.use(
|
||||||
|
|
@ -22,22 +31,22 @@ app.use(
|
||||||
cache: tempDirectory
|
cache: tempDirectory
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
app.use(favicon(path.join(__dirname, 'favicon.ico')))
|
app.use(favicon(path.join(directoryName, 'favicon.ico')))
|
||||||
app.set('views', path.join(__dirname, '/licenses'))
|
app.set('views', path.join(directoryName, '/licenses'))
|
||||||
|
|
||||||
// Setup static files
|
// Setup static files
|
||||||
app.use('/robots.txt', express.static('robots.txt'))
|
app.use('/robots.txt', express.static('robots.txt'))
|
||||||
app.use('/favicon.ico', express.static(`${__dirname}/favicon.ico`))
|
app.use('/favicon.ico', express.static(`${directoryName}/favicon.ico`))
|
||||||
app.use(
|
app.use(
|
||||||
'/themes',
|
'/themes',
|
||||||
postcssMiddleware({
|
postcssMiddleware({
|
||||||
plugins: [
|
plugins: [
|
||||||
require('postcss-preset-env')({
|
postcssPresetEnv({
|
||||||
overrideBrowserslist: '>= 0%'
|
overrideBrowserslist: '>= 0%'
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
src (request) {
|
src(request) {
|
||||||
return path.join(__dirname, 'themes', request.path)
|
return path.join(directoryName, 'themes', request.path)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
express.static('themes')
|
express.static('themes')
|
||||||
|
|
@ -46,7 +55,7 @@ app.use(
|
||||||
// Middleware
|
// Middleware
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
app.use(require('cors')())
|
app.use(cors())
|
||||||
// Parse URL-encoded bodies (as sent by HTML forms)
|
// Parse URL-encoded bodies (as sent by HTML forms)
|
||||||
app.use(
|
app.use(
|
||||||
express.urlencoded({
|
express.urlencoded({
|
||||||
|
|
@ -56,13 +65,9 @@ app.use(
|
||||||
// Parse JSON bodies (as sent by API clients)
|
// Parse JSON bodies (as sent by API clients)
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|
||||||
// Capture the id from the subdomain and options from parts of the url
|
|
||||||
app.use(require('./middleware/load-user'))
|
|
||||||
app.use(require('./middleware/load-options'))
|
|
||||||
|
|
||||||
// HTTP endpoints
|
// HTTP endpoints
|
||||||
app.post('/', require('./routes/post'))
|
app.post('/', postRoute)
|
||||||
app.get('/*', require('./routes/get'))
|
app.get('/*', getRoute)
|
||||||
|
|
||||||
// Start listening for HTTP requests
|
// Start listening for HTTP requests
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
|
|
|
||||||
131
test.js
131
test.js
|
|
@ -1,78 +1,79 @@
|
||||||
const { promises: fs } = require('fs')
|
import {promises as fs} from 'node:fs'
|
||||||
const writeJsonFile = require('write-json-file')
|
import writeJsonFile from 'write-json-file'
|
||||||
const CSS = require('css')
|
import {parse as parseCSS} from 'css'
|
||||||
const { isDomainId } = require('./routes/utils')
|
import hasFlag from 'has-flag'
|
||||||
const hasFlag = require('has-flag')
|
import getExtension from 'file-ext'
|
||||||
const getExtension = require('file-ext')
|
import path from 'path-extra'
|
||||||
const path = require('path-extra')
|
import is from '@sindresorhus/is'
|
||||||
const is = require('@sindresorhus/is')
|
import {isDomainId} from './routes/utils.js'
|
||||||
|
|
||||||
async function report (content, fix) {
|
async function report(content, fix) {
|
||||||
console.error(content)
|
console.error(content)
|
||||||
if (fix && hasFlag('--fix')) await fix()
|
if (fix && hasFlag('--fix')) {
|
||||||
|
await fix()
|
||||||
|
}
|
||||||
|
|
||||||
process.exitCode = 1
|
process.exitCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
const users = await fs.readdir('users')
|
||||||
const users = await fs.readdir('users')
|
|
||||||
|
|
||||||
for (const user of users) {
|
for await (const user of users) {
|
||||||
if (getExtension(user) !== 'json') {
|
if (getExtension(user) !== 'json') {
|
||||||
await report(`${user} is not a json file`, async () => {
|
await report(`${user} is not a json file`, async () => {
|
||||||
await fs.unlink(user)
|
await fs.unlink(user)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDomainId(path.base(user))) {
|
if (!isDomainId(path.base(user))) {
|
||||||
await report(`${user} is not a valid domain id.`)
|
await report(`${user} is not a valid domain id.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await fs.readFile(path.join('users', user), 'utf8')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fs.readFile(path.join('users', user), 'utf8')
|
const parsedData = JSON.parse(data)
|
||||||
|
|
||||||
try {
|
if (!parsedData.locked && !parsedData.copyright) {
|
||||||
const parsedData = JSON.parse(data)
|
report(`Copyright not specified in ${user}`)
|
||||||
|
|
||||||
if (!parsedData.locked && !parsedData.copyright) {
|
|
||||||
report(`Copyright not specified in ${user}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedData.version) {
|
|
||||||
await report(`Version tag found in ${user}`, async () => {
|
|
||||||
delete parsedData.version
|
|
||||||
await writeJsonFile(path.join('users', user), parsedData, { indent: 2 })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is.string(parsedData.gravatar)) {
|
|
||||||
await report(`Gravatar boolean encoded as string found in ${user}`, async () => {
|
|
||||||
parsedData.gravatar = parsedData.gravatar === 'true'
|
|
||||||
const stringified = `${JSON.stringify(parsedData, 0, 2)}\n`
|
|
||||||
await fs.writeFile(path.join('users', user), stringified)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch ({ message }) {
|
|
||||||
report(`Invalid JSON in ${user} (${message})`)
|
|
||||||
}
|
}
|
||||||
} catch ({ message }) {
|
|
||||||
report(`Unable to read ${user} (${message})`)
|
if (parsedData.version) {
|
||||||
|
await report(`Version tag found in ${user}`, async () => {
|
||||||
|
delete parsedData.version
|
||||||
|
await writeJsonFile(path.join('users', user), parsedData, {indent: 2})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is.string(parsedData.gravatar)) {
|
||||||
|
await report(`Gravatar boolean encoded as string found in ${user}`, async () => {
|
||||||
|
parsedData.gravatar = parsedData.gravatar === 'true'
|
||||||
|
const stringified = `${JSON.stringify(parsedData, 0, 2)}\n`
|
||||||
|
await fs.writeFile(path.join('users', user), stringified)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch ({message}) {
|
||||||
|
report(`Invalid JSON in ${user} (${message})`)
|
||||||
|
}
|
||||||
|
} catch ({message}) {
|
||||||
|
report(`Unable to read ${user} (${message})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const themes = await fs.readdir('themes')
|
||||||
|
|
||||||
|
for await (const theme of themes) {
|
||||||
|
if (getExtension(theme) === 'css') {
|
||||||
|
try {
|
||||||
|
const cssData = await fs.readFile(path.join('themes', theme), 'utf8')
|
||||||
|
try {
|
||||||
|
parseCSS(cssData)
|
||||||
|
} catch ({message}) {
|
||||||
|
report(`Invalid CSS in ${theme} (${message})`)
|
||||||
|
}
|
||||||
|
} catch ({message}) {
|
||||||
|
report(`Unable to read ${theme} (${message})`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const themes = await fs.readdir('themes')
|
|
||||||
|
|
||||||
for (const theme of themes) {
|
|
||||||
if (getExtension(theme) === 'css') {
|
|
||||||
try {
|
|
||||||
const cssData = await fs.readFile(path.join('themes', theme), 'utf8')
|
|
||||||
try {
|
|
||||||
CSS.parse(cssData)
|
|
||||||
} catch ({ message }) {
|
|
||||||
report(`Invalid CSS in ${theme} (${message})`)
|
|
||||||
}
|
|
||||||
} catch ({ message }) {
|
|
||||||
report(`Unable to read ${theme} (${message})`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
|
||||||
BIN
yarn.lock
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in a new issue