Compare commits
8 Commits
5302417824
...
4ff6322dd9
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ff6322dd9 | |||
| 23a466cd72 | |||
| 305cb6eca6 | |||
| 07f32f8e95 | |||
| 0e32cc3f17 | |||
| f220f9d9d7 | |||
| 4302176348 | |||
| 01c28aead2 |
5
frontend/wizard-vue/src/common/enum.ts
Normal file
5
frontend/wizard-vue/src/common/enum.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type EnumValue = {
|
||||||
|
label: string
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
export type Enum = EnumValue[]
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import TextField from "@/components/TextField.vue";
|
import TextField from "@/components/inputs/TextField.vue";
|
||||||
import Btn from "@/components/Btn.vue";
|
import Btn from "@/components/Btn.vue";
|
||||||
import {onMounted, onUnmounted, ref, watch} from "vue";
|
import {onMounted, onUnmounted, ref, watch} from "vue";
|
||||||
import Modal from "@/components/Modal.vue";
|
import Modal from "@/components/Modal.vue";
|
||||||
|
|||||||
@ -1,45 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type {Field} from "@/urlmaker/specs.ts";
|
|
||||||
import {getCurrentInstance, onMounted, useTemplateRef} from "vue";
|
|
||||||
|
|
||||||
const {field, focused} = defineProps<{
|
|
||||||
field: Field,
|
|
||||||
focused?: boolean,
|
|
||||||
}>();
|
|
||||||
const id = 'field' + getCurrentInstance()?.uid;
|
|
||||||
const model = defineModel();
|
|
||||||
|
|
||||||
const inputRef = useTemplateRef('field');
|
|
||||||
onMounted(() => {
|
|
||||||
if(focused) inputRef.value?.focus();
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="field">
|
|
||||||
<div class="label"><label :for="id">{{ field.label }}</label></div>
|
|
||||||
<div class="input">
|
|
||||||
<input :type="field.input_type" :name="field.name" :id="id" v-model="model" ref="field"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
div.field {
|
|
||||||
margin: 0 0 8px 0;
|
|
||||||
}
|
|
||||||
div.label {
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
div.input {
|
|
||||||
margin: 2px 0 0 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,12 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {fields, InputType} from '@/urlmaker/specs.ts';
|
import {fields, InputType, type SpecField} from '@/urlmaker/specs.ts';
|
||||||
import TextField from "@/components/TextField.vue";
|
import TextField from "@/components/inputs/TextField.vue";
|
||||||
|
import RadioButtons from "@/components/inputs/RadioButtons.vue";
|
||||||
import {useWizardStore} from "@/stores/wizard.ts";
|
import {useWizardStore} from "@/stores/wizard.ts";
|
||||||
import {groupBy} from "es-toolkit";
|
|
||||||
|
|
||||||
const store = useWizardStore();
|
const store = useWizardStore();
|
||||||
|
|
||||||
const groups = groupBy(fields, item => item.group || '');
|
const groups: SpecField[][] = [];
|
||||||
|
for (const field of fields) {
|
||||||
|
if(groups.length === 0 || groups[groups.length - 1][0].group != field.group) {
|
||||||
|
groups.push([field]);
|
||||||
|
} else {
|
||||||
|
groups[groups.length - 1].push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -16,12 +23,22 @@ const groups = groupBy(fields, item => item.group || '');
|
|||||||
<template v-for="field in group">
|
<template v-for="field in group">
|
||||||
<TextField
|
<TextField
|
||||||
v-if="field.input_type === InputType.Url || field.input_type === InputType.Text"
|
v-if="field.input_type === InputType.Url || field.input_type === InputType.Text"
|
||||||
|
v-show="!field.show_if || field.show_if(store.specs)"
|
||||||
:name="field.name"
|
:name="field.name"
|
||||||
:label="field.label"
|
:label="field.label"
|
||||||
:input_type="field.input_type"
|
:input_type="field.input_type"
|
||||||
:model-value="store.specs[field.name]"
|
:model-value="store.specs[field.name]"
|
||||||
@update:model-value="event => store.updateSpec(field.name, event)"
|
@update:model-value="event => store.updateSpec(field.name, event)"
|
||||||
></TextField>
|
></TextField>
|
||||||
|
<RadioButtons
|
||||||
|
v-if="field.input_type === InputType.Radio"
|
||||||
|
v-show="!field.show_if || field.show_if(store.specs)"
|
||||||
|
:name="field.name"
|
||||||
|
:label="field.label"
|
||||||
|
:values="field.enum!"
|
||||||
|
:model-value="store.specs[field.name]"
|
||||||
|
@update:model-value="event => store.updateSpec(field.name, event)"
|
||||||
|
></RadioButtons>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -29,6 +46,6 @@ const groups = groupBy(fields, item => item.group || '');
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
div.group {
|
div.group {
|
||||||
margin: 6px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
51
frontend/wizard-vue/src/components/inputs/RadioButtons.vue
Normal file
51
frontend/wizard-vue/src/components/inputs/RadioButtons.vue
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {getCurrentInstance} from "vue";
|
||||||
|
|
||||||
|
import type {Enum} from "@/common/enum.ts";
|
||||||
|
|
||||||
|
const {name, label, values} = defineProps<{
|
||||||
|
name: string
|
||||||
|
label: string,
|
||||||
|
values: Enum,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const componentId = 'field' + getCurrentInstance()?.uid;
|
||||||
|
|
||||||
|
const model = defineModel();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="field">
|
||||||
|
<span class="field-label"><label>{{ label }}</label></span>
|
||||||
|
<template class="value" v-for="enumValue in values">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
:name="name"
|
||||||
|
:value="enumValue.value"
|
||||||
|
:id="`${componentId}_${enumValue.value}`"
|
||||||
|
v-model="model"
|
||||||
|
/>
|
||||||
|
<label class="radio-label" :for="`${componentId}_${enumValue.value}`">{{ enumValue.label }}</label>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
div.field {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
.field-label {
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.radio-label {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
input, .radio-label, .field-label {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {emptySpecs, type SpecField, fields, type Specs} from "@/urlmaker/specs.ts";
|
import {defaultSpecs, type SpecField, fields, type Specs, type SpecValue} from "@/urlmaker/specs.ts";
|
||||||
import {computed, reactive} from "vue";
|
import {computed, reactive} from "vue";
|
||||||
import {debounce} from "es-toolkit";
|
import {debounce} from "es-toolkit";
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ const LOCAL_STORAGE_KEY = 'rssalchemy_store_wizard';
|
|||||||
export const useWizardStore = defineStore('wizard', () => {
|
export const useWizardStore = defineStore('wizard', () => {
|
||||||
|
|
||||||
const locStorageContent = localStorage.getItem(LOCAL_STORAGE_KEY);
|
const locStorageContent = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
const initialSpecs = locStorageContent ? JSON.parse(locStorageContent) as Specs : emptySpecs;
|
const initialSpecs = locStorageContent ? JSON.parse(locStorageContent) as Specs : defaultSpecs;
|
||||||
|
|
||||||
const specs = reactive(Object.assign({}, initialSpecs));
|
const specs = reactive(Object.assign({}, initialSpecs));
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ export const useWizardStore = defineStore('wizard', () => {
|
|||||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(specs));
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(specs));
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
function updateSpec(fieldName: keyof Specs, newValue: string) {
|
function updateSpec(fieldName: keyof Specs, newValue: SpecValue) {
|
||||||
specs[fieldName] = newValue;
|
(specs as Record<keyof Specs, SpecValue>)[fieldName] = newValue;
|
||||||
updateLocalStorage();
|
updateLocalStorage();
|
||||||
}
|
}
|
||||||
function updateSpecs(newValue: Specs) {
|
function updateSpecs(newValue: Specs) {
|
||||||
@ -31,7 +31,7 @@ export const useWizardStore = defineStore('wizard', () => {
|
|||||||
updateLocalStorage();
|
updateLocalStorage();
|
||||||
}
|
}
|
||||||
function reset() {
|
function reset() {
|
||||||
Object.assign(specs, emptySpecs);
|
Object.assign(specs, defaultSpecs);
|
||||||
updateLocalStorage();
|
updateLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type {Specs} from "@/urlmaker/specs.ts";
|
import {type Specs} from "@/urlmaker/specs.ts";
|
||||||
import {b64decode, b64encode, compress, decompress, decompressString} from "@/urlmaker/utils.ts";
|
import {b64decode, b64encode, compress, decompress, decompressString} from "@/urlmaker/utils.ts";
|
||||||
import {rssalchemy as pb} from '@/urlmaker/proto/specs.ts';
|
import {rssalchemy, rssalchemy as pb} from '@/urlmaker/proto/specs.ts';
|
||||||
|
|
||||||
const apiBase = import.meta.env.VITE_API_BASE || document.location.origin;
|
const apiBase = import.meta.env.VITE_API_BASE || document.location.origin;
|
||||||
const renderEndpoint = '/api/v1/render/'; // trailing slash
|
const renderEndpoint = '/api/v1/render/'; // trailing slash
|
||||||
@ -57,7 +57,7 @@ export async function encodePreset(specs: Specs): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function encodeSpecsPart(specs: Specs): Promise<string> {
|
export async function encodeSpecsPart(specs: Specs): Promise<string> {
|
||||||
const pbSpecs = pb.Specs.fromObject(specs);
|
const pbSpecs = pb.Specs.fromObject(specs as ReturnType<rssalchemy.Specs['toObject']>);
|
||||||
let data = pbSpecs.serializeBinary();
|
let data = pbSpecs.serializeBinary();
|
||||||
data = await compress(data);
|
data = await compress(data);
|
||||||
const encodedData = b64encode(data);
|
const encodedData = b64encode(data);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export namespace rssalchemy {
|
|||||||
selector_author?: string;
|
selector_author?: string;
|
||||||
selector_created?: string;
|
selector_created?: string;
|
||||||
created_extract_from?: ExtractFrom;
|
created_extract_from?: ExtractFrom;
|
||||||
|
created_attribute_name?: string;
|
||||||
selector_content?: string;
|
selector_content?: string;
|
||||||
selector_enclosure?: string;
|
selector_enclosure?: string;
|
||||||
cache_lifetime?: string;
|
cache_lifetime?: string;
|
||||||
@ -51,6 +52,9 @@ export namespace rssalchemy {
|
|||||||
if ("created_extract_from" in data && data.created_extract_from != undefined) {
|
if ("created_extract_from" in data && data.created_extract_from != undefined) {
|
||||||
this.created_extract_from = data.created_extract_from;
|
this.created_extract_from = data.created_extract_from;
|
||||||
}
|
}
|
||||||
|
if ("created_attribute_name" in data && data.created_attribute_name != undefined) {
|
||||||
|
this.created_attribute_name = data.created_attribute_name;
|
||||||
|
}
|
||||||
if ("selector_content" in data && data.selector_content != undefined) {
|
if ("selector_content" in data && data.selector_content != undefined) {
|
||||||
this.selector_content = data.selector_content;
|
this.selector_content = data.selector_content;
|
||||||
}
|
}
|
||||||
@ -110,6 +114,12 @@ export namespace rssalchemy {
|
|||||||
set created_extract_from(value: ExtractFrom) {
|
set created_extract_from(value: ExtractFrom) {
|
||||||
pb_1.Message.setField(this, 11, value);
|
pb_1.Message.setField(this, 11, value);
|
||||||
}
|
}
|
||||||
|
get created_attribute_name() {
|
||||||
|
return pb_1.Message.getFieldWithDefault(this, 12, "") as string;
|
||||||
|
}
|
||||||
|
set created_attribute_name(value: string) {
|
||||||
|
pb_1.Message.setField(this, 12, value);
|
||||||
|
}
|
||||||
get selector_content() {
|
get selector_content() {
|
||||||
return pb_1.Message.getFieldWithDefault(this, 8, "") as string;
|
return pb_1.Message.getFieldWithDefault(this, 8, "") as string;
|
||||||
}
|
}
|
||||||
@ -137,6 +147,7 @@ export namespace rssalchemy {
|
|||||||
selector_author?: string;
|
selector_author?: string;
|
||||||
selector_created?: string;
|
selector_created?: string;
|
||||||
created_extract_from?: ExtractFrom;
|
created_extract_from?: ExtractFrom;
|
||||||
|
created_attribute_name?: string;
|
||||||
selector_content?: string;
|
selector_content?: string;
|
||||||
selector_enclosure?: string;
|
selector_enclosure?: string;
|
||||||
cache_lifetime?: string;
|
cache_lifetime?: string;
|
||||||
@ -166,6 +177,9 @@ export namespace rssalchemy {
|
|||||||
if (data.created_extract_from != null) {
|
if (data.created_extract_from != null) {
|
||||||
message.created_extract_from = data.created_extract_from;
|
message.created_extract_from = data.created_extract_from;
|
||||||
}
|
}
|
||||||
|
if (data.created_attribute_name != null) {
|
||||||
|
message.created_attribute_name = data.created_attribute_name;
|
||||||
|
}
|
||||||
if (data.selector_content != null) {
|
if (data.selector_content != null) {
|
||||||
message.selector_content = data.selector_content;
|
message.selector_content = data.selector_content;
|
||||||
}
|
}
|
||||||
@ -187,6 +201,7 @@ export namespace rssalchemy {
|
|||||||
selector_author?: string;
|
selector_author?: string;
|
||||||
selector_created?: string;
|
selector_created?: string;
|
||||||
created_extract_from?: ExtractFrom;
|
created_extract_from?: ExtractFrom;
|
||||||
|
created_attribute_name?: string;
|
||||||
selector_content?: string;
|
selector_content?: string;
|
||||||
selector_enclosure?: string;
|
selector_enclosure?: string;
|
||||||
cache_lifetime?: string;
|
cache_lifetime?: string;
|
||||||
@ -215,6 +230,9 @@ export namespace rssalchemy {
|
|||||||
if (this.created_extract_from != null) {
|
if (this.created_extract_from != null) {
|
||||||
data.created_extract_from = this.created_extract_from;
|
data.created_extract_from = this.created_extract_from;
|
||||||
}
|
}
|
||||||
|
if (this.created_attribute_name != null) {
|
||||||
|
data.created_attribute_name = this.created_attribute_name;
|
||||||
|
}
|
||||||
if (this.selector_content != null) {
|
if (this.selector_content != null) {
|
||||||
data.selector_content = this.selector_content;
|
data.selector_content = this.selector_content;
|
||||||
}
|
}
|
||||||
@ -246,6 +264,8 @@ export namespace rssalchemy {
|
|||||||
writer.writeString(7, this.selector_created);
|
writer.writeString(7, this.selector_created);
|
||||||
if (this.created_extract_from != ExtractFrom.InnerText)
|
if (this.created_extract_from != ExtractFrom.InnerText)
|
||||||
writer.writeEnum(11, this.created_extract_from);
|
writer.writeEnum(11, this.created_extract_from);
|
||||||
|
if (this.created_attribute_name.length)
|
||||||
|
writer.writeString(12, this.created_attribute_name);
|
||||||
if (this.selector_content.length)
|
if (this.selector_content.length)
|
||||||
writer.writeString(8, this.selector_content);
|
writer.writeString(8, this.selector_content);
|
||||||
if (this.selector_enclosure.length)
|
if (this.selector_enclosure.length)
|
||||||
@ -285,6 +305,9 @@ export namespace rssalchemy {
|
|||||||
case 11:
|
case 11:
|
||||||
message.created_extract_from = reader.readEnum();
|
message.created_extract_from = reader.readEnum();
|
||||||
break;
|
break;
|
||||||
|
case 12:
|
||||||
|
message.created_attribute_name = reader.readString();
|
||||||
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
message.selector_content = reader.readString();
|
message.selector_content = reader.readString();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -1,14 +1,30 @@
|
|||||||
import {
|
import {
|
||||||
|
validateAttribute,
|
||||||
validateDuration,
|
validateDuration,
|
||||||
validateSelector,
|
validateSelector,
|
||||||
validateUrl,
|
validateUrl,
|
||||||
type validator
|
type validator
|
||||||
} from "@/urlmaker/validators.ts";
|
} from "@/urlmaker/validators.ts";
|
||||||
import {rssalchemy} from "@/urlmaker/proto/specs.ts";
|
import {rssalchemy} from "@/urlmaker/proto/specs.ts";
|
||||||
|
import type {Enum} from "@/common/enum.ts";
|
||||||
|
|
||||||
|
export const defaultSpecs = {
|
||||||
|
url: '',
|
||||||
|
selector_post: '',
|
||||||
|
selector_title: '',
|
||||||
|
selector_link: '',
|
||||||
|
selector_description: '',
|
||||||
|
selector_author: '',
|
||||||
|
selector_content: '',
|
||||||
|
selector_enclosure: '',
|
||||||
|
selector_created: '',
|
||||||
|
created_extract_from: rssalchemy.ExtractFrom.InnerText,
|
||||||
|
created_attribute_name: '',
|
||||||
|
cache_lifetime: '10m'
|
||||||
|
};
|
||||||
|
|
||||||
export type SpecKey = ReturnType<rssalchemy.Specs['toObject']>;
|
|
||||||
export type SpecValue = string | number;
|
export type SpecValue = string | number;
|
||||||
export type Specs = {[k in keyof SpecKey]: SpecValue};
|
export type Specs = typeof defaultSpecs;
|
||||||
|
|
||||||
export enum InputType {
|
export enum InputType {
|
||||||
Url = 'url',
|
Url = 'url',
|
||||||
@ -19,9 +35,8 @@ export enum InputType {
|
|||||||
export interface SpecField {
|
export interface SpecField {
|
||||||
name: keyof Specs
|
name: keyof Specs
|
||||||
input_type: InputType
|
input_type: InputType
|
||||||
enum_values?: {[k: number]: string}
|
enum?: Enum,
|
||||||
label: string
|
label: string
|
||||||
default: SpecValue
|
|
||||||
validate: validator
|
validate: validator
|
||||||
required?: boolean
|
required?: boolean
|
||||||
group?: string
|
group?: string
|
||||||
@ -33,7 +48,6 @@ export const fields: SpecField[] = [
|
|||||||
name: 'url',
|
name: 'url',
|
||||||
input_type: InputType.Url,
|
input_type: InputType.Url,
|
||||||
label: 'URL of page for converting',
|
label: 'URL of page for converting',
|
||||||
default: '',
|
|
||||||
validate: validateUrl,
|
validate: validateUrl,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
@ -41,35 +55,30 @@ export const fields: SpecField[] = [
|
|||||||
name: 'selector_post',
|
name: 'selector_post',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for post',
|
label: 'CSS Selector for post',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'selector_title',
|
name: 'selector_title',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for title',
|
label: 'CSS Selector for title',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'selector_link',
|
name: 'selector_link',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for link',
|
label: 'CSS Selector for link',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'selector_description',
|
name: 'selector_description',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for description',
|
label: 'CSS Selector for description',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'selector_author',
|
name: 'selector_author',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for author',
|
label: 'CSS Selector for author',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -77,45 +86,47 @@ export const fields: SpecField[] = [
|
|||||||
name: 'selector_created',
|
name: 'selector_created',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for created date',
|
label: 'CSS Selector for created date',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
group: 'created',
|
group: 'created',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'created_extract_from',
|
name: 'created_extract_from',
|
||||||
input_type: InputType.Radio,
|
input_type: InputType.Radio,
|
||||||
enum_values: rssalchemy.ExtractFrom,
|
enum: [
|
||||||
|
{label: 'Inner Text', value: rssalchemy.ExtractFrom.InnerText},
|
||||||
|
{label: 'Attribute', value: rssalchemy.ExtractFrom.Attribute},
|
||||||
|
],
|
||||||
label: 'Extract from',
|
label: 'Extract from',
|
||||||
default: rssalchemy.ExtractFrom.InnerText,
|
|
||||||
validate: value => Object.values(rssalchemy.ExtractFrom).includes(value),
|
validate: value => Object.values(rssalchemy.ExtractFrom).includes(value),
|
||||||
group: 'created',
|
group: 'created',
|
||||||
show_if: specs => !!specs.selector_created,
|
show_if: specs => !!specs.selector_created,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'created_attribute_name',
|
||||||
|
input_type: InputType.Text,
|
||||||
|
label: 'Attribute name',
|
||||||
|
validate: validateAttribute,
|
||||||
|
show_if: specs =>
|
||||||
|
!!specs.selector_created && specs.created_extract_from === rssalchemy.ExtractFrom.Attribute,
|
||||||
|
group: 'created',
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'selector_content',
|
name: 'selector_content',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for content',
|
label: 'CSS Selector for content',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'selector_enclosure',
|
name: 'selector_enclosure',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'CSS Selector for enclosure (e.g. image url)',
|
label: 'CSS Selector for enclosure (e.g. image url)',
|
||||||
default: '',
|
|
||||||
validate: validateSelector,
|
validate: validateSelector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cache_lifetime',
|
name: 'cache_lifetime',
|
||||||
input_type: InputType.Text,
|
input_type: InputType.Text,
|
||||||
label: 'Cache lifetime (format examples: 10s, 1m, 2h)',
|
label: 'Cache lifetime (format examples: 10s, 1m, 2h)',
|
||||||
default: '10m',
|
|
||||||
validate: validateDuration,
|
validate: validateDuration,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const emptySpecs = fields.reduce((o, f) => {
|
|
||||||
o[f.name] = f.default;
|
|
||||||
return o
|
|
||||||
}, {} as Specs);
|
|
||||||
|
|||||||
@ -26,6 +26,10 @@ export function validateSelector(s: SpecValue): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateAttribute(s: SpecValue): boolean {
|
||||||
|
return /([^\t\n\f \/>"'=]+)/.test(s as string);
|
||||||
|
}
|
||||||
|
|
||||||
export function validateDuration(s: SpecValue): boolean {
|
export function validateDuration(s: SpecValue): boolean {
|
||||||
return /^\d+[smh]$/.test(s as string);
|
return /^\d+[smh]$/.test(s as string);
|
||||||
}
|
}
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -5,6 +5,7 @@ go 1.23.2
|
|||||||
toolchain go1.24.0
|
toolchain go1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/AdguardTeam/urlfilter v0.20.0
|
||||||
github.com/ericchiang/css v1.4.0
|
github.com/ericchiang/css v1.4.0
|
||||||
github.com/felixge/fgprof v0.9.5
|
github.com/felixge/fgprof v0.9.5
|
||||||
github.com/go-playground/validator/v10 v10.23.0
|
github.com/go-playground/validator/v10 v10.23.0
|
||||||
@ -27,7 +28,6 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdguardTeam/golibs v0.29.0 // indirect
|
github.com/AdguardTeam/golibs v0.29.0 // indirect
|
||||||
github.com/AdguardTeam/urlfilter v0.20.0 // indirect
|
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
github.com/alessandro-c/gomemcached-lock v1.0.0 // indirect
|
github.com/alessandro-c/gomemcached-lock v1.0.0 // indirect
|
||||||
github.com/armon/go-metrics v0.4.1 // indirect
|
github.com/armon/go-metrics v0.4.1 // indirect
|
||||||
@ -112,4 +112,4 @@ require (
|
|||||||
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
|
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/ericchiang/css => github.com/egor3f/css v0.0.0-20250115151140-52c8c51084e5
|
replace github.com/ericchiang/css => github.com/egor3f/css v0.0.0-20250507004805-bfefe22b74a4
|
||||||
|
|||||||
23
go.sum
23
go.sum
@ -93,8 +93,8 @@ github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusg
|
|||||||
github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/egor3f/css v0.0.0-20250115151140-52c8c51084e5 h1:rqpFTlOasDC5OXOf8NA+XEdjPClBnPGxsQ484OXx6l4=
|
github.com/egor3f/css v0.0.0-20250507004805-bfefe22b74a4 h1:hDS4GEOnI8sYW2BqAzN9EA9Ks/3yOQGhOoO4/sjpDzw=
|
||||||
github.com/egor3f/css v0.0.0-20250115151140-52c8c51084e5/go.mod h1:sVSdL+MFR9Q4cKJMQzpIkHIDOLiK+7Wmjjhq7D+MubA=
|
github.com/egor3f/css v0.0.0-20250507004805-bfefe22b74a4/go.mod h1:sVSdL+MFR9Q4cKJMQzpIkHIDOLiK+7Wmjjhq7D+MubA=
|
||||||
github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg=
|
github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg=
|
||||||
github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og=
|
github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
@ -115,6 +115,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
|||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
@ -264,6 +266,8 @@ 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/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 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
|
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 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
@ -288,7 +292,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||||||
github.com/mennanov/limiters v1.11.0 h1:nTv4uSl3EAc+2fO4B3LXfxkcQe58LeKF9vmwIa3m6Lo=
|
github.com/mennanov/limiters v1.11.0 h1:nTv4uSl3EAc+2fO4B3LXfxkcQe58LeKF9vmwIa3m6Lo=
|
||||||
github.com/mennanov/limiters v1.11.0/go.mod h1:NFf49GLfiywZ4DFkqK9Ne7e+Ckwl1q0eSU+ALwSAxBk=
|
github.com/mennanov/limiters v1.11.0/go.mod h1:NFf49GLfiywZ4DFkqK9Ne7e+Ckwl1q0eSU+ALwSAxBk=
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
||||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||||
@ -337,6 +340,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||||
@ -358,6 +363,10 @@ github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 h1:AJNDS0kP60X
|
|||||||
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
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/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.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||||
@ -388,6 +397,10 @@ github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4ql
|
|||||||
github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||||
github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
|
github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
|
||||||
github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
|
github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
@ -400,6 +413,8 @@ github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK
|
|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w=
|
go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
|
go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw=
|
||||||
@ -424,8 +439,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
|||||||
@ -75,18 +75,28 @@ func (h *Handler) handleRender(c echo.Context) error {
|
|||||||
return echo.NewHTTPError(400, fmt.Errorf("decode specs: %w", err))
|
return echo.NewHTTPError(400, fmt.Errorf("decode specs: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extractFrom, ok := map[pb.ExtractFrom]models.ExtractFrom{
|
||||||
|
pb.ExtractFrom_InnerText: models.ExtractFrom_InnerText,
|
||||||
|
pb.ExtractFrom_Attribute: models.ExtractFrom_Attribute,
|
||||||
|
}[specs.CreatedExtractFrom]
|
||||||
|
if !ok {
|
||||||
|
return echo.NewHTTPError(400, "invalid extract from")
|
||||||
|
}
|
||||||
|
|
||||||
task := models.Task{
|
task := models.Task{
|
||||||
TaskType: models.TaskTypeExtract,
|
TaskType: models.TaskTypeExtract,
|
||||||
URL: specs.Url,
|
URL: specs.Url,
|
||||||
SelectorPost: specs.SelectorPost,
|
SelectorPost: specs.SelectorPost,
|
||||||
SelectorTitle: specs.SelectorTitle,
|
SelectorTitle: specs.SelectorTitle,
|
||||||
SelectorLink: specs.SelectorLink,
|
SelectorLink: specs.SelectorLink,
|
||||||
SelectorDescription: specs.SelectorDescription,
|
SelectorDescription: specs.SelectorDescription,
|
||||||
SelectorAuthor: specs.SelectorAuthor,
|
SelectorAuthor: specs.SelectorAuthor,
|
||||||
SelectorCreated: specs.SelectorCreated,
|
SelectorCreated: specs.SelectorCreated,
|
||||||
SelectorContent: specs.SelectorContent,
|
CreatedExtractFrom: extractFrom,
|
||||||
SelectorEnclosure: specs.SelectorEnclosure,
|
CreatedAttributeName: specs.CreatedAttributeName,
|
||||||
Headers: extractHeaders(c),
|
SelectorContent: specs.SelectorContent,
|
||||||
|
SelectorEnclosure: specs.SelectorEnclosure,
|
||||||
|
Headers: extractHeaders(c),
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheLifetime, err := time.ParseDuration(specs.CacheLifetime)
|
cacheLifetime, err := time.ParseDuration(specs.CacheLifetime)
|
||||||
|
|||||||
@ -69,20 +69,21 @@ func (ExtractFrom) EnumDescriptor() ([]byte, []int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Specs struct {
|
type Specs struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url" validate:"url"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
SelectorAuthor string `protobuf:"bytes,6,opt,name=selector_author,json=selectorAuthor,proto3" json:"selector_author" validate:"omitempty,selector"`
|
||||||
SelectorCreated string `protobuf:"bytes,7,opt,name=selector_created,json=selectorCreated,proto3" json:"selector_created" validate:"selector"`
|
SelectorCreated string `protobuf:"bytes,7,opt,name=selector_created,json=selectorCreated,proto3" json:"selector_created" validate:"selector"`
|
||||||
CreatedExtractFrom ExtractFrom `protobuf:"varint,11,opt,name=created_extract_from,json=createdExtractFrom,proto3,enum=rssalchemy.ExtractFrom" json:"created_extract_from"`
|
CreatedExtractFrom ExtractFrom `protobuf:"varint,11,opt,name=created_extract_from,json=createdExtractFrom,proto3,enum=rssalchemy.ExtractFrom" json:"created_extract_from"`
|
||||||
SelectorContent string `protobuf:"bytes,8,opt,name=selector_content,json=selectorContent,proto3" json:"selector_content" validate:"omitempty,selector"`
|
CreatedAttributeName string `protobuf:"bytes,12,opt,name=created_attribute_name,json=createdAttributeName,proto3" json:"created_attribute_name"`
|
||||||
SelectorEnclosure string `protobuf:"bytes,9,opt,name=selector_enclosure,json=selectorEnclosure,proto3" json:"selector_enclosure" validate:"selector"`
|
SelectorContent string `protobuf:"bytes,8,opt,name=selector_content,json=selectorContent,proto3" json:"selector_content" validate:"omitempty,selector"`
|
||||||
CacheLifetime string `protobuf:"bytes,10,opt,name=cache_lifetime,json=cacheLifetime,proto3" json:"cache_lifetime"`
|
SelectorEnclosure string `protobuf:"bytes,9,opt,name=selector_enclosure,json=selectorEnclosure,proto3" json:"selector_enclosure" validate:"selector"`
|
||||||
unknownFields protoimpl.UnknownFields
|
CacheLifetime string `protobuf:"bytes,10,opt,name=cache_lifetime,json=cacheLifetime,proto3" json:"cache_lifetime"`
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Specs) Reset() {
|
func (x *Specs) Reset() {
|
||||||
@ -171,6 +172,13 @@ func (x *Specs) GetCreatedExtractFrom() ExtractFrom {
|
|||||||
return ExtractFrom_InnerText
|
return ExtractFrom_InnerText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Specs) GetCreatedAttributeName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreatedAttributeName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Specs) GetSelectorContent() string {
|
func (x *Specs) GetSelectorContent() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.SelectorContent
|
return x.SelectorContent
|
||||||
@ -198,7 +206,7 @@ var file_proto_specs_proto_rawDesc = string([]byte{
|
|||||||
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x73, 0x2e, 0x70, 0x72,
|
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,
|
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,
|
0x13, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x07, 0x0a, 0x05, 0x53, 0x70, 0x65, 0x63, 0x73, 0x12, 0x30,
|
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x08, 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,
|
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,
|
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,
|
0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x75, 0x72, 0x6c, 0x22, 0x52, 0x03, 0x75, 0x72, 0x6c,
|
||||||
@ -225,46 +233,53 @@ var file_proto_specs_proto_rawDesc = string([]byte{
|
|||||||
0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d,
|
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,
|
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,
|
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,
|
0x6f, 0x6e, 0x12, 0x62, 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,
|
0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x39, 0x9a, 0x84, 0x9e,
|
||||||
0x03, 0x2a, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
|
0x03, 0x34, 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,
|
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,
|
0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c, 0x73, 0x65, 0x6c,
|
||||||
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x5b, 0x0a, 0x10,
|
0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
|
||||||
0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
|
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x5b, 0x0a, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0x9a, 0x84, 0x9e, 0x03, 0x2b, 0x6a, 0x73, 0x6f,
|
0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61,
|
0x42, 0x30, 0x9a, 0x84, 0x9e, 0x03, 0x2b, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c,
|
||||||
0x74, 0x65, 0x64, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73,
|
0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x20, 0x76,
|
||||||
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
|
||||||
0x6f, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x6b, 0x0a, 0x14, 0x63, 0x72, 0x65,
|
0x72, 0x22, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x72, 0x65, 0x61,
|
||||||
0x61, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x66, 0x72, 0x6f,
|
0x74, 0x65, 0x64, 0x12, 0x6b, 0x0a, 0x14, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x65,
|
||||||
0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x73, 0x73, 0x61, 0x6c, 0x63,
|
0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28,
|
||||||
0x68, 0x65, 0x6d, 0x79, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d,
|
0x0e, 0x32, 0x17, 0x2e, 0x72, 0x73, 0x73, 0x61, 0x6c, 0x63, 0x68, 0x65, 0x6d, 0x79, 0x2e, 0x45,
|
||||||
0x42, 0x20, 0x9a, 0x84, 0x9e, 0x03, 0x1b, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x72, 0x65,
|
0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x20, 0x9a, 0x84, 0x9e, 0x03,
|
||||||
0x61, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x66, 0x72, 0x6f,
|
0x1b, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x65,
|
||||||
0x6d, 0x22, 0x52, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x72, 0x61,
|
0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x52, 0x12, 0x63, 0x72,
|
||||||
0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x65, 0x0a, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
0x65, 0x61, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d,
|
||||||
0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
|
0x12, 0x58, 0x0a, 0x16, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72,
|
||||||
0x42, 0x3a, 0x9a, 0x84, 0x9e, 0x03, 0x35, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c,
|
0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x76,
|
0x42, 0x22, 0x9a, 0x84, 0x9e, 0x03, 0x1d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x72, 0x65,
|
||||||
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69, 0x74, 0x65, 0x6d, 0x70,
|
0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e,
|
||||||
0x74, 0x79, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x0f, 0x73, 0x65,
|
0x61, 0x6d, 0x65, 0x22, 0x52, 0x14, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x74,
|
||||||
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x61, 0x0a,
|
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x65, 0x0a, 0x10, 0x73, 0x65,
|
||||||
0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x73,
|
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x08,
|
||||||
0x75, 0x72, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x32, 0x9a, 0x84, 0x9e, 0x03, 0x2d,
|
0x20, 0x01, 0x28, 0x09, 0x42, 0x3a, 0x9a, 0x84, 0x9e, 0x03, 0x35, 0x6a, 0x73, 0x6f, 0x6e, 0x3a,
|
||||||
0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x65,
|
0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
|
0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x6f, 0x6d, 0x69,
|
||||||
0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x52, 0x11, 0x73,
|
0x74, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22,
|
||||||
0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65,
|
0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
0x12, 0x41, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x74, 0x69,
|
0x74, 0x12, 0x61, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x65, 0x6e,
|
||||||
0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0x9a, 0x84, 0x9e, 0x03, 0x15, 0x6a,
|
0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x32, 0x9a,
|
||||||
0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x74,
|
0x84, 0x9e, 0x03, 0x2d, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||||
0x69, 0x6d, 0x65, 0x22, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x74,
|
0x6f, 0x72, 0x5f, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x22, 0x20, 0x76, 0x61,
|
||||||
0x69, 0x6d, 0x65, 0x2a, 0x2b, 0x0a, 0x0b, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x46, 0x72,
|
0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x22, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
|
||||||
0x6f, 0x6d, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x54, 0x65, 0x78, 0x74, 0x10,
|
0x22, 0x52, 0x11, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x63, 0x6c, 0x6f,
|
||||||
0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x10, 0x01,
|
0x73, 0x75, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6c, 0x69,
|
||||||
0x42, 0x16, 0x5a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69,
|
0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0x9a, 0x84,
|
||||||
0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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, 0x2a, 0x2b, 0x0a, 0x0b, 0x45, 0x78, 0x74, 0x72, 0x61,
|
||||||
|
0x63, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x54,
|
||||||
|
0x65, 0x78, 0x74, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
|
||||||
|
0x74, 0x65, 0x10, 0x01, 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 (
|
var (
|
||||||
|
|||||||
@ -103,10 +103,11 @@ func (p *pageParser) extractPost(post playwright.Locator) (models.FeedItem, erro
|
|||||||
item.Description = newLocator(post, p.task.SelectorDescription).First().InnerText()
|
item.Description = newLocator(post, p.task.SelectorDescription).First().InnerText()
|
||||||
}
|
}
|
||||||
|
|
||||||
item.AuthorName = newLocator(post, p.task.SelectorAuthor).First().InnerText()
|
if len(p.task.SelectorAuthor) > 0 {
|
||||||
|
item.AuthorName = newLocator(post, p.task.SelectorAuthor).First().InnerText()
|
||||||
item.AuthorLink = newLocator(post, p.task.SelectorAuthor).First().GetAttribute("href")
|
item.AuthorLink = newLocator(post, p.task.SelectorAuthor).First().GetAttribute("href")
|
||||||
item.AuthorLink = absUrl(item.AuthorLink, page)
|
item.AuthorLink = absUrl(item.AuthorLink, page)
|
||||||
|
}
|
||||||
|
|
||||||
if len(p.task.SelectorContent) > 0 {
|
if len(p.task.SelectorContent) > 0 {
|
||||||
item.Content = p.extractContent(post)
|
item.Content = p.extractContent(post)
|
||||||
@ -114,7 +115,15 @@ func (p *pageParser) extractPost(post playwright.Locator) (models.FeedItem, erro
|
|||||||
|
|
||||||
item.Enclosure = newLocator(post, p.task.SelectorEnclosure).First().GetAttribute("src")
|
item.Enclosure = newLocator(post, p.task.SelectorEnclosure).First().GetAttribute("src")
|
||||||
|
|
||||||
createdDateStr := newLocator(post, p.task.SelectorCreated).First().InnerText()
|
var createdDateStr string
|
||||||
|
switch p.task.CreatedExtractFrom {
|
||||||
|
case models.ExtractFrom_InnerText:
|
||||||
|
createdDateStr = newLocator(post, p.task.SelectorCreated).First().InnerText()
|
||||||
|
case models.ExtractFrom_Attribute:
|
||||||
|
createdDateStr = newLocator(post, p.task.SelectorCreated).First().GetAttribute(p.task.CreatedAttributeName)
|
||||||
|
default:
|
||||||
|
return models.FeedItem{}, fmt.Errorf("invalid task.CreatedExtractFrom")
|
||||||
|
}
|
||||||
log.Debugf("date=%s", createdDateStr)
|
log.Debugf("date=%s", createdDateStr)
|
||||||
createdDate, err := p.dateParser.ParseDate(createdDateStr)
|
createdDate, err := p.dateParser.ParseDate(createdDateStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -13,19 +13,28 @@ const (
|
|||||||
TaskTypePageScreenshot = "page_screenshot"
|
TaskTypePageScreenshot = "page_screenshot"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ExtractFrom int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExtractFrom_InnerText ExtractFrom = 0
|
||||||
|
ExtractFrom_Attribute ExtractFrom = 1
|
||||||
|
)
|
||||||
|
|
||||||
type Task struct {
|
type Task struct {
|
||||||
// While adding new fields, dont forget to alter caching func
|
// While adding new fields, dont forget to alter caching func
|
||||||
TaskType TaskType
|
TaskType TaskType
|
||||||
URL string
|
URL string
|
||||||
SelectorPost string
|
SelectorPost string
|
||||||
SelectorTitle string
|
SelectorTitle string
|
||||||
SelectorLink string
|
SelectorLink string
|
||||||
SelectorDescription string
|
SelectorDescription string
|
||||||
SelectorAuthor string
|
SelectorAuthor string
|
||||||
SelectorCreated string
|
SelectorCreated string
|
||||||
SelectorContent string
|
CreatedExtractFrom ExtractFrom
|
||||||
SelectorEnclosure string
|
CreatedAttributeName string
|
||||||
Headers map[string]string
|
SelectorContent string
|
||||||
|
SelectorEnclosure string
|
||||||
|
Headers map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Task) CacheKey() string {
|
func (t Task) CacheKey() string {
|
||||||
|
|||||||
@ -25,4 +25,9 @@ rssalchemy:0:XY/RasMwDEV/pWgvG1QJ26N/pij2TWNqW8FWCqHs30daGN4epXMlHT1oq4kcLWZrc+O
|
|||||||
rssalchemy:1:dcyxCsIwEADQQUFwEHRw6FidhPTUsdAfcHQtFUJzaY8mJlyujZ8vuLu+4W3Po0hMNUDOuWI0hqTqgweGhM6OIQkaOGw0C/UOi6NukwvS2Nk5FUMS5eg9deUfvxSGlsoiGtVrNkrwIyoyLoT5vtftyGhfTQlzQoayq9dCHh878sPpl5DXAz5Xt6v/Ag
|
rssalchemy:1:dcyxCsIwEADQQUFwEHRw6FidhPTUsdAfcHQtFUJzaY8mJlyujZ8vuLu+4W3Po0hMNUDOuWI0hqTqgweGhM6OIQkaOGw0C/UOi6NukwvS2Nk5FUMS5eg9deUfvxSGlsoiGtVrNkrwIyoyLoT5vtftyGhfTQlzQoayq9dCHh878sPpl5DXAz5Xt6v/Ag
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Associated Press (technology)
|
||||||
|
```
|
||||||
|
rssalchemy:1:ZcuxCsIwEIBhpOAguBQXC4I4CmkUtzyCU3FyPZMjDfRyR+9QfHtHEcf/g3+1G81Eg/cgFV/aRyZvGMfKE+d3u07l2Q+QcZiZuGvHy7ecFZvwsPm3PRy3P6NLqHEuYoVrWAaNLHhfPLoEBk5YDZNLYOisEKoBybUplG/N+UQf
|
||||||
|
```
|
||||||
|
|
||||||
[^1]: To capture cookies, use [this manual](presets/cookies.md)
|
[^1]: To capture cookies, use [this manual](presets/cookies.md)
|
||||||
|
|||||||
@ -17,10 +17,11 @@ message Specs {
|
|||||||
string selector_title = 3 [(tagger.tags) = "json:\"selector_title\" 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_link = 4 [(tagger.tags) = "json:\"selector_link\" validate:\"selector\""];
|
||||||
string selector_description = 5 [(tagger.tags) = "json:\"selector_description\" validate:\"omitempty,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_author = 6 [(tagger.tags) = "json:\"selector_author\" validate:\"omitempty,selector\""];
|
||||||
|
|
||||||
string selector_created = 7 [(tagger.tags) = "json:\"selector_created\" validate:\"selector\""];
|
string selector_created = 7 [(tagger.tags) = "json:\"selector_created\" validate:\"selector\""];
|
||||||
ExtractFrom created_extract_from = 11 [(tagger.tags) = "json:\"created_extract_from\""];
|
ExtractFrom created_extract_from = 11 [(tagger.tags) = "json:\"created_extract_from\""];
|
||||||
|
string created_attribute_name = 12 [(tagger.tags) = "json:\"created_attribute_name\""];
|
||||||
|
|
||||||
string selector_content = 8 [(tagger.tags) = "json:\"selector_content\" validate:\"omitempty,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 selector_enclosure = 9 [(tagger.tags) = "json:\"selector_enclosure\" validate:\"selector\""];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user