* feat: big badge configuration

* fix: remove google fonts

* fix(plugins, server): remove cdn

* ci: fix format ignore

* feat(server): json configuration

* feat: add search route for adding in browser

* fix: add arm arch

* fix: docker action

* fix: engines fallback

* doc: change logo

* doc: fix logo margin

* doc: update version

* doc: change layout

* dev: add dependabot groups

* dev: fix dependabot and format
This commit is contained in:
Artemy Egorov 2024-07-24 10:45:59 +03:00 committed by GitHub
parent d0bc4ff518
commit 572531a02d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 2200 additions and 1025 deletions

View File

@ -1,2 +1,3 @@
node_modules
dist
packages/**/dist/**/*
packages/**/node_modules/**/*

View File

@ -10,3 +10,7 @@ updates:
target-branch: 'dev'
schedule:
interval: 'weekly'
groups:
deps:
patterns:
- '*'

View File

@ -1,53 +0,0 @@
#
name: Create and publish a Docker image
# Configures this workflow to run every time a change is pushed to the branch called `release`.
on:
push:
branches:
- main
- dev
release:
types:
- published
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

50
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: Create and publish a Docker image
on:
push:
branches:
- main
- dev
release:
types:
- published
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64

View File

@ -1,2 +1,4 @@
pnpm-lock.yaml
packages/**/dist/**/*
packages/**/node_modules/**/*
packages/server/static/third-party/**/*

View File

@ -1,13 +1,16 @@
<h1 align="center">
<a href="https://github.com/TxtDot/txtdot"><img src="https://github.com/TxtDot/.github/blob/main/imgs/TXTDot%20gh.png?raw=true" alt="txt." width="200"></a>
<br>
<a href="https://txtdot.github.io/documentation"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-green"></a>
<a href="https://github.com/TxtDot/instances"><img alt="Instances" src="https://img.shields.io/badge/Instances-green"></a>
<br>
<a href="https://github.com/TxtDot/txtdot/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/txtdot/txtdot?color=blue"></a>
<a href="https://github.com/TxtDot/txtdot/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release"></a>
<a href="https://matrix.to/#/#txtdot:matrix.org"><img alt="Matrix chat" src="https://img.shields.io/badge/chat-matrix-blue"></a>
</h1>
<div align="center">
![txtdot logo](https://github.com/TxtDot/.github/raw/main/imgs/txtdot.png)
<a href="https://txtdot.github.io/documentation"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-blue"></a>
<a href="https://github.com/TxtDot/instances"><img alt="Instances" src="https://img.shields.io/badge/Instances-blue"></a>
<br>
<a href="https://github.com/TxtDot/txtdot/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/txtdot/txtdot?color=blue"></a>
<a href="https://github.com/TxtDot/txtdot/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release"></a>
# txtdot
</div>
HTTP proxy that parses only text, links and pictures from pages
reducing internet traffic, removing ads and heavy scripts.

View File

@ -20,14 +20,12 @@ function Highlighter({ content }: { content: string }) {
return (
<>
<style>
@import
"https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-light.min.css";
@import
"https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-dark.min.css"
screen and (prefers-color-scheme: dark);
@import "/static/third-party/styles/atom-one-light.min.css"; @import
"/static/third-party/styles/atom-one-dark.min.css" screen and
(prefers-color-scheme: dark);
</style>
<script
src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"
src="/static/third-party/scripts/highlight.min.js"
type="text/javascript"
/>
<script>hljs.highlightAll();</script>

View File

@ -1,6 +1,6 @@
{
"name": "@txtdot/server",
"version": "1.8.0",
"version": "1.9.0",
"private": true,
"description": "txtdot is an HTTP proxy that parses only text, links and pictures from pages reducing internet bandwidth usage, removing ads and heavy scripts",
"main": "dist/app.js",
@ -12,7 +12,7 @@
"license": "MIT",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npm run clean-css && copyfiles ./templates/*.ejs ./templates/**/*.ejs .env ./dist/ && tsc",
"build": "npm run clean-css && copyfiles ./templates/*.ejs ./templates/**/*.ejs ./static/third-party/* ./static/third-party/**/* .env ./dist/ && tsc",
"start": "cd ./dist && node ./src/app.js",
"clean-css": "cleancss --batch static/*.css -o dist/static --batch-suffix \"\"",
"dev": "tsc-watch --onSuccess \"node ./dist/src/app.js\""
@ -21,7 +21,7 @@
"@fastify/one-line-logger": "^1.3.0",
"@fastify/static": "^7.0.3",
"@fastify/swagger": "^8.14.0",
"@fastify/swagger-ui": "^3.0.0",
"@fastify/swagger-ui": "^4.0.0",
"@fastify/view": "^9.0.0",
"@txtdot/plugins": "workspace:*",
"@txtdot/sdk": "workspace:*",
@ -32,7 +32,7 @@
"iconv-lite": "^0.6.3",
"ip-range-check": "^0.2.0",
"isomorphic-dompurify": "^2.10.0",
"json-schema-to-ts": "^3.0.1",
"json-schema-to-ts": "^3.1.0",
"linkedom": "^0.18.0",
"micromatch": "^4.0.5",
"sharp": "^0.33.3"

View File

@ -127,7 +127,9 @@ export class Distributor {
}
}
return await this.engines_fallback[0].handle(input);
return await this.engines_fallback[this.engines_fallback.length - 1].handle(
input
);
}
async processMiddlewares(

View File

@ -6,11 +6,65 @@ import { indexSchema } from '../../types/requests/browser';
import config from '../../config';
export default async function configurationRoute(fastify: FastifyInstance) {
fastify.get('/configuration', { schema: indexSchema }, async (_, reply) => {
return reply.view('/templates/configuration.ejs', {
engines: distributor.engines_fallback,
middlewares: distributor.middles_fallback,
config,
});
});
fastify.get(
'/configuration',
{ schema: indexSchema },
async (request, reply) => {
return reply.view('/templates/configuration.ejs', {
engines: distributor.engines_fallback,
middlewares: distributor.middles_fallback,
config,
base_url: `(${request.protocol}) - ${request.hostname}`,
});
}
);
fastify.get(
'/configuration/json',
{ schema: indexSchema },
async (request, reply) => {
reply.header('content-type', 'application/json');
return {
protocol: request.protocol,
hostname: request.hostname,
version: config.package.version,
...{ ...config.env, host: undefined, port: undefined },
engines: distributor.engines_fallback.map((engine) => {
return {
...engine,
routes: engine.routes.map((route) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (route.route as any).spec;
}),
};
}),
middlewares: distributor.middles_fallback.map((middleware) => {
return {
...middleware,
routes: middleware.middles.map((middle) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (middle.route as any).spec;
}),
middles: undefined,
};
}),
routes: [...config.dyn.routes].map((route) => {
return route;
}),
};
}
);
fastify.get(
'/configuration/badges/big',
{ schema: indexSchema },
async (request, reply) => {
reply.header('content-type', 'image/svg+xml');
return reply.view('/templates/big_badge.ejs', {
config,
base_url: `(${request.protocol}) - ${request.hostname}`,
});
}
);
}

View File

@ -1,6 +1,13 @@
import { FastifyInstance } from 'fastify';
import { redirectSchema, IRedirectSchema } from '../../types/requests/browser';
import {
redirectSchema,
IRedirectSchema,
ISearchSchema,
searchSchema,
} from '../../types/requests/browser';
import config from '../../config';
export default async function redirectRoute(fastify: FastifyInstance) {
fastify.get<IRedirectSchema>(
@ -17,4 +24,20 @@ export default async function redirectRoute(fastify: FastifyInstance) {
);
}
);
if (config.env.third_party.searx_url) {
fastify.get<ISearchSchema>(
'/search',
{ schema: searchSchema },
async (request, reply) => {
reply.redirect(
`/get?url=${encodeURIComponent(
config.env.third_party.searx_url +
'/search?q=' +
encodeURIComponent(request.query.q)
)}`
);
}
);
}
}

View File

@ -1,18 +1,15 @@
import { FastifySchema } from 'fastify';
import { engineList } from '../../plugin_manager';
import { FromSchema } from 'json-schema-to-ts';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
export interface IGetSchema {
Querystring: IGetQuerySchema;
export interface Query<T> {
Querystring: T;
}
export interface SQuery<T extends JSONSchema> {
Querystring: FromSchema<T>;
}
export interface IProxySchema {
Querystring: IProxyQuerySchema;
}
export interface IRedirectSchema {
Querystring: IRedirectQuerySchema;
}
// #region Query Schemas
export const redirectQuerySchema = {
type: 'object',
@ -27,11 +24,16 @@ export const redirectQuerySchema = {
'^(?!url).*$': { type: 'string' },
},
} as const;
export type IRedirectQuerySchema = {
url: string;
[key: string]: string;
};
export const searchQuerySchema = {
type: 'object',
required: ['q'],
properties: {
q: {
type: 'string',
description: 'Search query',
},
},
} as const;
export const getQuerySchema = {
type: 'object',
required: ['url'],
@ -51,8 +53,6 @@ export const getQuerySchema = {
},
},
} as const;
export type IGetQuerySchema = FromSchema<typeof getQuerySchema>;
export const proxyQuerySchema = {
type: 'object',
required: ['url'],
@ -63,28 +63,47 @@ export const proxyQuerySchema = {
},
},
} as const;
export type IProxyQuerySchema = FromSchema<typeof proxyQuerySchema>;
// #endregion
// #region Interfaces for typed requests
export type IGetSchema = SQuery<typeof getQuerySchema>;
export type IProxySchema = SQuery<typeof proxyQuerySchema>;
export type IRedirectSchema = Query<{
url: string;
[key: string]: string;
}>;
export type ISearchSchema = SQuery<typeof searchQuerySchema>;
// #endregion
// #region Schemas for fastify
export const indexSchema = {
hide: true,
produces: ['text/html'],
};
export const redirectSchema: FastifySchema = {
description: 'Universal redirection page',
hide: true,
querystring: redirectQuerySchema,
};
export const searchSchema: FastifySchema = {
description: 'Search page',
hide: true,
querystring: searchQuerySchema,
};
export const GetSchema: FastifySchema = {
description: 'Get page',
hide: true,
querystring: getQuerySchema,
produces: ['text/html', 'text/plain'],
};
export const ProxySchema: FastifySchema = {
description: 'Proxy resource',
hide: true,
querystring: proxyQuerySchema,
};
// #endregion

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

View File

@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

View File

@ -0,0 +1,52 @@
<%
const version = config.package.version;
const engines_count = config.plugin.engines.length;
const middlewares_count = config.plugin.middlewares.length;
const search = !!config.env.third_party.searx_url ? 'enabled' : 'disabled'
%>
<svg width="550" height="240" viewBox="0 0 550 240" fill="none"
xmlns="http://www.w3.org/2000/svg">
<rect width="550" height="240" rx="45" fill="#9D9D9D"/>
<rect x="116" y="11" width="423" height="66" rx="33" fill="#606060"/>
<rect x="11" y="87" width="528" height="66" rx="33" fill="#606060"/>
<rect x="18" y="92" width="110" height="56" rx="28" fill="#0B79D5"/>
<rect x="11" y="163" width="528" height="66" rx="33" fill="#606060"/>
<g clip-path="url(#clip0_2_2)">
<mask id="mask0_2_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="11" y="11" width="181" height="66">
<path d="M158.877 11H44.123C25.8297 11 11 25.7746 11 44C11 62.2254 25.8297 77 44.123 77H158.877C177.17 77 192 62.2254 192 44C192 25.7746 177.17 11 158.877 11Z" fill="white"/>
</mask>
<g mask="url(#mask0_2_2)">
<path d="M158.877 11H44.123C25.8297 11 11 25.7746 11 44C11 62.2254 25.8297 77 44.123 77H158.877C177.17 77 192 62.2254 192 44C192 25.7746 177.17 11 158.877 11Z" fill="#222222"/>
<path d="M60.2479 60.6991C61.5873 60.6991 62.8731 60.5923 64.1054 60.3788C65.3376 60.1119 66.5699 59.765 67.8021 59.338V68.2252C66.5163 68.8124 64.909 69.2928 62.9803 69.6664C61.1051 70.0401 59.0425 70.2269 56.7923 70.2269C54.167 70.2269 51.8097 69.7999 49.7202 68.9458C47.6843 68.0918 46.0503 66.6239 44.818 64.5422C43.6394 62.4605 43.05 59.5248 43.05 55.735V34.6778H37.3442V29.6336L43.934 25.6304L47.3897 16.4228H55.0243V25.7104H67.32V34.6778H55.0243V55.735C55.0243 57.3897 55.5064 58.6441 56.4708 59.4981C57.4352 60.2988 58.6942 60.6991 60.2479 60.6991ZM85.6104 47.0879L71.4663 25.7104H85.0478L93.5664 39.6418L102.165 25.7104H115.747L101.442 47.0879L116.39 69.4262H102.808L93.5664 54.454L84.3245 69.4262H70.743L85.6104 47.0879ZM141.554 60.6991C142.893 60.6991 144.179 60.5923 145.411 60.3788C146.643 60.1119 147.876 59.765 149.108 59.338V68.2252C147.822 68.8124 146.215 69.2928 144.286 69.6664C142.411 70.0401 140.348 70.2269 138.098 70.2269C135.473 70.2269 133.115 69.7999 131.026 68.9458C128.99 68.0918 127.356 66.6239 126.124 64.5422C124.945 62.4605 124.356 59.5248 124.356 55.735V34.6778H118.65V29.6336L125.24 25.6304L128.695 16.4228H136.33V25.7104H148.626V34.6778H136.33V55.735C136.33 57.3897 136.812 58.6441 137.777 59.4981C138.741 60.2988 140 60.6991 141.554 60.6991Z" fill="#EEEEEE"/>
<path d="M156.228 63.8216C156.228 61.3663 156.897 59.6582 158.237 58.6974C159.576 57.6833 161.21 57.1762 163.139 57.1762C165.014 57.1762 166.621 57.6833 167.961 58.6974C169.3 59.6582 169.97 61.3663 169.97 63.8216C169.97 66.1702 169.3 67.8783 167.961 68.9458C166.621 69.96 165.014 70.4671 163.139 70.4671C161.21 70.4671 159.576 69.96 158.237 68.9458C156.897 67.8783 156.228 66.1702 156.228 63.8216Z" fill="#33A3FF"/>
</g>
</g>
<text fill="white" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="25" font-weight="900" letter-spacing="0em">
<tspan x="203" y="53.0909"><%= base_url %></tspan>
</text>
<text fill="white" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="25" font-weight="900" letter-spacing="0em">
<tspan x="222" y="129.091">Search - <%= search %></tspan>
</text>
<text fill="white" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="25" font-weight="900" letter-spacing="0em">
<tspan x="35" y="129.091">v<%= version %></tspan>
</text>
<text fill="white" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="25" font-weight="900" letter-spacing="0em">
<tspan x="81" y="205.091">Engines: <%= engines_count %></tspan>
</text>
<text fill="white" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="25" font-weight="900" letter-spacing="0em">
<tspan x="277" y="205.091">Middlewares: <%= middlewares_count %></tspan>
</text>
<defs>
<clipPath id="clip0_2_2">
<rect width="181" height="66" fill="white" transform="translate(11 11)"/>
<style>
@font-face {
font-family: Inter;
src: url(static/third-party/fonts/Inter-Bold.woff2);
}
</style>
</clipPath>
</defs>
</svg>

View File

@ -1,4 +1,4 @@
<% search = config.env.third_party.searx_url %>
<% const search = config.env.third_party.searx_url %>
<%
@ -12,14 +12,8 @@ if (search) {
<span>Search</span>
</label>
<form action="/redirect" method="get" class="input-grid main-form-search">
<div class="input">
<input type="text" name="q" id="search" placeholder="Search">
</div>
<div class="input">
<input type="submit" id="submit" class="button" value="Go">
</div>
<input type="hidden" name="url" value="<%= config.env.third_party.searx_url %>/search"/>
<form action="/search" method="get" class="input-grid main-form-search">
<%- include("./search-form.ejs") %>
</form>
<%
}
@ -38,11 +32,15 @@ if (search) {
<label for="engine">Engine</label>
<select name="engine">
<option value="" selected>Default</option>
<% engineList.forEach((engine) => { %>
<option value="<%= engine %>">
<%= engine %>
</option>
<% }) %>
<%
engineList.forEach((engine) => {
%>
<option value="<%= engine %>">
<%= engine %>
</option>
<%
})
%>
</select>
</div>
<div class="input">

View File

@ -4,11 +4,9 @@
<%
if (config.env.third_party.searx_url) {
%>
<form class="form-search" action="/redirect" method="get">
<input type="text" name="q" id="search" placeholder="Search">
<input class="button" type="submit" value="Go"/>
<input type="hidden" name="url" value="<%= config.env.third_party.searx_url %>/search"/>
</form>
<form class="form-search" action="/search" method="get">
<%- include("./search-form.ejs") %>
</form>
<%
}
%>

View File

@ -0,0 +1,2 @@
<input type="text" name="q" id="search" placeholder="Search">
<input class="button" type="submit" value="Go"/>

View File

@ -31,6 +31,9 @@
</div>
<p><%= config.package.description %></p>
</header>
<div>
<%- include("./big_badge.ejs", {base_url, config}) %>
</div>
<div class="configuration">
<h2>Configuration</h2>
<pre> version: <%= config.package.version %><%= to_pretty(config.env) %></pre>

File diff suppressed because it is too large Load Diff