This commit is contained in:
Egor Aristov 2025-02-28 14:02:58 +03:00
parent 97a4d30475
commit 67c6bd9856
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
14 changed files with 6446 additions and 72 deletions

13
Makefile Normal file
View File

@ -0,0 +1,13 @@
PROTOBUF_TAGGER_PATH := ${GOPATH}/pkg/mod/github.com/srikrsna/protoc-gen-gotag@v1.0.2
all:
js_proto:
protoc -I=. -I=${PROTOBUF_TAGGER_PATH} --ts_out=./frontend/wizard-vue/src/urlmaker ./proto/specs.proto
sed -i '1 i //@ts-nocheck' ./frontend/wizard-vue/src/urlmaker/google/protobuf/descriptor.ts
go_proto:
protoc -I=. -I=${PROTOBUF_TAGGER_PATH} --go_out=. ./proto/specs.proto
protoc -I=. -I=${PROTOBUF_TAGGER_PATH} --gotag_out=. ./proto/specs.proto
proto: js_proto go_proto

View File

@ -50,7 +50,9 @@ func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
if !cfg.Debug {
e.Use(middleware.Recover())
}
setIPExtractor(e, cfg)

View File

@ -10,12 +10,14 @@
"dependencies": {
"@kalimahapps/vue-icons": "^1.7.1",
"es-toolkit": "^1.32.0",
"google-protobuf": "^3.21.4",
"pinia": "^2.3.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",
"@types/google-protobuf": "^3.15.12",
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^10.1.0",
@ -1664,6 +1666,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/google-protobuf": {
"version": "3.15.12",
"resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz",
"integrity": "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -3274,6 +3283,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/google-protobuf": {
"version": "3.21.4",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz",
"integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==",
"license": "(BSD-3-Clause AND Apache-2.0)"
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",

View File

@ -15,12 +15,14 @@
"dependencies": {
"@kalimahapps/vue-icons": "^1.7.1",
"es-toolkit": "^1.32.0",
"google-protobuf": "^3.21.4",
"pinia": "^2.3.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.0",
"@types/google-protobuf": "^3.15.12",
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/eslint-config-prettier": "^10.1.0",

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
import type {Specs} from "@/urlmaker/specs.ts";
import {b64decode, b64encode, compress, decompress, decompressString} from "@/urlmaker/utils.ts";
import {rssalchemy as pb} from '@/urlmaker/proto/specs.ts';
const apiBase = import.meta.env.VITE_API_BASE || document.location.origin;
const renderEndpoint = '/api/v1/render/'; // trailing slash
@ -23,7 +25,7 @@ export async function decodePreset(preset: string): Promise<Specs> {
}
export async function decodeSpecsPart(encodedData: string): Promise<Specs> {
console.log('Data len=' + encodedData.length);
console.log('Decoded data len=' + encodedData.length);
const m = encodedData.match(/(\d*):?([A-Za-z0-9+/=]+)/);
if(!m) {
throw 'Regex failed';
@ -33,11 +35,17 @@ export async function decodeSpecsPart(encodedData: string): Promise<Specs> {
encodedData = m[2];
let buf = b64decode(encodedData);
if (version === 0) {
const jsonData = await decompress(buf);
return JSON.parse(jsonData);
switch (version) {
case 0:
const jsonData = await decompressString(buf);
return JSON.parse(jsonData);
case 1:
const data = await decompress(buf);
//@ts-ignore
return pb.Specs.deserializeBinary(data).toObject();
default:
throw 'Unknown version'
}
throw 'Unknown version'
}
export async function encodeUrl(specs: Specs): Promise<string> {
@ -49,48 +57,15 @@ export async function encodePreset(specs: Specs): Promise<string> {
}
export async function encodeSpecsPart(specs: Specs): Promise<string> {
const jsonData = JSON.stringify(specs);
const buf = await compress(jsonData);
const encodedData = b64encode(buf);
console.log('Data len=' + encodedData.length);
const version = 0;
const pbSpecs = pb.Specs.fromObject(specs);
let data = pbSpecs.serializeBinary();
data = await compress(data);
const encodedData = b64encode(data);
console.log('Encoded data len=' + encodedData.length);
const version = 1;
return `${version}:${encodedData}`;
}
export function getScreenshotUrl(url: string): string {
return `${apiBase}${screenshotEndpoint}?url=${encodeURIComponent(url)}`;
}
function b64encode(buf: Uint8Array): string {
// @ts-ignore
const b64str = btoa(String.fromCharCode.apply(null, buf));
// @ts-ignore
return b64str.replaceAll('=', '');
}
function b64decode(s: string): Uint8Array {
return Uint8Array.from(atob(s), c => c.charCodeAt(0));
}
async function compress(s: string): Promise<Uint8Array> {
let byteArray = new TextEncoder().encode(s);
let cs = new CompressionStream('deflate-raw');
let writer = cs.writable.getWriter();
// noinspection ES6MissingAwait
writer.write(byteArray);
// noinspection ES6MissingAwait
writer.close();
let response = new Response(cs.readable);
return new Uint8Array(await response.arrayBuffer());
}
async function decompress(buf: Uint8Array): Promise<string> {
let ds = new DecompressionStream('deflate-raw');
let writer = ds.writable.getWriter();
// noinspection ES6MissingAwait
writer.write(buf);
// noinspection ES6MissingAwait
writer.close();
let response = new Response(ds.readable);
return response.text();
}

View File

@ -0,0 +1,283 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 5.29.3
* source: proto/specs.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as dependency_1 from "./../tagger/tagger";
import * as pb_1 from "google-protobuf";
export namespace rssalchemy {
export class Specs extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
url?: string;
selector_post?: string;
selector_title?: string;
selector_link?: string;
selector_description?: string;
selector_author?: string;
selector_created?: string;
selector_content?: string;
selector_enclosure?: string;
cache_lifetime?: string;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("url" in data && data.url != undefined) {
this.url = data.url;
}
if ("selector_post" in data && data.selector_post != undefined) {
this.selector_post = data.selector_post;
}
if ("selector_title" in data && data.selector_title != undefined) {
this.selector_title = data.selector_title;
}
if ("selector_link" in data && data.selector_link != undefined) {
this.selector_link = data.selector_link;
}
if ("selector_description" in data && data.selector_description != undefined) {
this.selector_description = data.selector_description;
}
if ("selector_author" in data && data.selector_author != undefined) {
this.selector_author = data.selector_author;
}
if ("selector_created" in data && data.selector_created != undefined) {
this.selector_created = data.selector_created;
}
if ("selector_content" in data && data.selector_content != undefined) {
this.selector_content = data.selector_content;
}
if ("selector_enclosure" in data && data.selector_enclosure != undefined) {
this.selector_enclosure = data.selector_enclosure;
}
if ("cache_lifetime" in data && data.cache_lifetime != undefined) {
this.cache_lifetime = data.cache_lifetime;
}
}
}
get url() {
return pb_1.Message.getFieldWithDefault(this, 1, "") as string;
}
set url(value: string) {
pb_1.Message.setField(this, 1, value);
}
get selector_post() {
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
}
set selector_post(value: string) {
pb_1.Message.setField(this, 2, value);
}
get selector_title() {
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
}
set selector_title(value: string) {
pb_1.Message.setField(this, 3, value);
}
get selector_link() {
return pb_1.Message.getFieldWithDefault(this, 4, "") as string;
}
set selector_link(value: string) {
pb_1.Message.setField(this, 4, value);
}
get selector_description() {
return pb_1.Message.getFieldWithDefault(this, 5, "") as string;
}
set selector_description(value: string) {
pb_1.Message.setField(this, 5, value);
}
get selector_author() {
return pb_1.Message.getFieldWithDefault(this, 6, "") as string;
}
set selector_author(value: string) {
pb_1.Message.setField(this, 6, value);
}
get selector_created() {
return pb_1.Message.getFieldWithDefault(this, 7, "") as string;
}
set selector_created(value: string) {
pb_1.Message.setField(this, 7, value);
}
get selector_content() {
return pb_1.Message.getFieldWithDefault(this, 8, "") as string;
}
set selector_content(value: string) {
pb_1.Message.setField(this, 8, value);
}
get selector_enclosure() {
return pb_1.Message.getFieldWithDefault(this, 9, "") as string;
}
set selector_enclosure(value: string) {
pb_1.Message.setField(this, 9, value);
}
get cache_lifetime() {
return pb_1.Message.getFieldWithDefault(this, 10, "") as string;
}
set cache_lifetime(value: string) {
pb_1.Message.setField(this, 10, value);
}
static fromObject(data: {
url?: string;
selector_post?: string;
selector_title?: string;
selector_link?: string;
selector_description?: string;
selector_author?: string;
selector_created?: string;
selector_content?: string;
selector_enclosure?: string;
cache_lifetime?: string;
}): Specs {
const message = new Specs({});
if (data.url != null) {
message.url = data.url;
}
if (data.selector_post != null) {
message.selector_post = data.selector_post;
}
if (data.selector_title != null) {
message.selector_title = data.selector_title;
}
if (data.selector_link != null) {
message.selector_link = data.selector_link;
}
if (data.selector_description != null) {
message.selector_description = data.selector_description;
}
if (data.selector_author != null) {
message.selector_author = data.selector_author;
}
if (data.selector_created != null) {
message.selector_created = data.selector_created;
}
if (data.selector_content != null) {
message.selector_content = data.selector_content;
}
if (data.selector_enclosure != null) {
message.selector_enclosure = data.selector_enclosure;
}
if (data.cache_lifetime != null) {
message.cache_lifetime = data.cache_lifetime;
}
return message;
}
toObject() {
const data: {
url?: string;
selector_post?: string;
selector_title?: string;
selector_link?: string;
selector_description?: string;
selector_author?: string;
selector_created?: string;
selector_content?: string;
selector_enclosure?: string;
cache_lifetime?: string;
} = {};
if (this.url != null) {
data.url = this.url;
}
if (this.selector_post != null) {
data.selector_post = this.selector_post;
}
if (this.selector_title != null) {
data.selector_title = this.selector_title;
}
if (this.selector_link != null) {
data.selector_link = this.selector_link;
}
if (this.selector_description != null) {
data.selector_description = this.selector_description;
}
if (this.selector_author != null) {
data.selector_author = this.selector_author;
}
if (this.selector_created != null) {
data.selector_created = this.selector_created;
}
if (this.selector_content != null) {
data.selector_content = this.selector_content;
}
if (this.selector_enclosure != null) {
data.selector_enclosure = this.selector_enclosure;
}
if (this.cache_lifetime != null) {
data.cache_lifetime = this.cache_lifetime;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.url.length)
writer.writeString(1, this.url);
if (this.selector_post.length)
writer.writeString(2, this.selector_post);
if (this.selector_title.length)
writer.writeString(3, this.selector_title);
if (this.selector_link.length)
writer.writeString(4, this.selector_link);
if (this.selector_description.length)
writer.writeString(5, this.selector_description);
if (this.selector_author.length)
writer.writeString(6, this.selector_author);
if (this.selector_created.length)
writer.writeString(7, this.selector_created);
if (this.selector_content.length)
writer.writeString(8, this.selector_content);
if (this.selector_enclosure.length)
writer.writeString(9, this.selector_enclosure);
if (this.cache_lifetime.length)
writer.writeString(10, this.cache_lifetime);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Specs {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Specs();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.url = reader.readString();
break;
case 2:
message.selector_post = reader.readString();
break;
case 3:
message.selector_title = reader.readString();
break;
case 4:
message.selector_link = reader.readString();
break;
case 5:
message.selector_description = reader.readString();
break;
case 6:
message.selector_author = reader.readString();
break;
case 7:
message.selector_created = reader.readString();
break;
case 8:
message.selector_content = reader.readString();
break;
case 9:
message.selector_enclosure = reader.readString();
break;
case 10:
message.cache_lifetime = reader.readString();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): Specs {
return Specs.deserialize(bytes);
}
}
}

View File

@ -0,0 +1,7 @@
/**
* Generated by the protoc-gen-ts. DO NOT EDIT!
* compiler version: 5.29.3
* source: tagger/tagger.proto
* git: https://github.com/thesayyn/protoc-gen-ts */
import * as dependency_1 from "./../google/protobuf/descriptor";
export namespace tagger { }

View File

@ -0,0 +1,41 @@
export async function compress(s: string|Uint8Array): Promise<Uint8Array> {
if(typeof s === 'string') {
s = new TextEncoder().encode(s);
}
let cs = new CompressionStream('deflate-raw');
let writer = cs.writable.getWriter();
// noinspection ES6MissingAwait
writer.write(s);
// noinspection ES6MissingAwait
writer.close();
let response = new Response(cs.readable);
return new Uint8Array(await response.arrayBuffer());
}
export async function decompress(buf: Uint8Array): Promise<Uint8Array> {
let ds = new DecompressionStream('deflate-raw');
let writer = ds.writable.getWriter();
// noinspection ES6MissingAwait
writer.write(buf);
// noinspection ES6MissingAwait
writer.close();
let response = new Response(ds.readable);
return response.bytes();
}
export async function decompressString(buf: Uint8Array): Promise<string> {
const binary = await decompress(buf);
// @ts-ignore
return String.fromCharCode.apply(null, binary);
}
export function b64encode(buf: Uint8Array): string {
// @ts-ignore
const b64str = btoa(String.fromCharCode.apply(null, buf));
// @ts-ignore
return b64str.replaceAll('=', '');
}
export function b64decode(s: string): Uint8Array {
return Uint8Array.from(atob(s), c => c.charCodeAt(0));
}

3
go.mod
View File

@ -16,8 +16,10 @@ require (
github.com/nats-io/nats.go v1.38.0
github.com/playwright-community/playwright-go v0.5001.0
github.com/redis/go-redis/v9 v9.7.0
github.com/srikrsna/protoc-gen-gotag v1.0.2
github.com/stretchr/testify v1.10.0
golang.org/x/time v0.8.0
google.golang.org/protobuf v1.35.2
)
require (
@ -97,7 +99,6 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
)

12
go.sum
View File

@ -92,6 +92,7 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
@ -226,6 +227,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -242,6 +244,7 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/markusmobius/go-dateparser v1.2.3 h1:TvrsIvr5uk+3v6poDjaicnAFJ5IgtFHgLiuMY2Eb7Nw=
@ -302,6 +305,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/playwright-community/playwright-go v0.5001.0 h1:EY3oB+rU9cUp6CLHguWE8VMZTwAg+83Yyb7dQqEmGLg=
github.com/playwright-community/playwright-go v0.5001.0/go.mod h1:kBNWs/w2aJ2ZUp1wEOOFLXgOqvppFngM5OS+qyhl+ZM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -332,6 +336,10 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/srikrsna/protoc-gen-gotag v1.0.2 h1:4okv8GlbVbvmL678VX0AobxaMkERlBbHvgWhUnbcrPM=
github.com/srikrsna/protoc-gen-gotag v1.0.2/go.mod h1:HiXK5kcp/ZRnNPahuJm3tzfGDoD8xzvLNdg5/PYKq7Q=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@ -342,6 +350,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
@ -383,6 +392,7 @@ go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -473,6 +483,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
@ -506,6 +517,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=

View File

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"github.com/egor3f/rssalchemy/internal/adapters"
"github.com/egor3f/rssalchemy/internal/api/http/pb"
"github.com/egor3f/rssalchemy/internal/models"
"github.com/egor3f/rssalchemy/internal/validators"
"github.com/go-playground/validator/v10"
@ -16,6 +17,7 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"golang.org/x/time/rate"
"google.golang.org/protobuf/proto"
"html"
"io"
"net/url"
@ -64,19 +66,6 @@ func (h *Handler) SetupRoutes(g *echo.Group) {
g.GET("/screenshot", h.handlePageScreenshot)
}
type Specs struct {
URL string `json:"URL" validate:"url"`
SelectorPost string `json:"selector_post" validate:"selector"`
SelectorTitle string `json:"selector_title" validate:"selector"`
SelectorLink string `json:"selector_link" validate:"selector"`
SelectorDescription string `json:"selector_description" validate:"omitempty,selector"`
SelectorAuthor string `json:"selector_author" validate:"selector"`
SelectorCreated string `json:"selector_created" validate:"selector"`
SelectorContent string `json:"selector_content" validate:"omitempty,selector"`
SelectorEnclosure string `json:"selector_enclosure" validate:"selector"`
CacheLifetime string `json:"cache_lifetime"`
}
func (h *Handler) handleRender(c echo.Context) error {
specsParam := c.Param("specs")
specs, err := h.decodeSpecs(specsParam)
@ -86,7 +75,7 @@ func (h *Handler) handleRender(c echo.Context) error {
task := models.Task{
TaskType: models.TaskTypeExtract,
URL: specs.URL,
URL: specs.Url,
SelectorPost: specs.SelectorPost,
SelectorTitle: specs.SelectorTitle,
SelectorLink: specs.SelectorLink,
@ -199,37 +188,44 @@ func (h *Handler) checkRateLimit(c echo.Context) bool {
return limiter.Allow()
}
func (h *Handler) decodeSpecs(specsParam string) (Specs, error) {
func (h *Handler) decodeSpecs(specsParam string) (*pb.Specs, error) {
var err error
version := 0
paramSplit := strings.Split(specsParam, ":")
if len(paramSplit) == 2 {
version, err = strconv.Atoi(paramSplit[0])
if err != nil {
return Specs{}, fmt.Errorf("invalid version: %s", paramSplit[0])
return nil, fmt.Errorf("invalid version: %s", paramSplit[0])
}
specsParam = paramSplit[1]
}
if version != 0 {
return Specs{}, fmt.Errorf("unknown version: %d", version)
}
decodedSpecsParam, err := base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(specsParam)
if err != nil {
return Specs{}, fmt.Errorf("failed to decode specs: %w", err)
return nil, fmt.Errorf("failed to decode specs: %w", err)
}
rc := flate.NewReader(bytes.NewReader(decodedSpecsParam))
decodedSpecsParam, err = io.ReadAll(rc)
if err != nil {
return Specs{}, fmt.Errorf("failed to unzip specs: %w", err)
return nil, fmt.Errorf("failed to unzip specs: %w", err)
}
var specs Specs
if err := json.Unmarshal(decodedSpecsParam, &specs); err != nil {
return Specs{}, fmt.Errorf("failed to unmarshal specs: %w", err)
specs := &pb.Specs{}
switch version {
case 0:
if err := json.Unmarshal(decodedSpecsParam, specs); err != nil {
return nil, fmt.Errorf("failed to unmarshal json specs: %w", err)
}
case 1:
if err := proto.Unmarshal(decodedSpecsParam, specs); err != nil {
return nil, fmt.Errorf("failed to unmarshal proto specs: %w", err)
}
default:
return nil, fmt.Errorf("unknown version: %d", version)
}
if err := h.validate.Struct(specs); err != nil {
return Specs{}, fmt.Errorf("specs are invalid: %w", err)
return nil, fmt.Errorf("specs are invalid: %w", err)
}
return specs, nil
}

View File

@ -0,0 +1,253 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.4
// protoc v5.29.3
// source: proto/specs.proto
package pb
import (
_ "github.com/srikrsna/protoc-gen-gotag/tagger"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Specs struct {
state protoimpl.MessageState `protogen:"open.v1"`
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url" validate:"url"`
SelectorPost string `protobuf:"bytes,2,opt,name=selector_post,json=selectorPost,proto3" json:"selector_post" validate:"selector"`
SelectorTitle string `protobuf:"bytes,3,opt,name=selector_title,json=selectorTitle,proto3" json:"selector_title" validate:"selector"`
SelectorLink string `protobuf:"bytes,4,opt,name=selector_link,json=selectorLink,proto3" json:"selector_link" validate:"selector"`
SelectorDescription string `protobuf:"bytes,5,opt,name=selector_description,json=selectorDescription,proto3" json:"selector_description" validate:"omitempty,selector"`
SelectorAuthor string `protobuf:"bytes,6,opt,name=selector_author,json=selectorAuthor,proto3" json:"selector_author" validate:"selector"`
SelectorCreated string `protobuf:"bytes,7,opt,name=selector_created,json=selectorCreated,proto3" json:"selector_created" validate:"selector"`
SelectorContent string `protobuf:"bytes,8,opt,name=selector_content,json=selectorContent,proto3" json:"selector_content" validate:"omitempty,selector"`
SelectorEnclosure string `protobuf:"bytes,9,opt,name=selector_enclosure,json=selectorEnclosure,proto3" json:"selector_enclosure" validate:"selector"`
CacheLifetime string `protobuf:"bytes,10,opt,name=cache_lifetime,json=cacheLifetime,proto3" json:"cache_lifetime"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Specs) Reset() {
*x = Specs{}
mi := &file_proto_specs_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Specs) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Specs) ProtoMessage() {}
func (x *Specs) ProtoReflect() protoreflect.Message {
mi := &file_proto_specs_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Specs.ProtoReflect.Descriptor instead.
func (*Specs) Descriptor() ([]byte, []int) {
return file_proto_specs_proto_rawDescGZIP(), []int{0}
}
func (x *Specs) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *Specs) GetSelectorPost() string {
if x != nil {
return x.SelectorPost
}
return ""
}
func (x *Specs) GetSelectorTitle() string {
if x != nil {
return x.SelectorTitle
}
return ""
}
func (x *Specs) GetSelectorLink() string {
if x != nil {
return x.SelectorLink
}
return ""
}
func (x *Specs) GetSelectorDescription() string {
if x != nil {
return x.SelectorDescription
}
return ""
}
func (x *Specs) GetSelectorAuthor() string {
if x != nil {
return x.SelectorAuthor
}
return ""
}
func (x *Specs) GetSelectorCreated() string {
if x != nil {
return x.SelectorCreated
}
return ""
}
func (x *Specs) GetSelectorContent() string {
if x != nil {
return x.SelectorContent
}
return ""
}
func (x *Specs) GetSelectorEnclosure() string {
if x != nil {
return x.SelectorEnclosure
}
return ""
}
func (x *Specs) GetCacheLifetime() string {
if x != nil {
return x.CacheLifetime
}
return ""
}
var File_proto_specs_proto protoreflect.FileDescriptor
var file_proto_specs_proto_rawDesc = string([]byte{
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x72, 0x73, 0x73, 0x61, 0x6c, 0x63, 0x68, 0x65, 0x6d, 0x79, 0x1a,
0x13, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x06, 0x0a, 0x05, 0x53, 0x70, 0x65, 0x63, 0x73, 0x12, 0x30,
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0x9a, 0x84, 0x9e,
0x03, 0x19, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x75, 0x72, 0x6c, 0x22, 0x20, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x75, 0x72, 0x6c, 0x22, 0x52, 0x03, 0x75, 0x72, 0x6c,
0x12, 0x52, 0x0a, 0x0d, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x73,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2d, 0x9a, 0x84, 0x9e, 0x03, 0x28, 0x6a, 0x73,
0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x73,
0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x50, 0x6f, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x0e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2e, 0x9a, 0x84,
0x9e, 0x03, 0x29, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
0x72, 0x5f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0d, 0x73, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x73,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x42, 0x2d, 0x9a, 0x84, 0x9e, 0x03, 0x28, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x20, 0x76, 0x61,
0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x22, 0x52, 0x0c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x12,
0x71, 0x0a, 0x14, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x3e, 0x9a,
0x84, 0x9e, 0x03, 0x39, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d,
0x70, 0x74, 0x79, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x13, 0x73,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x61,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0x9a, 0x84, 0x9e,
0x03, 0x2a, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0e, 0x73, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x5b, 0x0a, 0x10,
0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0x9a, 0x84, 0x9e, 0x03, 0x2b, 0x6a, 0x73, 0x6f,
0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x65, 0x0a, 0x10, 0x73, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20,
0x01, 0x28, 0x09, 0x42, 0x3a, 0x9a, 0x84, 0x9e, 0x03, 0x35, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22,
0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69, 0x74,
0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52,
0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x12, 0x61, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x65, 0x6e, 0x63,
0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x32, 0x9a, 0x84,
0x9e, 0x03, 0x2d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x22, 0x20, 0x76, 0x61, 0x6c,
0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22,
0x52, 0x11, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x63, 0x6c, 0x6f, 0x73,
0x75, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6c, 0x69, 0x66,
0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0x9a, 0x84, 0x9e,
0x03, 0x15, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6c, 0x69,
0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4c, 0x69,
0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x16, 0x5a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x62, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
file_proto_specs_proto_rawDescOnce sync.Once
file_proto_specs_proto_rawDescData []byte
)
func file_proto_specs_proto_rawDescGZIP() []byte {
file_proto_specs_proto_rawDescOnce.Do(func() {
file_proto_specs_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_specs_proto_rawDesc), len(file_proto_specs_proto_rawDesc)))
})
return file_proto_specs_proto_rawDescData
}
var file_proto_specs_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_proto_specs_proto_goTypes = []any{
(*Specs)(nil), // 0: rssalchemy.Specs
}
var file_proto_specs_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_proto_specs_proto_init() }
func file_proto_specs_proto_init() {
if File_proto_specs_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_specs_proto_rawDesc), len(file_proto_specs_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proto_specs_proto_goTypes,
DependencyIndexes: file_proto_specs_proto_depIdxs,
MessageInfos: file_proto_specs_proto_msgTypes,
}.Build()
File_proto_specs_proto = out.File
file_proto_specs_proto_goTypes = nil
file_proto_specs_proto_depIdxs = nil
}

20
proto/specs.proto Normal file
View File

@ -0,0 +1,20 @@
syntax = "proto3";
package rssalchemy;
import "tagger/tagger.proto";
option go_package = "internal/api/http/pb";
message Specs {
string url = 1 [(tagger.tags) = "json:\"url\" validate:\"url\""];
string selector_post = 2 [(tagger.tags) = "json:\"selector_post\" validate:\"selector\""];
string selector_title = 3 [(tagger.tags) = "json:\"selector_title\" validate:\"selector\""];
string selector_link = 4 [(tagger.tags) = "json:\"selector_link\" validate:\"selector\""];
string selector_description = 5 [(tagger.tags) = "json:\"selector_description\" validate:\"omitempty,selector\""];
string selector_author = 6 [(tagger.tags) = "json:\"selector_author\" validate:\"selector\""];
string selector_created = 7 [(tagger.tags) = "json:\"selector_created\" validate:\"selector\""];
string selector_content = 8 [(tagger.tags) = "json:\"selector_content\" validate:\"omitempty,selector\""];
string selector_enclosure = 9 [(tagger.tags) = "json:\"selector_enclosure\" validate:\"selector\""];
string cache_lifetime = 10 [(tagger.tags) = "json:\"cache_lifetime\""];
}